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