• 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/ir_context.h"
23 #include "source/opt/log.h"
24 #include "source/opt/reflect.h"
25 #include "source/util/make_unique.h"
26 
27 static const uint32_t kExtInstSetIndex = 4;
28 static const uint32_t kLexicalScopeIndex = 5;
29 static const uint32_t kInlinedAtIndex = 6;
30 
31 namespace spvtools {
32 namespace opt {
33 
IrLoader(const MessageConsumer & consumer,Module * m)34 IrLoader::IrLoader(const MessageConsumer& consumer, Module* m)
35     : consumer_(consumer),
36       module_(m),
37       source_("<instruction>"),
38       inst_index_(0),
39       last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
40 
IsLineInst(const spv_parsed_instruction_t * inst)41 bool IsLineInst(const spv_parsed_instruction_t* inst) {
42   const auto opcode = static_cast<SpvOp>(inst->opcode);
43   if (IsOpLineInst(opcode)) return true;
44   if (opcode != SpvOpExtInst) return false;
45   if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100)
46     return false;
47   const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
48   const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
49       NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
50   return ext_inst_key == NonSemanticShaderDebugInfo100DebugLine ||
51          ext_inst_key == NonSemanticShaderDebugInfo100DebugNoLine;
52 }
53 
AddInstruction(const spv_parsed_instruction_t * inst)54 bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
55   ++inst_index_;
56   if (IsLineInst(inst)) {
57     module()->SetContainsDebugInfo();
58     last_line_inst_.reset();
59     dbg_line_info_.emplace_back(module()->context(), *inst, last_dbg_scope_);
60     return true;
61   }
62 
63   // If it is a DebugScope or DebugNoScope of debug extension, we do not
64   // create a new instruction, but simply keep the information in
65   // struct DebugScope.
66   const auto opcode = static_cast<SpvOp>(inst->opcode);
67   if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) {
68     const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
69     if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
70         inst->ext_inst_type ==
71             SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
72       const CommonDebugInfoInstructions ext_inst_key =
73           CommonDebugInfoInstructions(ext_inst_index);
74       if (ext_inst_key == CommonDebugInfoDebugScope) {
75         uint32_t inlined_at = 0;
76         if (inst->num_words > kInlinedAtIndex)
77           inlined_at = inst->words[kInlinedAtIndex];
78         last_dbg_scope_ =
79             DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
80         module()->SetContainsDebugInfo();
81         return true;
82       }
83       if (ext_inst_key == CommonDebugInfoDebugNoScope) {
84         last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
85         module()->SetContainsDebugInfo();
86         return true;
87       }
88     } else {
89       const DebugInfoInstructions ext_inst_key =
90           DebugInfoInstructions(ext_inst_index);
91       if (ext_inst_key == DebugInfoDebugScope) {
92         uint32_t inlined_at = 0;
93         if (inst->num_words > kInlinedAtIndex)
94           inlined_at = inst->words[kInlinedAtIndex];
95         last_dbg_scope_ =
96             DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
97         module()->SetContainsDebugInfo();
98         return true;
99       }
100       if (ext_inst_key == DebugInfoDebugNoScope) {
101         last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
102         module()->SetContainsDebugInfo();
103         return true;
104       }
105     }
106   }
107 
108   std::unique_ptr<Instruction> spv_inst(
109       new Instruction(module()->context(), *inst, std::move(dbg_line_info_)));
110   if (!spv_inst->dbg_line_insts().empty()) {
111     if (extra_line_tracking_ &&
112         (!spv_inst->dbg_line_insts().back().IsNoLine())) {
113       last_line_inst_ = std::unique_ptr<Instruction>(
114           spv_inst->dbg_line_insts().back().Clone(module()->context()));
115       if (last_line_inst_->IsDebugLineInst())
116         last_line_inst_->SetResultId(module()->context()->TakeNextId());
117     }
118     dbg_line_info_.clear();
119   } else if (last_line_inst_ != nullptr) {
120     last_line_inst_->SetDebugScope(last_dbg_scope_);
121     spv_inst->dbg_line_insts().push_back(*last_line_inst_);
122     last_line_inst_ = std::unique_ptr<Instruction>(
123         spv_inst->dbg_line_insts().back().Clone(module()->context()));
124     if (last_line_inst_->IsDebugLineInst())
125       last_line_inst_->SetResultId(module()->context()->TakeNextId());
126   }
127 
128   const char* src = source_.c_str();
129   spv_position_t loc = {inst_index_, 0, 0};
130 
131   // Handle function and basic block boundaries first, then normal
132   // instructions.
133   if (opcode == SpvOpFunction) {
134     if (function_ != nullptr) {
135       Error(consumer_, src, loc, "function inside function");
136       return false;
137     }
138     function_ = MakeUnique<Function>(std::move(spv_inst));
139   } else if (opcode == SpvOpFunctionEnd) {
140     if (function_ == nullptr) {
141       Error(consumer_, src, loc,
142             "OpFunctionEnd without corresponding OpFunction");
143       return false;
144     }
145     if (block_ != nullptr) {
146       Error(consumer_, src, loc, "OpFunctionEnd inside basic block");
147       return false;
148     }
149     function_->SetFunctionEnd(std::move(spv_inst));
150     module_->AddFunction(std::move(function_));
151     function_ = nullptr;
152   } else if (opcode == SpvOpLabel) {
153     if (function_ == nullptr) {
154       Error(consumer_, src, loc, "OpLabel outside function");
155       return false;
156     }
157     if (block_ != nullptr) {
158       Error(consumer_, src, loc, "OpLabel inside basic block");
159       return false;
160     }
161     block_ = MakeUnique<BasicBlock>(std::move(spv_inst));
162   } else if (spvOpcodeIsBlockTerminator(opcode)) {
163     if (function_ == nullptr) {
164       Error(consumer_, src, loc, "terminator instruction outside function");
165       return false;
166     }
167     if (block_ == nullptr) {
168       Error(consumer_, src, loc, "terminator instruction outside basic block");
169       return false;
170     }
171     if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
172       spv_inst->SetDebugScope(last_dbg_scope_);
173     block_->AddInstruction(std::move(spv_inst));
174     function_->AddBasicBlock(std::move(block_));
175     block_ = nullptr;
176     last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
177     last_line_inst_.reset();
178     dbg_line_info_.clear();
179   } else {
180     if (function_ == nullptr) {  // Outside function definition
181       SPIRV_ASSERT(consumer_, block_ == nullptr);
182       if (opcode == SpvOpCapability) {
183         module_->AddCapability(std::move(spv_inst));
184       } else if (opcode == SpvOpExtension) {
185         module_->AddExtension(std::move(spv_inst));
186       } else if (opcode == SpvOpExtInstImport) {
187         module_->AddExtInstImport(std::move(spv_inst));
188       } else if (opcode == SpvOpMemoryModel) {
189         module_->SetMemoryModel(std::move(spv_inst));
190       } else if (opcode == SpvOpEntryPoint) {
191         module_->AddEntryPoint(std::move(spv_inst));
192       } else if (opcode == SpvOpExecutionMode ||
193                  opcode == SpvOpExecutionModeId) {
194         module_->AddExecutionMode(std::move(spv_inst));
195       } else if (IsDebug1Inst(opcode)) {
196         module_->AddDebug1Inst(std::move(spv_inst));
197       } else if (IsDebug2Inst(opcode)) {
198         module_->AddDebug2Inst(std::move(spv_inst));
199       } else if (IsDebug3Inst(opcode)) {
200         module_->AddDebug3Inst(std::move(spv_inst));
201       } else if (IsAnnotationInst(opcode)) {
202         module_->AddAnnotationInst(std::move(spv_inst));
203       } else if (IsTypeInst(opcode)) {
204         module_->AddType(std::move(spv_inst));
205       } else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
206                  opcode == SpvOpUndef) {
207         module_->AddGlobalValue(std::move(spv_inst));
208       } else if (opcode == SpvOpExtInst &&
209                  spvExtInstIsDebugInfo(inst->ext_inst_type)) {
210         module_->AddExtInstDebugInfo(std::move(spv_inst));
211       } else if (opcode == SpvOpExtInst &&
212                  spvExtInstIsNonSemantic(inst->ext_inst_type)) {
213         // If there are no functions, add the non-semantic instructions to the
214         // global values. Otherwise append it to the list of the last function.
215         auto func_begin = module_->begin();
216         auto func_end = module_->end();
217         if (func_begin == func_end) {
218           module_->AddGlobalValue(std::move(spv_inst));
219         } else {
220           (--func_end)->AddNonSemanticInstruction(std::move(spv_inst));
221         }
222       } else {
223         Errorf(consumer_, src, loc,
224                "Unhandled inst type (opcode: %d) found outside function "
225                "definition.",
226                opcode);
227         return false;
228       }
229     } else {
230       if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge)
231         last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
232       if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
233         spv_inst->SetDebugScope(last_dbg_scope_);
234       if (opcode == SpvOpExtInst &&
235           spvExtInstIsDebugInfo(inst->ext_inst_type)) {
236         const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
237         if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
238           const OpenCLDebugInfo100Instructions ext_inst_key =
239               OpenCLDebugInfo100Instructions(ext_inst_index);
240           switch (ext_inst_key) {
241             case OpenCLDebugInfo100DebugDeclare: {
242               if (block_ == nullptr)  // Inside function but outside blocks
243                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
244               else
245                 block_->AddInstruction(std::move(spv_inst));
246               break;
247             }
248             case OpenCLDebugInfo100DebugValue: {
249               if (block_ == nullptr)  // Inside function but outside blocks
250                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
251               else
252                 block_->AddInstruction(std::move(spv_inst));
253               break;
254             }
255             default: {
256               Errorf(consumer_, src, loc,
257                      "Debug info extension instruction other than DebugScope, "
258                      "DebugNoScope, DebugFunctionDefinition, DebugDeclare, and "
259                      "DebugValue found inside function",
260                      opcode);
261               return false;
262             }
263           }
264         } else if (inst->ext_inst_type ==
265                    SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
266           const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
267               NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
268           switch (ext_inst_key) {
269             case NonSemanticShaderDebugInfo100DebugDeclare:
270             case NonSemanticShaderDebugInfo100DebugValue:
271             case NonSemanticShaderDebugInfo100DebugScope:
272             case NonSemanticShaderDebugInfo100DebugNoScope:
273             case NonSemanticShaderDebugInfo100DebugFunctionDefinition: {
274               if (block_ == nullptr) {  // Inside function but outside blocks
275                 Errorf(consumer_, src, loc,
276                        "Debug info extension instruction found inside function "
277                        "but outside block",
278                        opcode);
279               } else {
280                 block_->AddInstruction(std::move(spv_inst));
281               }
282               break;
283             }
284             default: {
285               Errorf(consumer_, src, loc,
286                      "Debug info extension instruction other than DebugScope, "
287                      "DebugNoScope, DebugDeclare, and DebugValue found inside "
288                      "function",
289                      opcode);
290               return false;
291             }
292           }
293         } else {
294           const DebugInfoInstructions ext_inst_key =
295               DebugInfoInstructions(ext_inst_index);
296           switch (ext_inst_key) {
297             case DebugInfoDebugDeclare: {
298               if (block_ == nullptr)  // Inside function but outside blocks
299                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
300               else
301                 block_->AddInstruction(std::move(spv_inst));
302               break;
303             }
304             case DebugInfoDebugValue: {
305               if (block_ == nullptr)  // Inside function but outside blocks
306                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
307               else
308                 block_->AddInstruction(std::move(spv_inst));
309               break;
310             }
311             default: {
312               Errorf(consumer_, src, loc,
313                      "Debug info extension instruction other than DebugScope, "
314                      "DebugNoScope, DebugDeclare, and DebugValue found inside "
315                      "function",
316                      opcode);
317               return false;
318             }
319           }
320         }
321       } else {
322         if (block_ == nullptr) {  // Inside function but outside blocks
323           if (opcode != SpvOpFunctionParameter) {
324             Errorf(consumer_, src, loc,
325                    "Non-OpFunctionParameter (opcode: %d) found inside "
326                    "function but outside basic block",
327                    opcode);
328             return false;
329           }
330           function_->AddParameter(std::move(spv_inst));
331         } else {
332           block_->AddInstruction(std::move(spv_inst));
333         }
334       }
335     }
336   }
337   return true;
338 }
339 
340 // Resolves internal references among the module, functions, basic blocks, etc.
341 // This function should be called after adding all instructions.
EndModule()342 void IrLoader::EndModule() {
343   if (block_ && function_) {
344     // We're in the middle of a basic block, but the terminator is missing.
345     // Register the block anyway.  This lets us write tests with less
346     // boilerplate.
347     function_->AddBasicBlock(std::move(block_));
348     block_ = nullptr;
349   }
350   if (function_) {
351     // We're in the middle of a function, but the OpFunctionEnd is missing.
352     // Register the function anyway.  This lets us write tests with less
353     // boilerplate.
354     module_->AddFunction(std::move(function_));
355     function_ = nullptr;
356   }
357   for (auto& function : *module_) {
358     for (auto& bb : function) bb.SetParent(&function);
359   }
360 
361   // Copy any trailing Op*Line instruction into the module
362   module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_));
363 }
364 
365 }  // namespace opt
366 }  // namespace spvtools
367