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