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 #include "text_handler.h" 28 29 #include <cassert> 30 #include <cstdlib> 31 #include <cstring> 32 #include <tuple> 33 34 #include "assembly_grammar.h" 35 #include "binary.h" 36 #include "ext_inst.h" 37 #include "instruction.h" 38 #include "opcode.h" 39 #include "text.h" 40 #include "util/bitutils.h" 41 #include "util/hex_float.h" 42 43 namespace { 44 45 using spvutils::BitwiseCast; 46 using spvutils::FloatProxy; 47 using spvutils::HexFloat; 48 49 // Advances |text| to the start of the next line and writes the new position to 50 // |position|. 51 spv_result_t advanceLine(spv_text text, spv_position position) { 52 while (true) { 53 if (position->index >= text->length) return SPV_END_OF_STREAM; 54 switch (text->str[position->index]) { 55 case '\0': 56 return SPV_END_OF_STREAM; 57 case '\n': 58 position->column = 0; 59 position->line++; 60 position->index++; 61 return SPV_SUCCESS; 62 default: 63 position->column++; 64 position->index++; 65 break; 66 } 67 } 68 } 69 70 // Advances |text| to first non white space character and writes the new 71 // position to |position|. 72 // If a null terminator is found during the text advance, SPV_END_OF_STREAM is 73 // returned, SPV_SUCCESS otherwise. No error checking is performed on the 74 // parameters, its the users responsibility to ensure these are non null. 75 spv_result_t advance(spv_text text, spv_position position) { 76 // NOTE: Consume white space, otherwise don't advance. 77 if (position->index >= text->length) return SPV_END_OF_STREAM; 78 switch (text->str[position->index]) { 79 case '\0': 80 return SPV_END_OF_STREAM; 81 case ';': 82 if (spv_result_t error = advanceLine(text, position)) return error; 83 return advance(text, position); 84 case ' ': 85 case '\t': 86 case '\r': 87 position->column++; 88 position->index++; 89 return advance(text, position); 90 case '\n': 91 position->column = 0; 92 position->line++; 93 position->index++; 94 return advance(text, position); 95 default: 96 break; 97 } 98 return SPV_SUCCESS; 99 } 100 101 // Fetches the next word from the given text stream starting from the given 102 // *position. On success, writes the decoded word into *word and updates 103 // *position to the location past the returned word. 104 // 105 // A word ends at the next comment or whitespace. However, double-quoted 106 // strings remain intact, and a backslash always escapes the next character. 107 spv_result_t getWord(spv_text text, spv_position position, std::string* word) { 108 if (!text->str || !text->length) return SPV_ERROR_INVALID_TEXT; 109 if (!position) return SPV_ERROR_INVALID_POINTER; 110 111 const size_t start_index = position->index; 112 113 bool quoting = false; 114 bool escaping = false; 115 116 // NOTE: Assumes first character is not white space! 117 while (true) { 118 if (position->index >= text->length) { 119 word->assign(text->str + start_index, text->str + position->index); 120 return SPV_SUCCESS; 121 } 122 const char ch = text->str[position->index]; 123 if (ch == '\\') 124 escaping = !escaping; 125 else { 126 switch (ch) { 127 case '"': 128 if (!escaping) quoting = !quoting; 129 break; 130 case ' ': 131 case ';': 132 case '\t': 133 case '\n': 134 case '\r': 135 if (escaping || quoting) break; 136 // Fall through. 137 case '\0': { // NOTE: End of word found! 138 word->assign(text->str + start_index, text->str + position->index); 139 return SPV_SUCCESS; 140 } 141 default: 142 break; 143 } 144 escaping = false; 145 } 146 147 position->column++; 148 position->index++; 149 } 150 } 151 152 // Returns true if the characters in the text as position represent 153 // the start of an Opcode. 154 bool startsWithOp(spv_text text, spv_position position) { 155 if (text->length < position->index + 3) return false; 156 char ch0 = text->str[position->index]; 157 char ch1 = text->str[position->index + 1]; 158 char ch2 = text->str[position->index + 2]; 159 return ('O' == ch0 && 'p' == ch1 && ('A' <= ch2 && ch2 <= 'Z')); 160 } 161 162 } // anonymous namespace 163 164 namespace libspirv { 165 166 const IdType kUnknownType = {0, false, IdTypeClass::kBottom}; 167 168 // TODO(dneto): Reorder AssemblyContext definitions to match declaration order. 169 170 // This represents all of the data that is only valid for the duration of 171 // a single compilation. 172 uint32_t AssemblyContext::spvNamedIdAssignOrGet(const char* textValue) { 173 if (named_ids_.end() == named_ids_.find(textValue)) { 174 named_ids_[std::string(textValue)] = bound_++; 175 } 176 return named_ids_[textValue]; 177 } 178 uint32_t AssemblyContext::getBound() const { return bound_; } 179 180 spv_result_t AssemblyContext::advance() { 181 return ::advance(text_, ¤t_position_); 182 } 183 184 spv_result_t AssemblyContext::getWord(std::string* word, 185 spv_position next_position) { 186 *next_position = current_position_; 187 return ::getWord(text_, next_position, word); 188 } 189 190 bool AssemblyContext::startsWithOp() { 191 return ::startsWithOp(text_, ¤t_position_); 192 } 193 194 bool AssemblyContext::isStartOfNewInst() { 195 spv_position_t pos = current_position_; 196 if (::advance(text_, &pos)) return false; 197 if (::startsWithOp(text_, &pos)) return true; 198 199 std::string word; 200 pos = current_position_; 201 if (::getWord(text_, &pos, &word)) return false; 202 if ('%' != word.front()) return false; 203 204 if (::advance(text_, &pos)) return false; 205 if (::getWord(text_, &pos, &word)) return false; 206 if ("=" != word) return false; 207 208 if (::advance(text_, &pos)) return false; 209 if (::startsWithOp(text_, &pos)) return true; 210 return false; 211 } 212 213 char AssemblyContext::peek() const { 214 return text_->str[current_position_.index]; 215 } 216 217 bool AssemblyContext::hasText() const { 218 return text_->length > current_position_.index; 219 } 220 221 void AssemblyContext::seekForward(uint32_t size) { 222 current_position_.index += size; 223 current_position_.column += size; 224 } 225 226 spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value, 227 spv_instruction_t* pInst) { 228 pInst->words.insert(pInst->words.end(), value); 229 return SPV_SUCCESS; 230 } 231 232 spv_result_t AssemblyContext::binaryEncodeU64(const uint64_t value, 233 spv_instruction_t* pInst) { 234 uint32_t low = uint32_t(0x00000000ffffffff & value); 235 uint32_t high = uint32_t((0xffffffff00000000 & value) >> 32); 236 binaryEncodeU32(low, pInst); 237 binaryEncodeU32(high, pInst); 238 return SPV_SUCCESS; 239 } 240 241 spv_result_t AssemblyContext::binaryEncodeNumericLiteral( 242 const char* val, spv_result_t error_code, const IdType& type, 243 spv_instruction_t* pInst) { 244 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom; 245 const bool is_floating = libspirv::isScalarFloating(type); 246 const bool is_integer = libspirv::isScalarIntegral(type); 247 248 if (!is_bottom && !is_floating && !is_integer) { 249 return diagnostic(SPV_ERROR_INTERNAL) 250 << "The expected type is not a scalar integer or float type"; 251 } 252 253 // If this is bottom, but looks like a float, we should treat it like a 254 // float. 255 const bool looks_like_float = is_bottom && strchr(val, '.'); 256 257 // If we explicitly expect a floating-point number, we should handle that 258 // first. 259 if (is_floating || looks_like_float) 260 return binaryEncodeFloatingPointLiteral(val, error_code, type, pInst); 261 262 return binaryEncodeIntegerLiteral(val, error_code, type, pInst); 263 } 264 265 spv_result_t AssemblyContext::binaryEncodeString(const char* value, 266 spv_instruction_t* pInst) { 267 const size_t length = strlen(value); 268 const size_t wordCount = (length / 4) + 1; 269 const size_t oldWordCount = pInst->words.size(); 270 const size_t newWordCount = oldWordCount + wordCount; 271 272 // TODO(dneto): We can just defer this check until later. 273 if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) { 274 return diagnostic() << "Instruction too long: more than " 275 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words."; 276 } 277 278 pInst->words.resize(newWordCount); 279 280 // Make sure all the bytes in the last word are 0, in case we only 281 // write a partial word at the end. 282 pInst->words.back() = 0; 283 284 char* dest = (char*)&pInst->words[oldWordCount]; 285 strncpy(dest, value, length); 286 287 return SPV_SUCCESS; 288 } 289 290 spv_result_t AssemblyContext::recordTypeDefinition( 291 const spv_instruction_t* pInst) { 292 uint32_t value = pInst->words[1]; 293 if (types_.find(value) != types_.end()) { 294 return diagnostic() << "Value " << value 295 << " has already been used to generate a type"; 296 } 297 298 if (pInst->opcode == SpvOpTypeInt) { 299 if (pInst->words.size() != 4) 300 return diagnostic() << "Invalid OpTypeInt instruction"; 301 types_[value] = {pInst->words[2], pInst->words[3] != 0, 302 IdTypeClass::kScalarIntegerType}; 303 } else if (pInst->opcode == SpvOpTypeFloat) { 304 if (pInst->words.size() != 3) 305 return diagnostic() << "Invalid OpTypeFloat instruction"; 306 types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType}; 307 } else { 308 types_[value] = {0, false, IdTypeClass::kOtherType}; 309 } 310 return SPV_SUCCESS; 311 } 312 313 IdType AssemblyContext::getTypeOfTypeGeneratingValue(uint32_t value) const { 314 auto type = types_.find(value); 315 if (type == types_.end()) { 316 return kUnknownType; 317 } 318 return std::get<1>(*type); 319 } 320 321 IdType AssemblyContext::getTypeOfValueInstruction(uint32_t value) const { 322 auto type_value = value_types_.find(value); 323 if (type_value == value_types_.end()) { 324 return {0, false, IdTypeClass::kBottom}; 325 } 326 return getTypeOfTypeGeneratingValue(std::get<1>(*type_value)); 327 } 328 329 spv_result_t AssemblyContext::recordTypeIdForValue(uint32_t value, 330 uint32_t type) { 331 bool successfully_inserted = false; 332 std::tie(std::ignore, successfully_inserted) = 333 value_types_.insert(std::make_pair(value, type)); 334 if (!successfully_inserted) 335 return diagnostic() << "Value is being defined a second time"; 336 return SPV_SUCCESS; 337 } 338 339 spv_result_t AssemblyContext::recordIdAsExtInstImport( 340 uint32_t id, spv_ext_inst_type_t type) { 341 bool successfully_inserted = false; 342 std::tie(std::ignore, successfully_inserted) = 343 import_id_to_ext_inst_type_.insert(std::make_pair(id, type)); 344 if (!successfully_inserted) 345 return diagnostic() << "Import Id is being defined a second time"; 346 return SPV_SUCCESS; 347 } 348 349 spv_ext_inst_type_t AssemblyContext::getExtInstTypeForId(uint32_t id) const { 350 auto type = import_id_to_ext_inst_type_.find(id); 351 if (type == import_id_to_ext_inst_type_.end()) { 352 return SPV_EXT_INST_TYPE_NONE; 353 } 354 return std::get<1>(*type); 355 } 356 357 spv_result_t AssemblyContext::binaryEncodeFloatingPointLiteral( 358 const char* val, spv_result_t error_code, const IdType& type, 359 spv_instruction_t* pInst) { 360 const auto bit_width = assumedBitWidth(type); 361 switch (bit_width) { 362 case 16: { 363 spvutils::HexFloat<FloatProxy<spvutils::Float16>> hVal(0); 364 if (auto error = parseNumber(val, error_code, &hVal, 365 "Invalid 16-bit float literal: ")) 366 return error; 367 // getAsFloat will return the spvutils::Float16 value, and get_value 368 // will return a uint16_t representing the bits of the float. 369 // The encoding is therefore correct from the perspective of the SPIR-V 370 // spec since the top 16 bits will be 0. 371 return binaryEncodeU32( 372 static_cast<uint32_t>(hVal.value().getAsFloat().get_value()), pInst); 373 } break; 374 case 32: { 375 spvutils::HexFloat<FloatProxy<float>> fVal(0.0f); 376 if (auto error = parseNumber(val, error_code, &fVal, 377 "Invalid 32-bit float literal: ")) 378 return error; 379 return binaryEncodeU32(BitwiseCast<uint32_t>(fVal), pInst); 380 } break; 381 case 64: { 382 spvutils::HexFloat<FloatProxy<double>> dVal(0.0); 383 if (auto error = parseNumber(val, error_code, &dVal, 384 "Invalid 64-bit float literal: ")) 385 return error; 386 return binaryEncodeU64(BitwiseCast<uint64_t>(dVal), pInst); 387 } break; 388 default: 389 break; 390 } 391 return diagnostic() << "Unsupported " << bit_width << "-bit float literals"; 392 } 393 394 // Returns SPV_SUCCESS if the given value fits within the target scalar 395 // integral type. The target type may have an unusual bit width. 396 // If the value was originally specified as a hexadecimal number, then 397 // the overflow bits should be zero. If it was hex and the target type is 398 // signed, then return the sign-extended value through the 399 // updated_value_for_hex pointer argument. 400 // On failure, return the given error code and emit a diagnostic if that error 401 // code is not SPV_FAILED_MATCH. 402 template <typename T> 403 spv_result_t checkRangeAndIfHexThenSignExtend(T value, spv_result_t error_code, 404 const IdType& type, bool is_hex, 405 T* updated_value_for_hex) { 406 // The encoded result has three regions of bits that are of interest, from 407 // least to most significant: 408 // - magnitude bits, where the magnitude of the number would be stored if 409 // we were using a signed-magnitude representation. 410 // - an optional sign bit 411 // - overflow bits, up to bit 63 of a 64-bit number 412 // For example: 413 // Type Overflow Sign Magnitude 414 // --------------- -------- ---- --------- 415 // unsigned 8 bit 8-63 n/a 0-7 416 // signed 8 bit 8-63 7 0-6 417 // unsigned 16 bit 16-63 n/a 0-15 418 // signed 16 bit 16-63 15 0-14 419 420 // We'll use masks to define the three regions. 421 // At first we'll assume the number is unsigned. 422 const uint32_t bit_width = assumedBitWidth(type); 423 uint64_t magnitude_mask = 424 (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); 425 uint64_t sign_mask = 0; 426 uint64_t overflow_mask = ~magnitude_mask; 427 428 if (value < 0 || type.isSigned) { 429 // Accommodate the sign bit. 430 magnitude_mask >>= 1; 431 sign_mask = magnitude_mask + 1; 432 } 433 434 bool failed = false; 435 if (value < 0) { 436 // The top bits must all be 1 for a negative signed value. 437 failed = ((value & overflow_mask) != overflow_mask) || 438 ((value & sign_mask) != sign_mask); 439 } else { 440 if (is_hex) { 441 // Hex values are a bit special. They decode as unsigned values, but 442 // may represent a negative number. In this case, the overflow bits 443 // should be zero. 444 failed = (value & overflow_mask) != 0; 445 } else { 446 const uint64_t value_as_u64 = static_cast<uint64_t>(value); 447 // Check overflow in the ordinary case. 448 failed = (value_as_u64 & magnitude_mask) != value_as_u64; 449 } 450 } 451 452 if (failed) { 453 return error_code; 454 } 455 456 // Sign extend hex the number. 457 if (is_hex && (value & sign_mask)) 458 *updated_value_for_hex = (value | overflow_mask); 459 460 return SPV_SUCCESS; 461 } 462 463 spv_result_t AssemblyContext::binaryEncodeIntegerLiteral( 464 const char* val, spv_result_t error_code, const IdType& type, 465 spv_instruction_t* pInst) { 466 const bool is_bottom = type.type_class == libspirv::IdTypeClass::kBottom; 467 const uint32_t bit_width = assumedBitWidth(type); 468 469 if (bit_width > 64) 470 return diagnostic(SPV_ERROR_INTERNAL) << "Unsupported " << bit_width 471 << "-bit integer literals"; 472 473 // Either we are expecting anything or integer. 474 bool is_negative = val[0] == '-'; 475 bool can_be_signed = is_bottom || type.isSigned; 476 477 if (is_negative && !can_be_signed) { 478 return diagnostic() 479 << "Cannot put a negative number in an unsigned literal"; 480 } 481 482 const bool is_hex = val[0] == '0' && (val[1] == 'x' || val[1] == 'X'); 483 484 uint64_t decoded_bits; 485 if (is_negative) { 486 int64_t decoded_signed = 0; 487 488 if (auto error = parseNumber(val, error_code, &decoded_signed, 489 "Invalid signed integer literal: ")) 490 return error; 491 if (auto error = checkRangeAndIfHexThenSignExtend( 492 decoded_signed, error_code, type, is_hex, &decoded_signed)) { 493 diagnostic(error_code) 494 << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase 495 << decoded_signed << " does not fit in a " << std::dec << bit_width 496 << "-bit " << (type.isSigned ? "signed" : "unsigned") << " integer"; 497 return error; 498 } 499 decoded_bits = decoded_signed; 500 } else { 501 // There's no leading minus sign, so parse it as an unsigned integer. 502 if (auto error = parseNumber(val, error_code, &decoded_bits, 503 "Invalid unsigned integer literal: ")) 504 return error; 505 if (auto error = checkRangeAndIfHexThenSignExtend( 506 decoded_bits, error_code, type, is_hex, &decoded_bits)) { 507 diagnostic(error_code) 508 << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase 509 << decoded_bits << " does not fit in a " << std::dec << bit_width 510 << "-bit " << (type.isSigned ? "signed" : "unsigned") << " integer"; 511 return error; 512 } 513 } 514 if (bit_width > 32) { 515 return binaryEncodeU64(decoded_bits, pInst); 516 } else { 517 return binaryEncodeU32(uint32_t(decoded_bits), pInst); 518 } 519 } 520 } // namespace libspirv 521