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