Coverage Report

Created: 2025-06-13 06:49

/src/spirv-tools/source/opt/instruction.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2016 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_INSTRUCTION_H_
16
#define SOURCE_OPT_INSTRUCTION_H_
17
18
#include <cassert>
19
#include <functional>
20
#include <memory>
21
#include <string>
22
#include <utility>
23
#include <vector>
24
25
#include "NonSemanticShaderDebugInfo100.h"
26
#include "OpenCLDebugInfo100.h"
27
#include "source/binary.h"
28
#include "source/common_debug_info.h"
29
#include "source/latest_version_glsl_std_450_header.h"
30
#include "source/latest_version_spirv_header.h"
31
#include "source/opcode.h"
32
#include "source/operand.h"
33
#include "source/opt/reflect.h"
34
#include "source/util/ilist_node.h"
35
#include "source/util/small_vector.h"
36
#include "source/util/string_utils.h"
37
#include "spirv-tools/libspirv.h"
38
39
constexpr uint32_t kNoDebugScope = 0;
40
constexpr uint32_t kNoInlinedAt = 0;
41
42
namespace spvtools {
43
namespace opt {
44
45
class Function;
46
class IRContext;
47
class Module;
48
class InstructionList;
49
50
// Relaxed logical addressing:
51
//
52
// In the logical addressing model, pointers cannot be stored or loaded.  This
53
// is a useful assumption because it simplifies the aliasing significantly.
54
// However, for the purpose of legalizing code generated from HLSL, we will have
55
// to allow storing and loading of pointers to opaque objects and runtime
56
// arrays.  This relaxation of the rule still implies that function and private
57
// scope variables do not have any aliasing, so we can treat them as before.
58
// This will be call the relaxed logical addressing model.
59
//
60
// This relaxation of the rule will be allowed by |GetBaseAddress|, but it will
61
// enforce that no other pointers are stored or loaded.
62
63
// About operand:
64
//
65
// In the SPIR-V specification, the term "operand" is used to mean any single
66
// SPIR-V word following the leading wordcount-opcode word. Here, the term
67
// "operand" is used to mean a *logical* operand. A logical operand may consist
68
// of multiple SPIR-V words, which together make up the same component. For
69
// example, a logical operand of a 64-bit integer needs two words to express.
70
//
71
// Further, we categorize logical operands into *in* and *out* operands.
72
// In operands are operands actually serve as input to operations, while out
73
// operands are operands that represent ids generated from operations (result
74
// type id or result id). For example, for "OpIAdd %rtype %rid %inop1 %inop2",
75
// "%inop1" and "%inop2" are in operands, while "%rtype" and "%rid" are out
76
// operands.
77
78
// A *logical* operand to a SPIR-V instruction. It can be the type id, result
79
// id, or other additional operands carried in an instruction.
80
struct Operand {
81
  using OperandData = utils::SmallVector<uint32_t, 2>;
82
  Operand(spv_operand_type_t t, OperandData&& w)
83
14.9M
      : type(t), words(std::move(w)) {}
84
85
622k
  Operand(spv_operand_type_t t, const OperandData& w) : type(t), words(w) {}
86
87
  template <class InputIt>
88
  Operand(spv_operand_type_t t, InputIt firstOperandData,
89
          InputIt lastOperandData)
90
30.3M
      : type(t), words(firstOperandData, lastOperandData) {}
91
92
  spv_operand_type_t type;  // Type of this logical operand.
93
  OperandData words;        // Binary segments of this logical operand.
94
95
29.6k
  uint32_t AsId() const {
96
29.6k
    assert(spvIsIdType(type));
97
29.6k
    assert(words.size() == 1);
98
29.6k
    return words[0];
99
29.6k
  }
100
101
  // Returns a string operand as a std::string.
102
873k
  std::string AsString() const {
103
873k
    assert(type == SPV_OPERAND_TYPE_LITERAL_STRING);
104
873k
    return spvtools::utils::MakeString(words);
105
873k
  }
106
107
  // Returns a literal integer operand as a uint64_t
108
0
  uint64_t AsLiteralUint64() const {
109
0
    assert(type == SPV_OPERAND_TYPE_LITERAL_INTEGER ||
110
0
           type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER ||
111
0
           type == SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER ||
112
0
           type == SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER);
113
0
    assert(1 <= words.size());
114
0
    assert(words.size() <= 2);
115
0
    uint64_t result = 0;
116
0
    if (words.size() > 0) {  // Needed to avoid maybe-uninitialized GCC warning
117
0
      uint32_t low = words[0];
118
0
      result = uint64_t(low);
119
0
    }
120
0
    if (words.size() > 1) {
121
0
      uint32_t high = words[1];
122
0
      result = result | (uint64_t(high) << 32);
123
0
    }
124
0
    return result;
125
0
  }
126
127
882k
  friend bool operator==(const Operand& o1, const Operand& o2) {
128
882k
    return o1.type == o2.type && o1.words == o2.words;
129
882k
  }
130
131
  // TODO(antiagainst): create fields for literal number kind, width, etc.
132
};
133
134
882k
inline bool operator!=(const Operand& o1, const Operand& o2) {
135
882k
  return !(o1 == o2);
136
882k
}
137
138
// This structure is used to represent a DebugScope instruction from
139
// the OpenCL.100.DebugInfo extended instruction set. Note that we can
140
// ignore the result id of DebugScope instruction because it is not
141
// used for anything. We do not keep it to reduce the size of
142
// structure.
143
// TODO: Let validator check that the result id is not used anywhere.
144
class DebugScope {
145
 public:
146
  DebugScope(uint32_t lexical_scope, uint32_t inlined_at)
147
32.3M
      : lexical_scope_(lexical_scope), inlined_at_(inlined_at) {}
148
149
2.92M
  inline bool operator!=(const DebugScope& d) const {
150
2.92M
    return lexical_scope_ != d.lexical_scope_ || inlined_at_ != d.inlined_at_;
151
2.92M
  }
152
153
  // Accessor functions for |lexical_scope_|.
154
38.3M
  uint32_t GetLexicalScope() const { return lexical_scope_; }
155
0
  void SetLexicalScope(uint32_t scope) { lexical_scope_ = scope; }
156
157
  // Accessor functions for |inlined_at_|.
158
35.6M
  uint32_t GetInlinedAt() const { return inlined_at_; }
159
1.01M
  void SetInlinedAt(uint32_t at) { inlined_at_ = at; }
160
161
  // Pushes the binary segments for this DebugScope instruction into
162
  // the back of *|binary|.
163
  void ToBinary(uint32_t type_id, uint32_t result_id, uint32_t ext_set,
164
                std::vector<uint32_t>* binary) const;
165
166
 private:
167
  // The result id of the lexical scope in which this debug scope is
168
  // contained. The value is kNoDebugScope if there is no scope.
169
  uint32_t lexical_scope_;
170
171
  // The result id of DebugInlinedAt if instruction in this debug scope
172
  // is inlined. The value is kNoInlinedAt if it is not inlined.
173
  uint32_t inlined_at_;
174
};
175
176
// A SPIR-V instruction. It contains the opcode and any additional logical
177
// operand, including the result id (if any) and result type id (if any). It
178
// may also contain line-related debug instruction (OpLine, OpNoLine) directly
179
// appearing before this instruction. Note that the result id of an instruction
180
// should never change after the instruction being built. If the result id
181
// needs to change, the user should create a new instruction instead.
182
class Instruction : public utils::IntrusiveNodeBase<Instruction> {
183
 public:
184
  using OperandList = std::vector<Operand>;
185
  using iterator = OperandList::iterator;
186
  using const_iterator = OperandList::const_iterator;
187
188
  // Creates a default OpNop instruction.
189
  // This exists solely for containers that can't do without. Should be removed.
190
  Instruction()
191
2.38M
      : utils::IntrusiveNodeBase<Instruction>(),
192
2.38M
        context_(nullptr),
193
2.38M
        opcode_(spv::Op::OpNop),
194
2.38M
        has_type_id_(false),
195
2.38M
        has_result_id_(false),
196
2.38M
        unique_id_(0),
197
2.38M
        dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
198
199
  // Creates a default OpNop instruction.
200
  Instruction(IRContext*);
201
  // Creates an instruction with the given opcode |op| and no additional logical
202
  // operands.
203
  Instruction(IRContext*, spv::Op);
204
  // Creates an instruction using the given spv_parsed_instruction_t |inst|. All
205
  // the data inside |inst| will be copied and owned in this instance. And keep
206
  // record of line-related debug instructions |dbg_line| ahead of this
207
  // instruction, if any.
208
  Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
209
              std::vector<Instruction>&& dbg_line = {});
210
211
  Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
212
              const DebugScope& dbg_scope);
213
214
  // Creates an instruction with the given opcode |op|, type id: |ty_id|,
215
  // result id: |res_id| and input operands: |in_operands|.
216
  Instruction(IRContext* c, spv::Op op, uint32_t ty_id, uint32_t res_id,
217
              const OperandList& in_operands);
218
219
  // TODO: I will want to remove these, but will first have to remove the use of
220
  // std::vector<Instruction>.
221
1.80M
  Instruction(const Instruction&) = default;
222
0
  Instruction& operator=(const Instruction&) = default;
223
224
  Instruction(Instruction&&);
225
  Instruction& operator=(Instruction&&);
226
227
33.3M
  ~Instruction() override = default;
228
229
  // Returns a newly allocated instruction that has the same operands, result,
230
  // and type as |this|.  The new instruction is not linked into any list.
231
  // It is the responsibility of the caller to make sure that the storage is
232
  // removed. It is the caller's responsibility to make sure that there is only
233
  // one instruction for each result id.
234
  Instruction* Clone(IRContext* c) const;
235
236
58.1M
  IRContext* context() const { return context_; }
237
238
1.32G
  spv::Op opcode() const { return opcode_; }
239
  // Sets the opcode of this instruction to a specific opcode. Note this may
240
  // invalidate the instruction.
241
  // TODO(qining): Remove this function when instruction building and insertion
242
  // is well implemented.
243
2.39M
  void SetOpcode(spv::Op op) { opcode_ = op; }
244
94.2M
  uint32_t type_id() const {
245
94.2M
    return has_type_id_ ? GetSingleWordOperand(0) : 0;
246
94.2M
  }
247
515M
  uint32_t result_id() const {
248
515M
    return has_result_id_ ? GetSingleWordOperand(has_type_id_ ? 1 : 0) : 0;
249
515M
  }
250
33.0G
  uint32_t unique_id() const {
251
33.0G
    assert(unique_id_ != 0);
252
33.0G
    return unique_id_;
253
33.0G
  }
254
  // Returns the vector of line-related debug instructions attached to this
255
  // instruction and the caller can directly modify them.
256
33.1M
  std::vector<Instruction>& dbg_line_insts() { return dbg_line_insts_; }
257
13.5M
  const std::vector<Instruction>& dbg_line_insts() const {
258
13.5M
    return dbg_line_insts_;
259
13.5M
  }
260
261
60.2k
  const Instruction* dbg_line_inst() const {
262
60.2k
    return dbg_line_insts_.empty() ? nullptr : &dbg_line_insts_[0];
263
60.2k
  }
264
265
  // Clear line-related debug instructions attached to this instruction.
266
397k
  void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
267
268
  // Same semantics as in the base class except the list the InstructionList
269
  // containing |pos| will now assume ownership of |this|.
270
  // inline void MoveBefore(Instruction* pos);
271
  // inline void InsertAfter(Instruction* pos);
272
273
  // Begin and end iterators for operands.
274
60.0k
  iterator begin() { return operands_.begin(); }
275
60.0k
  iterator end() { return operands_.end(); }
276
2.87M
  const_iterator begin() const { return operands_.cbegin(); }
277
2.87M
  const_iterator end() const { return operands_.cend(); }
278
  // Const begin and end iterators for operands.
279
0
  const_iterator cbegin() const { return operands_.cbegin(); }
280
0
  const_iterator cend() const { return operands_.cend(); }
281
282
  // Gets the number of logical operands.
283
557M
  uint32_t NumOperands() const {
284
557M
    return static_cast<uint32_t>(operands_.size());
285
557M
  }
286
  // Gets the number of SPIR-V words occupied by all logical operands.
287
2.92M
  uint32_t NumOperandWords() const {
288
2.92M
    return NumInOperandWords() + TypeResultIdCount();
289
2.92M
  }
290
  // Gets the |index|-th logical operand.
291
  inline Operand& GetOperand(uint32_t index);
292
  inline const Operand& GetOperand(uint32_t index) const;
293
  // Adds |operand| to the list of operands of this instruction.
294
  // It is the responsibility of the caller to make sure
295
  // that the instruction remains valid.
296
  inline void AddOperand(Operand&& operand);
297
  // Adds a copy of |operand| to the list of operands of this instruction.
298
  inline void AddOperand(const Operand& operand);
299
  // Gets the |index|-th logical operand as a single SPIR-V word. This method is
300
  // not expected to be used with logical operands consisting of multiple SPIR-V
301
  // words.
302
  uint32_t GetSingleWordOperand(uint32_t index) const;
303
  // Sets the |index|-th in-operand's data to the given |data|.
304
  inline void SetInOperand(uint32_t index, Operand::OperandData&& data);
305
  // Sets the |index|-th operand's data to the given |data|.
306
  // This is for in-operands modification only, but with |index| expressed in
307
  // terms of operand index rather than in-operand index.
308
  inline void SetOperand(uint32_t index, Operand::OperandData&& data);
309
  // Replace all of the in operands with those in |new_operands|.
310
  inline void SetInOperands(OperandList&& new_operands);
311
  // Sets the result type id.
312
  inline void SetResultType(uint32_t ty_id);
313
0
  inline bool HasResultType() const { return has_type_id_; }
314
  // Sets the result id
315
  inline void SetResultId(uint32_t res_id);
316
29.7M
  inline bool HasResultId() const { return has_result_id_; }
317
  // Sets DebugScope.
318
  inline void SetDebugScope(const DebugScope& scope);
319
39.2M
  inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
320
  // Add debug line inst. Renew result id if Debug[No]Line
321
  void AddDebugLine(const Instruction* inst);
322
  // Updates DebugInlinedAt of DebugScope and OpLine.
323
  void UpdateDebugInlinedAt(uint32_t new_inlined_at);
324
  // Clear line-related debug instructions attached to this instruction
325
  // along with def-use entries.
326
  void ClearDbgLineInsts();
327
  // Return true if Shader100:Debug[No]Line
328
  bool IsDebugLineInst() const;
329
  // Return true if Op[No]Line or Shader100:Debug[No]Line
330
  bool IsLineInst() const;
331
  // Return true if OpLine or Shader100:DebugLine
332
  bool IsLine() const;
333
  // Return true if OpNoLine or Shader100:DebugNoLine
334
  bool IsNoLine() const;
335
21.4M
  inline uint32_t GetDebugInlinedAt() const {
336
21.4M
    return dbg_scope_.GetInlinedAt();
337
21.4M
  }
338
  // Updates lexical scope of DebugScope and OpLine.
339
  void UpdateLexicalScope(uint32_t scope);
340
  // Updates OpLine and DebugScope based on the information of |from|.
341
  void UpdateDebugInfoFrom(const Instruction* from);
342
  // Remove the |index|-th operand
343
6.26k
  void RemoveOperand(uint32_t index) {
344
6.26k
    operands_.erase(operands_.begin() + index);
345
6.26k
  }
346
  // Insert an operand before the |index|-th operand
347
0
  void InsertOperand(uint32_t index, Operand&& operand) {
348
0
    operands_.insert(operands_.begin() + index, operand);
349
0
  }
350
351
  // The following methods are similar to the above, but are for in operands.
352
94.9M
  uint32_t NumInOperands() const {
353
94.9M
    return static_cast<uint32_t>(operands_.size() - TypeResultIdCount());
354
94.9M
  }
355
  uint32_t NumInOperandWords() const;
356
8.50M
  Operand& GetInOperand(uint32_t index) {
357
8.50M
    return GetOperand(index + TypeResultIdCount());
358
8.50M
  }
359
26.5M
  const Operand& GetInOperand(uint32_t index) const {
360
26.5M
    return GetOperand(index + TypeResultIdCount());
361
26.5M
  }
362
74.9M
  uint32_t GetSingleWordInOperand(uint32_t index) const {
363
74.9M
    return GetSingleWordOperand(index + TypeResultIdCount());
364
74.9M
  }
365
930
  void RemoveInOperand(uint32_t index) {
366
930
    operands_.erase(operands_.begin() + index + TypeResultIdCount());
367
930
  }
368
369
  // Returns true if this instruction is OpNop.
370
  inline bool IsNop() const;
371
  // Turns this instruction to OpNop. This does not clear out all preceding
372
  // line-related debug instructions.
373
  inline void ToNop();
374
375
  // Runs the given function |f| on this instruction and optionally on the
376
  // preceding debug line instructions.  The function will always be run
377
  // if this is itself a debug line instruction.
378
  inline void ForEachInst(const std::function<void(Instruction*)>& f,
379
                          bool run_on_debug_line_insts = false);
380
  inline void ForEachInst(const std::function<void(const Instruction*)>& f,
381
                          bool run_on_debug_line_insts = false) const;
382
383
  // Runs the given function |f| on this instruction and optionally on the
384
  // preceding debug line instructions.  The function will always be run
385
  // if this is itself a debug line instruction. If |f| returns false,
386
  // iteration is terminated and this function returns false.
387
  inline bool WhileEachInst(const std::function<bool(Instruction*)>& f,
388
                            bool run_on_debug_line_insts = false);
389
  inline bool WhileEachInst(const std::function<bool(const Instruction*)>& f,
390
                            bool run_on_debug_line_insts = false) const;
391
392
  // Runs the given function |f| on all operand ids.
393
  //
394
  // |f| should not transform an ID into 0, as 0 is an invalid ID.
395
  inline void ForEachId(const std::function<void(uint32_t*)>& f);
396
  inline void ForEachId(const std::function<void(const uint32_t*)>& f) const;
397
398
  // Runs the given function |f| on all "in" operand ids.
399
  inline void ForEachInId(const std::function<void(uint32_t*)>& f);
400
  inline void ForEachInId(const std::function<void(const uint32_t*)>& f) const;
401
402
  // Runs the given function |f| on all "in" operand ids. If |f| returns false,
403
  // iteration is terminated and this function returns false.
404
  inline bool WhileEachInId(const std::function<bool(uint32_t*)>& f);
405
  inline bool WhileEachInId(
406
      const std::function<bool(const uint32_t*)>& f) const;
407
408
  // Runs the given function |f| on all "in" operands.
409
  inline void ForEachInOperand(const std::function<void(uint32_t*)>& f);
410
  inline void ForEachInOperand(
411
      const std::function<void(const uint32_t*)>& f) const;
412
413
  // Runs the given function |f| on all "in" operands. If |f| returns false,
414
  // iteration is terminated and this function return false.
415
  inline bool WhileEachInOperand(const std::function<bool(uint32_t*)>& f);
416
  inline bool WhileEachInOperand(
417
      const std::function<bool(const uint32_t*)>& f) const;
418
419
  // Returns true if it's an OpBranchConditional instruction
420
  // with branch weights.
421
  bool HasBranchWeights() const;
422
423
  // Returns true if any operands can be labels
424
  inline bool HasLabels() const;
425
426
  // Pushes the binary segments for this instruction into the back of *|binary|.
427
  void ToBinaryWithoutAttachedDebugInsts(std::vector<uint32_t>* binary) const;
428
429
  // Replaces the operands to the instruction with |new_operands|. The caller
430
  // is responsible for building a complete and valid list of operands for
431
  // this instruction.
432
  void ReplaceOperands(const OperandList& new_operands);
433
434
  // Returns true if the instruction annotates an id with a decoration.
435
  inline bool IsDecoration() const;
436
437
  // Returns true if the instruction is known to be a load from read-only
438
  // memory.
439
  bool IsReadOnlyLoad() const;
440
441
  // Returns the instruction that gives the base address of an address
442
  // calculation.  The instruction must be a load, as defined by |IsLoad|,
443
  // store, copy, or access chain instruction.  In logical addressing mode, will
444
  // return an OpVariable or OpFunctionParameter instruction. For relaxed
445
  // logical addressing, it would also return a load of a pointer to an opaque
446
  // object.  For physical addressing mode, could return other types of
447
  // instructions.
448
  Instruction* GetBaseAddress() const;
449
450
  // Returns true if the instruction loads from memory or samples an image, and
451
  // stores the result into an id. It considers only core instructions.
452
  // Memory-to-memory instructions are not considered loads.
453
  inline bool IsLoad() const;
454
455
  // Returns true if the instruction generates a pointer that is definitely
456
  // read-only.  This is determined by analysing the pointer type's storage
457
  // class and decorations that target the pointer's id.  It does not analyse
458
  // other instructions that the pointer may be derived from.  Thus if 'true' is
459
  // returned, the pointer is definitely read-only, while if 'false' is returned
460
  // it is possible that the pointer may actually be read-only if it is derived
461
  // from another pointer that is decorated as read-only.
462
  bool IsReadOnlyPointer() const;
463
464
  // The following functions check for the various descriptor types defined in
465
  // the Vulkan specification section 13.1.
466
467
  // Returns true if the instruction defines a pointer type that points to a
468
  // storage image.
469
  bool IsVulkanStorageImage() const;
470
471
  // Returns true if the instruction defines a pointer type that points to a
472
  // sampled image.
473
  bool IsVulkanSampledImage() const;
474
475
  // Returns true if the instruction defines a pointer type that points to a
476
  // storage texel buffer.
477
  bool IsVulkanStorageTexelBuffer() const;
478
479
  // Returns true if the instruction defines a pointer type that points to a
480
  // storage buffer.
481
  bool IsVulkanStorageBuffer() const;
482
483
  // Returns true if the instruction defines a variable in StorageBuffer or
484
  // Uniform storage class with a pointer type that points to a storage buffer.
485
  bool IsVulkanStorageBufferVariable() const;
486
487
  // Returns true if the instruction defines a pointer type that points to a
488
  // uniform buffer.
489
  bool IsVulkanUniformBuffer() const;
490
491
  // Returns true if the instruction is an atom operation that uses original
492
  // value.
493
  inline bool IsAtomicWithLoad() const;
494
495
  // Returns true if the instruction is an atom operation.
496
  inline bool IsAtomicOp() const;
497
498
  // Returns true if this instruction is a branch or switch instruction (either
499
  // conditional or not).
500
14.6M
  bool IsBranch() const { return spvOpcodeIsBranch(opcode()); }
501
502
  // Returns true if this instruction causes the function to finish execution
503
  // and return to its caller
504
0
  bool IsReturn() const { return spvOpcodeIsReturn(opcode()); }
505
506
  // Returns true if this instruction exits this function or aborts execution.
507
334k
  bool IsReturnOrAbort() const { return spvOpcodeIsReturnOrAbort(opcode()); }
508
509
  // Returns true if this instruction is a basic block terminator.
510
6.49M
  bool IsBlockTerminator() const {
511
6.49M
    return spvOpcodeIsBlockTerminator(opcode());
512
6.49M
  }
513
514
  // Returns true if |this| is an instruction that define an opaque type.  Since
515
  // runtime array have similar characteristics they are included as opaque
516
  // types.
517
  bool IsOpaqueType() const;
518
519
  // Returns true if |this| is an instruction which could be folded into a
520
  // constant value.
521
  bool IsFoldable() const;
522
523
  // Returns true if |this| is an instruction which could be folded into a
524
  // constant value by |FoldScalar|.
525
  bool IsFoldableByFoldScalar() const;
526
527
  // Returns true if |this| is an instruction which could be folded into a
528
  // constant value by |FoldVector|.
529
  bool IsFoldableByFoldVector() const;
530
531
  // Returns true if we are allowed to fold or otherwise manipulate the
532
  // instruction that defines |id| in the given context. This includes not
533
  // handling NaN values.
534
  bool IsFloatingPointFoldingAllowed() const;
535
536
  inline bool operator==(const Instruction&) const;
537
  inline bool operator!=(const Instruction&) const;
538
  inline bool operator<(const Instruction&) const;
539
540
  // Takes ownership of the instruction owned by |i| and inserts it immediately
541
  // before |this|. Returns the inserted instruction.
542
  Instruction* InsertBefore(std::unique_ptr<Instruction>&& i);
543
  // Takes ownership of the instructions in |list| and inserts them in order
544
  // immediately before |this|.  Returns the first inserted instruction.
545
  // Assumes the list is non-empty.
546
  Instruction* InsertBefore(std::vector<std::unique_ptr<Instruction>>&& list);
547
  using utils::IntrusiveNodeBase<Instruction>::InsertBefore;
548
549
  // Returns true if |this| is an instruction defining a constant, but not a
550
  // Spec constant.
551
  inline bool IsConstant() const;
552
553
  // Returns true if |this| is an instruction with an opcode safe to move
554
  bool IsOpcodeCodeMotionSafe() const;
555
556
  // Pretty-prints |inst|.
557
  //
558
  // Provides the disassembly of a specific instruction. Utilizes |inst|'s
559
  // context to provide the correct interpretation of types, constants, etc.
560
  //
561
  // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER
562
  // is always added to |options|.
563
  std::string PrettyPrint(uint32_t options = 0u) const;
564
565
  // Returns true if the result can be a vector and the result of each component
566
  // depends on the corresponding component of any vector inputs.
567
  bool IsScalarizable() const;
568
569
  // Return true if the only effect of this instructions is the result.
570
  bool IsOpcodeSafeToDelete() const;
571
572
  // Returns true if it is valid to use the result of |inst| as the base
573
  // pointer for a load or store.  In this case, valid is defined by the relaxed
574
  // logical addressing rules when using logical addressing.  Normal validation
575
  // rules for physical addressing.
576
  bool IsValidBasePointer() const;
577
578
  // Returns debug opcode of an OpenCL.100.DebugInfo instruction. If
579
  // it is not an OpenCL.100.DebugInfo instruction, just returns
580
  // OpenCLDebugInfo100InstructionsMax.
581
  OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
582
583
  // Returns debug opcode of an NonSemantic.Shader.DebugInfo.100 instruction. If
584
  // it is not an NonSemantic.Shader.DebugInfo.100 instruction, just return
585
  // NonSemanticShaderDebugInfo100InstructionsMax.
586
  NonSemanticShaderDebugInfo100Instructions GetShader100DebugOpcode() const;
587
588
  // Returns debug opcode of an OpenCL.100.DebugInfo or
589
  // NonSemantic.Shader.DebugInfo.100 instruction. Since these overlap, we
590
  // return the OpenCLDebugInfo code
591
  CommonDebugInfoInstructions GetCommonDebugOpcode() const;
592
593
  // Returns true if it is an OpenCL.DebugInfo.100 instruction.
594
0
  bool IsOpenCL100DebugInstr() const {
595
0
    return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
596
0
  }
597
598
  // Returns true if it is an NonSemantic.Shader.DebugInfo.100 instruction.
599
0
  bool IsShader100DebugInstr() const {
600
0
    return GetShader100DebugOpcode() !=
601
0
           NonSemanticShaderDebugInfo100InstructionsMax;
602
0
  }
603
30.7M
  bool IsCommonDebugInstr() const {
604
30.7M
    return GetCommonDebugOpcode() != CommonDebugInfoInstructionsMax;
605
30.7M
  }
606
607
  // Returns true if this instructions a non-semantic instruction.
608
  bool IsNonSemanticInstruction() const;
609
610
  // Dump this instruction on stderr.  Useful when running interactive
611
  // debuggers.
612
  void Dump() const;
613
614
 private:
615
  // Returns the total count of result type id and result id.
616
221M
  uint32_t TypeResultIdCount() const {
617
221M
    if (has_type_id_ && has_result_id_) return 2;
618
66.3M
    if (has_type_id_ || has_result_id_) return 1;
619
45.9M
    return 0;
620
66.3M
  }
621
622
  // Returns true if the instruction generates a read-only pointer, with the
623
  // same caveats documented in the comment for IsReadOnlyPointer.  The first
624
  // version assumes the module is a shader module.  The second assumes a
625
  // kernel.
626
  bool IsReadOnlyPointerShaders() const;
627
  bool IsReadOnlyPointerKernel() const;
628
629
  // Returns true if the result of |inst| can be used as the base image for an
630
  // instruction that samples a image, reads an image, or writes to an image.
631
  bool IsValidBaseImage() const;
632
633
  IRContext* context_;  // IR Context
634
  spv::Op opcode_;      // Opcode
635
  bool has_type_id_;    // True if the instruction has a type id
636
  bool has_result_id_;  // True if the instruction has a result id
637
  uint32_t unique_id_;  // Unique instruction id
638
  // All logical operands, including result type id and result id.
639
  OperandList operands_;
640
  // Op[No]Line or Debug[No]Line instructions preceding this instruction. Note
641
  // that for Instructions representing Op[No]Line or Debug[No]Line themselves,
642
  // this field should be empty.
643
  std::vector<Instruction> dbg_line_insts_;
644
645
  // DebugScope that wraps this instruction.
646
  DebugScope dbg_scope_;
647
648
  friend InstructionList;
649
};
650
651
// Pretty-prints |inst| to |str| and returns |str|.
652
//
653
// Provides the disassembly of a specific instruction. Utilizes |inst|'s context
654
// to provide the correct interpretation of types, constants, etc.
655
//
656
// Disassembly uses raw ids (not pretty printed names).
657
std::ostream& operator<<(std::ostream& str, const Instruction& inst);
658
659
0
inline bool Instruction::operator==(const Instruction& other) const {
660
0
  return unique_id() == other.unique_id();
661
0
}
662
663
0
inline bool Instruction::operator!=(const Instruction& other) const {
664
0
  return !(*this == other);
665
0
}
666
667
114M
inline bool Instruction::operator<(const Instruction& other) const {
668
114M
  return unique_id() < other.unique_id();
669
114M
}
670
671
414M
inline Operand& Instruction::GetOperand(uint32_t index) {
672
414M
  assert(index < operands_.size() && "operand index out of bound");
673
414M
  return operands_[index];
674
414M
}
675
676
910M
inline const Operand& Instruction::GetOperand(uint32_t index) const {
677
910M
  assert(index < operands_.size() && "operand index out of bound");
678
910M
  return operands_[index];
679
910M
}
680
681
4.56M
inline void Instruction::AddOperand(Operand&& operand) {
682
4.56M
  operands_.push_back(std::move(operand));
683
4.56M
}
684
685
0
inline void Instruction::AddOperand(const Operand& operand) {
686
0
  operands_.push_back(operand);
687
0
}
688
689
inline void Instruction::SetInOperand(uint32_t index,
690
4.20M
                                      Operand::OperandData&& data) {
691
4.20M
  SetOperand(index + TypeResultIdCount(), std::move(data));
692
4.20M
}
693
694
inline void Instruction::SetOperand(uint32_t index,
695
4.20M
                                    Operand::OperandData&& data) {
696
4.20M
  assert(index < operands_.size() && "operand index out of bound");
697
4.20M
  assert(index >= TypeResultIdCount() && "operand is not a in-operand");
698
4.20M
  operands_[index].words = std::move(data);
699
4.20M
}
700
701
2.43M
inline void Instruction::SetInOperands(OperandList&& new_operands) {
702
  // Remove the old in operands.
703
2.43M
  operands_.erase(operands_.begin() + TypeResultIdCount(), operands_.end());
704
  // Add the new in operands.
705
2.43M
  operands_.insert(operands_.end(), new_operands.begin(), new_operands.end());
706
2.43M
}
707
708
5.30M
inline void Instruction::SetResultId(uint32_t res_id) {
709
  // TODO(dsinclair): Allow setting a result id if there wasn't one
710
  // previously. Need to make room in the operands_ array to place the result,
711
  // and update the has_result_id_ flag.
712
5.30M
  assert(has_result_id_);
713
714
  // TODO(dsinclair): Allow removing the result id. This needs to make sure,
715
  // if there was a result id previously to remove it from the operands_ array
716
  // and reset the has_result_id_ flag.
717
5.30M
  assert(res_id != 0);
718
719
5.30M
  auto ridx = has_type_id_ ? 1 : 0;
720
5.30M
  operands_[ridx].words = {res_id};
721
5.30M
}
722
723
718k
inline void Instruction::SetDebugScope(const DebugScope& scope) {
724
718k
  dbg_scope_ = scope;
725
718k
  for (auto& i : dbg_line_insts_) {
726
3.20k
    i.dbg_scope_ = scope;
727
3.20k
  }
728
718k
}
729
730
48.2k
inline void Instruction::SetResultType(uint32_t ty_id) {
731
  // TODO(dsinclair): Allow setting a type id if there wasn't one
732
  // previously. Need to make room in the operands_ array to place the result,
733
  // and update the has_type_id_ flag.
734
48.2k
  assert(has_type_id_);
735
736
  // TODO(dsinclair): Allow removing the type id. This needs to make sure,
737
  // if there was a type id previously to remove it from the operands_ array
738
  // and reset the has_type_id_ flag.
739
48.2k
  assert(ty_id != 0);
740
741
48.2k
  operands_.front().words = {ty_id};
742
48.2k
}
743
744
2.90M
inline bool Instruction::IsNop() const {
745
2.90M
  return opcode_ == spv::Op::OpNop && !has_type_id_ && !has_result_id_ &&
746
2.90M
         operands_.empty();
747
2.90M
}
748
749
790k
inline void Instruction::ToNop() {
750
790k
  opcode_ = spv::Op::OpNop;
751
790k
  has_type_id_ = false;
752
790k
  has_result_id_ = false;
753
790k
  operands_.clear();
754
790k
}
755
756
inline bool Instruction::WhileEachInst(
757
381M
    const std::function<bool(Instruction*)>& f, bool run_on_debug_line_insts) {
758
381M
  if (run_on_debug_line_insts) {
759
231M
    for (auto& dbg_line : dbg_line_insts_) {
760
188k
      if (!f(&dbg_line)) return false;
761
188k
    }
762
231M
  }
763
381M
  return f(this);
764
381M
}
765
766
inline bool Instruction::WhileEachInst(
767
    const std::function<bool(const Instruction*)>& f,
768
6.61M
    bool run_on_debug_line_insts) const {
769
6.61M
  if (run_on_debug_line_insts) {
770
5.79M
    for (auto& dbg_line : dbg_line_insts_) {
771
5.99k
      if (!f(&dbg_line)) return false;
772
5.99k
    }
773
5.79M
  }
774
6.61M
  return f(this);
775
6.61M
}
776
777
inline void Instruction::ForEachInst(const std::function<void(Instruction*)>& f,
778
34.7M
                                     bool run_on_debug_line_insts) {
779
34.7M
  WhileEachInst(
780
34.7M
      [&f](Instruction* inst) {
781
34.7M
        f(inst);
782
34.7M
        return true;
783
34.7M
      },
784
34.7M
      run_on_debug_line_insts);
785
34.7M
}
786
787
inline void Instruction::ForEachInst(
788
    const std::function<void(const Instruction*)>& f,
789
801k
    bool run_on_debug_line_insts) const {
790
801k
  WhileEachInst(
791
802k
      [&f](const Instruction* inst) {
792
802k
        f(inst);
793
802k
        return true;
794
802k
      },
795
801k
      run_on_debug_line_insts);
796
801k
}
797
798
0
inline void Instruction::ForEachId(const std::function<void(uint32_t*)>& f) {
799
0
  for (auto& operand : operands_)
800
0
    if (spvIsIdType(operand.type)) f(&operand.words[0]);
801
0
}
802
803
inline void Instruction::ForEachId(
804
0
    const std::function<void(const uint32_t*)>& f) const {
805
0
  for (const auto& operand : operands_)
806
0
    if (spvIsIdType(operand.type)) f(&operand.words[0]);
807
0
}
808
809
inline bool Instruction::WhileEachInId(
810
22.9M
    const std::function<bool(uint32_t*)>& f) {
811
73.2M
  for (auto& operand : operands_) {
812
73.2M
    if (spvIsInIdType(operand.type) && !f(&operand.words[0])) {
813
596k
      return false;
814
596k
    }
815
73.2M
  }
816
22.3M
  return true;
817
22.9M
}
818
819
inline bool Instruction::WhileEachInId(
820
22.5M
    const std::function<bool(const uint32_t*)>& f) const {
821
61.0M
  for (const auto& operand : operands_) {
822
61.0M
    if (spvIsInIdType(operand.type) && !f(&operand.words[0])) {
823
742k
      return false;
824
742k
    }
825
61.0M
  }
826
21.8M
  return true;
827
22.5M
}
828
829
21.8M
inline void Instruction::ForEachInId(const std::function<void(uint32_t*)>& f) {
830
35.2M
  WhileEachInId([&f](uint32_t* id) {
831
35.2M
    f(id);
832
35.2M
    return true;
833
35.2M
  });
834
21.8M
}
835
836
inline void Instruction::ForEachInId(
837
14.7M
    const std::function<void(const uint32_t*)>& f) const {
838
18.8M
  WhileEachInId([&f](const uint32_t* id) {
839
18.8M
    f(id);
840
18.8M
    return true;
841
18.8M
  });
842
14.7M
}
843
844
inline bool Instruction::WhileEachInOperand(
845
178k
    const std::function<bool(uint32_t*)>& f) {
846
649k
  for (auto& operand : operands_) {
847
649k
    switch (operand.type) {
848
144k
      case SPV_OPERAND_TYPE_RESULT_ID:
849
277k
      case SPV_OPERAND_TYPE_TYPE_ID:
850
277k
        break;
851
372k
      default:
852
372k
        if (!f(&operand.words[0])) return false;
853
371k
        break;
854
649k
    }
855
649k
  }
856
178k
  return true;
857
178k
}
858
859
inline bool Instruction::WhileEachInOperand(
860
2.81M
    const std::function<bool(const uint32_t*)>& f) const {
861
11.2M
  for (const auto& operand : operands_) {
862
11.2M
    switch (operand.type) {
863
2.81M
      case SPV_OPERAND_TYPE_RESULT_ID:
864
5.62M
      case SPV_OPERAND_TYPE_TYPE_ID:
865
5.62M
        break;
866
5.61M
      default:
867
5.61M
        if (!f(&operand.words[0])) return false;
868
5.61M
        break;
869
11.2M
    }
870
11.2M
  }
871
2.81M
  return true;
872
2.81M
}
873
874
inline void Instruction::ForEachInOperand(
875
144k
    const std::function<void(uint32_t*)>& f) {
876
300k
  WhileEachInOperand([&f](uint32_t* operand) {
877
300k
    f(operand);
878
300k
    return true;
879
300k
  });
880
144k
}
881
882
inline void Instruction::ForEachInOperand(
883
0
    const std::function<void(const uint32_t*)>& f) const {
884
0
  WhileEachInOperand([&f](const uint32_t* operand) {
885
0
    f(operand);
886
0
    return true;
887
0
  });
888
0
}
889
890
0
inline bool Instruction::HasLabels() const {
891
0
  switch (opcode_) {
892
0
    case spv::Op::OpSelectionMerge:
893
0
    case spv::Op::OpBranch:
894
0
    case spv::Op::OpLoopMerge:
895
0
    case spv::Op::OpBranchConditional:
896
0
    case spv::Op::OpSwitch:
897
0
    case spv::Op::OpPhi:
898
0
      return true;
899
0
      break;
900
0
    default:
901
0
      break;
902
0
  }
903
0
  return false;
904
0
}
905
906
28.3M
bool Instruction::IsDecoration() const {
907
28.3M
  return spvOpcodeIsDecoration(opcode());
908
28.3M
}
909
910
5.47M
bool Instruction::IsLoad() const { return spvOpcodeIsLoad(opcode()); }
911
912
13.1M
bool Instruction::IsAtomicWithLoad() const {
913
13.1M
  return spvOpcodeIsAtomicWithLoad(opcode());
914
13.1M
}
915
916
0
bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); }
917
918
377k
bool Instruction::IsConstant() const {
919
377k
  return IsConstantInst(opcode()) && !IsSpecConstantInst(opcode());
920
377k
}
921
}  // namespace opt
922
}  // namespace spvtools
923
924
#endif  // SOURCE_OPT_INSTRUCTION_H_