Home | History | Annotate | Download | only in source
      1 // Copyright (c) 2015-2016 The Khronos Group Inc.
      2 //
      3 // Permission is hereby granted, free of charge, to any person obtaining a
      4 // copy of this software and/or associated documentation files (the
      5 // "Materials"), to deal in the Materials without restriction, including
      6 // without limitation the rights to use, copy, modify, merge, publish,
      7 // distribute, sublicense, and/or sell copies of the Materials, and to
      8 // permit persons to whom the Materials are furnished to do so, subject to
      9 // the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included
     12 // in all copies or substantial portions of the Materials.
     13 //
     14 // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
     15 // KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
     16 // SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
     17 //    https://www.khronos.org/registry/
     18 //
     19 // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     22 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
     23 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     24 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     25 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
     26 
     27 #ifndef LIBSPIRV_TEXT_HANDLER_H_
     28 #define LIBSPIRV_TEXT_HANDLER_H_
     29 
     30 #include <iomanip>
     31 #include <sstream>
     32 #include <type_traits>
     33 #include <unordered_map>
     34 
     35 #include "diagnostic.h"
     36 #include "instruction.h"
     37 #include "spirv-tools/libspirv.h"
     38 #include "text.h"
     39 
     40 namespace libspirv {
     41 // Structures
     42 
     43 // This is a lattice for tracking types.
     44 enum class IdTypeClass {
     45   kBottom = 0,  // We have no information yet.
     46   kScalarIntegerType,
     47   kScalarFloatType,
     48   kOtherType
     49 };
     50 
     51 // Contains ID type information that needs to be tracked across all Ids.
     52 // Bitwidth is only valid when type_class is kScalarIntegerType or
     53 // kScalarFloatType.
     54 struct IdType {
     55   uint32_t bitwidth;  // Safe to assume that we will not have > 2^32 bits.
     56   bool isSigned;      // This is only significant if type_class is integral.
     57   IdTypeClass type_class;
     58 };
     59 
     60 // Default equality operator for IdType. Tests if all members are the same.
     61 inline bool operator==(const IdType& first, const IdType& second) {
     62   return (first.bitwidth == second.bitwidth) &&
     63          (first.isSigned == second.isSigned) &&
     64          (first.type_class == second.type_class);
     65 }
     66 
     67 // Tests whether any member of the IdTypes do not match.
     68 inline bool operator!=(const IdType& first, const IdType& second) {
     69   return !(first == second);
     70 }
     71 
     72 // A value representing an unknown type.
     73 extern const IdType kUnknownType;
     74 
     75 // Returns true if the type is a scalar integer type.
     76 inline bool isScalarIntegral(const IdType& type) {
     77   return type.type_class == IdTypeClass::kScalarIntegerType;
     78 }
     79 
     80 // Returns true if the type is a scalar floating point type.
     81 inline bool isScalarFloating(const IdType& type) {
     82   return type.type_class == IdTypeClass::kScalarFloatType;
     83 }
     84 
     85 // Returns the number of bits in the type.
     86 // This is only valid for bottom, scalar integer, and scalar floating
     87 // classes.  For bottom, assume 32 bits.
     88 inline int assumedBitWidth(const IdType& type) {
     89   switch (type.type_class) {
     90     case IdTypeClass::kBottom:
     91       return 32;
     92     case IdTypeClass::kScalarIntegerType:
     93     case IdTypeClass::kScalarFloatType:
     94       return type.bitwidth;
     95     default:
     96       break;
     97   }
     98   // We don't care about this case.
     99   return 0;
    100 }
    101 
    102 // A templated class with a static member function Clamp, where Clamp
    103 // sets a referenced value of type T to 0 if T is an unsigned
    104 // integer type, and returns true if it modified the referenced
    105 // value.
    106 template <typename T, typename = void>
    107 class ClampToZeroIfUnsignedType {
    108  public:
    109   // The default specialization does not clamp the value.
    110   static bool Clamp(T*) { return false; }
    111 };
    112 
    113 // The specialization of ClampToZeroIfUnsignedType for unsigned integer
    114 // types.
    115 template <typename T>
    116 class ClampToZeroIfUnsignedType<
    117     T, typename std::enable_if<std::is_unsigned<T>::value>::type> {
    118  public:
    119   static bool Clamp(T* value_pointer) {
    120     if (*value_pointer) {
    121       *value_pointer = 0;
    122       return true;
    123     }
    124     return false;
    125   }
    126 };
    127 
    128 // Encapsulates the data used during the assembly of a SPIR-V module.
    129 class AssemblyContext {
    130  public:
    131   AssemblyContext(spv_text text, spv_diagnostic* diagnostic_arg)
    132       : current_position_({}),
    133         pDiagnostic_(diagnostic_arg),
    134         text_(text),
    135         bound_(1) {}
    136 
    137   // Assigns a new integer value to the given text ID, or returns the previously
    138   // assigned integer value if the ID has been seen before.
    139   uint32_t spvNamedIdAssignOrGet(const char* textValue);
    140 
    141   // Returns the largest largest numeric ID that has been assigned.
    142   uint32_t getBound() const;
    143 
    144   // Advances position to point to the next word in the input stream.
    145   // Returns SPV_SUCCESS on success.
    146   spv_result_t advance();
    147 
    148   // Sets word to the next word in the input text. Fills next_position with
    149   // the next location past the end of the word.
    150   spv_result_t getWord(std::string* word, spv_position next_position);
    151 
    152   // Returns true if the next word in the input is the start of a new Opcode.
    153   bool startsWithOp();
    154 
    155   // Returns true if the next word in the input is the start of a new
    156   // instruction.
    157   bool isStartOfNewInst();
    158 
    159   // Returns a diagnostic object initialized with current position in the input
    160   // stream, and for the given error code. Any data written to this object will
    161   // show up in pDiagnsotic on destruction.
    162   DiagnosticStream diagnostic(spv_result_t error) {
    163     return DiagnosticStream(current_position_, pDiagnostic_, error);
    164   }
    165 
    166   // Returns a diagnostic object with the default assembly error code.
    167   DiagnosticStream diagnostic() {
    168     // The default failure for assembly is invalid text.
    169     return diagnostic(SPV_ERROR_INVALID_TEXT);
    170   }
    171 
    172   // Returns then next character in the input stream.
    173   char peek() const;
    174 
    175   // Returns true if there is more text in the input stream.
    176   bool hasText() const;
    177 
    178   // Seeks the input stream forward by 'size' characters.
    179   void seekForward(uint32_t size);
    180 
    181   // Sets the current position in the input stream to the given position.
    182   void setPosition(const spv_position_t& newPosition) {
    183     current_position_ = newPosition;
    184   }
    185 
    186   // Returns the current position in the input stream.
    187   const spv_position_t& position() const { return current_position_; }
    188 
    189   // Appends the given 32-bit value to the given instruction.
    190   // Returns SPV_SUCCESS if the value could be correctly inserted in the
    191   // instruction.
    192   spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t* pInst);
    193 
    194   // Appends the given string to the given instruction.
    195   // Returns SPV_SUCCESS if the value could be correctly inserted in the
    196   // instruction.
    197   spv_result_t binaryEncodeString(const char* value, spv_instruction_t* pInst);
    198 
    199   // Appends the given numeric literal to the given instruction.
    200   // Validates and respects the bitwidth supplied in the IdType argument.
    201   // If the type is of class kBottom the value will be encoded as a
    202   // 32-bit integer.
    203   // Returns SPV_SUCCESS if the value could be correctly added to the
    204   // instruction.  Returns the given error code on failure, and emits
    205   // a diagnostic if that error code is not SPV_FAILED_MATCH.
    206   spv_result_t binaryEncodeNumericLiteral(const char* numeric_literal,
    207                                           spv_result_t error_code,
    208                                           const IdType& type,
    209                                           spv_instruction_t* pInst);
    210 
    211   // Returns the IdType associated with this type-generating value.
    212   // If the type has not been previously recorded with recordTypeDefinition,
    213   // kUnknownType  will be returned.
    214   IdType getTypeOfTypeGeneratingValue(uint32_t value) const;
    215 
    216   // Returns the IdType that represents the return value of this Value
    217   // generating instruction.
    218   // If the value has not been recorded with recordTypeIdForValue, or the type
    219   // could not be determined kUnknownType will be returned.
    220   IdType getTypeOfValueInstruction(uint32_t value) const;
    221 
    222   // Tracks the type-defining instruction. The result of the tracking can
    223   // later be queried using getValueType.
    224   // pInst is expected to be completely filled in by the time this instruction
    225   // is called.
    226   // Returns SPV_SUCCESS on success, or SPV_ERROR_INVALID_VALUE on error.
    227   spv_result_t recordTypeDefinition(const spv_instruction_t* pInst);
    228 
    229   // Tracks the relationship between the value and its type.
    230   spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type);
    231 
    232   // Records the given Id as being the import of the given extended instruction
    233   // type.
    234   spv_result_t recordIdAsExtInstImport(uint32_t id, spv_ext_inst_type_t type);
    235 
    236   // Returns the extended instruction type corresponding to the import with
    237   // the given Id, if it exists.  Returns SPV_EXT_INST_TYPE_NONE if the
    238   // id is not the id for an extended instruction type.
    239   spv_ext_inst_type_t getExtInstTypeForId(uint32_t id) const;
    240 
    241   // Parses a numeric value of a given type from the given text.  The number
    242   // should take up the entire string, and should be within bounds for the
    243   // target type.  On success, returns SPV_SUCCESS and populates the object
    244   // referenced by value_pointer. On failure, returns the given error code,
    245   // and emits a diagnostic if that error code is not SPV_FAILED_MATCH.
    246   template <typename T>
    247   spv_result_t parseNumber(const char* text, spv_result_t error_code,
    248                            T* value_pointer,
    249                            const char* error_message_fragment) {
    250     // C++11 doesn't define std::istringstream(int8_t&), so calling this method
    251     // with a single-byte type leads to implementation-defined behaviour.
    252     // Similarly for uint8_t.
    253     static_assert(sizeof(T) > 1,
    254                   "Don't use a single-byte type this parse method");
    255 
    256     std::istringstream text_stream(text);
    257     // Allow both decimal and hex input for integers.
    258     // It also allows octal input, but we don't care about that case.
    259     text_stream >> std::setbase(0);
    260     text_stream >> *value_pointer;
    261 
    262     // We should have read something.
    263     bool ok = (text[0] != 0) && !text_stream.bad();
    264     // It should have been all the text.
    265     ok = ok && text_stream.eof();
    266     // It should have been in range.
    267     ok = ok && !text_stream.fail();
    268 
    269     // Work around a bug in the GNU C++11 library. It will happily parse
    270     // "-1" for uint16_t as 65535.
    271     if (ok && text[0] == '-')
    272       ok = !ClampToZeroIfUnsignedType<T>::Clamp(value_pointer);
    273 
    274     if (ok) return SPV_SUCCESS;
    275     return diagnostic(error_code) << error_message_fragment << text;
    276   }
    277 
    278  private:
    279 
    280   // Appends the given floating point literal to the given instruction.
    281   // Returns SPV_SUCCESS if the value was correctly parsed.  Otherwise
    282   // returns the given error code, and emits a diagnostic if that error
    283   // code is not SPV_FAILED_MATCH.
    284   // Only 32 and 64 bit floating point numbers are supported.
    285   spv_result_t binaryEncodeFloatingPointLiteral(const char* numeric_literal,
    286                                                 spv_result_t error_code,
    287                                                 const IdType& type,
    288                                                 spv_instruction_t* pInst);
    289 
    290   // Appends the given integer literal to the given instruction.
    291   // Returns SPV_SUCCESS if the value was correctly parsed.  Otherwise
    292   // returns the given error code, and emits a diagnostic if that error
    293   // code is not SPV_FAILED_MATCH.
    294   // Integers up to 64 bits are supported.
    295   spv_result_t binaryEncodeIntegerLiteral(const char* numeric_literal,
    296                                           spv_result_t error_code,
    297                                           const IdType& type,
    298                                           spv_instruction_t* pInst);
    299 
    300   // Writes the given 64-bit literal value into the instruction.
    301   // return SPV_SUCCESS if the value could be written in the instruction.
    302   spv_result_t binaryEncodeU64(const uint64_t value, spv_instruction_t* pInst);
    303   // Maps ID names to their corresponding numerical ids.
    304   using spv_named_id_table = std::unordered_map<std::string, uint32_t>;
    305   // Maps type-defining IDs to their IdType.
    306   using spv_id_to_type_map = std::unordered_map<uint32_t, IdType>;
    307   // Maps Ids to the id of their type.
    308   using spv_id_to_type_id = std::unordered_map<uint32_t, uint32_t>;
    309 
    310   spv_named_id_table named_ids_;
    311   spv_id_to_type_map types_;
    312   spv_id_to_type_id value_types_;
    313   // Maps an extended instruction import Id to the extended instruction type.
    314   std::unordered_map<uint32_t, spv_ext_inst_type_t> import_id_to_ext_inst_type_;
    315   spv_position_t current_position_;
    316   spv_diagnostic* pDiagnostic_;
    317   spv_text text_;
    318   uint32_t bound_;
    319 };
    320 }
    321 #endif  // _LIBSPIRV_TEXT_HANDLER_H_
    322