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