• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "source/opt/ir_loader.h"
16 
17 #include <utility>
18 
19 #include "DebugInfo.h"
20 #include "OpenCLDebugInfo100.h"
21 #include "source/ext_inst.h"
22 #include "source/opt/log.h"
23 #include "source/opt/reflect.h"
24 #include "source/util/make_unique.h"
25 
26 static const uint32_t kExtInstSetIndex = 4;
27 static const uint32_t kLexicalScopeIndex = 5;
28 static const uint32_t kInlinedAtIndex = 6;
29 
30 namespace spvtools {
31 namespace opt {
32 
IrLoader(const MessageConsumer & consumer,Module * m)33 IrLoader::IrLoader(const MessageConsumer& consumer, Module* m)
34     : consumer_(consumer),
35       module_(m),
36       source_("<instruction>"),
37       inst_index_(0),
38       last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
39 
AddInstruction(const spv_parsed_instruction_t * inst)40 bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
41   ++inst_index_;
42   const auto opcode = static_cast<SpvOp>(inst->opcode);
43   if (IsDebugLineInst(opcode)) {
44     module()->SetContainsDebugInfo();
45     last_line_inst_.reset();
46     dbg_line_info_.push_back(
47         Instruction(module()->context(), *inst, last_dbg_scope_));
48     return true;
49   }
50 
51   // If it is a DebugScope or DebugNoScope of debug extension, we do not
52   // create a new instruction, but simply keep the information in
53   // struct DebugScope.
54   if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) {
55     const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
56     if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
57       const OpenCLDebugInfo100Instructions ext_inst_key =
58           OpenCLDebugInfo100Instructions(ext_inst_index);
59       if (ext_inst_key == OpenCLDebugInfo100DebugScope) {
60         uint32_t inlined_at = 0;
61         if (inst->num_words > kInlinedAtIndex)
62           inlined_at = inst->words[kInlinedAtIndex];
63         last_dbg_scope_ =
64             DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
65         module()->SetContainsDebugInfo();
66         return true;
67       }
68       if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) {
69         last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
70         module()->SetContainsDebugInfo();
71         return true;
72       }
73     } else {
74       const DebugInfoInstructions ext_inst_key =
75           DebugInfoInstructions(ext_inst_index);
76       if (ext_inst_key == DebugInfoDebugScope) {
77         uint32_t inlined_at = 0;
78         if (inst->num_words > kInlinedAtIndex)
79           inlined_at = inst->words[kInlinedAtIndex];
80         last_dbg_scope_ =
81             DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
82         module()->SetContainsDebugInfo();
83         return true;
84       }
85       if (ext_inst_key == DebugInfoDebugNoScope) {
86         last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
87         module()->SetContainsDebugInfo();
88         return true;
89       }
90     }
91   }
92 
93   std::unique_ptr<Instruction> spv_inst(
94       new Instruction(module()->context(), *inst, std::move(dbg_line_info_)));
95   if (!spv_inst->dbg_line_insts().empty()) {
96     if (extra_line_tracking_ &&
97         (spv_inst->dbg_line_insts().back().opcode() != SpvOpNoLine)) {
98       last_line_inst_ = std::unique_ptr<Instruction>(
99           spv_inst->dbg_line_insts().back().Clone(module()->context()));
100     }
101     dbg_line_info_.clear();
102   } else if (last_line_inst_ != nullptr) {
103     last_line_inst_->SetDebugScope(last_dbg_scope_);
104     spv_inst->dbg_line_insts().push_back(*last_line_inst_);
105   }
106 
107   const char* src = source_.c_str();
108   spv_position_t loc = {inst_index_, 0, 0};
109 
110   // Handle function and basic block boundaries first, then normal
111   // instructions.
112   if (opcode == SpvOpFunction) {
113     if (function_ != nullptr) {
114       Error(consumer_, src, loc, "function inside function");
115       return false;
116     }
117     function_ = MakeUnique<Function>(std::move(spv_inst));
118   } else if (opcode == SpvOpFunctionEnd) {
119     if (function_ == nullptr) {
120       Error(consumer_, src, loc,
121             "OpFunctionEnd without corresponding OpFunction");
122       return false;
123     }
124     if (block_ != nullptr) {
125       Error(consumer_, src, loc, "OpFunctionEnd inside basic block");
126       return false;
127     }
128     function_->SetFunctionEnd(std::move(spv_inst));
129     module_->AddFunction(std::move(function_));
130     function_ = nullptr;
131   } else if (opcode == SpvOpLabel) {
132     if (function_ == nullptr) {
133       Error(consumer_, src, loc, "OpLabel outside function");
134       return false;
135     }
136     if (block_ != nullptr) {
137       Error(consumer_, src, loc, "OpLabel inside basic block");
138       return false;
139     }
140     block_ = MakeUnique<BasicBlock>(std::move(spv_inst));
141   } else if (spvOpcodeIsBlockTerminator(opcode)) {
142     if (function_ == nullptr) {
143       Error(consumer_, src, loc, "terminator instruction outside function");
144       return false;
145     }
146     if (block_ == nullptr) {
147       Error(consumer_, src, loc, "terminator instruction outside basic block");
148       return false;
149     }
150     if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
151       spv_inst->SetDebugScope(last_dbg_scope_);
152     block_->AddInstruction(std::move(spv_inst));
153     function_->AddBasicBlock(std::move(block_));
154     block_ = nullptr;
155     last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
156     last_line_inst_.reset();
157     dbg_line_info_.clear();
158   } else {
159     if (function_ == nullptr) {  // Outside function definition
160       SPIRV_ASSERT(consumer_, block_ == nullptr);
161       if (opcode == SpvOpCapability) {
162         module_->AddCapability(std::move(spv_inst));
163       } else if (opcode == SpvOpExtension) {
164         module_->AddExtension(std::move(spv_inst));
165       } else if (opcode == SpvOpExtInstImport) {
166         module_->AddExtInstImport(std::move(spv_inst));
167       } else if (opcode == SpvOpMemoryModel) {
168         module_->SetMemoryModel(std::move(spv_inst));
169       } else if (opcode == SpvOpEntryPoint) {
170         module_->AddEntryPoint(std::move(spv_inst));
171       } else if (opcode == SpvOpExecutionMode) {
172         module_->AddExecutionMode(std::move(spv_inst));
173       } else if (IsDebug1Inst(opcode)) {
174         module_->AddDebug1Inst(std::move(spv_inst));
175       } else if (IsDebug2Inst(opcode)) {
176         module_->AddDebug2Inst(std::move(spv_inst));
177       } else if (IsDebug3Inst(opcode)) {
178         module_->AddDebug3Inst(std::move(spv_inst));
179       } else if (IsAnnotationInst(opcode)) {
180         module_->AddAnnotationInst(std::move(spv_inst));
181       } else if (IsTypeInst(opcode)) {
182         module_->AddType(std::move(spv_inst));
183       } else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
184                  opcode == SpvOpUndef) {
185         module_->AddGlobalValue(std::move(spv_inst));
186       } else if (opcode == SpvOpExtInst &&
187                  spvExtInstIsDebugInfo(inst->ext_inst_type)) {
188         module_->AddExtInstDebugInfo(std::move(spv_inst));
189       } else if (opcode == SpvOpExtInst &&
190                  spvExtInstIsNonSemantic(inst->ext_inst_type)) {
191         // If there are no functions, add the non-semantic instructions to the
192         // global values. Otherwise append it to the list of the last function.
193         auto func_begin = module_->begin();
194         auto func_end = module_->end();
195         if (func_begin == func_end) {
196           module_->AddGlobalValue(std::move(spv_inst));
197         } else {
198           (--func_end)->AddNonSemanticInstruction(std::move(spv_inst));
199         }
200       } else {
201         Errorf(consumer_, src, loc,
202                "Unhandled inst type (opcode: %d) found outside function "
203                "definition.",
204                opcode);
205         return false;
206       }
207     } else {
208       if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge)
209         last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
210       if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
211         spv_inst->SetDebugScope(last_dbg_scope_);
212       if (opcode == SpvOpExtInst &&
213           spvExtInstIsDebugInfo(inst->ext_inst_type)) {
214         const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
215         if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
216           const OpenCLDebugInfo100Instructions ext_inst_key =
217               OpenCLDebugInfo100Instructions(ext_inst_index);
218           switch (ext_inst_key) {
219             case OpenCLDebugInfo100DebugDeclare: {
220               if (block_ == nullptr)  // Inside function but outside blocks
221                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
222               else
223                 block_->AddInstruction(std::move(spv_inst));
224               break;
225             }
226             case OpenCLDebugInfo100DebugValue: {
227               if (block_ == nullptr)  // Inside function but outside blocks
228                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
229               else
230                 block_->AddInstruction(std::move(spv_inst));
231               break;
232             }
233             default: {
234               Errorf(consumer_, src, loc,
235                      "Debug info extension instruction other than DebugScope, "
236                      "DebugNoScope, DebugDeclare, and DebugValue found inside "
237                      "function",
238                      opcode);
239               return false;
240             }
241           }
242         } else {
243           const DebugInfoInstructions ext_inst_key =
244               DebugInfoInstructions(ext_inst_index);
245           switch (ext_inst_key) {
246             case DebugInfoDebugDeclare: {
247               if (block_ == nullptr)  // Inside function but outside blocks
248                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
249               else
250                 block_->AddInstruction(std::move(spv_inst));
251               break;
252             }
253             case DebugInfoDebugValue: {
254               if (block_ == nullptr)  // Inside function but outside blocks
255                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
256               else
257                 block_->AddInstruction(std::move(spv_inst));
258               break;
259             }
260             default: {
261               Errorf(consumer_, src, loc,
262                      "Debug info extension instruction other than DebugScope, "
263                      "DebugNoScope, DebugDeclare, and DebugValue found inside "
264                      "function",
265                      opcode);
266               return false;
267             }
268           }
269         }
270       } else {
271         if (block_ == nullptr) {  // Inside function but outside blocks
272           if (opcode != SpvOpFunctionParameter) {
273             Errorf(consumer_, src, loc,
274                    "Non-OpFunctionParameter (opcode: %d) found inside "
275                    "function but outside basic block",
276                    opcode);
277             return false;
278           }
279           function_->AddParameter(std::move(spv_inst));
280         } else {
281           block_->AddInstruction(std::move(spv_inst));
282         }
283       }
284     }
285   }
286   return true;
287 }
288 
289 // Resolves internal references among the module, functions, basic blocks, etc.
290 // This function should be called after adding all instructions.
EndModule()291 void IrLoader::EndModule() {
292   if (block_ && function_) {
293     // We're in the middle of a basic block, but the terminator is missing.
294     // Register the block anyway.  This lets us write tests with less
295     // boilerplate.
296     function_->AddBasicBlock(std::move(block_));
297     block_ = nullptr;
298   }
299   if (function_) {
300     // We're in the middle of a function, but the OpFunctionEnd is missing.
301     // Register the function anyway.  This lets us write tests with less
302     // boilerplate.
303     module_->AddFunction(std::move(function_));
304     function_ = nullptr;
305   }
306   for (auto& function : *module_) {
307     for (auto& bb : function) bb.SetParent(&function);
308   }
309 
310   // Copy any trailing Op*Line instruction into the module
311   module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_));
312 }
313 
314 }  // namespace opt
315 }  // namespace spvtools
316