Home | History | Annotate | Download | only in opt
      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