/src/spirv-tools/source/opt/ir_loader.cpp
Line | Count | Source |
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 | | |
35 | | IrLoader::IrLoader(const MessageConsumer& consumer, Module* m) |
36 | 62.6k | : consumer_(consumer), |
37 | 62.6k | module_(m), |
38 | 62.6k | source_("<instruction>"), |
39 | 62.6k | inst_index_(0), |
40 | 62.6k | last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {} |
41 | | |
42 | 26.0M | bool IsLineInst(const spv_parsed_instruction_t* inst) { |
43 | 26.0M | const auto opcode = static_cast<spv::Op>(inst->opcode); |
44 | 26.0M | if (IsOpLineInst(opcode)) return true; |
45 | 25.9M | if (!spvIsExtendedInstruction(opcode)) return false; |
46 | 107k | if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) |
47 | 106k | return false; |
48 | 120 | const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; |
49 | 120 | const NonSemanticShaderDebugInfoInstructions ext_inst_key = |
50 | 120 | NonSemanticShaderDebugInfoInstructions(ext_inst_index); |
51 | 120 | return ext_inst_key == NonSemanticShaderDebugInfoDebugLine || |
52 | 120 | ext_inst_key == NonSemanticShaderDebugInfoDebugNoLine; |
53 | 107k | } |
54 | | |
55 | 26.0M | bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { |
56 | 26.0M | ++inst_index_; |
57 | 26.0M | if (IsLineInst(inst)) { |
58 | 21.9k | module()->SetContainsDebugInfo(); |
59 | 21.9k | last_line_inst_.reset(); |
60 | 21.9k | dbg_line_info_.emplace_back(module()->context(), *inst, last_dbg_scope_); |
61 | 21.9k | return true; |
62 | 21.9k | } |
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 | 25.9M | const auto opcode = static_cast<spv::Op>(inst->opcode); |
68 | 25.9M | if (spvIsExtendedInstruction(opcode) && |
69 | 107k | spvExtInstIsDebugInfo(inst->ext_inst_type)) { |
70 | 529 | const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; |
71 | 529 | if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || |
72 | 434 | inst->ext_inst_type == |
73 | 434 | SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { |
74 | 215 | const CommonDebugInfoInstructions ext_inst_key = |
75 | 215 | CommonDebugInfoInstructions(ext_inst_index); |
76 | 215 | if (ext_inst_key == CommonDebugInfoDebugScope) { |
77 | 8 | uint32_t inlined_at = 0; |
78 | 8 | if (inst->num_words > kInlinedAtIndex) |
79 | 6 | inlined_at = inst->words[kInlinedAtIndex]; |
80 | 8 | last_dbg_scope_ = |
81 | 8 | DebugScope(inst->words[kLexicalScopeIndex], inlined_at); |
82 | 8 | module()->SetContainsDebugInfo(); |
83 | 8 | return true; |
84 | 8 | } |
85 | 207 | if (ext_inst_key == CommonDebugInfoDebugNoScope) { |
86 | 1 | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
87 | 1 | module()->SetContainsDebugInfo(); |
88 | 1 | return true; |
89 | 1 | } |
90 | 314 | } else { |
91 | 314 | const DebugInfoInstructions ext_inst_key = |
92 | 314 | DebugInfoInstructions(ext_inst_index); |
93 | 314 | if (ext_inst_key == DebugInfoDebugScope) { |
94 | 85 | uint32_t inlined_at = 0; |
95 | 85 | if (inst->num_words > kInlinedAtIndex) |
96 | 14 | inlined_at = inst->words[kInlinedAtIndex]; |
97 | 85 | last_dbg_scope_ = |
98 | 85 | DebugScope(inst->words[kLexicalScopeIndex], inlined_at); |
99 | 85 | module()->SetContainsDebugInfo(); |
100 | 85 | return true; |
101 | 85 | } |
102 | 229 | if (ext_inst_key == DebugInfoDebugNoScope) { |
103 | 19 | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
104 | 19 | module()->SetContainsDebugInfo(); |
105 | 19 | return true; |
106 | 19 | } |
107 | 229 | } |
108 | 529 | } |
109 | | |
110 | 25.9M | std::unique_ptr<Instruction> spv_inst( |
111 | 25.9M | new Instruction(module()->context(), *inst, std::move(dbg_line_info_))); |
112 | 25.9M | if (!spv_inst->dbg_line_insts().empty()) { |
113 | 4.93k | if (extra_line_tracking_ && |
114 | 4.93k | (!spv_inst->dbg_line_insts().back().IsNoLine())) { |
115 | 785 | last_line_inst_ = std::unique_ptr<Instruction>( |
116 | 785 | spv_inst->dbg_line_insts().back().Clone(module()->context())); |
117 | 785 | if (last_line_inst_->IsDebugLineInst()) |
118 | 0 | last_line_inst_->SetResultId(module()->context()->TakeNextId()); |
119 | 785 | } |
120 | 4.93k | dbg_line_info_.clear(); |
121 | 25.9M | } else if (last_line_inst_ != nullptr) { |
122 | 1.32k | last_line_inst_->SetDebugScope(last_dbg_scope_); |
123 | 1.32k | spv_inst->dbg_line_insts().push_back(*last_line_inst_); |
124 | 1.32k | last_line_inst_ = std::unique_ptr<Instruction>( |
125 | 1.32k | spv_inst->dbg_line_insts().back().Clone(module()->context())); |
126 | 1.32k | if (last_line_inst_->IsDebugLineInst()) |
127 | 0 | last_line_inst_->SetResultId(module()->context()->TakeNextId()); |
128 | 1.32k | } |
129 | | |
130 | 25.9M | const char* src = source_.c_str(); |
131 | 25.9M | spv_position_t loc = {inst_index_, 0, 0}; |
132 | | |
133 | | // Handle function and basic block boundaries first, then normal |
134 | | // instructions. |
135 | 25.9M | if (opcode == spv::Op::OpFunction) { |
136 | 75.7k | if (function_ != nullptr) { |
137 | 2 | Error(consumer_, src, loc, "function inside function"); |
138 | 2 | return false; |
139 | 2 | } |
140 | 75.7k | function_ = MakeUnique<Function>(std::move(spv_inst)); |
141 | 25.9M | } else if (opcode == spv::Op::OpFunctionEnd) { |
142 | 74.1k | if (function_ == nullptr) { |
143 | 5 | Error(consumer_, src, loc, |
144 | 5 | "OpFunctionEnd without corresponding OpFunction"); |
145 | 5 | return false; |
146 | 5 | } |
147 | 74.1k | if (block_ != nullptr) { |
148 | 3 | Error(consumer_, src, loc, "OpFunctionEnd inside basic block"); |
149 | 3 | return false; |
150 | 3 | } |
151 | 74.1k | function_->SetFunctionEnd(std::move(spv_inst)); |
152 | 74.1k | module_->AddFunction(std::move(function_)); |
153 | 74.1k | function_ = nullptr; |
154 | 25.8M | } else if (opcode == spv::Op::OpLabel) { |
155 | 765k | if (function_ == nullptr) { |
156 | 2 | Error(consumer_, src, loc, "OpLabel outside function"); |
157 | 2 | return false; |
158 | 2 | } |
159 | 765k | if (block_ != nullptr) { |
160 | 3 | Error(consumer_, src, loc, "OpLabel inside basic block"); |
161 | 3 | return false; |
162 | 3 | } |
163 | 765k | block_ = MakeUnique<BasicBlock>(std::move(spv_inst)); |
164 | 25.0M | } else if (spvOpcodeIsBlockTerminator(opcode)) { |
165 | 764k | if (function_ == nullptr) { |
166 | 27 | Error(consumer_, src, loc, "terminator instruction outside function"); |
167 | 27 | return false; |
168 | 27 | } |
169 | 764k | if (block_ == nullptr) { |
170 | 10 | Error(consumer_, src, loc, "terminator instruction outside basic block"); |
171 | 10 | return false; |
172 | 10 | } |
173 | 764k | if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) |
174 | 11 | spv_inst->SetDebugScope(last_dbg_scope_); |
175 | 764k | block_->AddInstruction(std::move(spv_inst)); |
176 | 764k | function_->AddBasicBlock(std::move(block_)); |
177 | 764k | block_ = nullptr; |
178 | 764k | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
179 | 764k | last_line_inst_.reset(); |
180 | 764k | dbg_line_info_.clear(); |
181 | 24.3M | } else if (opcode == spv::Op::OpGraphARM) { |
182 | 28 | if (graph_ != nullptr) { |
183 | 2 | Error(consumer_, src, loc, "graph inside graph"); |
184 | 2 | return false; |
185 | 2 | } |
186 | 26 | graph_ = MakeUnique<Graph>(std::move(spv_inst)); |
187 | 24.3M | } else if (opcode == spv::Op::OpGraphEndARM) { |
188 | 3 | if (graph_ == nullptr) { |
189 | 3 | Error(consumer_, src, loc, |
190 | 3 | "OpGraphEndARM without corresponding OpGraphARM"); |
191 | 3 | return false; |
192 | 3 | } |
193 | 0 | graph_->SetGraphEnd(std::move(spv_inst)); |
194 | 0 | module_->AddGraph(std::move(graph_)); |
195 | 0 | graph_ = nullptr; |
196 | 24.3M | } else if (opcode == spv::Op::OpGraphConstantARM) { |
197 | 28 | module_->AddGlobalValue(std::move(spv_inst)); |
198 | 24.3M | } else if (graph_ != nullptr) { // Inside graph definition |
199 | 57 | if (opcode == spv::Op::OpGraphInputARM) { |
200 | 0 | graph_->AddInput(std::move(spv_inst)); |
201 | 57 | } else if (opcode == spv::Op::OpGraphSetOutputARM) { |
202 | 47 | graph_->AddOutput(std::move(spv_inst)); |
203 | 47 | } else { |
204 | 10 | switch (opcode) { |
205 | 0 | case spv::Op::OpExtInst: |
206 | 1 | case spv::Op::OpCompositeExtract: |
207 | 1 | graph_->AddInstruction(std::move(spv_inst)); |
208 | 1 | break; |
209 | 9 | default: |
210 | 9 | Errorf(consumer_, src, loc, |
211 | 9 | "unhandled instruction (opcode %d) inside graph", opcode); |
212 | 9 | return false; |
213 | 10 | } |
214 | 10 | } |
215 | 24.3M | } else if (function_ != nullptr) { // Inside function definition |
216 | 3.32M | if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) |
217 | 233k | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
218 | 3.32M | if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) |
219 | 2.70k | spv_inst->SetDebugScope(last_dbg_scope_); |
220 | 3.32M | if (spvIsExtendedInstruction(opcode) && |
221 | 106k | spvExtInstIsDebugInfo(inst->ext_inst_type)) { |
222 | 57 | const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; |
223 | 57 | if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { |
224 | 0 | const OpenCLDebugInfo100Instructions ext_inst_key = |
225 | 0 | OpenCLDebugInfo100Instructions(ext_inst_index); |
226 | 0 | switch (ext_inst_key) { |
227 | 0 | case OpenCLDebugInfo100DebugDeclare: { |
228 | 0 | if (block_ == nullptr) // Inside function but outside blocks |
229 | 0 | function_->AddDebugInstructionInHeader(std::move(spv_inst)); |
230 | 0 | else |
231 | 0 | block_->AddInstruction(std::move(spv_inst)); |
232 | 0 | break; |
233 | 0 | } |
234 | 0 | case OpenCLDebugInfo100DebugValue: { |
235 | 0 | if (block_ == nullptr) // Inside function but outside blocks |
236 | 0 | function_->AddDebugInstructionInHeader(std::move(spv_inst)); |
237 | 0 | else |
238 | 0 | block_->AddInstruction(std::move(spv_inst)); |
239 | 0 | break; |
240 | 0 | } |
241 | 0 | default: { |
242 | 0 | Errorf(consumer_, src, loc, |
243 | 0 | "Debug info extension instruction other than DebugScope, " |
244 | 0 | "DebugNoScope, DebugFunctionDefinition, DebugDeclare, and " |
245 | 0 | "DebugValue found inside function", |
246 | 0 | opcode); |
247 | 0 | return false; |
248 | 0 | } |
249 | 0 | } |
250 | 57 | } else if (inst->ext_inst_type == |
251 | 57 | SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { |
252 | 37 | const NonSemanticShaderDebugInfoInstructions ext_inst_key = |
253 | 37 | NonSemanticShaderDebugInfoInstructions(ext_inst_index); |
254 | 37 | switch (ext_inst_key) { |
255 | 4 | case NonSemanticShaderDebugInfoDebugDeclare: |
256 | 30 | case NonSemanticShaderDebugInfoDebugValue: |
257 | 30 | case NonSemanticShaderDebugInfoDebugScope: |
258 | 30 | case NonSemanticShaderDebugInfoDebugNoScope: |
259 | 33 | case NonSemanticShaderDebugInfoDebugFunctionDefinition: { |
260 | 33 | if (block_ == nullptr) { // Inside function but outside blocks |
261 | 27 | Errorf(consumer_, src, loc, |
262 | 27 | "Debug info extension instruction found inside function " |
263 | 27 | "but outside block", |
264 | 27 | opcode); |
265 | 27 | } else { |
266 | 6 | block_->AddInstruction(std::move(spv_inst)); |
267 | 6 | } |
268 | 33 | break; |
269 | 30 | } |
270 | 4 | default: { |
271 | 4 | Errorf(consumer_, src, loc, |
272 | 4 | "Debug info extension instruction other than DebugScope, " |
273 | 4 | "DebugNoScope, DebugDeclare, and DebugValue found inside " |
274 | 4 | "function", |
275 | 4 | opcode); |
276 | 4 | return false; |
277 | 30 | } |
278 | 37 | } |
279 | 37 | } else { |
280 | 20 | const DebugInfoInstructions ext_inst_key = |
281 | 20 | DebugInfoInstructions(ext_inst_index); |
282 | 20 | switch (ext_inst_key) { |
283 | 0 | case DebugInfoDebugDeclare: { |
284 | 0 | if (block_ == nullptr) // Inside function but outside blocks |
285 | 0 | function_->AddDebugInstructionInHeader(std::move(spv_inst)); |
286 | 0 | else |
287 | 0 | block_->AddInstruction(std::move(spv_inst)); |
288 | 0 | break; |
289 | 0 | } |
290 | 18 | case DebugInfoDebugValue: { |
291 | 18 | if (block_ == nullptr) // Inside function but outside blocks |
292 | 6 | function_->AddDebugInstructionInHeader(std::move(spv_inst)); |
293 | 12 | else |
294 | 12 | block_->AddInstruction(std::move(spv_inst)); |
295 | 18 | break; |
296 | 0 | } |
297 | 2 | default: { |
298 | 2 | Errorf(consumer_, src, loc, |
299 | 2 | "Debug info extension instruction other than DebugScope, " |
300 | 2 | "DebugNoScope, DebugDeclare, and DebugValue found inside " |
301 | 2 | "function", |
302 | 2 | opcode); |
303 | 2 | return false; |
304 | 0 | } |
305 | 20 | } |
306 | 20 | } |
307 | 3.32M | } else { |
308 | 3.32M | if (block_ == nullptr) { // Inside function but outside blocks |
309 | 40.2k | if (opcode != spv::Op::OpFunctionParameter) { |
310 | 65 | Errorf(consumer_, src, loc, |
311 | 65 | "Non-OpFunctionParameter (opcode: %d) found inside " |
312 | 65 | "function but outside basic block", |
313 | 65 | opcode); |
314 | 65 | return false; |
315 | 65 | } |
316 | 40.1k | function_->AddParameter(std::move(spv_inst)); |
317 | 3.28M | } else { |
318 | 3.28M | block_->AddInstruction(std::move(spv_inst)); |
319 | 3.28M | } |
320 | 3.32M | } |
321 | 20.9M | } else { // Outside function or graph definition |
322 | 20.9M | SPIRV_ASSERT(consumer_, function_ == nullptr); |
323 | 20.9M | SPIRV_ASSERT(consumer_, block_ == nullptr); |
324 | 20.9M | SPIRV_ASSERT(consumer_, graph_ == nullptr); |
325 | 20.9M | if (opcode == spv::Op::OpCapability || |
326 | 20.9M | opcode == spv::Op::OpConditionalCapabilityINTEL) { |
327 | 71.9k | module_->AddCapability(std::move(spv_inst)); |
328 | 20.9M | } else if (opcode == spv::Op::OpExtension || |
329 | 20.8M | opcode == spv::Op::OpConditionalExtensionINTEL) { |
330 | 27.5k | module_->AddExtension(std::move(spv_inst)); |
331 | 20.8M | } else if (opcode == spv::Op::OpExtInstImport) { |
332 | 25.4k | module_->AddExtInstImport(std::move(spv_inst)); |
333 | 20.8M | } else if (opcode == spv::Op::OpMemoryModel) { |
334 | 54.5k | module_->SetMemoryModel(std::move(spv_inst)); |
335 | 20.8M | } else if (opcode == spv::Op::OpSamplerImageAddressingModeNV) { |
336 | 97 | module_->SetSampledImageAddressMode(std::move(spv_inst)); |
337 | 20.8M | } else if (opcode == spv::Op::OpEntryPoint) { |
338 | 42.9k | module_->AddEntryPoint(std::move(spv_inst)); |
339 | 20.7M | } else if (opcode == spv::Op::OpGraphEntryPointARM) { |
340 | 61 | module_->AddGraphEntryPoint(std::move(spv_inst)); |
341 | 20.7M | } else if (opcode == spv::Op::OpExecutionMode || |
342 | 20.7M | opcode == spv::Op::OpExecutionModeId) { |
343 | 47.8k | module_->AddExecutionMode(std::move(spv_inst)); |
344 | 20.7M | } else if (IsDebug1Inst(opcode)) { |
345 | 24.0k | module_->AddDebug1Inst(std::move(spv_inst)); |
346 | 20.6M | } else if (IsDebug2Inst(opcode)) { |
347 | 238k | module_->AddDebug2Inst(std::move(spv_inst)); |
348 | 20.4M | } else if (IsDebug3Inst(opcode)) { |
349 | 81 | module_->AddDebug3Inst(std::move(spv_inst)); |
350 | 20.4M | } else if (IsAnnotationInst(opcode)) { |
351 | 19.3M | module_->AddAnnotationInst(std::move(spv_inst)); |
352 | 19.3M | } else if (IsTypeInst(opcode)) { |
353 | 561k | module_->AddType(std::move(spv_inst)); |
354 | 582k | } else if (IsConstantInst(opcode) || opcode == spv::Op::OpVariable || |
355 | 9.73k | opcode == spv::Op::OpUntypedVariableKHR || |
356 | 581k | opcode == spv::Op::OpUndef) { |
357 | 581k | module_->AddGlobalValue(std::move(spv_inst)); |
358 | 581k | } else if (spvIsExtendedInstruction(opcode) && |
359 | 665 | spvExtInstIsDebugInfo(inst->ext_inst_type)) { |
360 | 359 | module_->AddExtInstDebugInfo(std::move(spv_inst)); |
361 | 421 | } else if (spvIsExtendedInstruction(opcode) && |
362 | 306 | spvExtInstIsNonSemantic(inst->ext_inst_type)) { |
363 | | // If there are no functions or graphs, add the non-semantic instructions |
364 | | // to the global values. Otherwise append it to the list of the last |
365 | | // function or graph. |
366 | 285 | auto func_begin = module_->begin(); |
367 | 285 | auto func_end = module_->end(); |
368 | | // graphs come last so we need to check first |
369 | 285 | if (module_->graphs().size() > 0) { |
370 | 0 | module_->graphs().back()->AddNonSemanticInstruction( |
371 | 0 | std::move(spv_inst)); |
372 | | // then check functions |
373 | 285 | } else if (func_begin != func_end) { |
374 | 14 | (--func_end)->AddNonSemanticInstruction(std::move(spv_inst)); |
375 | 271 | } else { |
376 | 271 | module_->AddGlobalValue(std::move(spv_inst)); |
377 | 271 | } |
378 | 285 | } else { |
379 | 136 | Errorf(consumer_, src, loc, |
380 | 136 | "Unhandled inst type (opcode: %d) found outside function " |
381 | 136 | "definition.", |
382 | 136 | opcode); |
383 | 136 | return false; |
384 | 136 | } |
385 | 20.9M | } |
386 | 25.9M | return true; |
387 | 25.9M | } |
388 | | |
389 | | // Resolves internal references among the module, functions, basic blocks, etc. |
390 | | // This function should be called after adding all instructions. |
391 | 62.6k | void IrLoader::EndModule() { |
392 | 62.6k | if (block_ && function_) { |
393 | | // We're in the middle of a basic block, but the terminator is missing. |
394 | | // Register the block anyway. This lets us write tests with less |
395 | | // boilerplate. |
396 | 950 | function_->AddBasicBlock(std::move(block_)); |
397 | 950 | block_ = nullptr; |
398 | 950 | } |
399 | 62.6k | if (function_) { |
400 | | // We're in the middle of a function, but the OpFunctionEnd is missing. |
401 | | // Register the function anyway. This lets us write tests with less |
402 | | // boilerplate. |
403 | 1.55k | module_->AddFunction(std::move(function_)); |
404 | 1.55k | function_ = nullptr; |
405 | 1.55k | } |
406 | 75.7k | for (auto& function : *module_) { |
407 | 765k | for (auto& bb : function) bb.SetParent(&function); |
408 | 75.7k | } |
409 | | |
410 | | // Copy any trailing Op*Line instruction into the module |
411 | 62.6k | module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_)); |
412 | 62.6k | } |
413 | | |
414 | | } // namespace opt |
415 | | } // namespace spvtools |