• 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         module_->AddExecutionMode(std::move(spv_inst));
194       } else if (IsDebug1Inst(opcode)) {
195         module_->AddDebug1Inst(std::move(spv_inst));
196       } else if (IsDebug2Inst(opcode)) {
197         module_->AddDebug2Inst(std::move(spv_inst));
198       } else if (IsDebug3Inst(opcode)) {
199         module_->AddDebug3Inst(std::move(spv_inst));
200       } else if (IsAnnotationInst(opcode)) {
201         module_->AddAnnotationInst(std::move(spv_inst));
202       } else if (IsTypeInst(opcode)) {
203         module_->AddType(std::move(spv_inst));
204       } else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
205                  opcode == SpvOpUndef) {
206         module_->AddGlobalValue(std::move(spv_inst));
207       } else if (opcode == SpvOpExtInst &&
208                  spvExtInstIsDebugInfo(inst->ext_inst_type)) {
209         module_->AddExtInstDebugInfo(std::move(spv_inst));
210       } else if (opcode == SpvOpExtInst &&
211                  spvExtInstIsNonSemantic(inst->ext_inst_type)) {
212         // If there are no functions, add the non-semantic instructions to the
213         // global values. Otherwise append it to the list of the last function.
214         auto func_begin = module_->begin();
215         auto func_end = module_->end();
216         if (func_begin == func_end) {
217           module_->AddGlobalValue(std::move(spv_inst));
218         } else {
219           (--func_end)->AddNonSemanticInstruction(std::move(spv_inst));
220         }
221       } else {
222         Errorf(consumer_, src, loc,
223                "Unhandled inst type (opcode: %d) found outside function "
224                "definition.",
225                opcode);
226         return false;
227       }
228     } else {
229       if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge)
230         last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
231       if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
232         spv_inst->SetDebugScope(last_dbg_scope_);
233       if (opcode == SpvOpExtInst &&
234           spvExtInstIsDebugInfo(inst->ext_inst_type)) {
235         const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
236         if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
237           const OpenCLDebugInfo100Instructions ext_inst_key =
238               OpenCLDebugInfo100Instructions(ext_inst_index);
239           switch (ext_inst_key) {
240             case OpenCLDebugInfo100DebugDeclare: {
241               if (block_ == nullptr)  // Inside function but outside blocks
242                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
243               else
244                 block_->AddInstruction(std::move(spv_inst));
245               break;
246             }
247             case OpenCLDebugInfo100DebugValue: {
248               if (block_ == nullptr)  // Inside function but outside blocks
249                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
250               else
251                 block_->AddInstruction(std::move(spv_inst));
252               break;
253             }
254             default: {
255               Errorf(consumer_, src, loc,
256                      "Debug info extension instruction other than DebugScope, "
257                      "DebugNoScope, DebugFunctionDefinition, DebugDeclare, and "
258                      "DebugValue found inside function",
259                      opcode);
260               return false;
261             }
262           }
263         } else if (inst->ext_inst_type ==
264                    SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
265           const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
266               NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
267           switch (ext_inst_key) {
268             case NonSemanticShaderDebugInfo100DebugDeclare:
269             case NonSemanticShaderDebugInfo100DebugValue:
270             case NonSemanticShaderDebugInfo100DebugScope:
271             case NonSemanticShaderDebugInfo100DebugNoScope:
272             case NonSemanticShaderDebugInfo100DebugFunctionDefinition: {
273               if (block_ == nullptr) {  // Inside function but outside blocks
274                 Errorf(consumer_, src, loc,
275                        "Debug info extension instruction found inside function "
276                        "but outside block",
277                        opcode);
278               } else {
279                 block_->AddInstruction(std::move(spv_inst));
280               }
281               break;
282             }
283             default: {
284               Errorf(consumer_, src, loc,
285                      "Debug info extension instruction other than DebugScope, "
286                      "DebugNoScope, DebugDeclare, and DebugValue found inside "
287                      "function",
288                      opcode);
289               return false;
290             }
291           }
292         } else {
293           const DebugInfoInstructions ext_inst_key =
294               DebugInfoInstructions(ext_inst_index);
295           switch (ext_inst_key) {
296             case DebugInfoDebugDeclare: {
297               if (block_ == nullptr)  // Inside function but outside blocks
298                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
299               else
300                 block_->AddInstruction(std::move(spv_inst));
301               break;
302             }
303             case DebugInfoDebugValue: {
304               if (block_ == nullptr)  // Inside function but outside blocks
305                 function_->AddDebugInstructionInHeader(std::move(spv_inst));
306               else
307                 block_->AddInstruction(std::move(spv_inst));
308               break;
309             }
310             default: {
311               Errorf(consumer_, src, loc,
312                      "Debug info extension instruction other than DebugScope, "
313                      "DebugNoScope, DebugDeclare, and DebugValue found inside "
314                      "function",
315                      opcode);
316               return false;
317             }
318           }
319         }
320       } else {
321         if (block_ == nullptr) {  // Inside function but outside blocks
322           if (opcode != SpvOpFunctionParameter) {
323             Errorf(consumer_, src, loc,
324                    "Non-OpFunctionParameter (opcode: %d) found inside "
325                    "function but outside basic block",
326                    opcode);
327             return false;
328           }
329           function_->AddParameter(std::move(spv_inst));
330         } else {
331           block_->AddInstruction(std::move(spv_inst));
332         }
333       }
334     }
335   }
336   return true;
337 }
338 
339 // Resolves internal references among the module, functions, basic blocks, etc.
340 // This function should be called after adding all instructions.
EndModule()341 void IrLoader::EndModule() {
342   if (block_ && function_) {
343     // We're in the middle of a basic block, but the terminator is missing.
344     // Register the block anyway.  This lets us write tests with less
345     // boilerplate.
346     function_->AddBasicBlock(std::move(block_));
347     block_ = nullptr;
348   }
349   if (function_) {
350     // We're in the middle of a function, but the OpFunctionEnd is missing.
351     // Register the function anyway.  This lets us write tests with less
352     // boilerplate.
353     module_->AddFunction(std::move(function_));
354     function_ = nullptr;
355   }
356   for (auto& function : *module_) {
357     for (auto& bb : function) bb.SetParent(&function);
358   }
359 
360   // Copy any trailing Op*Line instruction into the module
361   module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_));
362 }
363 
364 }  // namespace opt
365 }  // namespace spvtools
366