9c153deb684f8151c83a8570d57f58c30d1427f0
[platform/upstream/SPIRV-Tools.git] / source / opt / ir_builder.h
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 LIBSPIRV_OPT_IR_BUILDER_H_
16 #define LIBSPIRV_OPT_IR_BUILDER_H_
17
18 #include "opt/basic_block.h"
19 #include "opt/instruction.h"
20 #include "opt/ir_context.h"
21
22 namespace spvtools {
23 namespace opt {
24
25 // In SPIR-V, ids are encoded as uint16_t, this id is guaranteed to be always
26 // invalid.
27 const uint32_t kInvalidId = std::numeric_limits<uint32_t>::max();
28
29 // Helper class to abstract instruction construction and insertion.
30 // The instruction builder can preserve the following analyses (specified via
31 // the constructors):
32 //   - Def-use analysis
33 //   - Instruction to block analysis
34 class InstructionBuilder {
35  public:
36   using InsertionPointTy = spvtools::ir::BasicBlock::iterator;
37
38   // Creates an InstructionBuilder, all new instructions will be inserted before
39   // the instruction |insert_before|.
40   InstructionBuilder(
41       ir::IRContext* context, ir::Instruction* insert_before,
42       ir::IRContext::Analysis preserved_analyses = ir::IRContext::kAnalysisNone)
43       : InstructionBuilder(context, context->get_instr_block(insert_before),
44                            InsertionPointTy(insert_before),
45                            preserved_analyses) {}
46
47   // Creates an InstructionBuilder, all new instructions will be inserted at the
48   // end of the basic block |parent_block|.
49   InstructionBuilder(
50       ir::IRContext* context, ir::BasicBlock* parent_block,
51       ir::IRContext::Analysis preserved_analyses = ir::IRContext::kAnalysisNone)
52       : InstructionBuilder(context, parent_block, parent_block->end(),
53                            preserved_analyses) {}
54
55   // Creates a new selection merge instruction.
56   // The id |merge_id| is the merge basic block id.
57   ir::Instruction* AddSelectionMerge(
58       uint32_t merge_id,
59       uint32_t selection_control = SpvSelectionControlMaskNone) {
60     std::unique_ptr<ir::Instruction> new_branch_merge(new ir::Instruction(
61         GetContext(), SpvOpSelectionMerge, 0, 0,
62         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}},
63          {spv_operand_type_t::SPV_OPERAND_TYPE_SELECTION_CONTROL,
64           {selection_control}}}));
65     return AddInstruction(std::move(new_branch_merge));
66   }
67
68   // Creates a new branch instruction to |label_id|.
69   // Note that the user must make sure the final basic block is
70   // well formed.
71   ir::Instruction* AddBranch(uint32_t label_id) {
72     std::unique_ptr<ir::Instruction> new_branch(new ir::Instruction(
73         GetContext(), SpvOpBranch, 0, 0,
74         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}}));
75     return AddInstruction(std::move(new_branch));
76   }
77
78   // Creates a new conditional instruction and the associated selection merge
79   // instruction if requested.
80   // The id |cond_id| is the id of the condition instruction, must be of
81   // type bool.
82   // The id |true_id| is the id of the basic block to branch to if the condition
83   // is true.
84   // The id |false_id| is the id of the basic block to branch to if the
85   // condition is false.
86   // The id |merge_id| is the id of the merge basic block for the selection
87   // merge instruction. If |merge_id| equals kInvalidId then no selection merge
88   // instruction will be created.
89   // The value |selection_control| is the selection control flag for the
90   // selection merge instruction.
91   // Note that the user must make sure the final basic block is
92   // well formed.
93   ir::Instruction* AddConditionalBranch(
94       uint32_t cond_id, uint32_t true_id, uint32_t false_id,
95       uint32_t merge_id = kInvalidId,
96       uint32_t selection_control = SpvSelectionControlMaskNone) {
97     if (merge_id != kInvalidId) {
98       AddSelectionMerge(merge_id, selection_control);
99     }
100     std::unique_ptr<ir::Instruction> new_branch(new ir::Instruction(
101         GetContext(), SpvOpBranchConditional, 0, 0,
102         {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}},
103          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}},
104          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}}));
105     return AddInstruction(std::move(new_branch));
106   }
107
108   // Creates a phi instruction.
109   // The id |type| must be the id of the phi instruction's type.
110   // The vector |incomings| must be a sequence of pairs of <definition id,
111   // parent id>.
112   ir::Instruction* AddPhi(uint32_t type,
113                           const std::vector<uint32_t>& incomings) {
114     assert(incomings.size() % 2 == 0 && "A sequence of pairs is expected");
115     std::vector<ir::Operand> phi_ops;
116     for (size_t i = 0; i < incomings.size(); i++) {
117       phi_ops.push_back({SPV_OPERAND_TYPE_ID, {incomings[i]}});
118     }
119     std::unique_ptr<ir::Instruction> phi_inst(new ir::Instruction(
120         GetContext(), SpvOpPhi, type, GetContext()->TakeNextId(), phi_ops));
121     return AddInstruction(std::move(phi_inst));
122   }
123
124   // Creates a select instruction.
125   // |type| must match the types of |true_value| and |false_value|. It is up to
126   // the caller to ensure that |cond| is a correct type (bool or vector of
127   // bool) for |type|.
128   ir::Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value,
129                              uint32_t false_value) {
130     std::unique_ptr<ir::Instruction> select(new ir::Instruction(
131         GetContext(), SpvOpSelect, type, GetContext()->TakeNextId(),
132         std::initializer_list<ir::Operand>{
133             {SPV_OPERAND_TYPE_ID, {cond}},
134             {SPV_OPERAND_TYPE_ID, {true_value}},
135             {SPV_OPERAND_TYPE_ID, {false_value}}}));
136     return AddInstruction(std::move(select));
137   }
138
139   // Create a composite construct.
140   // |type| should be a composite type and the number of elements it has should
141   // match the size od |ids|.
142   ir::Instruction* AddCompositeConstruct(uint32_t type,
143                                          const std::vector<uint32_t>& ids) {
144     std::vector<ir::Operand> ops;
145     for (auto id : ids) {
146       ops.emplace_back(SPV_OPERAND_TYPE_ID,
147                        std::initializer_list<uint32_t>{id});
148     }
149     std::unique_ptr<ir::Instruction> construct(
150         new ir::Instruction(GetContext(), SpvOpCompositeConstruct, type,
151                             GetContext()->TakeNextId(), ops));
152     return AddInstruction(std::move(construct));
153   }
154
155   ir::Instruction* AddCompositeExtract(
156       uint32_t type, uint32_t id_of_composite,
157       const std::vector<uint32_t>& index_list) {
158     std::vector<ir::Operand> operands;
159     operands.push_back({SPV_OPERAND_TYPE_ID, {id_of_composite}});
160
161     for (uint32_t index : index_list) {
162       operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}});
163     }
164
165     std::unique_ptr<ir::Instruction> new_inst(
166         new ir::Instruction(GetContext(), SpvOpCompositeExtract, type,
167                             GetContext()->TakeNextId(), operands));
168     return AddInstruction(std::move(new_inst));
169   }
170
171   // Inserts the new instruction before the insertion point.
172   ir::Instruction* AddInstruction(std::unique_ptr<ir::Instruction>&& insn) {
173     ir::Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
174     UpdateInstrToBlockMapping(insn_ptr);
175     UpdateDefUseMgr(insn_ptr);
176     return insn_ptr;
177   }
178
179   // Returns the insertion point iterator.
180   InsertionPointTy GetInsertPoint() { return insert_before_; }
181
182   // Change the insertion point to insert before the instruction
183   // |insert_before|.
184   void SetInsertPoint(ir::Instruction* insert_before) {
185     parent_ = context_->get_instr_block(insert_before);
186     insert_before_ = InsertionPointTy(insert_before);
187   }
188
189   // Change the insertion point to insert at the end of the basic block
190   // |parent_block|.
191   void SetInsertPoint(ir::BasicBlock* parent_block) {
192     parent_ = parent_block;
193     insert_before_ = parent_block->end();
194   }
195
196   // Returns the context which instructions are constructed for.
197   ir::IRContext* GetContext() const { return context_; }
198
199   // Returns the set of preserved analyses.
200   inline ir::IRContext::Analysis GetPreservedAnalysis() const {
201     return preserved_analyses_;
202   }
203
204  private:
205   InstructionBuilder(ir::IRContext* context, ir::BasicBlock* parent,
206                      InsertionPointTy insert_before,
207                      ir::IRContext::Analysis preserved_analyses)
208       : context_(context),
209         parent_(parent),
210         insert_before_(insert_before),
211         preserved_analyses_(preserved_analyses) {
212     assert(!(preserved_analyses_ &
213              ~(ir::IRContext::kAnalysisDefUse |
214                ir::IRContext::kAnalysisInstrToBlockMapping)));
215   }
216
217   // Returns true if the users requested to update |analysis|.
218   inline bool IsAnalysisUpdateRequested(
219       ir::IRContext::Analysis analysis) const {
220     return preserved_analyses_ & analysis;
221   }
222
223   // Updates the def/use manager if the user requested it. If he did not request
224   // an update, this function does nothing.
225   inline void UpdateDefUseMgr(ir::Instruction* insn) {
226     if (IsAnalysisUpdateRequested(ir::IRContext::kAnalysisDefUse))
227       GetContext()->get_def_use_mgr()->AnalyzeInstDefUse(insn);
228   }
229
230   // Updates the instruction to block analysis if the user requested it. If he
231   // did not request an update, this function does nothing.
232   inline void UpdateInstrToBlockMapping(ir::Instruction* insn) {
233     if (IsAnalysisUpdateRequested(
234             ir::IRContext::kAnalysisInstrToBlockMapping) &&
235         parent_)
236       GetContext()->set_instr_block(insn, parent_);
237   }
238
239   ir::IRContext* context_;
240   ir::BasicBlock* parent_;
241   InsertionPointTy insert_before_;
242   const ir::IRContext::Analysis preserved_analyses_;
243 };
244
245 }  // namespace opt
246 }  // namespace spvtools
247
248 #endif  // LIBSPIRV_OPT_IR_BUILDER_H_