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/log.h" |
23 | #include "source/opt/reflect.h" |
24 | #include "source/util/make_unique.h" |
25 | |
26 | static const uint32_t kExtInstSetIndex = 4; |
27 | static const uint32_t kLexicalScopeIndex = 5; |
28 | static const uint32_t kInlinedAtIndex = 6; |
29 | |
30 | namespace spvtools { |
31 | namespace opt { |
32 | |
33 | IrLoader::IrLoader(const MessageConsumer& consumer, Module* m) |
34 | : consumer_(consumer), |
35 | module_(m), |
36 | source_("<instruction>" ), |
37 | inst_index_(0), |
38 | last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {} |
39 | |
40 | bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { |
41 | ++inst_index_; |
42 | const auto opcode = static_cast<SpvOp>(inst->opcode); |
43 | if (IsDebugLineInst(opcode)) { |
44 | dbg_line_info_.push_back( |
45 | Instruction(module()->context(), *inst, last_dbg_scope_)); |
46 | return true; |
47 | } |
48 | |
49 | // If it is a DebugScope or DebugNoScope of debug extension, we do not |
50 | // create a new instruction, but simply keep the information in |
51 | // struct DebugScope. |
52 | if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) { |
53 | const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; |
54 | if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { |
55 | const OpenCLDebugInfo100Instructions ext_inst_key = |
56 | OpenCLDebugInfo100Instructions(ext_inst_index); |
57 | if (ext_inst_key == OpenCLDebugInfo100DebugScope) { |
58 | uint32_t inlined_at = 0; |
59 | if (inst->num_words > kInlinedAtIndex) |
60 | inlined_at = inst->words[kInlinedAtIndex]; |
61 | last_dbg_scope_ = |
62 | DebugScope(inst->words[kLexicalScopeIndex], inlined_at); |
63 | module()->SetContainsDebugScope(); |
64 | return true; |
65 | } |
66 | if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) { |
67 | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
68 | module()->SetContainsDebugScope(); |
69 | return true; |
70 | } |
71 | } else { |
72 | const DebugInfoInstructions ext_inst_key = |
73 | DebugInfoInstructions(ext_inst_index); |
74 | if (ext_inst_key == DebugInfoDebugScope) { |
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()->SetContainsDebugScope(); |
81 | return true; |
82 | } |
83 | if (ext_inst_key == DebugInfoDebugNoScope) { |
84 | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
85 | module()->SetContainsDebugScope(); |
86 | return true; |
87 | } |
88 | } |
89 | } |
90 | |
91 | std::unique_ptr<Instruction> spv_inst( |
92 | new Instruction(module()->context(), *inst, std::move(dbg_line_info_))); |
93 | dbg_line_info_.clear(); |
94 | |
95 | const char* src = source_.c_str(); |
96 | spv_position_t loc = {inst_index_, 0, 0}; |
97 | |
98 | // Handle function and basic block boundaries first, then normal |
99 | // instructions. |
100 | if (opcode == SpvOpFunction) { |
101 | if (function_ != nullptr) { |
102 | Error(consumer_, src, loc, "function inside function" ); |
103 | return false; |
104 | } |
105 | function_ = MakeUnique<Function>(std::move(spv_inst)); |
106 | } else if (opcode == SpvOpFunctionEnd) { |
107 | if (function_ == nullptr) { |
108 | Error(consumer_, src, loc, |
109 | "OpFunctionEnd without corresponding OpFunction" ); |
110 | return false; |
111 | } |
112 | if (block_ != nullptr) { |
113 | Error(consumer_, src, loc, "OpFunctionEnd inside basic block" ); |
114 | return false; |
115 | } |
116 | function_->SetFunctionEnd(std::move(spv_inst)); |
117 | module_->AddFunction(std::move(function_)); |
118 | function_ = nullptr; |
119 | } else if (opcode == SpvOpLabel) { |
120 | if (function_ == nullptr) { |
121 | Error(consumer_, src, loc, "OpLabel outside function" ); |
122 | return false; |
123 | } |
124 | if (block_ != nullptr) { |
125 | Error(consumer_, src, loc, "OpLabel inside basic block" ); |
126 | return false; |
127 | } |
128 | block_ = MakeUnique<BasicBlock>(std::move(spv_inst)); |
129 | } else if (IsTerminatorInst(opcode)) { |
130 | if (function_ == nullptr) { |
131 | Error(consumer_, src, loc, "terminator instruction outside function" ); |
132 | return false; |
133 | } |
134 | if (block_ == nullptr) { |
135 | Error(consumer_, src, loc, "terminator instruction outside basic block" ); |
136 | return false; |
137 | } |
138 | block_->AddInstruction(std::move(spv_inst)); |
139 | function_->AddBasicBlock(std::move(block_)); |
140 | block_ = nullptr; |
141 | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
142 | } else { |
143 | if (function_ == nullptr) { // Outside function definition |
144 | SPIRV_ASSERT(consumer_, block_ == nullptr); |
145 | if (opcode == SpvOpCapability) { |
146 | module_->AddCapability(std::move(spv_inst)); |
147 | } else if (opcode == SpvOpExtension) { |
148 | module_->AddExtension(std::move(spv_inst)); |
149 | } else if (opcode == SpvOpExtInstImport) { |
150 | module_->AddExtInstImport(std::move(spv_inst)); |
151 | } else if (opcode == SpvOpMemoryModel) { |
152 | module_->SetMemoryModel(std::move(spv_inst)); |
153 | } else if (opcode == SpvOpEntryPoint) { |
154 | module_->AddEntryPoint(std::move(spv_inst)); |
155 | } else if (opcode == SpvOpExecutionMode) { |
156 | module_->AddExecutionMode(std::move(spv_inst)); |
157 | } else if (IsDebug1Inst(opcode)) { |
158 | module_->AddDebug1Inst(std::move(spv_inst)); |
159 | } else if (IsDebug2Inst(opcode)) { |
160 | module_->AddDebug2Inst(std::move(spv_inst)); |
161 | } else if (IsDebug3Inst(opcode)) { |
162 | module_->AddDebug3Inst(std::move(spv_inst)); |
163 | } else if (IsAnnotationInst(opcode)) { |
164 | module_->AddAnnotationInst(std::move(spv_inst)); |
165 | } else if (IsTypeInst(opcode)) { |
166 | module_->AddType(std::move(spv_inst)); |
167 | } else if (IsConstantInst(opcode) || opcode == SpvOpVariable || |
168 | opcode == SpvOpUndef || |
169 | (opcode == SpvOpExtInst && |
170 | spvExtInstIsNonSemantic(inst->ext_inst_type))) { |
171 | module_->AddGlobalValue(std::move(spv_inst)); |
172 | } else if (opcode == SpvOpExtInst && |
173 | spvExtInstIsDebugInfo(inst->ext_inst_type)) { |
174 | module_->AddExtInstDebugInfo(std::move(spv_inst)); |
175 | } else { |
176 | Errorf(consumer_, src, loc, |
177 | "Unhandled inst type (opcode: %d) found outside function " |
178 | "definition." , |
179 | opcode); |
180 | return false; |
181 | } |
182 | } else { |
183 | if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) |
184 | last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); |
185 | if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) |
186 | spv_inst->SetDebugScope(last_dbg_scope_); |
187 | if (opcode == SpvOpExtInst && |
188 | spvExtInstIsDebugInfo(inst->ext_inst_type)) { |
189 | const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; |
190 | if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { |
191 | const OpenCLDebugInfo100Instructions ext_inst_key = |
192 | OpenCLDebugInfo100Instructions(ext_inst_index); |
193 | switch (ext_inst_key) { |
194 | case OpenCLDebugInfo100DebugDeclare: { |
195 | if (block_ == nullptr) // Inside function but outside blocks |
196 | function_->AddDebugInstructionInHeader(std::move(spv_inst)); |
197 | else |
198 | block_->AddInstruction(std::move(spv_inst)); |
199 | break; |
200 | } |
201 | case OpenCLDebugInfo100DebugValue: { |
202 | if (block_ == nullptr) // Inside function but outside blocks |
203 | function_->AddDebugInstructionInHeader(std::move(spv_inst)); |
204 | else |
205 | block_->AddInstruction(std::move(spv_inst)); |
206 | break; |
207 | } |
208 | default: { |
209 | Errorf(consumer_, src, loc, |
210 | "Debug info extension instruction other than DebugScope, " |
211 | "DebugNoScope, DebugDeclare, and DebugValue found inside " |
212 | "function" , |
213 | opcode); |
214 | return false; |
215 | } |
216 | } |
217 | } else { |
218 | const DebugInfoInstructions ext_inst_key = |
219 | DebugInfoInstructions(ext_inst_index); |
220 | switch (ext_inst_key) { |
221 | case DebugInfoDebugDeclare: { |
222 | if (block_ == nullptr) // Inside function but outside blocks |
223 | function_->AddDebugInstructionInHeader(std::move(spv_inst)); |
224 | else |
225 | block_->AddInstruction(std::move(spv_inst)); |
226 | break; |
227 | } |
228 | case DebugInfoDebugValue: { |
229 | if (block_ == nullptr) // Inside function but outside blocks |
230 | function_->AddDebugInstructionInHeader(std::move(spv_inst)); |
231 | else |
232 | block_->AddInstruction(std::move(spv_inst)); |
233 | break; |
234 | } |
235 | default: { |
236 | Errorf(consumer_, src, loc, |
237 | "Debug info extension instruction other than DebugScope, " |
238 | "DebugNoScope, DebugDeclare, and DebugValue found inside " |
239 | "function" , |
240 | opcode); |
241 | return false; |
242 | } |
243 | } |
244 | } |
245 | } else { |
246 | if (block_ == nullptr) { // Inside function but outside blocks |
247 | if (opcode != SpvOpFunctionParameter) { |
248 | Errorf(consumer_, src, loc, |
249 | "Non-OpFunctionParameter (opcode: %d) found inside " |
250 | "function but outside basic block" , |
251 | opcode); |
252 | return false; |
253 | } |
254 | function_->AddParameter(std::move(spv_inst)); |
255 | } else { |
256 | block_->AddInstruction(std::move(spv_inst)); |
257 | } |
258 | } |
259 | } |
260 | } |
261 | return true; |
262 | } |
263 | |
264 | // Resolves internal references among the module, functions, basic blocks, etc. |
265 | // This function should be called after adding all instructions. |
266 | void IrLoader::EndModule() { |
267 | if (block_ && function_) { |
268 | // We're in the middle of a basic block, but the terminator is missing. |
269 | // Register the block anyway. This lets us write tests with less |
270 | // boilerplate. |
271 | function_->AddBasicBlock(std::move(block_)); |
272 | block_ = nullptr; |
273 | } |
274 | if (function_) { |
275 | // We're in the middle of a function, but the OpFunctionEnd is missing. |
276 | // Register the function anyway. This lets us write tests with less |
277 | // boilerplate. |
278 | module_->AddFunction(std::move(function_)); |
279 | function_ = nullptr; |
280 | } |
281 | for (auto& function : *module_) { |
282 | for (auto& bb : function) bb.SetParent(&function); |
283 | } |
284 | |
285 | // Copy any trailing Op*Line instruction into the module |
286 | module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_)); |
287 | } |
288 | |
289 | } // namespace opt |
290 | } // namespace spvtools |
291 | |