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