1 // Copyright (c) 2018 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #ifndef LIBSPIRV_OPT_IR_BUILDER_H_
16 #define LIBSPIRV_OPT_IR_BUILDER_H_
18 #include "opt/basic_block.h"
19 #include "opt/instruction.h"
20 #include "opt/ir_context.h"
25 // In SPIR-V, ids are encoded as uint16_t, this id is guaranteed to be always
27 const uint32_t kInvalidId = std::numeric_limits<uint32_t>::max();
29 // Helper class to abstract instruction construction and insertion.
30 // The instruction builder can preserve the following analyses (specified via
33 // - Instruction to block analysis
34 class InstructionBuilder {
36 using InsertionPointTy = spvtools::ir::BasicBlock::iterator;
38 // Creates an InstructionBuilder, all new instructions will be inserted before
39 // the instruction |insert_before|.
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) {}
47 // Creates an InstructionBuilder, all new instructions will be inserted at the
48 // end of the basic block |parent_block|.
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) {}
55 // Creates a new selection merge instruction.
56 // The id |merge_id| is the merge basic block id.
57 ir::Instruction* AddSelectionMerge(
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));
68 // Creates a new branch instruction to |label_id|.
69 // Note that the user must make sure the final basic block is
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));
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
82 // The id |true_id| is the id of the basic block to branch to if the condition
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
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);
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));
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,
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]}});
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));
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
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));
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});
149 std::unique_ptr<ir::Instruction> construct(
150 new ir::Instruction(GetContext(), SpvOpCompositeConstruct, type,
151 GetContext()->TakeNextId(), ops));
152 return AddInstruction(std::move(construct));
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}});
161 for (uint32_t index : index_list) {
162 operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}});
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));
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);
179 // Returns the insertion point iterator.
180 InsertionPointTy GetInsertPoint() { return insert_before_; }
182 // Change the insertion point to insert before the instruction
184 void SetInsertPoint(ir::Instruction* insert_before) {
185 parent_ = context_->get_instr_block(insert_before);
186 insert_before_ = InsertionPointTy(insert_before);
189 // Change the insertion point to insert at the end of the basic block
191 void SetInsertPoint(ir::BasicBlock* parent_block) {
192 parent_ = parent_block;
193 insert_before_ = parent_block->end();
196 // Returns the context which instructions are constructed for.
197 ir::IRContext* GetContext() const { return context_; }
199 // Returns the set of preserved analyses.
200 inline ir::IRContext::Analysis GetPreservedAnalysis() const {
201 return preserved_analyses_;
205 InstructionBuilder(ir::IRContext* context, ir::BasicBlock* parent,
206 InsertionPointTy insert_before,
207 ir::IRContext::Analysis preserved_analyses)
210 insert_before_(insert_before),
211 preserved_analyses_(preserved_analyses) {
212 assert(!(preserved_analyses_ &
213 ~(ir::IRContext::kAnalysisDefUse |
214 ir::IRContext::kAnalysisInstrToBlockMapping)));
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;
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);
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) &&
236 GetContext()->set_instr_block(insn, parent_);
239 ir::IRContext* context_;
240 ir::BasicBlock* parent_;
241 InsertionPointTy insert_before_;
242 const ir::IRContext::Analysis preserved_analyses_;
246 } // namespace spvtools
248 #endif // LIBSPIRV_OPT_IR_BUILDER_H_