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