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