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