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