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