1 // Copyright (c) 2016 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #ifndef LIBSPIRV_OPT_INSTRUCTION_H_
16 #define LIBSPIRV_OPT_INSTRUCTION_H_
25 #include "util/ilist_node.h"
27 #include "latest_version_spirv_header.h"
29 #include "spirv-tools/libspirv.h"
37 class InstructionList;
39 // Relaxed logcial addressing:
41 // In the logical addressing model, pointers cannot be stored or loaded. This
42 // is a useful assumption because it simplifies the aliasing significantly.
43 // However, for the purpose of legalizing code generated from HLSL, we will have
44 // to allow storing and loading of pointers to opaque objects and runtime
45 // arrays. This relaxation of the rule still implies that function and private
46 // scope variables do not have any aliasing, so we can treat them as before.
47 // This will be call the relaxed logical addressing model.
49 // This relaxation of the rule will be allowed by |GetBaseAddress|, but it will
50 // enforce that no other pointers are stored or loaded.
54 // In the SPIR-V specification, the term "operand" is used to mean any single
55 // SPIR-V word following the leading wordcount-opcode word. Here, the term
56 // "operand" is used to mean a *logical* operand. A logical operand may consist
57 // of multiple SPIR-V words, which together make up the same component. For
58 // example, a logical operand of a 64-bit integer needs two words to express.
60 // Further, we categorize logical operands into *in* and *out* operands.
61 // In operands are operands actually serve as input to operations, while out
62 // operands are operands that represent ids generated from operations (result
63 // type id or result id). For example, for "OpIAdd %rtype %rid %inop1 %inop2",
64 // "%inop1" and "%inop2" are in operands, while "%rtype" and "%rid" are out
67 // A *logical* operand to a SPIR-V instruction. It can be the type id, result
68 // id, or other additional operands carried in an instruction.
70 Operand(spv_operand_type_t t, std::vector<uint32_t>&& w)
71 : type(t), words(std::move(w)) {}
73 Operand(spv_operand_type_t t, const std::vector<uint32_t>& w)
74 : type(t), words(w) {}
76 spv_operand_type_t type; // Type of this logical operand.
77 std::vector<uint32_t> words; // Binary segments of this logical operand.
79 friend bool operator==(const Operand& o1, const Operand& o2) {
80 return o1.type == o2.type && o1.words == o2.words;
83 // TODO(antiagainst): create fields for literal number kind, width, etc.
86 inline bool operator!=(const Operand& o1, const Operand& o2) {
90 // A SPIR-V instruction. It contains the opcode and any additional logical
91 // operand, including the result id (if any) and result type id (if any). It
92 // may also contain line-related debug instruction (OpLine, OpNoLine) directly
93 // appearing before this instruction. Note that the result id of an instruction
94 // should never change after the instruction being built. If the result id
95 // needs to change, the user should create a new instruction instead.
96 class Instruction : public utils::IntrusiveNodeBase<Instruction> {
98 using iterator = std::vector<Operand>::iterator;
99 using const_iterator = std::vector<Operand>::const_iterator;
101 // Creates a default OpNop instruction.
102 // This exists solely for containers that can't do without. Should be removed.
104 : utils::IntrusiveNodeBase<Instruction>(),
111 // Creates a default OpNop instruction.
112 Instruction(IRContext*);
113 // Creates an instruction with the given opcode |op| and no additional logical
115 Instruction(IRContext*, SpvOp);
116 // Creates an instruction using the given spv_parsed_instruction_t |inst|. All
117 // the data inside |inst| will be copied and owned in this instance. And keep
118 // record of line-related debug instructions |dbg_line| ahead of this
119 // instruction, if any.
120 Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
121 std::vector<Instruction>&& dbg_line = {});
123 // Creates an instruction with the given opcode |op|, type id: |ty_id|,
124 // result id: |res_id| and input operands: |in_operands|.
125 Instruction(IRContext* c, SpvOp op, uint32_t ty_id, uint32_t res_id,
126 const std::vector<Operand>& in_operands);
128 // TODO: I will want to remove these, but will first have to remove the use of
129 // std::vector<Instruction>.
130 Instruction(const Instruction&) = default;
131 Instruction& operator=(const Instruction&) = default;
133 Instruction(Instruction&&);
134 Instruction& operator=(Instruction&&);
136 virtual ~Instruction() = default;
138 // Returns a newly allocated instruction that has the same operands, result,
139 // and type as |this|. The new instruction is not linked into any list.
140 // It is the responsibility of the caller to make sure that the storage is
141 // removed. It is the caller's responsibility to make sure that there is only
142 // one instruction for each result id.
143 Instruction* Clone(IRContext* c) const;
145 IRContext* context() const { return context_; }
147 SpvOp opcode() const { return opcode_; }
148 // Sets the opcode of this instruction to a specific opcode. Note this may
149 // invalidate the instruction.
150 // TODO(qining): Remove this function when instruction building and insertion
151 // is well implemented.
152 void SetOpcode(SpvOp op) { opcode_ = op; }
153 uint32_t type_id() const { return type_id_; }
154 uint32_t result_id() const { return result_id_; }
155 uint32_t unique_id() const {
156 assert(unique_id_ != 0);
159 // Returns the vector of line-related debug instructions attached to this
160 // instruction and the caller can directly modify them.
161 std::vector<Instruction>& dbg_line_insts() { return dbg_line_insts_; }
162 const std::vector<Instruction>& dbg_line_insts() const {
163 return dbg_line_insts_;
166 // Same semantics as in the base class except the list the InstructionList
167 // containing |pos| will now assume ownership of |this|.
168 // inline void MoveBefore(Instruction* pos);
169 // inline void InsertAfter(Instruction* pos);
171 // Begin and end iterators for operands.
172 iterator begin() { return operands_.begin(); }
173 iterator end() { return operands_.end(); }
174 const_iterator begin() const { return operands_.cbegin(); }
175 const_iterator end() const { return operands_.cend(); }
176 // Const begin and end iterators for operands.
177 const_iterator cbegin() const { return operands_.cbegin(); }
178 const_iterator cend() const { return operands_.cend(); }
180 // Gets the number of logical operands.
181 uint32_t NumOperands() const {
182 return static_cast<uint32_t>(operands_.size());
184 // Gets the number of SPIR-V words occupied by all logical operands.
185 uint32_t NumOperandWords() const {
186 return NumInOperandWords() + TypeResultIdCount();
188 // Gets the |index|-th logical operand.
189 inline const Operand& GetOperand(uint32_t index) const;
190 // Adds |operand| to the list of operands of this instruction.
191 // It is the responsibility of the caller to make sure
192 // that the instruction remains valid.
193 inline void AddOperand(Operand&& operand);
194 // Gets the |index|-th logical operand as a single SPIR-V word. This method is
195 // not expected to be used with logical operands consisting of multiple SPIR-V
197 uint32_t GetSingleWordOperand(uint32_t index) const;
198 // Sets the |index|-th in-operand's data to the given |data|.
199 inline void SetInOperand(uint32_t index, std::vector<uint32_t>&& data);
200 // Sets the |index|-th operand's data to the given |data|.
201 // This is for in-operands modification only, but with |index| expressed in
202 // terms of operand index rather than in-operand index.
203 inline void SetOperand(uint32_t index, std::vector<uint32_t>&& data);
204 // Replace all of the in operands with those in |new_operands|.
205 inline void SetInOperands(std::vector<Operand>&& new_operands);
206 // Sets the result type id.
207 inline void SetResultType(uint32_t ty_id);
208 // Sets the result id
209 inline void SetResultId(uint32_t res_id);
210 inline bool HasResultId() const { return result_id_ != 0; }
211 // Remove the |index|-th operand
212 void RemoveOperand(uint32_t index) {
213 operands_.erase(operands_.begin() + index);
216 // The following methods are similar to the above, but are for in operands.
217 uint32_t NumInOperands() const {
218 return static_cast<uint32_t>(operands_.size() - TypeResultIdCount());
220 uint32_t NumInOperandWords() const;
221 const Operand& GetInOperand(uint32_t index) const {
222 return GetOperand(index + TypeResultIdCount());
224 uint32_t GetSingleWordInOperand(uint32_t index) const {
225 return GetSingleWordOperand(index + TypeResultIdCount());
227 void RemoveInOperand(uint32_t index) {
228 operands_.erase(operands_.begin() + index + TypeResultIdCount());
231 // Returns true if this instruction is OpNop.
232 inline bool IsNop() const;
233 // Turns this instruction to OpNop. This does not clear out all preceding
234 // line-related debug instructions.
237 // Runs the given function |f| on this instruction and optionally on the
238 // preceding debug line instructions. The function will always be run
239 // if this is itself a debug line instruction.
240 inline void ForEachInst(const std::function<void(Instruction*)>& f,
241 bool run_on_debug_line_insts = false);
242 inline void ForEachInst(const std::function<void(const Instruction*)>& f,
243 bool run_on_debug_line_insts = false) const;
245 // Runs the given function |f| on this instruction and optionally on the
246 // preceding debug line instructions. The function will always be run
247 // if this is itself a debug line instruction. If |f| returns false,
248 // iteration is terminated and this function returns false.
249 inline bool WhileEachInst(const std::function<bool(Instruction*)>& f,
250 bool run_on_debug_line_insts = false);
251 inline bool WhileEachInst(const std::function<bool(const Instruction*)>& f,
252 bool run_on_debug_line_insts = false) const;
254 // Runs the given function |f| on all operand ids.
256 // |f| should not transform an ID into 0, as 0 is an invalid ID.
257 inline void ForEachId(const std::function<void(uint32_t*)>& f);
258 inline void ForEachId(const std::function<void(const uint32_t*)>& f) const;
260 // Runs the given function |f| on all "in" operand ids.
261 inline void ForEachInId(const std::function<void(uint32_t*)>& f);
262 inline void ForEachInId(const std::function<void(const uint32_t*)>& f) const;
264 // Runs the given function |f| on all "in" operand ids. If |f| returns false,
265 // iteration is terminated and this function returns false.
266 inline bool WhileEachInId(const std::function<bool(uint32_t*)>& f);
267 inline bool WhileEachInId(
268 const std::function<bool(const uint32_t*)>& f) const;
270 // Runs the given function |f| on all "in" operands.
271 inline void ForEachInOperand(const std::function<void(uint32_t*)>& f);
272 inline void ForEachInOperand(
273 const std::function<void(const uint32_t*)>& f) const;
275 // Runs the given function |f| on all "in" operands. If |f| returns false,
276 // iteration is terminated and this function return false.
277 inline bool WhileEachInOperand(const std::function<bool(uint32_t*)>& f);
278 inline bool WhileEachInOperand(
279 const std::function<bool(const uint32_t*)>& f) const;
281 // Returns true if any operands can be labels
282 inline bool HasLabels() const;
284 // Pushes the binary segments for this instruction into the back of *|binary|.
285 void ToBinaryWithoutAttachedDebugInsts(std::vector<uint32_t>* binary) const;
287 // Replaces the operands to the instruction with |new_operands|. The caller
288 // is responsible for building a complete and valid list of operands for
290 void ReplaceOperands(const std::vector<Operand>& new_operands);
292 // Returns true if the instruction annotates an id with a decoration.
293 inline bool IsDecoration() const;
295 // Returns true if the instruction is known to be a load from read-only
297 bool IsReadOnlyLoad() const;
299 // Returns the instruction that gives the base address of an address
300 // calculation. The instruction must be a load, as defined by |IsLoad|,
301 // store, copy, or access chain instruction. In logical addressing mode, will
302 // return an OpVariable or OpFunctionParameter instruction. For relaxed
303 // logical addressing, it would also return a load of a pointer to an opaque
304 // object. For physical addressing mode, could return other types of
306 Instruction* GetBaseAddress() const;
308 // Returns true if the instruction loads from memory or samples an image, and
309 // stores the result into an id. It considers only core instructions.
310 // Memory-to-memory instructions are not considered loads.
311 inline bool IsLoad() const;
313 // Returns true if the instruction declares a variable that is read-only.
314 bool IsReadOnlyVariable() const;
316 // The following functions check for the various descriptor types defined in
317 // the Vulkan specification section 13.1.
319 // Returns true if the instruction defines a pointer type that points to a
321 bool IsVulkanStorageImage() const;
323 // Returns true if the instruction defines a pointer type that points to a
325 bool IsVulkanSampledImage() const;
327 // Returns true if the instruction defines a pointer type that points to a
328 // storage texel buffer.
329 bool IsVulkanStorageTexelBuffer() const;
331 // Returns true if the instruction defines a pointer type that points to a
333 bool IsVulkanStorageBuffer() const;
335 // Returns true if the instruction defines a pointer type that points to a
337 bool IsVulkanUniformBuffer() const;
339 // Returns true if the instruction is an atom operation.
340 inline bool IsAtomicOp() const;
342 // Returns true if this instruction is a branch or switch instruction (either
343 // conditional or not).
344 bool IsBranch() const { return spvOpcodeIsBranch(opcode()); }
346 // Returns true if this instruction causes the function to finish execution
347 // and return to its caller
348 bool IsReturn() const { return spvOpcodeIsReturn(opcode()); }
350 // Returns true if this instruction exits this function or aborts execution.
351 bool IsReturnOrAbort() const { return spvOpcodeIsReturnOrAbort(opcode()); }
353 // Returns the id for the |element|'th subtype. If the |this| is not a
354 // composite type, this function returns 0.
355 uint32_t GetTypeComponent(uint32_t element) const;
357 // Returns true if this instruction is a basic block terminator.
358 bool IsBlockTerminator() const {
359 return spvOpcodeIsBlockTerminator(opcode());
362 // Returns true if |this| is an instruction that define an opaque type. Since
363 // runtime array have similar characteristics they are included as opaque
365 bool IsOpaqueType() const;
367 // Returns true if |this| is an instruction which could be folded into a
369 bool IsFoldable() const;
371 inline bool operator==(const Instruction&) const;
372 inline bool operator!=(const Instruction&) const;
373 inline bool operator<(const Instruction&) const;
375 Instruction* InsertBefore(std::vector<std::unique_ptr<Instruction>>&& list);
376 Instruction* InsertBefore(std::unique_ptr<Instruction>&& i);
377 using utils::IntrusiveNodeBase<Instruction>::InsertBefore;
379 // Returns true if |this| is an instruction defining a constant, but not a
381 inline bool IsConstant() const;
383 // Pretty-prints |inst|.
385 // Provides the disassembly of a specific instruction. Utilizes |inst|'s
386 // context to provide the correct interpretation of types, constants, etc.
388 // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER
389 // is always added to |options|.
390 std::string PrettyPrint(uint32_t options = 0u) const;
393 // Returns the total count of result type id and result id.
394 uint32_t TypeResultIdCount() const {
395 return (type_id_ != 0) + (result_id_ != 0);
398 // Returns true if the instruction declares a variable that is read-only. The
399 // first version assumes the module is a shader module. The second assumes a
401 bool IsReadOnlyVariableShaders() const;
402 bool IsReadOnlyVariableKernel() const;
404 // Returns true if it is valid to use the result of |inst| as the base
405 // pointer for a load or store. In this case, valid is defined by the relaxed
406 // logical addressing rules when using logical addressing. Normal validation
407 // rules for physical addressing.
408 bool IsValidBasePointer() const;
410 // Returns true if the result of |inst| can be used as the base image for an
411 // instruction that samples a image, reads an image, or writes to an image.
412 bool IsValidBaseImage() const;
414 IRContext* context_; // IR Context
415 SpvOp opcode_; // Opcode
416 uint32_t type_id_; // Result type id. A value of 0 means no result type id.
417 uint32_t result_id_; // Result id. A value of 0 means no result id.
418 uint32_t unique_id_; // Unique instruction id
419 // All logical operands, including result type id and result id.
420 std::vector<Operand> operands_;
421 // Opline and OpNoLine instructions preceding this instruction. Note that for
422 // Instructions representing OpLine or OpNonLine itself, this field should be
424 std::vector<Instruction> dbg_line_insts_;
426 friend InstructionList;
429 // Pretty-prints |inst| to |str| and returns |str|.
431 // Provides the disassembly of a specific instruction. Utilizes |inst|'s context
432 // to provide the correct interpretation of types, constants, etc.
434 // Disassembly uses raw ids (not pretty printed names).
435 std::ostream& operator<<(std::ostream& str, const ir::Instruction& inst);
437 inline bool Instruction::operator==(const Instruction& other) const {
438 return unique_id() == other.unique_id();
441 inline bool Instruction::operator!=(const Instruction& other) const {
442 return !(*this == other);
445 inline bool Instruction::operator<(const Instruction& other) const {
446 return unique_id() < other.unique_id();
449 inline const Operand& Instruction::GetOperand(uint32_t index) const {
450 assert(index < operands_.size() && "operand index out of bound");
451 return operands_[index];
454 inline void Instruction::AddOperand(Operand&& operand) {
455 operands_.push_back(std::move(operand));
458 inline void Instruction::SetInOperand(uint32_t index,
459 std::vector<uint32_t>&& data) {
460 SetOperand(index + TypeResultIdCount(), std::move(data));
463 inline void Instruction::SetOperand(uint32_t index,
464 std::vector<uint32_t>&& data) {
465 assert(index < operands_.size() && "operand index out of bound");
466 assert(index >= TypeResultIdCount() && "operand is not a in-operand");
467 operands_[index].words = std::move(data);
470 inline void Instruction::SetInOperands(std::vector<Operand>&& new_operands) {
471 // Remove the old in operands.
472 operands_.erase(operands_.begin() + TypeResultIdCount(), operands_.end());
473 // Add the new in operands.
474 operands_.insert(operands_.end(), new_operands.begin(), new_operands.end());
477 inline void Instruction::SetResultId(uint32_t res_id) {
479 auto ridx = (type_id_ != 0) ? 1 : 0;
480 assert(operands_[ridx].type == SPV_OPERAND_TYPE_RESULT_ID);
481 operands_[ridx].words = {res_id};
484 inline void Instruction::SetResultType(uint32_t ty_id) {
487 assert(operands_.front().type == SPV_OPERAND_TYPE_TYPE_ID);
488 operands_.front().words = {ty_id};
492 inline bool Instruction::IsNop() const {
493 return opcode_ == SpvOpNop && type_id_ == 0 && result_id_ == 0 &&
497 inline void Instruction::ToNop() {
499 type_id_ = result_id_ = 0;
503 inline bool Instruction::WhileEachInst(
504 const std::function<bool(Instruction*)>& f, bool run_on_debug_line_insts) {
505 if (run_on_debug_line_insts) {
506 for (auto& dbg_line : dbg_line_insts_) {
507 if (!f(&dbg_line)) return false;
513 inline bool Instruction::WhileEachInst(
514 const std::function<bool(const Instruction*)>& f,
515 bool run_on_debug_line_insts) const {
516 if (run_on_debug_line_insts) {
517 for (auto& dbg_line : dbg_line_insts_) {
518 if (!f(&dbg_line)) return false;
524 inline void Instruction::ForEachInst(const std::function<void(Instruction*)>& f,
525 bool run_on_debug_line_insts) {
527 [&f](Instruction* inst) {
531 run_on_debug_line_insts);
534 inline void Instruction::ForEachInst(
535 const std::function<void(const Instruction*)>& f,
536 bool run_on_debug_line_insts) const {
538 [&f](const Instruction* inst) {
542 run_on_debug_line_insts);
545 inline void Instruction::ForEachId(const std::function<void(uint32_t*)>& f) {
546 for (auto& opnd : operands_)
547 if (spvIsIdType(opnd.type)) f(&opnd.words[0]);
548 if (type_id_ != 0u) type_id_ = GetSingleWordOperand(0u);
549 if (result_id_ != 0u)
550 result_id_ = GetSingleWordOperand(type_id_ == 0u ? 0u : 1u);
553 inline void Instruction::ForEachId(
554 const std::function<void(const uint32_t*)>& f) const {
555 for (const auto& opnd : operands_)
556 if (spvIsIdType(opnd.type)) f(&opnd.words[0]);
559 inline bool Instruction::WhileEachInId(
560 const std::function<bool(uint32_t*)>& f) {
561 for (auto& opnd : operands_) {
563 case SPV_OPERAND_TYPE_RESULT_ID:
564 case SPV_OPERAND_TYPE_TYPE_ID:
567 if (spvIsIdType(opnd.type)) {
568 if (!f(&opnd.words[0])) return false;
576 inline bool Instruction::WhileEachInId(
577 const std::function<bool(const uint32_t*)>& f) const {
578 for (const auto& opnd : operands_) {
580 case SPV_OPERAND_TYPE_RESULT_ID:
581 case SPV_OPERAND_TYPE_TYPE_ID:
584 if (spvIsIdType(opnd.type)) {
585 if (!f(&opnd.words[0])) return false;
593 inline void Instruction::ForEachInId(const std::function<void(uint32_t*)>& f) {
594 WhileEachInId([&f](uint32_t* id) {
600 inline void Instruction::ForEachInId(
601 const std::function<void(const uint32_t*)>& f) const {
602 WhileEachInId([&f](const uint32_t* id) {
608 inline bool Instruction::WhileEachInOperand(
609 const std::function<bool(uint32_t*)>& f) {
610 for (auto& opnd : operands_) {
612 case SPV_OPERAND_TYPE_RESULT_ID:
613 case SPV_OPERAND_TYPE_TYPE_ID:
616 if (!f(&opnd.words[0])) return false;
623 inline bool Instruction::WhileEachInOperand(
624 const std::function<bool(const uint32_t*)>& f) const {
625 for (const auto& opnd : operands_) {
627 case SPV_OPERAND_TYPE_RESULT_ID:
628 case SPV_OPERAND_TYPE_TYPE_ID:
631 if (!f(&opnd.words[0])) return false;
638 inline void Instruction::ForEachInOperand(
639 const std::function<void(uint32_t*)>& f) {
640 WhileEachInOperand([&f](uint32_t* op) {
646 inline void Instruction::ForEachInOperand(
647 const std::function<void(const uint32_t*)>& f) const {
648 WhileEachInOperand([&f](const uint32_t* op) {
654 inline bool Instruction::HasLabels() const {
656 case SpvOpSelectionMerge:
659 case SpvOpBranchConditional:
670 bool Instruction::IsDecoration() const {
671 return spvOpcodeIsDecoration(opcode());
674 bool Instruction::IsLoad() const { return spvOpcodeIsLoad(opcode()); }
676 bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); }
678 bool Instruction::IsConstant() const {
679 return IsCompileTimeConstantInst(opcode());
682 } // namespace spvtools
684 #endif // LIBSPIRV_OPT_INSTRUCTION_H_