/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 | 60.0k | : consumer_(consumer), |
37 | 60.0k | module_(m), |
38 | 60.0k | source_("<instruction>"), |
39 | 60.0k | inst_index_(0), |
40 | 60.0k | last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {} |
41 | | |
42 | 25.5M | bool IsLineInst(const spv_parsed_instruction_t* inst) { |
43 | 25.5M | const auto opcode = static_cast<spv::Op>(inst->opcode); |
44 | 25.5M | if (IsOpLineInst(opcode)) return true; |
45 | 25.5M | if (!spvIsExtendedInstruction(opcode)) return false; |
46 | 102k | if (inst->ext_inst_type != SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) |
47 | 102k | 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 | 102k | } |
54 | | |
55 | 25.5M | bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { |
56 | 25.5M | ++inst_index_; |
57 | 25.5M | if (IsLineInst(inst)) { |
58 | 24.2k | module()->SetContainsDebugInfo(); |
59 | 24.2k | last_line_inst_.reset(); |
60 | 24.2k | dbg_line_info_.emplace_back(module()->context(), *inst, last_dbg_scope_); |
61 | 24.2k | return true; |
62 | 24.2k | } |
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.5M | const auto opcode = static_cast<spv::Op>(inst->opcode); |
68 | 25.5M | if (spvIsExtendedInstruction(opcode) && |
69 | 102k | spvExtInstIsDebugInfo(inst->ext_inst_type)) { |
70 | 533 | const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; |
71 | 533 | if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || |
72 | 437 | inst->ext_inst_type == |
73 | 437 | SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { |
74 | 216 | const CommonDebugInfoInstructions ext_inst_key = |
75 | 216 | CommonDebugInfoInstructions(ext_inst_index); |
76 | 216 | if (ext_inst_key == CommonDebugInfoDebugScope) { |
77 | 9 | uint32_t inlined_at = 0; |
78 | 9 | if (inst->num_words > kInlinedAtIndex) |
79 | 6 | inlined_at = inst->words[kInlinedAtIndex]; |
80 | 9 | last_dbg_scope_ = |
81 | 9 | DebugScope(inst->words[kLexicalScopeIndex], inlined_at); |
82 | 9 | module()->SetContainsDebugInfo(); |
83 | 9 | return true; |
84 | 9 | } |
85 | 207 | if (ext_inst_key == CommonDebugInfoDebugNoScope) { |
86 | 2 | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
87 | 2 | module()->SetContainsDebugInfo(); |
88 | 2 | return true; |
89 | 2 | } |
90 | 317 | } else { |
91 | 317 | const DebugInfoInstructions ext_inst_key = |
92 | 317 | DebugInfoInstructions(ext_inst_index); |
93 | 317 | if (ext_inst_key == DebugInfoDebugScope) { |
94 | 84 | uint32_t inlined_at = 0; |
95 | 84 | if (inst->num_words > kInlinedAtIndex) |
96 | 13 | inlined_at = inst->words[kInlinedAtIndex]; |
97 | 84 | last_dbg_scope_ = |
98 | 84 | DebugScope(inst->words[kLexicalScopeIndex], inlined_at); |
99 | 84 | module()->SetContainsDebugInfo(); |
100 | 84 | return true; |
101 | 84 | } |
102 | 233 | if (ext_inst_key == DebugInfoDebugNoScope) { |
103 | 16 | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
104 | 16 | module()->SetContainsDebugInfo(); |
105 | 16 | return true; |
106 | 16 | } |
107 | 233 | } |
108 | 533 | } |
109 | | |
110 | 25.5M | std::unique_ptr<Instruction> spv_inst( |
111 | 25.5M | new Instruction(module()->context(), *inst, std::move(dbg_line_info_))); |
112 | 25.5M | if (!spv_inst->dbg_line_insts().empty()) { |
113 | 5.61k | if (extra_line_tracking_ && |
114 | 5.61k | (!spv_inst->dbg_line_insts().back().IsNoLine())) { |
115 | 961 | last_line_inst_ = std::unique_ptr<Instruction>( |
116 | 961 | spv_inst->dbg_line_insts().back().Clone(module()->context())); |
117 | 961 | if (last_line_inst_->IsDebugLineInst()) |
118 | 0 | last_line_inst_->SetResultId(module()->context()->TakeNextId()); |
119 | 961 | } |
120 | 5.61k | dbg_line_info_.clear(); |
121 | 25.5M | } else if (last_line_inst_ != nullptr) { |
122 | 1.31k | last_line_inst_->SetDebugScope(last_dbg_scope_); |
123 | 1.31k | spv_inst->dbg_line_insts().push_back(*last_line_inst_); |
124 | 1.31k | last_line_inst_ = std::unique_ptr<Instruction>( |
125 | 1.31k | spv_inst->dbg_line_insts().back().Clone(module()->context())); |
126 | 1.31k | if (last_line_inst_->IsDebugLineInst()) |
127 | 0 | last_line_inst_->SetResultId(module()->context()->TakeNextId()); |
128 | 1.31k | } |
129 | | |
130 | 25.5M | const char* src = source_.c_str(); |
131 | 25.5M | spv_position_t loc = {inst_index_, 0, 0}; |
132 | | |
133 | | // Handle function and basic block boundaries first, then normal |
134 | | // instructions. |
135 | 25.5M | if (opcode == spv::Op::OpFunction) { |
136 | 73.3k | if (function_ != nullptr) { |
137 | 2 | Error(consumer_, src, loc, "function inside function"); |
138 | 2 | return false; |
139 | 2 | } |
140 | 73.3k | function_ = MakeUnique<Function>(std::move(spv_inst)); |
141 | 25.4M | } else if (opcode == spv::Op::OpFunctionEnd) { |
142 | 71.8k | if (function_ == nullptr) { |
143 | 6 | Error(consumer_, src, loc, |
144 | 6 | "OpFunctionEnd without corresponding OpFunction"); |
145 | 6 | return false; |
146 | 6 | } |
147 | 71.8k | if (block_ != nullptr) { |
148 | 3 | Error(consumer_, src, loc, "OpFunctionEnd inside basic block"); |
149 | 3 | return false; |
150 | 3 | } |
151 | 71.8k | function_->SetFunctionEnd(std::move(spv_inst)); |
152 | 71.8k | module_->AddFunction(std::move(function_)); |
153 | 71.8k | function_ = nullptr; |
154 | 25.3M | } else if (opcode == spv::Op::OpLabel) { |
155 | 719k | if (function_ == nullptr) { |
156 | 2 | Error(consumer_, src, loc, "OpLabel outside function"); |
157 | 2 | return false; |
158 | 2 | } |
159 | 719k | if (block_ != nullptr) { |
160 | 3 | Error(consumer_, src, loc, "OpLabel inside basic block"); |
161 | 3 | return false; |
162 | 3 | } |
163 | 719k | block_ = MakeUnique<BasicBlock>(std::move(spv_inst)); |
164 | 24.6M | } else if (spvOpcodeIsBlockTerminator(opcode)) { |
165 | 718k | if (function_ == nullptr) { |
166 | 27 | Error(consumer_, src, loc, "terminator instruction outside function"); |
167 | 27 | return false; |
168 | 27 | } |
169 | 718k | if (block_ == nullptr) { |
170 | 10 | Error(consumer_, src, loc, "terminator instruction outside basic block"); |
171 | 10 | return false; |
172 | 10 | } |
173 | 718k | if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) |
174 | 11 | spv_inst->SetDebugScope(last_dbg_scope_); |
175 | 718k | block_->AddInstruction(std::move(spv_inst)); |
176 | 718k | function_->AddBasicBlock(std::move(block_)); |
177 | 718k | block_ = nullptr; |
178 | 718k | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
179 | 718k | last_line_inst_.reset(); |
180 | 718k | dbg_line_info_.clear(); |
181 | 23.9M | } else if (opcode == spv::Op::OpGraphARM) { |
182 | 45 | if (graph_ != nullptr) { |
183 | 2 | Error(consumer_, src, loc, "graph inside graph"); |
184 | 2 | return false; |
185 | 2 | } |
186 | 43 | graph_ = MakeUnique<Graph>(std::move(spv_inst)); |
187 | 23.9M | } else if (opcode == spv::Op::OpGraphEndARM) { |
188 | 4 | if (graph_ == nullptr) { |
189 | 3 | Error(consumer_, src, loc, |
190 | 3 | "OpGraphEndARM without corresponding OpGraphARM"); |
191 | 3 | return false; |
192 | 3 | } |
193 | 1 | graph_->SetGraphEnd(std::move(spv_inst)); |
194 | 1 | module_->AddGraph(std::move(graph_)); |
195 | 1 | graph_ = nullptr; |
196 | 23.9M | } else if (opcode == spv::Op::OpGraphConstantARM) { |
197 | 29 | module_->AddGlobalValue(std::move(spv_inst)); |
198 | 23.9M | } else if (graph_ != nullptr) { // Inside graph definition |
199 | 226 | if (opcode == spv::Op::OpGraphInputARM) { |
200 | 1 | graph_->AddInput(std::move(spv_inst)); |
201 | 225 | } else if (opcode == spv::Op::OpGraphSetOutputARM) { |
202 | 196 | graph_->AddOutput(std::move(spv_inst)); |
203 | 196 | } else { |
204 | 29 | switch (opcode) { |
205 | 1 | case spv::Op::OpExtInst: |
206 | 19 | case spv::Op::OpCompositeExtract: |
207 | 19 | graph_->AddInstruction(std::move(spv_inst)); |
208 | 19 | break; |
209 | 10 | default: |
210 | 10 | Errorf(consumer_, src, loc, |
211 | 10 | "unhandled instruction (opcode %d) inside graph", opcode); |
212 | 10 | return false; |
213 | 29 | } |
214 | 29 | } |
215 | 23.9M | } else if (function_ != nullptr) { // Inside function definition |
216 | 3.24M | if (opcode == spv::Op::OpLoopMerge || opcode == spv::Op::OpSelectionMerge) |
217 | 214k | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
218 | 3.24M | if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) |
219 | 2.71k | spv_inst->SetDebugScope(last_dbg_scope_); |
220 | 3.24M | if (spvIsExtendedInstruction(opcode) && |
221 | 101k | spvExtInstIsDebugInfo(inst->ext_inst_type)) { |
222 | 61 | const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; |
223 | 61 | 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 | 61 | } else if (inst->ext_inst_type == |
251 | 61 | SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { |
252 | 41 | const NonSemanticShaderDebugInfoInstructions ext_inst_key = |
253 | 41 | NonSemanticShaderDebugInfoInstructions(ext_inst_index); |
254 | 41 | switch (ext_inst_key) { |
255 | 4 | case NonSemanticShaderDebugInfoDebugDeclare: |
256 | 34 | case NonSemanticShaderDebugInfoDebugValue: |
257 | 34 | case NonSemanticShaderDebugInfoDebugScope: |
258 | 34 | case NonSemanticShaderDebugInfoDebugNoScope: |
259 | 38 | case NonSemanticShaderDebugInfoDebugFunctionDefinition: { |
260 | 38 | if (block_ == nullptr) { // Inside function but outside blocks |
261 | 25 | Errorf(consumer_, src, loc, |
262 | 25 | "Debug info extension instruction found inside function " |
263 | 25 | "but outside block", |
264 | 25 | opcode); |
265 | 25 | } else { |
266 | 13 | block_->AddInstruction(std::move(spv_inst)); |
267 | 13 | } |
268 | 38 | break; |
269 | 34 | } |
270 | 3 | default: { |
271 | 3 | Errorf(consumer_, src, loc, |
272 | 3 | "Debug info extension instruction other than DebugScope, " |
273 | 3 | "DebugNoScope, DebugDeclare, and DebugValue found inside " |
274 | 3 | "function", |
275 | 3 | opcode); |
276 | 3 | return false; |
277 | 34 | } |
278 | 41 | } |
279 | 41 | } 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.24M | } else { |
308 | 3.24M | if (block_ == nullptr) { // Inside function but outside blocks |
309 | 38.8k | 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 | 38.8k | function_->AddParameter(std::move(spv_inst)); |
317 | 3.20M | } else { |
318 | 3.20M | block_->AddInstruction(std::move(spv_inst)); |
319 | 3.20M | } |
320 | 3.24M | } |
321 | 20.6M | } else { // Outside function or graph definition |
322 | 20.6M | SPIRV_ASSERT(consumer_, function_ == nullptr); |
323 | 20.6M | SPIRV_ASSERT(consumer_, block_ == nullptr); |
324 | 20.6M | SPIRV_ASSERT(consumer_, graph_ == nullptr); |
325 | 20.6M | if (opcode == spv::Op::OpCapability || |
326 | 20.6M | opcode == spv::Op::OpConditionalCapabilityINTEL) { |
327 | 69.9k | module_->AddCapability(std::move(spv_inst)); |
328 | 20.6M | } else if (opcode == spv::Op::OpExtension || |
329 | 20.5M | opcode == spv::Op::OpConditionalExtensionINTEL) { |
330 | 28.5k | module_->AddExtension(std::move(spv_inst)); |
331 | 20.5M | } else if (opcode == spv::Op::OpExtInstImport) { |
332 | 25.2k | module_->AddExtInstImport(std::move(spv_inst)); |
333 | 20.5M | } else if (opcode == spv::Op::OpMemoryModel) { |
334 | 52.5k | module_->SetMemoryModel(std::move(spv_inst)); |
335 | 20.5M | } else if (opcode == spv::Op::OpSamplerImageAddressingModeNV) { |
336 | 128 | module_->SetSampledImageAddressMode(std::move(spv_inst)); |
337 | 20.5M | } else if (opcode == spv::Op::OpEntryPoint) { |
338 | 41.6k | module_->AddEntryPoint(std::move(spv_inst)); |
339 | 20.4M | } else if (opcode == spv::Op::OpGraphEntryPointARM) { |
340 | 62 | module_->AddGraphEntryPoint(std::move(spv_inst)); |
341 | 20.4M | } else if (opcode == spv::Op::OpExecutionMode || |
342 | 20.4M | opcode == spv::Op::OpExecutionModeId) { |
343 | 47.6k | module_->AddExecutionMode(std::move(spv_inst)); |
344 | 20.4M | } else if (IsDebug1Inst(opcode)) { |
345 | 22.3k | module_->AddDebug1Inst(std::move(spv_inst)); |
346 | 20.4M | } else if (IsDebug2Inst(opcode)) { |
347 | 239k | module_->AddDebug2Inst(std::move(spv_inst)); |
348 | 20.1M | } else if (IsDebug3Inst(opcode)) { |
349 | 246 | module_->AddDebug3Inst(std::move(spv_inst)); |
350 | 20.1M | } else if (IsAnnotationInst(opcode)) { |
351 | 19.0M | module_->AddAnnotationInst(std::move(spv_inst)); |
352 | 19.0M | } else if (IsTypeInst(opcode)) { |
353 | 552k | module_->AddType(std::move(spv_inst)); |
354 | 573k | } else if (IsConstantInst(opcode) || opcode == spv::Op::OpVariable || |
355 | 8.91k | opcode == spv::Op::OpUntypedVariableKHR || |
356 | 572k | opcode == spv::Op::OpUndef) { |
357 | 572k | module_->AddGlobalValue(std::move(spv_inst)); |
358 | 572k | } else if (spvIsExtendedInstruction(opcode) && |
359 | 709 | spvExtInstIsDebugInfo(inst->ext_inst_type)) { |
360 | 361 | module_->AddExtInstDebugInfo(std::move(spv_inst)); |
361 | 458 | } else if (spvIsExtendedInstruction(opcode) && |
362 | 348 | 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 | 326 | auto func_begin = module_->begin(); |
367 | 326 | auto func_end = module_->end(); |
368 | | // graphs come last so we need to check first |
369 | 326 | if (module_->graphs().size() > 0) { |
370 | 0 | module_->graphs().back()->AddNonSemanticInstruction( |
371 | 0 | std::move(spv_inst)); |
372 | | // then check functions |
373 | 326 | } else if (func_begin != func_end) { |
374 | 42 | (--func_end)->AddNonSemanticInstruction(std::move(spv_inst)); |
375 | 284 | } else { |
376 | 284 | module_->AddGlobalValue(std::move(spv_inst)); |
377 | 284 | } |
378 | 326 | } else { |
379 | 132 | Errorf(consumer_, src, loc, |
380 | 132 | "Unhandled inst type (opcode: %d) found outside function " |
381 | 132 | "definition.", |
382 | 132 | opcode); |
383 | 132 | return false; |
384 | 132 | } |
385 | 20.6M | } |
386 | 25.5M | return true; |
387 | 25.5M | } |
388 | | |
389 | | // Resolves internal references among the module, functions, basic blocks, etc. |
390 | | // This function should be called after adding all instructions. |
391 | 60.0k | void IrLoader::EndModule() { |
392 | 60.0k | 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 | 910 | function_->AddBasicBlock(std::move(block_)); |
397 | 910 | block_ = nullptr; |
398 | 910 | } |
399 | 60.0k | 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.48k | module_->AddFunction(std::move(function_)); |
404 | 1.48k | function_ = nullptr; |
405 | 1.48k | } |
406 | 73.3k | for (auto& function : *module_) { |
407 | 719k | for (auto& bb : function) bb.SetParent(&function); |
408 | 73.3k | } |
409 | | |
410 | | // Copy any trailing Op*Line instruction into the module |
411 | 60.0k | module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_)); |
412 | 60.0k | } |
413 | | |
414 | | } // namespace opt |
415 | | } // namespace spvtools |