/src/spirv-tools/source/opt/ir_builder.h
Line | Count | Source |
1 | | // Copyright (c) 2018 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 | | #ifndef SOURCE_OPT_IR_BUILDER_H_ |
16 | | #define SOURCE_OPT_IR_BUILDER_H_ |
17 | | |
18 | | #include <cassert> |
19 | | #include <limits> |
20 | | #include <memory> |
21 | | #include <utility> |
22 | | #include <vector> |
23 | | |
24 | | #include "source/opt/basic_block.h" |
25 | | #include "source/opt/constants.h" |
26 | | #include "source/opt/instruction.h" |
27 | | #include "source/opt/ir_context.h" |
28 | | |
29 | | namespace spvtools { |
30 | | namespace opt { |
31 | | |
32 | | // In SPIR-V, ids are encoded as uint16_t, this id is guaranteed to be always |
33 | | // invalid. |
34 | | constexpr uint32_t kInvalidId = std::numeric_limits<uint32_t>::max(); |
35 | | |
36 | | // Helper class to abstract instruction construction and insertion. |
37 | | // The instruction builder can preserve the following analyses (specified via |
38 | | // the constructors): |
39 | | // - Def-use analysis |
40 | | // - Instruction to block analysis |
41 | | class InstructionBuilder { |
42 | | public: |
43 | | using InsertionPointTy = BasicBlock::iterator; |
44 | | |
45 | | // Creates an InstructionBuilder, all new instructions will be inserted before |
46 | | // the instruction |insert_before|. |
47 | | InstructionBuilder( |
48 | | IRContext* context, Instruction* insert_before, |
49 | | IRContext::Analysis preserved_analyses = IRContext::kAnalysisNone) |
50 | 37.6k | : InstructionBuilder(context, context->get_instr_block(insert_before), |
51 | 37.6k | InsertionPointTy(insert_before), |
52 | 37.6k | preserved_analyses) {} |
53 | | |
54 | | // Creates an InstructionBuilder, all new instructions will be inserted at the |
55 | | // end of the basic block |parent_block|. |
56 | | InstructionBuilder( |
57 | | IRContext* context, BasicBlock* parent_block, |
58 | | IRContext::Analysis preserved_analyses = IRContext::kAnalysisNone) |
59 | 139k | : InstructionBuilder(context, parent_block, parent_block->end(), |
60 | 139k | preserved_analyses) {} |
61 | | |
62 | 15 | Instruction* AddNullaryOp(uint32_t type_id, spv::Op opcode) { |
63 | 15 | uint32_t result_id = 0; |
64 | 15 | if (type_id != 0) { |
65 | 14 | result_id = GetContext()->TakeNextId(); |
66 | 14 | if (result_id == 0) { |
67 | 0 | return nullptr; |
68 | 0 | } |
69 | 14 | } |
70 | 15 | std::unique_ptr<Instruction> new_inst( |
71 | 15 | new Instruction(GetContext(), opcode, type_id, result_id, {})); |
72 | 15 | return AddInstruction(std::move(new_inst)); |
73 | 15 | } |
74 | | |
75 | 14 | Instruction* AddUnaryOp(uint32_t type_id, spv::Op opcode, uint32_t operand1) { |
76 | 14 | uint32_t result_id = 0; |
77 | 14 | if (type_id != 0) { |
78 | 0 | result_id = GetContext()->TakeNextId(); |
79 | 0 | if (result_id == 0) { |
80 | 0 | return nullptr; |
81 | 0 | } |
82 | 0 | } |
83 | 14 | std::unique_ptr<Instruction> newUnOp(new Instruction( |
84 | 14 | GetContext(), opcode, type_id, result_id, |
85 | 14 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}})); |
86 | 14 | return AddInstruction(std::move(newUnOp)); |
87 | 14 | } |
88 | | |
89 | | Instruction* AddBinaryOp(uint32_t type_id, spv::Op opcode, uint32_t operand1, |
90 | 165 | uint32_t operand2) { |
91 | 165 | uint32_t result_id = 0; |
92 | 165 | if (type_id != 0) { |
93 | 165 | result_id = GetContext()->TakeNextId(); |
94 | 165 | if (result_id == 0) { |
95 | 0 | return nullptr; |
96 | 0 | } |
97 | 165 | } |
98 | 165 | std::unique_ptr<Instruction> newBinOp(new Instruction( |
99 | 165 | GetContext(), opcode, type_id, |
100 | 165 | opcode == spv::Op::OpStore ? 0 : result_id, |
101 | 165 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, |
102 | 165 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}})); |
103 | 165 | return AddInstruction(std::move(newBinOp)); |
104 | 165 | } |
105 | | |
106 | | Instruction* AddTernaryOp(uint32_t type_id, spv::Op opcode, uint32_t operand1, |
107 | 0 | uint32_t operand2, uint32_t operand3) { |
108 | 0 | uint32_t result_id = 0; |
109 | 0 | if (type_id != 0) { |
110 | 0 | result_id = GetContext()->TakeNextId(); |
111 | 0 | if (result_id == 0) { |
112 | 0 | return nullptr; |
113 | 0 | } |
114 | 0 | } |
115 | 0 | std::unique_ptr<Instruction> newTernOp(new Instruction( |
116 | 0 | GetContext(), opcode, type_id, result_id, |
117 | 0 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, |
118 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, |
119 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}})); |
120 | 0 | return AddInstruction(std::move(newTernOp)); |
121 | 0 | } |
122 | | |
123 | | Instruction* AddQuadOp(uint32_t type_id, spv::Op opcode, uint32_t operand1, |
124 | | uint32_t operand2, uint32_t operand3, |
125 | 0 | uint32_t operand4) { |
126 | 0 | uint32_t result_id = 0; |
127 | 0 | if (type_id != 0) { |
128 | 0 | result_id = GetContext()->TakeNextId(); |
129 | 0 | if (result_id == 0) { |
130 | 0 | return nullptr; |
131 | 0 | } |
132 | 0 | } |
133 | 0 | std::unique_ptr<Instruction> newQuadOp(new Instruction( |
134 | 0 | GetContext(), opcode, type_id, result_id, |
135 | 0 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, |
136 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, |
137 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}, |
138 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand4}}})); |
139 | 0 | return AddInstruction(std::move(newQuadOp)); |
140 | 0 | } |
141 | | |
142 | | Instruction* AddIdLiteralOp(uint32_t type_id, spv::Op opcode, uint32_t id, |
143 | 0 | uint32_t uliteral) { |
144 | 0 | uint32_t result_id = 0; |
145 | 0 | if (type_id != 0) { |
146 | 0 | result_id = GetContext()->TakeNextId(); |
147 | 0 | if (result_id == 0) { |
148 | 0 | return nullptr; |
149 | 0 | } |
150 | 0 | } |
151 | 0 | std::unique_ptr<Instruction> newBinOp(new Instruction( |
152 | 0 | GetContext(), opcode, type_id, result_id, |
153 | 0 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {id}}, |
154 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {uliteral}}})); |
155 | 0 | return AddInstruction(std::move(newBinOp)); |
156 | 0 | } |
157 | | |
158 | | // Creates an N-ary instruction of |opcode|. |
159 | | // |typid| must be the id of the instruction's type. |
160 | | // |operands| must be a sequence of operand ids. |
161 | | // Use |result| for the result id if non-zero. |
162 | | Instruction* AddNaryOp(uint32_t type_id, spv::Op opcode, |
163 | | const std::vector<uint32_t>& operands, |
164 | 75 | uint32_t result = 0) { |
165 | 75 | std::vector<Operand> ops; |
166 | 385 | for (size_t i = 0; i < operands.size(); i++) { |
167 | 310 | ops.push_back({SPV_OPERAND_TYPE_ID, {operands[i]}}); |
168 | 310 | } |
169 | 75 | if (result == 0) { |
170 | 75 | result = GetContext()->TakeNextId(); |
171 | 75 | if (result == 0) { |
172 | 0 | return nullptr; |
173 | 0 | } |
174 | 75 | } |
175 | 75 | std::unique_ptr<Instruction> new_inst( |
176 | 75 | new Instruction(GetContext(), opcode, type_id, result, ops)); |
177 | 75 | return AddInstruction(std::move(new_inst)); |
178 | 75 | } |
179 | | |
180 | | // Creates a new selection merge instruction. |
181 | | // The id |merge_id| is the merge basic block id. |
182 | | Instruction* AddSelectionMerge( |
183 | | uint32_t merge_id, uint32_t selection_control = static_cast<uint32_t>( |
184 | 10.2k | spv::SelectionControlMask::MaskNone)) { |
185 | 10.2k | std::unique_ptr<Instruction> new_branch_merge(new Instruction( |
186 | 10.2k | GetContext(), spv::Op::OpSelectionMerge, 0, 0, |
187 | 10.2k | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, |
188 | 10.2k | {spv_operand_type_t::SPV_OPERAND_TYPE_SELECTION_CONTROL, |
189 | 10.2k | {selection_control}}})); |
190 | 10.2k | return AddInstruction(std::move(new_branch_merge)); |
191 | 10.2k | } |
192 | | |
193 | | // Creates a new loop merge instruction. |
194 | | // The id |merge_id| is the basic block id of the merge block. |
195 | | // |continue_id| is the id of the continue block. |
196 | | // |loop_control| are the loop control flags to be added to the instruction. |
197 | | Instruction* AddLoopMerge(uint32_t merge_id, uint32_t continue_id, |
198 | | uint32_t loop_control = static_cast<uint32_t>( |
199 | 0 | spv::LoopControlMask::MaskNone)) { |
200 | 0 | std::unique_ptr<Instruction> new_branch_merge(new Instruction( |
201 | 0 | GetContext(), spv::Op::OpLoopMerge, 0, 0, |
202 | 0 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, |
203 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}}, |
204 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {loop_control}}})); |
205 | 0 | return AddInstruction(std::move(new_branch_merge)); |
206 | 0 | } |
207 | | |
208 | | // Creates a new branch instruction to |label_id|. |
209 | | // Note that the user must make sure the final basic block is |
210 | | // well formed. |
211 | 128k | Instruction* AddBranch(uint32_t label_id) { |
212 | 128k | std::unique_ptr<Instruction> new_branch(new Instruction( |
213 | 128k | GetContext(), spv::Op::OpBranch, 0, 0, |
214 | 128k | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}})); |
215 | 128k | return AddInstruction(std::move(new_branch)); |
216 | 128k | } |
217 | | |
218 | | // Creates a new conditional instruction and the associated selection merge |
219 | | // instruction if requested. |
220 | | // The id |cond_id| is the id of the condition instruction, must be of |
221 | | // type bool. |
222 | | // The id |true_id| is the id of the basic block to branch to if the condition |
223 | | // is true. |
224 | | // The id |false_id| is the id of the basic block to branch to if the |
225 | | // condition is false. |
226 | | // The id |merge_id| is the id of the merge basic block for the selection |
227 | | // merge instruction. If |merge_id| equals kInvalidId then no selection merge |
228 | | // instruction will be created. |
229 | | // The value |selection_control| is the selection control flag for the |
230 | | // selection merge instruction. |
231 | | // Note that the user must make sure the final basic block is |
232 | | // well formed. |
233 | | Instruction* AddConditionalBranch( |
234 | | uint32_t cond_id, uint32_t true_id, uint32_t false_id, |
235 | | uint32_t merge_id = kInvalidId, |
236 | | uint32_t selection_control = |
237 | 4.61k | static_cast<uint32_t>(spv::SelectionControlMask::MaskNone)) { |
238 | 4.61k | if (merge_id != kInvalidId) { |
239 | 4.61k | AddSelectionMerge(merge_id, selection_control); |
240 | 4.61k | } |
241 | 4.61k | std::unique_ptr<Instruction> new_branch(new Instruction( |
242 | 4.61k | GetContext(), spv::Op::OpBranchConditional, 0, 0, |
243 | 4.61k | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}}, |
244 | 4.61k | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}}, |
245 | 4.61k | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}})); |
246 | 4.61k | return AddInstruction(std::move(new_branch)); |
247 | 4.61k | } |
248 | | |
249 | | // Creates a new switch instruction and the associated selection merge |
250 | | // instruction if requested. |
251 | | // The id |selector_id| is the id of the selector instruction, must be of |
252 | | // type int. |
253 | | // The id |default_id| is the id of the default basic block to branch to. |
254 | | // The vector |targets| is the pair of literal/branch id. |
255 | | // The id |merge_id| is the id of the merge basic block for the selection |
256 | | // merge instruction. If |merge_id| equals kInvalidId then no selection merge |
257 | | // instruction will be created. |
258 | | // The value |selection_control| is the selection control flag for the |
259 | | // selection merge instruction. |
260 | | // Note that the user must make sure the final basic block is |
261 | | // well formed. |
262 | | Instruction* AddSwitch( |
263 | | uint32_t selector_id, uint32_t default_id, |
264 | | const std::vector<std::pair<Operand::OperandData, uint32_t>>& targets, |
265 | | uint32_t merge_id = kInvalidId, |
266 | | uint32_t selection_control = |
267 | 5.68k | static_cast<uint32_t>(spv::SelectionControlMask::MaskNone)) { |
268 | 5.68k | if (merge_id != kInvalidId) { |
269 | 5.68k | AddSelectionMerge(merge_id, selection_control); |
270 | 5.68k | } |
271 | 5.68k | std::vector<Operand> operands; |
272 | 5.68k | operands.emplace_back( |
273 | 5.68k | Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {selector_id}}); |
274 | 5.68k | operands.emplace_back( |
275 | 5.68k | Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {default_id}}); |
276 | 5.68k | for (auto& target : targets) { |
277 | 0 | operands.emplace_back( |
278 | 0 | Operand{spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, |
279 | 0 | target.first}); |
280 | 0 | operands.emplace_back( |
281 | 0 | Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {target.second}}); |
282 | 0 | } |
283 | 5.68k | std::unique_ptr<Instruction> new_switch( |
284 | 5.68k | new Instruction(GetContext(), spv::Op::OpSwitch, 0, 0, operands)); |
285 | 5.68k | return AddInstruction(std::move(new_switch)); |
286 | 5.68k | } |
287 | | |
288 | | // Creates a phi instruction. |
289 | | // The id |type| must be the id of the phi instruction's type. |
290 | | // The vector |incomings| must be a sequence of pairs of <definition id, |
291 | | // parent id>. |
292 | | Instruction* AddPhi(uint32_t type, const std::vector<uint32_t>& incomings, |
293 | 75 | uint32_t result = 0) { |
294 | 75 | assert(incomings.size() % 2 == 0 && "A sequence of pairs is expected"); |
295 | 75 | return AddNaryOp(type, spv::Op::OpPhi, incomings, result); |
296 | 75 | } |
297 | | |
298 | | // Creates an addition instruction. |
299 | | // The id |type| must be the id of the instruction's type, must be the same as |
300 | | // |op1| and |op2| types. |
301 | | // The id |op1| is the left hand side of the operation. |
302 | | // The id |op2| is the right hand side of the operation. |
303 | 0 | Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) { |
304 | 0 | uint32_t result_id = GetContext()->TakeNextId(); |
305 | 0 | if (result_id == 0) { |
306 | 0 | return nullptr; |
307 | 0 | } |
308 | 0 | std::unique_ptr<Instruction> inst(new Instruction( |
309 | 0 | GetContext(), spv::Op::OpIAdd, type, result_id, |
310 | 0 | {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); |
311 | 0 | return AddInstruction(std::move(inst)); |
312 | 0 | } |
313 | | |
314 | | // Creates a less than instruction for unsigned integer. |
315 | | // The id |op1| is the left hand side of the operation. |
316 | | // The id |op2| is the right hand side of the operation. |
317 | | // It is assumed that |op1| and |op2| have the same underlying type. |
318 | 0 | Instruction* AddULessThan(uint32_t op1, uint32_t op2) { |
319 | 0 | analysis::Bool bool_type; |
320 | 0 | uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); |
321 | 0 | uint32_t result_id = GetContext()->TakeNextId(); |
322 | 0 | if (result_id == 0) { |
323 | 0 | return nullptr; |
324 | 0 | } |
325 | 0 | std::unique_ptr<Instruction> inst(new Instruction( |
326 | 0 | GetContext(), spv::Op::OpULessThan, type, result_id, |
327 | 0 | {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); |
328 | 0 | return AddInstruction(std::move(inst)); |
329 | 0 | } |
330 | | |
331 | | // Creates a less than instruction for signed integer. |
332 | | // The id |op1| is the left hand side of the operation. |
333 | | // The id |op2| is the right hand side of the operation. |
334 | | // It is assumed that |op1| and |op2| have the same underlying type. |
335 | 0 | Instruction* AddSLessThan(uint32_t op1, uint32_t op2) { |
336 | 0 | analysis::Bool bool_type; |
337 | 0 | uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); |
338 | 0 | uint32_t result_id = GetContext()->TakeNextId(); |
339 | 0 | if (result_id == 0) { |
340 | 0 | return nullptr; |
341 | 0 | } |
342 | 0 | std::unique_ptr<Instruction> inst(new Instruction( |
343 | 0 | GetContext(), spv::Op::OpSLessThan, type, result_id, |
344 | 0 | {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); |
345 | 0 | return AddInstruction(std::move(inst)); |
346 | 0 | } |
347 | | |
348 | | // Creates an OpILessThan or OpULessThen instruction depending on the sign of |
349 | | // |op1|. The id |op1| is the left hand side of the operation. The id |op2| is |
350 | | // the right hand side of the operation. It is assumed that |op1| and |op2| |
351 | | // have the same underlying type. |
352 | 0 | Instruction* AddLessThan(uint32_t op1, uint32_t op2) { |
353 | 0 | Instruction* op1_insn = context_->get_def_use_mgr()->GetDef(op1); |
354 | 0 | analysis::Type* type = |
355 | 0 | GetContext()->get_type_mgr()->GetType(op1_insn->type_id()); |
356 | 0 | analysis::Integer* int_type = type->AsInteger(); |
357 | 0 | assert(int_type && "Operand is not of int type"); |
358 | | |
359 | 0 | if (int_type->IsSigned()) |
360 | 0 | return AddSLessThan(op1, op2); |
361 | 0 | else |
362 | 0 | return AddULessThan(op1, op2); |
363 | 0 | } |
364 | | |
365 | | // Creates a select instruction. |
366 | | // |type| must match the types of |true_value| and |false_value|. It is up to |
367 | | // the caller to ensure that |cond| is a correct type (bool or vector of |
368 | | // bool) for |type|. |
369 | | Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value, |
370 | 9.73k | uint32_t false_value) { |
371 | 9.73k | uint32_t result_id = GetContext()->TakeNextId(); |
372 | 9.73k | if (result_id == 0) { |
373 | 0 | return nullptr; |
374 | 0 | } |
375 | 9.73k | std::unique_ptr<Instruction> select(new Instruction( |
376 | 9.73k | GetContext(), spv::Op::OpSelect, type, result_id, |
377 | 9.73k | std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cond}}, |
378 | 9.73k | {SPV_OPERAND_TYPE_ID, {true_value}}, |
379 | 9.73k | {SPV_OPERAND_TYPE_ID, {false_value}}})); |
380 | 9.73k | return AddInstruction(std::move(select)); |
381 | 9.73k | } |
382 | | |
383 | | // Returns a pointer to the definition of a signed 32-bit integer constant |
384 | | // with the given value. Returns |nullptr| if the constant does not exist and |
385 | | // cannot be created. |
386 | 0 | Instruction* GetSintConstant(int32_t value) { |
387 | 0 | return GetIntConstant<int32_t>(value, true); |
388 | 0 | } |
389 | | |
390 | | // Create a composite construct. |
391 | | // |type| should be a composite type and the number of elements it has should |
392 | | // match the size od |ids|. |
393 | | Instruction* AddCompositeConstruct(uint32_t type, |
394 | 10.0k | const std::vector<uint32_t>& ids) { |
395 | 10.0k | std::vector<Operand> ops; |
396 | 20.4k | for (auto id : ids) { |
397 | 20.4k | ops.emplace_back(SPV_OPERAND_TYPE_ID, |
398 | 20.4k | std::initializer_list<uint32_t>{id}); |
399 | 20.4k | } |
400 | 10.0k | uint32_t result_id = GetContext()->TakeNextId(); |
401 | 10.0k | if (result_id == 0) { |
402 | 0 | return nullptr; |
403 | 0 | } |
404 | 10.0k | std::unique_ptr<Instruction> construct(new Instruction( |
405 | 10.0k | GetContext(), spv::Op::OpCompositeConstruct, type, result_id, ops)); |
406 | 10.0k | return AddInstruction(std::move(construct)); |
407 | 10.0k | } |
408 | | |
409 | | // Returns a pointer to the definition of an unsigned 32-bit integer constant |
410 | | // with the given value. Returns |nullptr| if the constant does not exist and |
411 | | // cannot be created. |
412 | 5.73k | Instruction* GetUintConstant(uint32_t value) { |
413 | 5.73k | return GetIntConstant<uint32_t>(value, false); |
414 | 5.73k | } |
415 | | |
416 | 5.68k | uint32_t GetUintConstantId(uint32_t value) { |
417 | 5.68k | Instruction* uint_inst = GetUintConstant(value); |
418 | 5.68k | return (uint_inst != nullptr ? uint_inst->result_id() : 0); |
419 | 5.68k | } |
420 | | |
421 | | // Adds either a signed or unsigned 32 bit integer constant to the binary |
422 | | // depending on the |sign|. If |sign| is true then the value is added as a |
423 | | // signed constant otherwise as an unsigned constant. If |sign| is false the |
424 | | // value must not be a negative number. Returns false if the constant does |
425 | | // not exists and could be be created. |
426 | | template <typename T> |
427 | 5.73k | Instruction* GetIntConstant(T value, bool sign) { |
428 | | // Assert that we are not trying to store a negative number in an unsigned |
429 | | // type. |
430 | 5.73k | if (!sign) |
431 | 5.73k | assert(value >= 0 && |
432 | 5.73k | "Trying to add a signed integer with an unsigned type!"); |
433 | | |
434 | 5.73k | analysis::Integer int_type{32, sign}; |
435 | | |
436 | | // Get or create the integer type. This rebuilds the type and manages the |
437 | | // memory for the rebuilt type. |
438 | 5.73k | uint32_t type_id = |
439 | 5.73k | GetContext()->get_type_mgr()->GetTypeInstruction(&int_type); |
440 | | |
441 | 5.73k | if (type_id == 0) { |
442 | 0 | return nullptr; |
443 | 0 | } |
444 | | |
445 | | // Get the memory managed type so that it is safe to be stored by |
446 | | // GetConstant. |
447 | 5.73k | analysis::Type* rebuilt_type = |
448 | 5.73k | GetContext()->get_type_mgr()->GetType(type_id); |
449 | | |
450 | | // Even if the value is negative we need to pass the bit pattern as a |
451 | | // uint32_t to GetConstant. |
452 | 5.73k | uint32_t word = value; |
453 | | |
454 | | // Create the constant value. |
455 | 5.73k | const analysis::Constant* constant = |
456 | 5.73k | GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word}); |
457 | | |
458 | | // Create the OpConstant instruction using the type and the value. |
459 | 5.73k | return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant); |
460 | 5.73k | } Unexecuted instantiation: spvtools::opt::Instruction* spvtools::opt::InstructionBuilder::GetIntConstant<int>(int, bool) spvtools::opt::Instruction* spvtools::opt::InstructionBuilder::GetIntConstant<unsigned int>(unsigned int, bool) Line | Count | Source | 427 | 5.73k | Instruction* GetIntConstant(T value, bool sign) { | 428 | | // Assert that we are not trying to store a negative number in an unsigned | 429 | | // type. | 430 | 5.73k | if (!sign) | 431 | 5.73k | assert(value >= 0 && | 432 | 5.73k | "Trying to add a signed integer with an unsigned type!"); | 433 | | | 434 | 5.73k | analysis::Integer int_type{32, sign}; | 435 | | | 436 | | // Get or create the integer type. This rebuilds the type and manages the | 437 | | // memory for the rebuilt type. | 438 | 5.73k | uint32_t type_id = | 439 | 5.73k | GetContext()->get_type_mgr()->GetTypeInstruction(&int_type); | 440 | | | 441 | 5.73k | if (type_id == 0) { | 442 | 0 | return nullptr; | 443 | 0 | } | 444 | | | 445 | | // Get the memory managed type so that it is safe to be stored by | 446 | | // GetConstant. | 447 | 5.73k | analysis::Type* rebuilt_type = | 448 | 5.73k | GetContext()->get_type_mgr()->GetType(type_id); | 449 | | | 450 | | // Even if the value is negative we need to pass the bit pattern as a | 451 | | // uint32_t to GetConstant. | 452 | 5.73k | uint32_t word = value; | 453 | | | 454 | | // Create the constant value. | 455 | 5.73k | const analysis::Constant* constant = | 456 | 5.73k | GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word}); | 457 | | | 458 | | // Create the OpConstant instruction using the type and the value. | 459 | 5.73k | return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant); | 460 | 5.73k | } |
|
461 | | |
462 | 0 | Instruction* GetBoolConstant(bool value) { |
463 | 0 | analysis::Bool type; |
464 | 0 | uint32_t type_id = GetContext()->get_type_mgr()->GetTypeInstruction(&type); |
465 | 0 | analysis::Type* rebuilt_type = |
466 | 0 | GetContext()->get_type_mgr()->GetType(type_id); |
467 | 0 | uint32_t word = value; |
468 | 0 | const analysis::Constant* constant = |
469 | 0 | GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word}); |
470 | 0 | return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant); |
471 | 0 | } |
472 | | |
473 | 0 | uint32_t GetBoolConstantId(bool value) { |
474 | 0 | Instruction* inst = GetBoolConstant(value); |
475 | 0 | return (inst != nullptr ? inst->result_id() : 0); |
476 | 0 | } |
477 | | |
478 | | Instruction* AddCompositeExtract(uint32_t type, uint32_t id_of_composite, |
479 | 0 | const std::vector<uint32_t>& index_list) { |
480 | 0 | std::vector<Operand> operands; |
481 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {id_of_composite}}); |
482 | |
|
483 | 0 | for (uint32_t index : index_list) { |
484 | 0 | operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}); |
485 | 0 | } |
486 | |
|
487 | 0 | uint32_t result_id = GetContext()->TakeNextId(); |
488 | 0 | if (result_id == 0) { |
489 | 0 | return nullptr; |
490 | 0 | } |
491 | 0 | std::unique_ptr<Instruction> new_inst(new Instruction( |
492 | 0 | GetContext(), spv::Op::OpCompositeExtract, type, result_id, operands)); |
493 | 0 | return AddInstruction(std::move(new_inst)); |
494 | 0 | } |
495 | | |
496 | | // Creates an unreachable instruction. |
497 | 0 | Instruction* AddUnreachable() { |
498 | 0 | std::unique_ptr<Instruction> select( |
499 | 0 | new Instruction(GetContext(), spv::Op::OpUnreachable, 0, 0, |
500 | 0 | std::initializer_list<Operand>{})); |
501 | 0 | return AddInstruction(std::move(select)); |
502 | 0 | } |
503 | | |
504 | | Instruction* AddOpcodeAccessChain(spv::Op opcode, uint32_t type_id, |
505 | | uint32_t base_ptr_id, |
506 | 173 | const std::vector<uint32_t>& ids) { |
507 | 173 | assert(opcode == spv::Op::OpAccessChain || |
508 | 173 | opcode == spv::Op::OpInBoundsAccessChain); |
509 | 173 | std::vector<Operand> operands; |
510 | 173 | operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}}); |
511 | | |
512 | 173 | for (uint32_t index_id : ids) { |
513 | 173 | operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}}); |
514 | 173 | } |
515 | | |
516 | 173 | uint32_t result_id = GetContext()->TakeNextId(); |
517 | 173 | if (result_id == 0) { |
518 | 0 | return nullptr; |
519 | 0 | } |
520 | 173 | std::unique_ptr<Instruction> new_inst( |
521 | 173 | new Instruction(GetContext(), opcode, type_id, result_id, operands)); |
522 | 173 | return AddInstruction(std::move(new_inst)); |
523 | 173 | } |
524 | | |
525 | | Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id, |
526 | 173 | const std::vector<uint32_t>& ids) { |
527 | 173 | return AddOpcodeAccessChain(spv::Op::OpAccessChain, type_id, base_ptr_id, |
528 | 173 | ids); |
529 | 173 | } |
530 | | Instruction* AddInBoundsAccessChain(uint32_t type_id, uint32_t base_ptr_id, |
531 | 0 | const std::vector<uint32_t>& ids) { |
532 | 0 | return AddOpcodeAccessChain(spv::Op::OpInBoundsAccessChain, type_id, |
533 | 0 | base_ptr_id, ids); |
534 | 0 | } |
535 | | |
536 | | Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id, |
537 | 4.61k | uint32_t alignment = 0) { |
538 | 4.61k | std::vector<Operand> operands; |
539 | 4.61k | operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}}); |
540 | 4.61k | if (alignment != 0) { |
541 | 0 | operands.push_back( |
542 | 0 | {SPV_OPERAND_TYPE_MEMORY_ACCESS, |
543 | 0 | {static_cast<uint32_t>(spv::MemoryAccessMask::Aligned)}}); |
544 | 0 | operands.push_back({SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {alignment}}); |
545 | 0 | } |
546 | | |
547 | 4.61k | uint32_t result_id = GetContext()->TakeNextId(); |
548 | 4.61k | if (result_id == 0) { |
549 | 0 | return nullptr; |
550 | 0 | } |
551 | 4.61k | std::unique_ptr<Instruction> new_inst(new Instruction( |
552 | 4.61k | GetContext(), spv::Op::OpLoad, type_id, result_id, operands)); |
553 | 4.61k | return AddInstruction(std::move(new_inst)); |
554 | 4.61k | } |
555 | | |
556 | 0 | Instruction* AddCopyObject(uint32_t type_id, uint32_t value_id) { |
557 | 0 | std::vector<Operand> operands{{SPV_OPERAND_TYPE_ID, {value_id}}}; |
558 | 0 |
|
559 | 0 | uint32_t result_id = GetContext()->TakeNextId(); |
560 | 0 | if (result_id == 0) { |
561 | 0 | return nullptr; |
562 | 0 | } |
563 | 0 | std::unique_ptr<Instruction> new_inst(new Instruction( |
564 | 0 | GetContext(), spv::Op::OpCopyObject, type_id, result_id, operands)); |
565 | 0 | return AddInstruction(std::move(new_inst)); |
566 | 0 | } |
567 | | |
568 | 0 | Instruction* AddVariable(uint32_t type_id, uint32_t storage_class) { |
569 | 0 | std::vector<Operand> operands; |
570 | 0 | operands.push_back({SPV_OPERAND_TYPE_STORAGE_CLASS, {storage_class}}); |
571 | 0 | uint32_t result_id = GetContext()->TakeNextId(); |
572 | 0 | if (result_id == 0) { |
573 | 0 | return nullptr; |
574 | 0 | } |
575 | 0 | std::unique_ptr<Instruction> new_inst(new Instruction( |
576 | 0 | GetContext(), spv::Op::OpVariable, type_id, result_id, operands)); |
577 | 0 | return AddInstruction(std::move(new_inst)); |
578 | 0 | } |
579 | | |
580 | 0 | Instruction* AddStore(uint32_t ptr_id, uint32_t obj_id) { |
581 | 0 | std::vector<Operand> operands; |
582 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}}); |
583 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {obj_id}}); |
584 | |
|
585 | 0 | std::unique_ptr<Instruction> new_inst( |
586 | 0 | new Instruction(GetContext(), spv::Op::OpStore, 0, 0, operands)); |
587 | 0 | return AddInstruction(std::move(new_inst)); |
588 | 0 | } |
589 | | |
590 | | Instruction* AddFunctionCall(uint32_t result_type, uint32_t function, |
591 | 15 | const std::vector<uint32_t>& parameters) { |
592 | 15 | std::vector<Operand> operands; |
593 | 15 | operands.push_back({SPV_OPERAND_TYPE_ID, {function}}); |
594 | 15 | for (uint32_t id : parameters) { |
595 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); |
596 | 0 | } |
597 | | |
598 | 15 | uint32_t result_id = GetContext()->TakeNextId(); |
599 | 15 | if (result_id == 0) { |
600 | 0 | return nullptr; |
601 | 0 | } |
602 | 15 | std::unique_ptr<Instruction> new_inst( |
603 | 15 | new Instruction(GetContext(), spv::Op::OpFunctionCall, result_type, |
604 | 15 | result_id, operands)); |
605 | 15 | return AddInstruction(std::move(new_inst)); |
606 | 15 | } |
607 | | |
608 | | Instruction* AddVectorShuffle(uint32_t result_type, uint32_t vec1, |
609 | | uint32_t vec2, |
610 | 0 | const std::vector<uint32_t>& components) { |
611 | 0 | std::vector<Operand> operands; |
612 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {vec1}}); |
613 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {vec2}}); |
614 | 0 | for (uint32_t id : components) { |
615 | 0 | operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {id}}); |
616 | 0 | } |
617 | |
|
618 | 0 | uint32_t result_id = GetContext()->TakeNextId(); |
619 | 0 | if (result_id == 0) { |
620 | 0 | return nullptr; |
621 | 0 | } |
622 | | |
623 | 0 | std::unique_ptr<Instruction> new_inst( |
624 | 0 | new Instruction(GetContext(), spv::Op::OpVectorShuffle, result_type, |
625 | 0 | result_id, operands)); |
626 | 0 | return AddInstruction(std::move(new_inst)); |
627 | 0 | } |
628 | | |
629 | | Instruction* AddDecoration(uint32_t target_id, spv::Decoration d, |
630 | 0 | const std::vector<uint32_t>& literals) { |
631 | 0 | std::vector<Operand> operands; |
632 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {target_id}}); |
633 | 0 | operands.push_back({SPV_OPERAND_TYPE_DECORATION, {uint32_t(d)}}); |
634 | 0 | for (uint32_t literal : literals) { |
635 | 0 | operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {literal}}); |
636 | 0 | } |
637 | |
|
638 | 0 | std::unique_ptr<Instruction> new_inst( |
639 | 0 | new Instruction(GetContext(), spv::Op::OpDecorate, 0, 0, operands)); |
640 | | // Decorations are annotation instructions. Add it via the IR context, |
641 | | // so the decoration manager will be updated. |
642 | | // Decorations don't belong to basic blocks, so there is no need |
643 | | // to update the instruction to block mapping. |
644 | 0 | Instruction* result = new_inst.get(); |
645 | 0 | GetContext()->AddAnnotationInst(std::move(new_inst)); |
646 | 0 | return result; |
647 | 0 | } |
648 | | |
649 | | Instruction* AddNaryExtendedInstruction( |
650 | | uint32_t result_type, uint32_t set, uint32_t instruction, |
651 | 0 | const std::vector<uint32_t>& ext_operands) { |
652 | 0 | std::vector<Operand> operands; |
653 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {set}}); |
654 | 0 | operands.push_back( |
655 | 0 | {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {instruction}}); |
656 | 0 | for (uint32_t id : ext_operands) { |
657 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); |
658 | 0 | } |
659 | |
|
660 | 0 | uint32_t result_id = GetContext()->TakeNextId(); |
661 | 0 | if (result_id == 0) { |
662 | 0 | return nullptr; |
663 | 0 | } |
664 | | |
665 | 0 | std::unique_ptr<Instruction> new_inst(new Instruction( |
666 | 0 | GetContext(), spv::Op::OpExtInst, result_type, result_id, operands)); |
667 | 0 | return AddInstruction(std::move(new_inst)); |
668 | 0 | } |
669 | | |
670 | | Instruction* AddSampledImage(uint32_t sampled_image_type_id, |
671 | 0 | uint32_t image_id, uint32_t sampler_id) { |
672 | 0 | std::vector<Operand> operands; |
673 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {image_id}}); |
674 | 0 | operands.push_back({SPV_OPERAND_TYPE_ID, {sampler_id}}); |
675 | |
|
676 | 0 | uint32_t result_id = GetContext()->TakeNextId(); |
677 | 0 | if (result_id == 0) { |
678 | 0 | return nullptr; |
679 | 0 | } |
680 | | |
681 | 0 | std::unique_ptr<Instruction> new_inst( |
682 | 0 | new Instruction(GetContext(), spv::Op::OpSampledImage, |
683 | 0 | sampled_image_type_id, result_id, operands)); |
684 | 0 | return AddInstruction(std::move(new_inst)); |
685 | 0 | } |
686 | | |
687 | | // Inserts the new instruction before the insertion point. |
688 | 174k | Instruction* AddInstruction(std::unique_ptr<Instruction>&& insn) { |
689 | 174k | Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn)); |
690 | 174k | UpdateInstrToBlockMapping(insn_ptr); |
691 | 174k | UpdateDefUseMgr(insn_ptr); |
692 | 174k | return insn_ptr; |
693 | 174k | } |
694 | | |
695 | | // Returns the insertion point iterator. |
696 | 0 | InsertionPointTy GetInsertPoint() { return insert_before_; } |
697 | | |
698 | | // Change the insertion point to insert before the instruction |
699 | | // |insert_before|. |
700 | 0 | void SetInsertPoint(Instruction* insert_before) { |
701 | 0 | parent_ = context_->get_instr_block(insert_before); |
702 | 0 | insert_before_ = InsertionPointTy(insert_before); |
703 | 0 | } |
704 | | |
705 | | // Change the insertion point to insert at the end of the basic block |
706 | | // |parent_block|. |
707 | 0 | void SetInsertPoint(BasicBlock* parent_block) { |
708 | 0 | parent_ = parent_block; |
709 | 0 | insert_before_ = parent_block->end(); |
710 | 0 | } |
711 | | |
712 | | // Returns the context which instructions are constructed for. |
713 | 911k | IRContext* GetContext() const { return context_; } |
714 | | |
715 | | // Returns the set of preserved analyses. |
716 | 0 | inline IRContext::Analysis GetPreservedAnalysis() const { |
717 | 0 | return preserved_analyses_; |
718 | 0 | } |
719 | | |
720 | | private: |
721 | | InstructionBuilder(IRContext* context, BasicBlock* parent, |
722 | | InsertionPointTy insert_before, |
723 | | IRContext::Analysis preserved_analyses) |
724 | 176k | : context_(context), |
725 | 176k | parent_(parent), |
726 | 176k | insert_before_(insert_before), |
727 | 176k | preserved_analyses_(preserved_analyses) { |
728 | 176k | assert(!(preserved_analyses_ & ~(IRContext::kAnalysisDefUse | |
729 | 176k | IRContext::kAnalysisInstrToBlockMapping))); |
730 | 176k | } |
731 | | |
732 | | // Returns true if the users requested to update |analysis|. |
733 | 348k | inline bool IsAnalysisUpdateRequested(IRContext::Analysis analysis) const { |
734 | 348k | if (!GetContext()->AreAnalysesValid(analysis)) { |
735 | | // Do not try to update something that is not built. |
736 | 7.88k | return false; |
737 | 7.88k | } |
738 | 340k | return preserved_analyses_ & analysis; |
739 | 348k | } |
740 | | |
741 | | // Updates the def/use manager if the user requested it. If an update was not |
742 | | // requested, this function does nothing. |
743 | 174k | inline void UpdateDefUseMgr(Instruction* insn) { |
744 | 174k | if (IsAnalysisUpdateRequested(IRContext::kAnalysisDefUse)) |
745 | 174k | GetContext()->get_def_use_mgr()->AnalyzeInstDefUse(insn); |
746 | 174k | } |
747 | | |
748 | | // Updates the instruction to block analysis if the user requested it. If |
749 | | // an update was not requested, this function does nothing. |
750 | 174k | inline void UpdateInstrToBlockMapping(Instruction* insn) { |
751 | 174k | if (IsAnalysisUpdateRequested(IRContext::kAnalysisInstrToBlockMapping) && |
752 | 166k | parent_) |
753 | 166k | GetContext()->set_instr_block(insn, parent_); |
754 | 174k | } |
755 | | |
756 | | IRContext* context_; |
757 | | BasicBlock* parent_; |
758 | | InsertionPointTy insert_before_; |
759 | | const IRContext::Analysis preserved_analyses_; |
760 | | }; |
761 | | |
762 | | } // namespace opt |
763 | | } // namespace spvtools |
764 | | |
765 | | #endif // SOURCE_OPT_IR_BUILDER_H_ |