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