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.h" 28 29 #include <algorithm> 30 #include <cassert> 31 #include <cctype> 32 #include <cstdio> 33 #include <cstdlib> 34 #include <cstring> 35 #include <memory> 36 #include <sstream> 37 #include <string> 38 #include <unordered_map> 39 #include <vector> 40 41 #include "assembly_grammar.h" 42 #include "binary.h" 43 #include "diagnostic.h" 44 #include "ext_inst.h" 45 #include "instruction.h" 46 #include "opcode.h" 47 #include "operand.h" 48 #include "spirv-tools/libspirv.h" 49 #include "spirv_constant.h" 50 #include "table.h" 51 #include "text_handler.h" 52 #include "util/bitutils.h" 53 54 bool spvIsValidIDCharacter(const char value) { 55 return value == '_' || 0 != ::isalnum(value); 56 } 57 58 // Returns true if the given string represents a valid ID name. 59 bool spvIsValidID(const char* textValue) { 60 const char* c = textValue; 61 for (; *c != '\0'; ++c) { 62 if (!spvIsValidIDCharacter(*c)) { 63 return false; 64 } 65 } 66 // If the string was empty, then the ID also is not valid. 67 return c != textValue; 68 } 69 70 // Text API 71 72 spv_result_t spvTextToLiteral(const char* textValue, spv_literal_t* pLiteral) { 73 bool isSigned = false; 74 int numPeriods = 0; 75 bool isString = false; 76 77 const size_t len = strlen(textValue); 78 if (len == 0) return SPV_FAILED_MATCH; 79 80 for (uint64_t index = 0; index < len; ++index) { 81 switch (textValue[index]) { 82 case '0': 83 case '1': 84 case '2': 85 case '3': 86 case '4': 87 case '5': 88 case '6': 89 case '7': 90 case '8': 91 case '9': 92 break; 93 case '.': 94 numPeriods++; 95 break; 96 case '-': 97 if (index == 0) { 98 isSigned = true; 99 } else { 100 isString = true; 101 } 102 break; 103 default: 104 isString = true; 105 index = len; // break out of the loop too. 106 break; 107 } 108 } 109 110 pLiteral->type = spv_literal_type_t(99); 111 112 if (isString || numPeriods > 1 || (isSigned && len == 1)) { 113 if (len < 2 || textValue[0] != '"' || textValue[len - 1] != '"') 114 return SPV_FAILED_MATCH; 115 bool escaping = false; 116 for (const char* val = textValue + 1; val != textValue + len - 1; ++val) { 117 if ((*val == '\\') && (!escaping)) { 118 escaping = true; 119 } else { 120 // Have to save space for the null-terminator 121 if (pLiteral->str.size() >= SPV_LIMIT_LITERAL_STRING_BYTES_MAX) 122 return SPV_ERROR_OUT_OF_MEMORY; 123 pLiteral->str.push_back(*val); 124 escaping = false; 125 } 126 } 127 128 pLiteral->type = SPV_LITERAL_TYPE_STRING; 129 } else if (numPeriods == 1) { 130 double d = std::strtod(textValue, nullptr); 131 float f = (float)d; 132 if (d == (double)f) { 133 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_32; 134 pLiteral->value.f = f; 135 } else { 136 pLiteral->type = SPV_LITERAL_TYPE_FLOAT_64; 137 pLiteral->value.d = d; 138 } 139 } else if (isSigned) { 140 int64_t i64 = strtoll(textValue, nullptr, 10); 141 int32_t i32 = (int32_t)i64; 142 if (i64 == (int64_t)i32) { 143 pLiteral->type = SPV_LITERAL_TYPE_INT_32; 144 pLiteral->value.i32 = i32; 145 } else { 146 pLiteral->type = SPV_LITERAL_TYPE_INT_64; 147 pLiteral->value.i64 = i64; 148 } 149 } else { 150 uint64_t u64 = strtoull(textValue, nullptr, 10); 151 uint32_t u32 = (uint32_t)u64; 152 if (u64 == (uint64_t)u32) { 153 pLiteral->type = SPV_LITERAL_TYPE_UINT_32; 154 pLiteral->value.u32 = u32; 155 } else { 156 pLiteral->type = SPV_LITERAL_TYPE_UINT_64; 157 pLiteral->value.u64 = u64; 158 } 159 } 160 161 return SPV_SUCCESS; 162 } 163 164 namespace { 165 166 /// Parses an immediate integer from text, guarding against overflow. If 167 /// successful, adds the parsed value to pInst, advances the context past it, 168 /// and returns SPV_SUCCESS. Otherwise, leaves pInst alone, emits diagnostics, 169 /// and returns SPV_ERROR_INVALID_TEXT. 170 spv_result_t encodeImmediate(libspirv::AssemblyContext* context, 171 const char* text, spv_instruction_t* pInst) { 172 assert(*text == '!'); 173 uint32_t parse_result; 174 if (auto error = 175 context->parseNumber(text + 1, SPV_ERROR_INVALID_TEXT, &parse_result, 176 "Invalid immediate integer: !")) 177 return error; 178 context->binaryEncodeU32(parse_result, pInst); 179 context->seekForward(static_cast<uint32_t>(strlen(text))); 180 return SPV_SUCCESS; 181 } 182 183 } // anonymous namespace 184 185 /// @brief Translate an Opcode operand to binary form 186 /// 187 /// @param[in] grammar the grammar to use for compilation 188 /// @param[in, out] context the dynamic compilation info 189 /// @param[in] type of the operand 190 /// @param[in] textValue word of text to be parsed 191 /// @param[out] pInst return binary Opcode 192 /// @param[in,out] pExpectedOperands the operand types expected 193 /// 194 /// @return result code 195 spv_result_t spvTextEncodeOperand(const libspirv::AssemblyGrammar& grammar, 196 libspirv::AssemblyContext* context, 197 const spv_operand_type_t type, 198 const char* textValue, 199 spv_instruction_t* pInst, 200 spv_operand_pattern_t* pExpectedOperands) { 201 // NOTE: Handle immediate int in the stream 202 if ('!' == textValue[0]) { 203 if (auto error = encodeImmediate(context, textValue, pInst)) { 204 return error; 205 } 206 *pExpectedOperands = 207 spvAlternatePatternFollowingImmediate(*pExpectedOperands); 208 return SPV_SUCCESS; 209 } 210 211 // Optional literal operands can fail to parse. In that case use 212 // SPV_FAILED_MATCH to avoid emitting a diagostic. Use the following 213 // for those situations. 214 spv_result_t error_code_for_literals = 215 spvOperandIsOptional(type) ? SPV_FAILED_MATCH : SPV_ERROR_INVALID_TEXT; 216 217 switch (type) { 218 case SPV_OPERAND_TYPE_ID: 219 case SPV_OPERAND_TYPE_TYPE_ID: 220 case SPV_OPERAND_TYPE_RESULT_ID: 221 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: 222 case SPV_OPERAND_TYPE_SCOPE_ID: 223 case SPV_OPERAND_TYPE_OPTIONAL_ID: { 224 if ('%' == textValue[0]) { 225 textValue++; 226 } else { 227 return context->diagnostic() << "Expected id to start with %."; 228 } 229 if (!spvIsValidID(textValue)) { 230 return context->diagnostic() << "Invalid ID " << textValue; 231 } 232 const uint32_t id = context->spvNamedIdAssignOrGet(textValue); 233 if (type == SPV_OPERAND_TYPE_TYPE_ID) pInst->resultTypeId = id; 234 spvInstructionAddWord(pInst, id); 235 236 // Set the extended instruction type. 237 // The import set id is the 3rd operand of OpExtInst. 238 if (pInst->opcode == SpvOpExtInst && pInst->words.size() == 4) { 239 auto ext_inst_type = context->getExtInstTypeForId(pInst->words[3]); 240 if (ext_inst_type == SPV_EXT_INST_TYPE_NONE) { 241 return context->diagnostic() 242 << "Invalid extended instruction import Id " 243 << pInst->words[2]; 244 } 245 pInst->extInstType = ext_inst_type; 246 } 247 } break; 248 249 case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { 250 // The assembler accepts the symbolic name for an extended instruction, 251 // and emits its corresponding number. 252 spv_ext_inst_desc extInst; 253 if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst)) { 254 return context->diagnostic() << "Invalid extended instruction name '" 255 << textValue << "'."; 256 } 257 spvInstructionAddWord(pInst, extInst->ext_inst); 258 259 // Prepare to parse the operands for the extended instructions. 260 spvPrependOperandTypes(extInst->operandTypes, pExpectedOperands); 261 } break; 262 263 case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { 264 // The assembler accepts the symbolic name for the opcode, but without 265 // the "Op" prefix. For example, "IAdd" is accepted. The number 266 // of the opcode is emitted. 267 SpvOp opcode; 268 if (grammar.lookupSpecConstantOpcode(textValue, &opcode)) { 269 return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) 270 << " '" << textValue << "'."; 271 } 272 spv_opcode_desc opcodeEntry = nullptr; 273 if (grammar.lookupOpcode(opcode, &opcodeEntry)) { 274 return context->diagnostic(SPV_ERROR_INTERNAL) 275 << "OpSpecConstant opcode table out of sync"; 276 } 277 spvInstructionAddWord(pInst, uint32_t(opcodeEntry->opcode)); 278 279 // Prepare to parse the operands for the opcode. Except skip the 280 // type Id and result Id, since they've already been processed. 281 assert(opcodeEntry->hasType); 282 assert(opcodeEntry->hasResult); 283 assert(opcodeEntry->numTypes >= 2); 284 spvPrependOperandTypes(opcodeEntry->operandTypes + 2, pExpectedOperands); 285 } break; 286 287 case SPV_OPERAND_TYPE_LITERAL_INTEGER: 288 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: { 289 // The current operand is an *unsigned* 32-bit integer. 290 // That's just how the grammar works. 291 libspirv::IdType expected_type = { 292 32, false, libspirv::IdTypeClass::kScalarIntegerType}; 293 if (auto error = context->binaryEncodeNumericLiteral( 294 textValue, error_code_for_literals, expected_type, pInst)) { 295 return error; 296 } 297 } break; 298 299 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: 300 // This is a context-independent literal number which can be a 32-bit 301 // number of floating point value. 302 if (auto error = context->binaryEncodeNumericLiteral( 303 textValue, error_code_for_literals, libspirv::kUnknownType, 304 pInst)) { 305 return error; 306 } 307 break; 308 309 case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: 310 case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { 311 libspirv::IdType expected_type = libspirv::kUnknownType; 312 // The encoding for OpConstant, OpSpecConstant and OpSwitch all 313 // depend on either their own result-id or the result-id of 314 // one of their parameters. 315 if (SpvOpConstant == pInst->opcode || 316 SpvOpSpecConstant == pInst->opcode) { 317 // The type of the literal is determined by the type Id of the 318 // instruction. 319 expected_type = 320 context->getTypeOfTypeGeneratingValue(pInst->resultTypeId); 321 if (!libspirv::isScalarFloating(expected_type) && 322 !libspirv::isScalarIntegral(expected_type)) { 323 spv_opcode_desc d; 324 const char* opcode_name = "opcode"; 325 if (SPV_SUCCESS == grammar.lookupOpcode(pInst->opcode, &d)) { 326 opcode_name = d->name; 327 } 328 return context->diagnostic() 329 << "Type for " << opcode_name 330 << " must be a scalar floating point or integer type"; 331 } 332 } else if (pInst->opcode == SpvOpSwitch) { 333 // The type of the literal is the same as the type of the selector. 334 expected_type = context->getTypeOfValueInstruction(pInst->words[1]); 335 if (!libspirv::isScalarIntegral(expected_type)) { 336 return context->diagnostic() 337 << "The selector operand for OpSwitch must be the result" 338 " of an instruction that generates an integer scalar"; 339 } 340 } 341 if (auto error = context->binaryEncodeNumericLiteral( 342 textValue, error_code_for_literals, expected_type, pInst)) { 343 return error; 344 } 345 } break; 346 347 case SPV_OPERAND_TYPE_LITERAL_STRING: 348 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: { 349 spv_literal_t literal = {}; 350 spv_result_t error = spvTextToLiteral(textValue, &literal); 351 if (error != SPV_SUCCESS) { 352 if (error == SPV_ERROR_OUT_OF_MEMORY) return error; 353 return context->diagnostic(error_code_for_literals) 354 << "Invalid literal string '" << textValue << "'."; 355 } 356 if (literal.type != SPV_LITERAL_TYPE_STRING) { 357 return context->diagnostic() 358 << "Expected literal string, found literal number '" << textValue 359 << "'."; 360 } 361 362 // NOTE: Special case for extended instruction library import 363 if (SpvOpExtInstImport == pInst->opcode) { 364 const spv_ext_inst_type_t ext_inst_type = 365 spvExtInstImportTypeGet(literal.str.c_str()); 366 if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) { 367 return context->diagnostic() 368 << "Invalid extended instruction import '" << literal.str 369 << "'"; 370 } 371 if ((error = context->recordIdAsExtInstImport(pInst->words[1], 372 ext_inst_type))) 373 return error; 374 } 375 376 if (context->binaryEncodeString(literal.str.c_str(), pInst)) 377 return SPV_ERROR_INVALID_TEXT; 378 } break; 379 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: 380 case SPV_OPERAND_TYPE_FUNCTION_CONTROL: 381 case SPV_OPERAND_TYPE_LOOP_CONTROL: 382 case SPV_OPERAND_TYPE_IMAGE: 383 case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: 384 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: 385 case SPV_OPERAND_TYPE_SELECTION_CONTROL: { 386 uint32_t value; 387 if (grammar.parseMaskOperand(type, textValue, &value)) { 388 return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) 389 << " operand '" << textValue << "'."; 390 } 391 if (auto error = context->binaryEncodeU32(value, pInst)) return error; 392 // Prepare to parse the operands for this logical operand. 393 grammar.prependOperandTypesForMask(type, value, pExpectedOperands); 394 } break; 395 case SPV_OPERAND_TYPE_OPTIONAL_CIV: { 396 auto error = spvTextEncodeOperand( 397 grammar, context, SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER, textValue, 398 pInst, pExpectedOperands); 399 if (error == SPV_FAILED_MATCH) { 400 // It's not a literal number -- is it a literal string? 401 error = spvTextEncodeOperand(grammar, context, 402 SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING, 403 textValue, pInst, pExpectedOperands); 404 } 405 if (error == SPV_FAILED_MATCH) { 406 // It's not a literal -- is it an ID? 407 error = 408 spvTextEncodeOperand(grammar, context, SPV_OPERAND_TYPE_OPTIONAL_ID, 409 textValue, pInst, pExpectedOperands); 410 } 411 if (error) { 412 return context->diagnostic(error) 413 << "Invalid word following !<integer>: " << textValue; 414 } 415 if (pExpectedOperands->empty()) { 416 pExpectedOperands->push_back(SPV_OPERAND_TYPE_OPTIONAL_CIV); 417 } 418 } break; 419 default: { 420 // NOTE: All non literal operands are handled here using the operand 421 // table. 422 spv_operand_desc entry; 423 if (grammar.lookupOperand(type, textValue, strlen(textValue), &entry)) { 424 return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) 425 << " '" << textValue << "'."; 426 } 427 if (context->binaryEncodeU32(entry->value, pInst)) { 428 return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) 429 << " '" << textValue << "'."; 430 } 431 432 // Prepare to parse the operands for this logical operand. 433 spvPrependOperandTypes(entry->operandTypes, pExpectedOperands); 434 } break; 435 } 436 return SPV_SUCCESS; 437 } 438 439 namespace { 440 441 /// Encodes an instruction started by !<integer> at the given position in text. 442 /// 443 /// Puts the encoded words into *pInst. If successful, moves position past the 444 /// instruction and returns SPV_SUCCESS. Otherwise, returns an error code and 445 /// leaves position pointing to the error in text. 446 spv_result_t encodeInstructionStartingWithImmediate( 447 const libspirv::AssemblyGrammar& grammar, 448 libspirv::AssemblyContext* context, spv_instruction_t* pInst) { 449 std::string firstWord; 450 spv_position_t nextPosition = {}; 451 auto error = context->getWord(&firstWord, &nextPosition); 452 if (error) return context->diagnostic(error) << "Internal Error"; 453 454 if ((error = encodeImmediate(context, firstWord.c_str(), pInst))) { 455 return error; 456 } 457 while (context->advance() != SPV_END_OF_STREAM) { 458 // A beginning of a new instruction means we're done. 459 if (context->isStartOfNewInst()) return SPV_SUCCESS; 460 461 // Otherwise, there must be an operand that's either a literal, an ID, or 462 // an immediate. 463 std::string operandValue; 464 if ((error = context->getWord(&operandValue, &nextPosition))) 465 return context->diagnostic(error) << "Internal Error"; 466 467 if (operandValue == "=") 468 return context->diagnostic() << firstWord << " not allowed before =."; 469 470 // Needed to pass to spvTextEncodeOpcode(), but it shouldn't ever be 471 // expanded. 472 spv_operand_pattern_t dummyExpectedOperands; 473 error = spvTextEncodeOperand( 474 grammar, context, SPV_OPERAND_TYPE_OPTIONAL_CIV, operandValue.c_str(), 475 pInst, &dummyExpectedOperands); 476 if (error) return error; 477 context->setPosition(nextPosition); 478 } 479 return SPV_SUCCESS; 480 } 481 482 /// @brief Translate single Opcode and operands to binary form 483 /// 484 /// @param[in] grammar the grammar to use for compilation 485 /// @param[in, out] context the dynamic compilation info 486 /// @param[in] text stream to translate 487 /// @param[out] pInst returned binary Opcode 488 /// @param[in,out] pPosition in the text stream 489 /// 490 /// @return result code 491 spv_result_t spvTextEncodeOpcode(const libspirv::AssemblyGrammar& grammar, 492 libspirv::AssemblyContext* context, 493 spv_instruction_t* pInst) { 494 // Check for !<integer> first. 495 if ('!' == context->peek()) { 496 return encodeInstructionStartingWithImmediate(grammar, context, pInst); 497 } 498 499 std::string firstWord; 500 spv_position_t nextPosition = {}; 501 spv_result_t error = context->getWord(&firstWord, &nextPosition); 502 if (error) return context->diagnostic() << "Internal Error"; 503 504 std::string opcodeName; 505 std::string result_id; 506 spv_position_t result_id_position = {}; 507 if (context->startsWithOp()) { 508 opcodeName = firstWord; 509 } else { 510 result_id = firstWord; 511 if ('%' != result_id.front()) { 512 return context->diagnostic() 513 << "Expected <opcode> or <result-id> at the beginning " 514 "of an instruction, found '" 515 << result_id << "'."; 516 } 517 result_id_position = context->position(); 518 519 // The '=' sign. 520 context->setPosition(nextPosition); 521 if (context->advance()) 522 return context->diagnostic() << "Expected '=', found end of stream."; 523 std::string equal_sign; 524 error = context->getWord(&equal_sign, &nextPosition); 525 if ("=" != equal_sign) 526 return context->diagnostic() << "'=' expected after result id."; 527 528 // The <opcode> after the '=' sign. 529 context->setPosition(nextPosition); 530 if (context->advance()) 531 return context->diagnostic() << "Expected opcode, found end of stream."; 532 error = context->getWord(&opcodeName, &nextPosition); 533 if (error) return context->diagnostic(error) << "Internal Error"; 534 if (!context->startsWithOp()) { 535 return context->diagnostic() << "Invalid Opcode prefix '" << opcodeName 536 << "'."; 537 } 538 } 539 540 // NOTE: The table contains Opcode names without the "Op" prefix. 541 const char* pInstName = opcodeName.data() + 2; 542 543 spv_opcode_desc opcodeEntry; 544 error = grammar.lookupOpcode(pInstName, &opcodeEntry); 545 if (error) { 546 return context->diagnostic(error) << "Invalid Opcode name '" << opcodeName 547 << "'"; 548 } 549 if (opcodeEntry->hasResult && result_id.empty()) { 550 return context->diagnostic() 551 << "Expected <result-id> at the beginning of an instruction, found '" 552 << firstWord << "'."; 553 } 554 pInst->opcode = opcodeEntry->opcode; 555 context->setPosition(nextPosition); 556 // Reserve the first word for the instruction. 557 spvInstructionAddWord(pInst, 0); 558 559 // Maintains the ordered list of expected operand types. 560 // For many instructions we only need the {numTypes, operandTypes} 561 // entries in opcodeEntry. However, sometimes we need to modify 562 // the list as we parse the operands. This occurs when an operand 563 // has its own logical operands (such as the LocalSize operand for 564 // ExecutionMode), or for extended instructions that may have their 565 // own operands depending on the selected extended instruction. 566 spv_operand_pattern_t expectedOperands( 567 opcodeEntry->operandTypes, 568 opcodeEntry->operandTypes + opcodeEntry->numTypes); 569 570 while (!expectedOperands.empty()) { 571 const spv_operand_type_t type = expectedOperands.front(); 572 expectedOperands.pop_front(); 573 574 // Expand optional tuples lazily. 575 if (spvExpandOperandSequenceOnce(type, &expectedOperands)) continue; 576 577 if (type == SPV_OPERAND_TYPE_RESULT_ID && !result_id.empty()) { 578 // Handle the <result-id> for value generating instructions. 579 // We've already consumed it from the text stream. Here 580 // we inject its words into the instruction. 581 spv_position_t temp_pos = context->position(); 582 error = spvTextEncodeOperand(grammar, context, SPV_OPERAND_TYPE_RESULT_ID, 583 result_id.c_str(), pInst, nullptr); 584 result_id_position = context->position(); 585 // Because we are injecting we have to reset the position afterwards. 586 context->setPosition(temp_pos); 587 if (error) return error; 588 } else { 589 // Find the next word. 590 error = context->advance(); 591 if (error == SPV_END_OF_STREAM) { 592 if (spvOperandIsOptional(type)) { 593 // This would have been the last potential operand for the 594 // instruction, 595 // and we didn't find one. We're finished parsing this instruction. 596 break; 597 } else { 598 return context->diagnostic() 599 << "Expected operand, found end of stream."; 600 } 601 } 602 assert(error == SPV_SUCCESS && "Somebody added another way to fail"); 603 604 if (context->isStartOfNewInst()) { 605 if (spvOperandIsOptional(type)) { 606 break; 607 } else { 608 return context->diagnostic() 609 << "Expected operand, found next instruction instead."; 610 } 611 } 612 613 std::string operandValue; 614 error = context->getWord(&operandValue, &nextPosition); 615 if (error) return context->diagnostic(error) << "Internal Error"; 616 617 error = spvTextEncodeOperand(grammar, context, type, operandValue.c_str(), 618 pInst, &expectedOperands); 619 620 if (error == SPV_FAILED_MATCH && spvOperandIsOptional(type)) 621 return SPV_SUCCESS; 622 623 if (error) return error; 624 625 context->setPosition(nextPosition); 626 } 627 } 628 629 if (spvOpcodeGeneratesType(pInst->opcode)) { 630 if (context->recordTypeDefinition(pInst) != SPV_SUCCESS) { 631 return SPV_ERROR_INVALID_TEXT; 632 } 633 } else if (opcodeEntry->hasType) { 634 // SPIR-V dictates that if an instruction has both a return value and a 635 // type ID then the type id is first, and the return value is second. 636 assert(opcodeEntry->hasResult && 637 "Unknown opcode: has a type but no result."); 638 context->recordTypeIdForValue(pInst->words[2], pInst->words[1]); 639 } 640 641 if (pInst->words.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) { 642 return context->diagnostic() 643 << "Instruction too long: " << pInst->words.size() 644 << " words, but the limit is " 645 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX; 646 } 647 648 pInst->words[0] = 649 spvOpcodeMake(uint16_t(pInst->words.size()), opcodeEntry->opcode); 650 651 return SPV_SUCCESS; 652 } 653 654 enum { kAssemblerVersion = 0 }; 655 656 // Populates a binary stream's |header|. The target environment is specified via 657 // |env| and Id bound is via |bound|. 658 spv_result_t SetHeader(spv_target_env env, const uint32_t bound, 659 uint32_t* header) { 660 if (!header) return SPV_ERROR_INVALID_BINARY; 661 662 header[SPV_INDEX_MAGIC_NUMBER] = SpvMagicNumber; 663 header[SPV_INDEX_VERSION_NUMBER] = spvVersionForTargetEnv(env); 664 header[SPV_INDEX_GENERATOR_NUMBER] = 665 SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, kAssemblerVersion); 666 header[SPV_INDEX_BOUND] = bound; 667 header[SPV_INDEX_SCHEMA] = 0; // NOTE: Reserved 668 669 return SPV_SUCCESS; 670 } 671 672 // Translates a given assembly language module into binary form. 673 // If a diagnostic is generated, it is not yet marked as being 674 // for a text-based input. 675 spv_result_t spvTextToBinaryInternal(const libspirv::AssemblyGrammar& grammar, 676 const spv_text text, spv_binary* pBinary, 677 spv_diagnostic* pDiagnostic) { 678 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC; 679 libspirv::AssemblyContext context(text, pDiagnostic); 680 if (!text->str) return context.diagnostic() << "Missing assembly text."; 681 682 if (!grammar.isValid()) { 683 return SPV_ERROR_INVALID_TABLE; 684 } 685 if (!pBinary) return SPV_ERROR_INVALID_POINTER; 686 687 // NOTE: Ensure diagnostic is zero initialised 688 *pDiagnostic = {}; 689 690 std::vector<spv_instruction_t> instructions; 691 692 // Skip past whitespace and comments. 693 context.advance(); 694 695 while (context.hasText()) { 696 instructions.push_back({}); 697 spv_instruction_t& inst = instructions.back(); 698 699 if (spvTextEncodeOpcode(grammar, &context, &inst)) { 700 return SPV_ERROR_INVALID_TEXT; 701 } 702 703 if (context.advance()) break; 704 } 705 706 size_t totalSize = SPV_INDEX_INSTRUCTION; 707 for (auto& inst : instructions) { 708 totalSize += inst.words.size(); 709 } 710 711 uint32_t* data = new uint32_t[totalSize]; 712 if (!data) return SPV_ERROR_OUT_OF_MEMORY; 713 uint64_t currentIndex = SPV_INDEX_INSTRUCTION; 714 for (auto& inst : instructions) { 715 memcpy(data + currentIndex, inst.words.data(), 716 sizeof(uint32_t) * inst.words.size()); 717 currentIndex += inst.words.size(); 718 } 719 720 if (auto error = SetHeader(grammar.target_env(), context.getBound(), data)) 721 return error; 722 723 spv_binary binary = new spv_binary_t(); 724 if (!binary) { 725 delete[] data; 726 return SPV_ERROR_OUT_OF_MEMORY; 727 } 728 binary->code = data; 729 binary->wordCount = totalSize; 730 731 *pBinary = binary; 732 733 return SPV_SUCCESS; 734 } 735 736 } // anonymous namespace 737 738 spv_result_t spvTextToBinary(const spv_const_context context, 739 const char* input_text, 740 const size_t input_text_size, spv_binary* pBinary, 741 spv_diagnostic* pDiagnostic) { 742 spv_text_t text = {input_text, input_text_size}; 743 libspirv::AssemblyGrammar grammar(context); 744 745 spv_result_t result = 746 spvTextToBinaryInternal(grammar, &text, pBinary, pDiagnostic); 747 if (pDiagnostic && *pDiagnostic) (*pDiagnostic)->isTextSource = true; 748 749 return result; 750 } 751 752 void spvTextDestroy(spv_text text) { 753 if (!text) return; 754 delete[] text->str; 755 delete text; 756 } 757