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 <limits> |
19 | #include <memory> |
20 | #include <utility> |
21 | #include <vector> |
22 | |
23 | #include "source/opt/basic_block.h" |
24 | #include "source/opt/constants.h" |
25 | #include "source/opt/instruction.h" |
26 | #include "source/opt/ir_context.h" |
27 | |
28 | namespace spvtools { |
29 | namespace opt { |
30 | |
31 | // In SPIR-V, ids are encoded as uint16_t, this id is guaranteed to be always |
32 | // invalid. |
33 | const uint32_t kInvalidId = std::numeric_limits<uint32_t>::max(); |
34 | |
35 | // Helper class to abstract instruction construction and insertion. |
36 | // The instruction builder can preserve the following analyses (specified via |
37 | // the constructors): |
38 | // - Def-use analysis |
39 | // - Instruction to block analysis |
40 | class InstructionBuilder { |
41 | public: |
42 | using InsertionPointTy = BasicBlock::iterator; |
43 | |
44 | // Creates an InstructionBuilder, all new instructions will be inserted before |
45 | // the instruction |insert_before|. |
46 | InstructionBuilder( |
47 | IRContext* context, Instruction* insert_before, |
48 | IRContext::Analysis preserved_analyses = IRContext::kAnalysisNone) |
49 | : InstructionBuilder(context, context->get_instr_block(insert_before), |
50 | InsertionPointTy(insert_before), |
51 | preserved_analyses) {} |
52 | |
53 | // Creates an InstructionBuilder, all new instructions will be inserted at the |
54 | // end of the basic block |parent_block|. |
55 | InstructionBuilder( |
56 | IRContext* context, BasicBlock* parent_block, |
57 | IRContext::Analysis preserved_analyses = IRContext::kAnalysisNone) |
58 | : InstructionBuilder(context, parent_block, parent_block->end(), |
59 | preserved_analyses) {} |
60 | |
61 | Instruction* AddNullaryOp(uint32_t type_id, SpvOp opcode) { |
62 | uint32_t result_id = 0; |
63 | if (type_id != 0) { |
64 | result_id = GetContext()->TakeNextId(); |
65 | if (result_id == 0) { |
66 | return nullptr; |
67 | } |
68 | } |
69 | std::unique_ptr<Instruction> new_inst( |
70 | new Instruction(GetContext(), opcode, type_id, result_id, {})); |
71 | return AddInstruction(std::move(new_inst)); |
72 | } |
73 | |
74 | Instruction* AddUnaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1) { |
75 | uint32_t result_id = 0; |
76 | if (type_id != 0) { |
77 | result_id = GetContext()->TakeNextId(); |
78 | if (result_id == 0) { |
79 | return nullptr; |
80 | } |
81 | } |
82 | std::unique_ptr<Instruction> newUnOp(new Instruction( |
83 | GetContext(), opcode, type_id, result_id, |
84 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}})); |
85 | return AddInstruction(std::move(newUnOp)); |
86 | } |
87 | |
88 | Instruction* AddBinaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, |
89 | uint32_t operand2) { |
90 | uint32_t result_id = 0; |
91 | if (type_id != 0) { |
92 | result_id = GetContext()->TakeNextId(); |
93 | if (result_id == 0) { |
94 | return nullptr; |
95 | } |
96 | } |
97 | std::unique_ptr<Instruction> newBinOp(new Instruction( |
98 | GetContext(), opcode, type_id, opcode == SpvOpStore ? 0 : result_id, |
99 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, |
100 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}})); |
101 | return AddInstruction(std::move(newBinOp)); |
102 | } |
103 | |
104 | Instruction* AddTernaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, |
105 | uint32_t operand2, uint32_t operand3) { |
106 | uint32_t result_id = 0; |
107 | if (type_id != 0) { |
108 | result_id = GetContext()->TakeNextId(); |
109 | if (result_id == 0) { |
110 | return nullptr; |
111 | } |
112 | } |
113 | std::unique_ptr<Instruction> newTernOp(new Instruction( |
114 | GetContext(), opcode, type_id, result_id, |
115 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, |
116 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, |
117 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}})); |
118 | return AddInstruction(std::move(newTernOp)); |
119 | } |
120 | |
121 | Instruction* AddQuadOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, |
122 | uint32_t operand2, uint32_t operand3, |
123 | uint32_t operand4) { |
124 | uint32_t result_id = 0; |
125 | if (type_id != 0) { |
126 | result_id = GetContext()->TakeNextId(); |
127 | if (result_id == 0) { |
128 | return nullptr; |
129 | } |
130 | } |
131 | std::unique_ptr<Instruction> newQuadOp(new Instruction( |
132 | GetContext(), opcode, type_id, result_id, |
133 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, |
134 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, |
135 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}, |
136 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand4}}})); |
137 | return AddInstruction(std::move(newQuadOp)); |
138 | } |
139 | |
140 | Instruction* AddIdLiteralOp(uint32_t type_id, SpvOp opcode, uint32_t id, |
141 | uint32_t uliteral) { |
142 | uint32_t result_id = 0; |
143 | if (type_id != 0) { |
144 | result_id = GetContext()->TakeNextId(); |
145 | if (result_id == 0) { |
146 | return nullptr; |
147 | } |
148 | } |
149 | std::unique_ptr<Instruction> newBinOp(new Instruction( |
150 | GetContext(), opcode, type_id, result_id, |
151 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {id}}, |
152 | {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {uliteral}}})); |
153 | return AddInstruction(std::move(newBinOp)); |
154 | } |
155 | |
156 | // Creates an N-ary instruction of |opcode|. |
157 | // |typid| must be the id of the instruction's type. |
158 | // |operands| must be a sequence of operand ids. |
159 | // Use |result| for the result id if non-zero. |
160 | Instruction* AddNaryOp(uint32_t type_id, SpvOp opcode, |
161 | const std::vector<uint32_t>& operands, |
162 | uint32_t result = 0) { |
163 | std::vector<Operand> ops; |
164 | for (size_t i = 0; i < operands.size(); i++) { |
165 | ops.push_back({SPV_OPERAND_TYPE_ID, {operands[i]}}); |
166 | } |
167 | // TODO(1841): Handle id overflow. |
168 | std::unique_ptr<Instruction> new_inst(new Instruction( |
169 | GetContext(), opcode, type_id, |
170 | result != 0 ? result : GetContext()->TakeNextId(), ops)); |
171 | return AddInstruction(std::move(new_inst)); |
172 | } |
173 | |
174 | // Creates a new selection merge instruction. |
175 | // The id |merge_id| is the merge basic block id. |
176 | Instruction* AddSelectionMerge( |
177 | uint32_t merge_id, |
178 | uint32_t selection_control = SpvSelectionControlMaskNone) { |
179 | std::unique_ptr<Instruction> new_branch_merge(new Instruction( |
180 | GetContext(), SpvOpSelectionMerge, 0, 0, |
181 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, |
182 | {spv_operand_type_t::SPV_OPERAND_TYPE_SELECTION_CONTROL, |
183 | {selection_control}}})); |
184 | return AddInstruction(std::move(new_branch_merge)); |
185 | } |
186 | |
187 | // Creates a new loop merge instruction. |
188 | // The id |merge_id| is the basic block id of the merge block. |
189 | // |continue_id| is the id of the continue block. |
190 | // |loop_control| are the loop control flags to be added to the instruction. |
191 | Instruction* AddLoopMerge(uint32_t merge_id, uint32_t continue_id, |
192 | uint32_t loop_control = SpvLoopControlMaskNone) { |
193 | std::unique_ptr<Instruction> new_branch_merge(new Instruction( |
194 | GetContext(), SpvOpLoopMerge, 0, 0, |
195 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, |
196 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}}, |
197 | {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {loop_control}}})); |
198 | return AddInstruction(std::move(new_branch_merge)); |
199 | } |
200 | |
201 | // Creates a new branch instruction to |label_id|. |
202 | // Note that the user must make sure the final basic block is |
203 | // well formed. |
204 | Instruction* AddBranch(uint32_t label_id) { |
205 | std::unique_ptr<Instruction> new_branch(new Instruction( |
206 | GetContext(), SpvOpBranch, 0, 0, |
207 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}})); |
208 | return AddInstruction(std::move(new_branch)); |
209 | } |
210 | |
211 | // Creates a new conditional instruction and the associated selection merge |
212 | // instruction if requested. |
213 | // The id |cond_id| is the id of the condition instruction, must be of |
214 | // type bool. |
215 | // The id |true_id| is the id of the basic block to branch to if the condition |
216 | // is true. |
217 | // The id |false_id| is the id of the basic block to branch to if the |
218 | // condition is false. |
219 | // The id |merge_id| is the id of the merge basic block for the selection |
220 | // merge instruction. If |merge_id| equals kInvalidId then no selection merge |
221 | // instruction will be created. |
222 | // The value |selection_control| is the selection control flag for the |
223 | // selection merge instruction. |
224 | // Note that the user must make sure the final basic block is |
225 | // well formed. |
226 | Instruction* AddConditionalBranch( |
227 | uint32_t cond_id, uint32_t true_id, uint32_t false_id, |
228 | uint32_t merge_id = kInvalidId, |
229 | uint32_t selection_control = SpvSelectionControlMaskNone) { |
230 | if (merge_id != kInvalidId) { |
231 | AddSelectionMerge(merge_id, selection_control); |
232 | } |
233 | std::unique_ptr<Instruction> new_branch(new Instruction( |
234 | GetContext(), SpvOpBranchConditional, 0, 0, |
235 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}}, |
236 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}}, |
237 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}})); |
238 | return AddInstruction(std::move(new_branch)); |
239 | } |
240 | |
241 | // Creates a new switch instruction and the associated selection merge |
242 | // instruction if requested. |
243 | // The id |selector_id| is the id of the selector instruction, must be of |
244 | // type int. |
245 | // The id |default_id| is the id of the default basic block to branch to. |
246 | // The vector |targets| is the pair of literal/branch id. |
247 | // The id |merge_id| is the id of the merge basic block for the selection |
248 | // merge instruction. If |merge_id| equals kInvalidId then no selection merge |
249 | // instruction will be created. |
250 | // The value |selection_control| is the selection control flag for the |
251 | // selection merge instruction. |
252 | // Note that the user must make sure the final basic block is |
253 | // well formed. |
254 | Instruction* AddSwitch( |
255 | uint32_t selector_id, uint32_t default_id, |
256 | const std::vector<std::pair<Operand::OperandData, uint32_t>>& targets, |
257 | uint32_t merge_id = kInvalidId, |
258 | uint32_t selection_control = SpvSelectionControlMaskNone) { |
259 | if (merge_id != kInvalidId) { |
260 | AddSelectionMerge(merge_id, selection_control); |
261 | } |
262 | std::vector<Operand> operands; |
263 | operands.emplace_back( |
264 | Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {selector_id}}); |
265 | operands.emplace_back( |
266 | Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {default_id}}); |
267 | for (auto& target : targets) { |
268 | operands.emplace_back( |
269 | Operand{spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, |
270 | target.first}); |
271 | operands.emplace_back( |
272 | Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {target.second}}); |
273 | } |
274 | std::unique_ptr<Instruction> new_switch( |
275 | new Instruction(GetContext(), SpvOpSwitch, 0, 0, operands)); |
276 | return AddInstruction(std::move(new_switch)); |
277 | } |
278 | |
279 | // Creates a phi instruction. |
280 | // The id |type| must be the id of the phi instruction's type. |
281 | // The vector |incomings| must be a sequence of pairs of <definition id, |
282 | // parent id>. |
283 | Instruction* AddPhi(uint32_t type, const std::vector<uint32_t>& incomings, |
284 | uint32_t result = 0) { |
285 | assert(incomings.size() % 2 == 0 && "A sequence of pairs is expected" ); |
286 | return AddNaryOp(type, SpvOpPhi, incomings, result); |
287 | } |
288 | |
289 | // Creates an addition instruction. |
290 | // The id |type| must be the id of the instruction's type, must be the same as |
291 | // |op1| and |op2| types. |
292 | // The id |op1| is the left hand side of the operation. |
293 | // The id |op2| is the right hand side of the operation. |
294 | Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) { |
295 | // TODO(1841): Handle id overflow. |
296 | std::unique_ptr<Instruction> inst(new Instruction( |
297 | GetContext(), SpvOpIAdd, type, GetContext()->TakeNextId(), |
298 | {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); |
299 | return AddInstruction(std::move(inst)); |
300 | } |
301 | |
302 | // Creates a less than instruction for unsigned integer. |
303 | // The id |op1| is the left hand side of the operation. |
304 | // The id |op2| is the right hand side of the operation. |
305 | // It is assumed that |op1| and |op2| have the same underlying type. |
306 | Instruction* AddULessThan(uint32_t op1, uint32_t op2) { |
307 | analysis::Bool bool_type; |
308 | uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); |
309 | // TODO(1841): Handle id overflow. |
310 | std::unique_ptr<Instruction> inst(new Instruction( |
311 | GetContext(), SpvOpULessThan, type, GetContext()->TakeNextId(), |
312 | {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); |
313 | return AddInstruction(std::move(inst)); |
314 | } |
315 | |
316 | // Creates a less than instruction for signed integer. |
317 | // The id |op1| is the left hand side of the operation. |
318 | // The id |op2| is the right hand side of the operation. |
319 | // It is assumed that |op1| and |op2| have the same underlying type. |
320 | Instruction* AddSLessThan(uint32_t op1, uint32_t op2) { |
321 | analysis::Bool bool_type; |
322 | uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); |
323 | // TODO(1841): Handle id overflow. |
324 | std::unique_ptr<Instruction> inst(new Instruction( |
325 | GetContext(), SpvOpSLessThan, type, GetContext()->TakeNextId(), |
326 | {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); |
327 | return AddInstruction(std::move(inst)); |
328 | } |
329 | |
330 | // Creates an OpILessThan or OpULessThen instruction depending on the sign of |
331 | // |op1|. The id |op1| is the left hand side of the operation. The id |op2| is |
332 | // the right hand side of the operation. It is assumed that |op1| and |op2| |
333 | // have the same underlying type. |
334 | Instruction* AddLessThan(uint32_t op1, uint32_t op2) { |
335 | Instruction* op1_insn = context_->get_def_use_mgr()->GetDef(op1); |
336 | analysis::Type* type = |
337 | GetContext()->get_type_mgr()->GetType(op1_insn->type_id()); |
338 | analysis::Integer* int_type = type->AsInteger(); |
339 | assert(int_type && "Operand is not of int type" ); |
340 | |
341 | if (int_type->IsSigned()) |
342 | return AddSLessThan(op1, op2); |
343 | else |
344 | return AddULessThan(op1, op2); |
345 | } |
346 | |
347 | // Creates a select instruction. |
348 | // |type| must match the types of |true_value| and |false_value|. It is up to |
349 | // the caller to ensure that |cond| is a correct type (bool or vector of |
350 | // bool) for |type|. |
351 | Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value, |
352 | uint32_t false_value) { |
353 | // TODO(1841): Handle id overflow. |
354 | std::unique_ptr<Instruction> select(new Instruction( |
355 | GetContext(), SpvOpSelect, type, GetContext()->TakeNextId(), |
356 | std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cond}}, |
357 | {SPV_OPERAND_TYPE_ID, {true_value}}, |
358 | {SPV_OPERAND_TYPE_ID, {false_value}}})); |
359 | return AddInstruction(std::move(select)); |
360 | } |
361 | |
362 | // Adds a signed int32 constant to the binary. |
363 | // The |value| parameter is the constant value to be added. |
364 | Instruction* GetSintConstant(int32_t value) { |
365 | return GetIntConstant<int32_t>(value, true); |
366 | } |
367 | |
368 | // Create a composite construct. |
369 | // |type| should be a composite type and the number of elements it has should |
370 | // match the size od |ids|. |
371 | Instruction* AddCompositeConstruct(uint32_t type, |
372 | const std::vector<uint32_t>& ids) { |
373 | std::vector<Operand> ops; |
374 | for (auto id : ids) { |
375 | ops.emplace_back(SPV_OPERAND_TYPE_ID, |
376 | std::initializer_list<uint32_t>{id}); |
377 | } |
378 | // TODO(1841): Handle id overflow. |
379 | std::unique_ptr<Instruction> construct( |
380 | new Instruction(GetContext(), SpvOpCompositeConstruct, type, |
381 | GetContext()->TakeNextId(), ops)); |
382 | return AddInstruction(std::move(construct)); |
383 | } |
384 | // Adds an unsigned int32 constant to the binary. |
385 | // The |value| parameter is the constant value to be added. |
386 | Instruction* GetUintConstant(uint32_t value) { |
387 | return GetIntConstant<uint32_t>(value, false); |
388 | } |
389 | |
390 | uint32_t GetUintConstantId(uint32_t value) { |
391 | Instruction* uint_inst = GetUintConstant(value); |
392 | return uint_inst->result_id(); |
393 | } |
394 | |
395 | // Adds either a signed or unsigned 32 bit integer constant to the binary |
396 | // depedning on the |sign|. If |sign| is true then the value is added as a |
397 | // signed constant otherwise as an unsigned constant. If |sign| is false the |
398 | // value must not be a negative number. |
399 | template <typename T> |
400 | Instruction* GetIntConstant(T value, bool sign) { |
401 | // Assert that we are not trying to store a negative number in an unsigned |
402 | // type. |
403 | if (!sign) |
404 | assert(value >= 0 && |
405 | "Trying to add a signed integer with an unsigned type!" ); |
406 | |
407 | analysis::Integer int_type{32, sign}; |
408 | |
409 | // Get or create the integer type. This rebuilds the type and manages the |
410 | // memory for the rebuilt type. |
411 | uint32_t type_id = |
412 | GetContext()->get_type_mgr()->GetTypeInstruction(&int_type); |
413 | |
414 | // Get the memory managed type so that it is safe to be stored by |
415 | // GetConstant. |
416 | analysis::Type* rebuilt_type = |
417 | GetContext()->get_type_mgr()->GetType(type_id); |
418 | |
419 | // Even if the value is negative we need to pass the bit pattern as a |
420 | // uint32_t to GetConstant. |
421 | uint32_t word = value; |
422 | |
423 | // Create the constant value. |
424 | const analysis::Constant* constant = |
425 | GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word}); |
426 | |
427 | // Create the OpConstant instruction using the type and the value. |
428 | return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant); |
429 | } |
430 | |
431 | Instruction* (uint32_t type, uint32_t id_of_composite, |
432 | const std::vector<uint32_t>& index_list) { |
433 | std::vector<Operand> operands; |
434 | operands.push_back({SPV_OPERAND_TYPE_ID, {id_of_composite}}); |
435 | |
436 | for (uint32_t index : index_list) { |
437 | operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}); |
438 | } |
439 | |
440 | // TODO(1841): Handle id overflow. |
441 | std::unique_ptr<Instruction> new_inst( |
442 | new Instruction(GetContext(), SpvOpCompositeExtract, type, |
443 | GetContext()->TakeNextId(), operands)); |
444 | return AddInstruction(std::move(new_inst)); |
445 | } |
446 | |
447 | // Creates an unreachable instruction. |
448 | Instruction* AddUnreachable() { |
449 | std::unique_ptr<Instruction> select( |
450 | new Instruction(GetContext(), SpvOpUnreachable, 0, 0, |
451 | std::initializer_list<Operand>{})); |
452 | return AddInstruction(std::move(select)); |
453 | } |
454 | |
455 | Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id, |
456 | std::vector<uint32_t> ids) { |
457 | std::vector<Operand> operands; |
458 | operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}}); |
459 | |
460 | for (uint32_t index_id : ids) { |
461 | operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}}); |
462 | } |
463 | |
464 | // TODO(1841): Handle id overflow. |
465 | std::unique_ptr<Instruction> new_inst( |
466 | new Instruction(GetContext(), SpvOpAccessChain, type_id, |
467 | GetContext()->TakeNextId(), operands)); |
468 | return AddInstruction(std::move(new_inst)); |
469 | } |
470 | |
471 | Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id) { |
472 | std::vector<Operand> operands; |
473 | operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}}); |
474 | |
475 | // TODO(1841): Handle id overflow. |
476 | std::unique_ptr<Instruction> new_inst( |
477 | new Instruction(GetContext(), SpvOpLoad, type_id, |
478 | GetContext()->TakeNextId(), operands)); |
479 | return AddInstruction(std::move(new_inst)); |
480 | } |
481 | |
482 | Instruction* AddStore(uint32_t ptr_id, uint32_t obj_id) { |
483 | std::vector<Operand> operands; |
484 | operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}}); |
485 | operands.push_back({SPV_OPERAND_TYPE_ID, {obj_id}}); |
486 | |
487 | std::unique_ptr<Instruction> new_inst( |
488 | new Instruction(GetContext(), SpvOpStore, 0, 0, operands)); |
489 | return AddInstruction(std::move(new_inst)); |
490 | } |
491 | |
492 | Instruction* AddFunctionCall(uint32_t result_type, uint32_t function, |
493 | const std::vector<uint32_t>& parameters) { |
494 | std::vector<Operand> operands; |
495 | operands.push_back({SPV_OPERAND_TYPE_ID, {function}}); |
496 | for (uint32_t id : parameters) { |
497 | operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); |
498 | } |
499 | |
500 | uint32_t result_id = GetContext()->TakeNextId(); |
501 | if (result_id == 0) { |
502 | return nullptr; |
503 | } |
504 | std::unique_ptr<Instruction> new_inst(new Instruction( |
505 | GetContext(), SpvOpFunctionCall, result_type, result_id, operands)); |
506 | return AddInstruction(std::move(new_inst)); |
507 | } |
508 | |
509 | Instruction* AddVectorShuffle(uint32_t result_type, uint32_t vec1, |
510 | uint32_t vec2, |
511 | const std::vector<uint32_t>& components) { |
512 | std::vector<Operand> operands; |
513 | operands.push_back({SPV_OPERAND_TYPE_ID, {vec1}}); |
514 | operands.push_back({SPV_OPERAND_TYPE_ID, {vec2}}); |
515 | for (uint32_t id : components) { |
516 | operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {id}}); |
517 | } |
518 | |
519 | uint32_t result_id = GetContext()->TakeNextId(); |
520 | if (result_id == 0) { |
521 | return nullptr; |
522 | } |
523 | |
524 | std::unique_ptr<Instruction> new_inst(new Instruction( |
525 | GetContext(), SpvOpVectorShuffle, result_type, result_id, operands)); |
526 | return AddInstruction(std::move(new_inst)); |
527 | } |
528 | |
529 | Instruction* AddNaryExtendedInstruction( |
530 | uint32_t result_type, uint32_t set, uint32_t instruction, |
531 | const std::vector<uint32_t>& ext_operands) { |
532 | std::vector<Operand> operands; |
533 | operands.push_back({SPV_OPERAND_TYPE_ID, {set}}); |
534 | operands.push_back( |
535 | {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {instruction}}); |
536 | for (uint32_t id : ext_operands) { |
537 | operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); |
538 | } |
539 | |
540 | uint32_t result_id = GetContext()->TakeNextId(); |
541 | if (result_id == 0) { |
542 | return nullptr; |
543 | } |
544 | |
545 | std::unique_ptr<Instruction> new_inst(new Instruction( |
546 | GetContext(), SpvOpExtInst, result_type, result_id, operands)); |
547 | return AddInstruction(std::move(new_inst)); |
548 | } |
549 | |
550 | // Inserts the new instruction before the insertion point. |
551 | Instruction* AddInstruction(std::unique_ptr<Instruction>&& insn) { |
552 | Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn)); |
553 | UpdateInstrToBlockMapping(insn_ptr); |
554 | UpdateDefUseMgr(insn_ptr); |
555 | return insn_ptr; |
556 | } |
557 | |
558 | // Returns the insertion point iterator. |
559 | InsertionPointTy GetInsertPoint() { return insert_before_; } |
560 | |
561 | // Change the insertion point to insert before the instruction |
562 | // |insert_before|. |
563 | void SetInsertPoint(Instruction* insert_before) { |
564 | parent_ = context_->get_instr_block(insert_before); |
565 | insert_before_ = InsertionPointTy(insert_before); |
566 | } |
567 | |
568 | // Change the insertion point to insert at the end of the basic block |
569 | // |parent_block|. |
570 | void SetInsertPoint(BasicBlock* parent_block) { |
571 | parent_ = parent_block; |
572 | insert_before_ = parent_block->end(); |
573 | } |
574 | |
575 | // Returns the context which instructions are constructed for. |
576 | IRContext* GetContext() const { return context_; } |
577 | |
578 | // Returns the set of preserved analyses. |
579 | inline IRContext::Analysis GetPreservedAnalysis() const { |
580 | return preserved_analyses_; |
581 | } |
582 | |
583 | private: |
584 | InstructionBuilder(IRContext* context, BasicBlock* parent, |
585 | InsertionPointTy insert_before, |
586 | IRContext::Analysis preserved_analyses) |
587 | : context_(context), |
588 | parent_(parent), |
589 | insert_before_(insert_before), |
590 | preserved_analyses_(preserved_analyses) { |
591 | assert(!(preserved_analyses_ & ~(IRContext::kAnalysisDefUse | |
592 | IRContext::kAnalysisInstrToBlockMapping))); |
593 | } |
594 | |
595 | // Returns true if the users requested to update |analysis|. |
596 | inline bool IsAnalysisUpdateRequested(IRContext::Analysis analysis) const { |
597 | if (!GetContext()->AreAnalysesValid(analysis)) { |
598 | // Do not try to update something that is not built. |
599 | return false; |
600 | } |
601 | return preserved_analyses_ & analysis; |
602 | } |
603 | |
604 | // Updates the def/use manager if the user requested it. If he did not request |
605 | // an update, this function does nothing. |
606 | inline void UpdateDefUseMgr(Instruction* insn) { |
607 | if (IsAnalysisUpdateRequested(IRContext::kAnalysisDefUse)) |
608 | GetContext()->get_def_use_mgr()->AnalyzeInstDefUse(insn); |
609 | } |
610 | |
611 | // Updates the instruction to block analysis if the user requested it. If he |
612 | // did not request an update, this function does nothing. |
613 | inline void UpdateInstrToBlockMapping(Instruction* insn) { |
614 | if (IsAnalysisUpdateRequested(IRContext::kAnalysisInstrToBlockMapping) && |
615 | parent_) |
616 | GetContext()->set_instr_block(insn, parent_); |
617 | } |
618 | |
619 | IRContext* context_; |
620 | BasicBlock* parent_; |
621 | InsertionPointTy insert_before_; |
622 | const IRContext::Analysis preserved_analyses_; |
623 | }; |
624 | |
625 | } // namespace opt |
626 | } // namespace spvtools |
627 | |
628 | #endif // SOURCE_OPT_IR_BUILDER_H_ |
629 | |