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 // Inserts the new instruction before the insertion point.
156 ir::Instruction* AddInstruction(std::unique_ptr<ir::Instruction>&& insn) {
157 ir::Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn));
158 UpdateInstrToBlockMapping(insn_ptr);
159 UpdateDefUseMgr(insn_ptr);
163 // Returns the insertion point iterator.
164 InsertionPointTy GetInsertPoint() { return insert_before_; }
166 // Change the insertion point to insert before the instruction
168 void SetInsertPoint(ir::Instruction* insert_before) {
169 parent_ = context_->get_instr_block(insert_before);
170 insert_before_ = InsertionPointTy(insert_before);
173 // Change the insertion point to insert at the end of the basic block
175 void SetInsertPoint(ir::BasicBlock* parent_block) {
176 parent_ = parent_block;
177 insert_before_ = parent_block->end();
180 // Returns the context which instructions are constructed for.
181 ir::IRContext* GetContext() const { return context_; }
183 // Returns the set of preserved analyses.
184 inline ir::IRContext::Analysis GetPreservedAnalysis() const {
185 return preserved_analyses_;
189 InstructionBuilder(ir::IRContext* context, ir::BasicBlock* parent,
190 InsertionPointTy insert_before,
191 ir::IRContext::Analysis preserved_analyses)
194 insert_before_(insert_before),
195 preserved_analyses_(preserved_analyses) {
196 assert(!(preserved_analyses_ &
197 ~(ir::IRContext::kAnalysisDefUse |
198 ir::IRContext::kAnalysisInstrToBlockMapping)));
201 // Returns true if the users requested to update |analysis|.
202 inline bool IsAnalysisUpdateRequested(
203 ir::IRContext::Analysis analysis) const {
204 return preserved_analyses_ & analysis;
207 // Updates the def/use manager if the user requested it. If he did not request
208 // an update, this function does nothing.
209 inline void UpdateDefUseMgr(ir::Instruction* insn) {
210 if (IsAnalysisUpdateRequested(ir::IRContext::kAnalysisDefUse))
211 GetContext()->get_def_use_mgr()->AnalyzeInstDefUse(insn);
214 // Updates the instruction to block analysis if the user requested it. If he
215 // did not request an update, this function does nothing.
216 inline void UpdateInstrToBlockMapping(ir::Instruction* insn) {
217 if (IsAnalysisUpdateRequested(
218 ir::IRContext::kAnalysisInstrToBlockMapping) &&
220 GetContext()->set_instr_block(insn, parent_);
223 ir::IRContext* context_;
224 ir::BasicBlock* parent_;
225 InsertionPointTy insert_before_;
226 const ir::IRContext::Analysis preserved_analyses_;
230 } // namespace spvtools
232 #endif // LIBSPIRV_OPT_IR_BUILDER_H_