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 LIBSPIRV_OPT_INSTRUCTION_H_ 16 #define LIBSPIRV_OPT_INSTRUCTION_H_ 17 18 #include <cassert> 19 #include <functional> 20 #include <utility> 21 #include <vector> 22 23 #include "operand.h" 24 25 #include "spirv-tools/libspirv.h" 26 #include "spirv/1.2/spirv.h" 27 28 namespace spvtools { 29 namespace ir { 30 31 class Function; 32 class Module; 33 34 // About operand: 35 // 36 // In the SPIR-V specification, the term "operand" is used to mean any single 37 // SPIR-V word following the leading wordcount-opcode word. Here, the term 38 // "operand" is used to mean a *logical* operand. A logical operand may consist 39 // of mulitple SPIR-V words, which together make up the same component. For 40 // example, a logical operand of a 64-bit integer needs two words to express. 41 // 42 // Further, we categorize logical operands into *in* and *out* operands. 43 // In operands are operands actually serve as input to operations, while out 44 // operands are operands that represent ids generated from operations (result 45 // type id or result id). For example, for "OpIAdd %rtype %rid %inop1 %inop2", 46 // "%inop1" and "%inop2" are in operands, while "%rtype" and "%rid" are out 47 // operands. 48 49 // A *logical* operand to a SPIR-V instruction. It can be the type id, result 50 // id, or other additional operands carried in an instruction. 51 struct Operand { 52 Operand(spv_operand_type_t t, std::vector<uint32_t>&& w) 53 : type(t), words(std::move(w)) {} 54 55 Operand(spv_operand_type_t t, const std::vector<uint32_t>& w) 56 : type(t), words(w) {} 57 58 spv_operand_type_t type; // Type of this logical operand. 59 std::vector<uint32_t> words; // Binary segments of this logical operand. 60 61 // TODO(antiagainst): create fields for literal number kind, width, etc. 62 }; 63 64 // A SPIR-V instruction. It contains the opcode and any additional logical 65 // operand, including the result id (if any) and result type id (if any). It 66 // may also contain line-related debug instruction (OpLine, OpNoLine) directly 67 // appearing before this instruction. Note that the result id of an instruction 68 // should never change after the instruction being built. If the result id 69 // needs to change, the user should create a new instruction instead. 70 class Instruction { 71 public: 72 using iterator = std::vector<Operand>::iterator; 73 using const_iterator = std::vector<Operand>::const_iterator; 74 75 // Creates a default OpNop instruction. 76 Instruction() : opcode_(SpvOpNop), type_id_(0), result_id_(0) {} 77 // Creates an instruction with the given opcode |op| and no additional logical 78 // operands. 79 Instruction(SpvOp op) : opcode_(op), type_id_(0), result_id_(0) {} 80 // Creates an instruction using the given spv_parsed_instruction_t |inst|. All 81 // the data inside |inst| will be copied and owned in this instance. And keep 82 // record of line-related debug instructions |dbg_line| ahead of this 83 // instruction, if any. 84 Instruction(const spv_parsed_instruction_t& inst, 85 std::vector<Instruction>&& dbg_line = {}); 86 87 // Creates an instruction with the given opcode |op|, type id: |ty_id|, 88 // result id: |res_id| and input operands: |in_operands|. 89 Instruction(SpvOp op, uint32_t ty_id, uint32_t res_id, 90 const std::vector<Operand>& in_operands); 91 92 Instruction(const Instruction&) = default; 93 Instruction& operator=(const Instruction&) = default; 94 95 Instruction(Instruction&&); 96 Instruction& operator=(Instruction&&); 97 98 SpvOp opcode() const { return opcode_; } 99 // Sets the opcode of this instruction to a specific opcode. Note this may 100 // invalidate the instruction. 101 // TODO(qining): Remove this function when instruction building and insertion 102 // is well implemented. 103 void SetOpcode(SpvOp op) { opcode_ = op; } 104 uint32_t type_id() const { return type_id_; } 105 uint32_t result_id() const { return result_id_; } 106 // Returns the vector of line-related debug instructions attached to this 107 // instruction and the caller can directly modify them. 108 std::vector<Instruction>& dbg_line_insts() { return dbg_line_insts_; } 109 const std::vector<Instruction>& dbg_line_insts() const { 110 return dbg_line_insts_; 111 } 112 113 // Begin and end iterators for operands. 114 iterator begin() { return operands_.begin(); } 115 iterator end() { return operands_.end(); } 116 const_iterator begin() const { return operands_.cbegin(); } 117 const_iterator end() const { return operands_.cend(); } 118 // Const begin and end iterators for operands. 119 const_iterator cbegin() const { return operands_.cbegin(); } 120 const_iterator cend() const { return operands_.cend(); } 121 122 // Gets the number of logical operands. 123 uint32_t NumOperands() const { 124 return static_cast<uint32_t>(operands_.size()); 125 } 126 // Gets the number of SPIR-V words occupied by all logical operands. 127 uint32_t NumOperandWords() const { 128 return NumInOperandWords() + TypeResultIdCount(); 129 } 130 // Gets the |index|-th logical operand. 131 inline const Operand& GetOperand(uint32_t index) const; 132 // Gets the |index|-th logical operand as a single SPIR-V word. This method is 133 // not expected to be used with logical operands consisting of multiple SPIR-V 134 // words. 135 uint32_t GetSingleWordOperand(uint32_t index) const; 136 // Sets the |index|-th in-operand's data to the given |data|. 137 inline void SetInOperand(uint32_t index, std::vector<uint32_t>&& data); 138 // Sets the result type id. 139 inline void SetResultType(uint32_t ty_id); 140 // Sets the result id 141 inline void SetResultId(uint32_t res_id); 142 143 // The following methods are similar to the above, but are for in operands. 144 uint32_t NumInOperands() const { 145 return static_cast<uint32_t>(operands_.size() - TypeResultIdCount()); 146 } 147 uint32_t NumInOperandWords() const; 148 const Operand& GetInOperand(uint32_t index) const { 149 return GetOperand(index + TypeResultIdCount()); 150 } 151 uint32_t GetSingleWordInOperand(uint32_t index) const { 152 return GetSingleWordOperand(index + TypeResultIdCount()); 153 } 154 155 // Returns true if this instruction is OpNop. 156 inline bool IsNop() const; 157 // Turns this instruction to OpNop. This does not clear out all preceding 158 // line-related debug instructions. 159 inline void ToNop(); 160 161 // Runs the given function |f| on this instruction and optionally on the 162 // preceding debug line instructions. The function will always be run 163 // if this is itself a debug line instruction. 164 inline void ForEachInst(const std::function<void(Instruction*)>& f, 165 bool run_on_debug_line_insts = false); 166 inline void ForEachInst(const std::function<void(const Instruction*)>& f, 167 bool run_on_debug_line_insts = false) const; 168 169 // Runs the given function |f| on all "in" operand ids 170 inline void ForEachInId(const std::function<void(uint32_t*)>& f); 171 inline void ForEachInId(const std::function<void(const uint32_t*)>& f) const; 172 173 // Returns true if any operands can be labels 174 inline bool HasLabels() const; 175 176 // Pushes the binary segments for this instruction into the back of *|binary|. 177 void ToBinaryWithoutAttachedDebugInsts(std::vector<uint32_t>* binary) const; 178 179 private: 180 // Returns the toal count of result type id and result id. 181 uint32_t TypeResultIdCount() const { 182 return (type_id_ != 0) + (result_id_ != 0); 183 } 184 185 SpvOp opcode_; // Opcode 186 uint32_t type_id_; // Result type id. A value of 0 means no result type id. 187 uint32_t result_id_; // Result id. A value of 0 means no result id. 188 // All logical operands, including result type id and result id. 189 std::vector<Operand> operands_; 190 // Opline and OpNoLine instructions preceding this instruction. Note that for 191 // Instructions representing OpLine or OpNonLine itself, this field should be 192 // empty. 193 std::vector<Instruction> dbg_line_insts_; 194 }; 195 196 inline const Operand& Instruction::GetOperand(uint32_t index) const { 197 assert(index < operands_.size() && "operand index out of bound"); 198 return operands_[index]; 199 }; 200 201 inline void Instruction::SetInOperand(uint32_t index, 202 std::vector<uint32_t>&& data) { 203 assert(index + TypeResultIdCount() < operands_.size() && 204 "operand index out of bound"); 205 operands_[index + TypeResultIdCount()].words = std::move(data); 206 } 207 208 inline void Instruction::SetResultId(uint32_t res_id) { 209 result_id_ = res_id; 210 auto ridx = (type_id_ != 0) ? 1 : 0; 211 assert(operands_[ridx].type == SPV_OPERAND_TYPE_RESULT_ID); 212 operands_[ridx].words = {res_id}; 213 } 214 215 inline void Instruction::SetResultType(uint32_t ty_id) { 216 if (type_id_ != 0) { 217 type_id_ = ty_id; 218 assert(operands_.front().type == SPV_OPERAND_TYPE_TYPE_ID); 219 operands_.front().words = {ty_id}; 220 } 221 } 222 223 inline bool Instruction::IsNop() const { 224 return opcode_ == SpvOpNop && type_id_ == 0 && result_id_ == 0 && 225 operands_.empty(); 226 } 227 228 inline void Instruction::ToNop() { 229 opcode_ = SpvOpNop; 230 type_id_ = result_id_ = 0; 231 operands_.clear(); 232 } 233 234 inline void Instruction::ForEachInst(const std::function<void(Instruction*)>& f, 235 bool run_on_debug_line_insts) { 236 if (run_on_debug_line_insts) 237 for (auto& dbg_line : dbg_line_insts_) f(&dbg_line); 238 f(this); 239 } 240 241 inline void Instruction::ForEachInst( 242 const std::function<void(const Instruction*)>& f, 243 bool run_on_debug_line_insts) const { 244 if (run_on_debug_line_insts) 245 for (auto& dbg_line : dbg_line_insts_) f(&dbg_line); 246 f(this); 247 } 248 249 inline void Instruction::ForEachInId(const std::function<void(uint32_t*)>& f) { 250 for (auto& opnd : operands_) { 251 switch (opnd.type) { 252 case SPV_OPERAND_TYPE_RESULT_ID: 253 case SPV_OPERAND_TYPE_TYPE_ID: 254 break; 255 default: 256 if (spvIsIdType(opnd.type)) f(&opnd.words[0]); 257 break; 258 } 259 } 260 } 261 262 inline void Instruction::ForEachInId( 263 const std::function<void(const uint32_t*)>& f) const { 264 for (const auto& opnd : operands_) { 265 switch (opnd.type) { 266 case SPV_OPERAND_TYPE_RESULT_ID: 267 case SPV_OPERAND_TYPE_TYPE_ID: 268 break; 269 default: 270 if (spvIsIdType(opnd.type)) f(&opnd.words[0]); 271 break; 272 } 273 } 274 } 275 276 inline bool Instruction::HasLabels() const { 277 switch (opcode_) { 278 case SpvOpSelectionMerge: 279 case SpvOpBranch: 280 case SpvOpLoopMerge: 281 case SpvOpBranchConditional: 282 case SpvOpSwitch: 283 case SpvOpPhi: 284 return true; 285 break; 286 default: 287 break; 288 } 289 return false; 290 } 291 292 } // namespace ir 293 } // namespace spvtools 294 295 #endif // LIBSPIRV_OPT_INSTRUCTION_H_ 296