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