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