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
26static const uint32_t kExtInstSetIndex = 4;
27static const uint32_t kLexicalScopeIndex = 5;
28static const uint32_t kInlinedAtIndex = 6;
29
30namespace spvtools {
31namespace opt {
32
33IrLoader::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
40bool 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.
266void 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