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