Coverage Report

Created: 2025-12-31 06:15

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