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