1 // Copyright (c) 2015-2016 The Khronos Group Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include "source/text.h"
27 #include <unordered_map>
31 #include "source/assembly_grammar.h"
32 #include "source/binary.h"
33 #include "source/diagnostic.h"
34 #include "source/ext_inst.h"
35 #include "source/instruction.h"
36 #include "source/opcode.h"
37 #include "source/operand.h"
38 #include "source/spirv_constant.h"
39 #include "source/spirv_target_env.h"
40 #include "source/table.h"
41 #include "source/text_handler.h"
42 #include "source/util/bitutils.h"
43 #include "source/util/parse_number.h"
44 #include "spirv-tools/libspirv.h"
46 bool spvIsValidIDCharacter(const char value
) {
47 return value
== '_' || 0 != ::isalnum(value
);
50 // Returns true if the given string represents a valid ID name.
51 bool spvIsValidID(const char* textValue
) {
52 const char* c
= textValue
;
53 for (; *c
!= '\0'; ++c
) {
54 if (!spvIsValidIDCharacter(*c
)) {
58 // If the string was empty, then the ID also is not valid.
59 return c
!= textValue
;
64 spv_result_t
spvTextToLiteral(const char* textValue
, spv_literal_t
* pLiteral
) {
65 bool isSigned
= false;
67 bool isString
= false;
69 const size_t len
= strlen(textValue
);
70 if (len
== 0) return SPV_FAILED_MATCH
;
72 for (uint64_t index
= 0; index
< len
; ++index
) {
73 switch (textValue
[index
]) {
97 index
= len
; // break out of the loop too.
102 pLiteral
->type
= spv_literal_type_t(99);
104 if (isString
|| numPeriods
> 1 || (isSigned
&& len
== 1)) {
105 if (len
< 2 || textValue
[0] != '"' || textValue
[len
- 1] != '"')
106 return SPV_FAILED_MATCH
;
107 bool escaping
= false;
108 for (const char* val
= textValue
+ 1; val
!= textValue
+ len
- 1; ++val
) {
109 if ((*val
== '\\') && (!escaping
)) {
112 // Have to save space for the null-terminator
113 if (pLiteral
->str
.size() >= SPV_LIMIT_LITERAL_STRING_BYTES_MAX
)
114 return SPV_ERROR_OUT_OF_MEMORY
;
115 pLiteral
->str
.push_back(*val
);
120 pLiteral
->type
= SPV_LITERAL_TYPE_STRING
;
121 } else if (numPeriods
== 1) {
122 double d
= std::strtod(textValue
, nullptr);
124 if (d
== (double)f
) {
125 pLiteral
->type
= SPV_LITERAL_TYPE_FLOAT_32
;
126 pLiteral
->value
.f
= f
;
128 pLiteral
->type
= SPV_LITERAL_TYPE_FLOAT_64
;
129 pLiteral
->value
.d
= d
;
131 } else if (isSigned
) {
132 int64_t i64
= strtoll(textValue
, nullptr, 10);
133 int32_t i32
= (int32_t)i64
;
134 if (i64
== (int64_t)i32
) {
135 pLiteral
->type
= SPV_LITERAL_TYPE_INT_32
;
136 pLiteral
->value
.i32
= i32
;
138 pLiteral
->type
= SPV_LITERAL_TYPE_INT_64
;
139 pLiteral
->value
.i64
= i64
;
142 uint64_t u64
= strtoull(textValue
, nullptr, 10);
143 uint32_t u32
= (uint32_t)u64
;
144 if (u64
== (uint64_t)u32
) {
145 pLiteral
->type
= SPV_LITERAL_TYPE_UINT_32
;
146 pLiteral
->value
.u32
= u32
;
148 pLiteral
->type
= SPV_LITERAL_TYPE_UINT_64
;
149 pLiteral
->value
.u64
= u64
;
158 /// Parses an immediate integer from text, guarding against overflow. If
159 /// successful, adds the parsed value to pInst, advances the context past it,
160 /// and returns SPV_SUCCESS. Otherwise, leaves pInst alone, emits diagnostics,
161 /// and returns SPV_ERROR_INVALID_TEXT.
162 spv_result_t
encodeImmediate(spvtools::AssemblyContext
* context
,
163 const char* text
, spv_instruction_t
* pInst
) {
164 assert(*text
== '!');
165 uint32_t parse_result
;
166 if (!spvtools::utils::ParseNumber(text
+ 1, &parse_result
)) {
167 return context
->diagnostic(SPV_ERROR_INVALID_TEXT
)
168 << "Invalid immediate integer: !" << text
+ 1;
170 context
->binaryEncodeU32(parse_result
, pInst
);
171 context
->seekForward(static_cast<uint32_t>(strlen(text
)));
175 } // anonymous namespace
177 /// @brief Translate an Opcode operand to binary form
179 /// @param[in] grammar the grammar to use for compilation
180 /// @param[in, out] context the dynamic compilation info
181 /// @param[in] type of the operand
182 /// @param[in] textValue word of text to be parsed
183 /// @param[out] pInst return binary Opcode
184 /// @param[in,out] pExpectedOperands the operand types expected
186 /// @return result code
187 spv_result_t
spvTextEncodeOperand(const spvtools::AssemblyGrammar
& grammar
,
188 spvtools::AssemblyContext
* context
,
189 const spv_operand_type_t type
,
190 const char* textValue
,
191 spv_instruction_t
* pInst
,
192 spv_operand_pattern_t
* pExpectedOperands
) {
193 // NOTE: Handle immediate int in the stream
194 if ('!' == textValue
[0]) {
195 if (auto error
= encodeImmediate(context
, textValue
, pInst
)) {
199 spvAlternatePatternFollowingImmediate(*pExpectedOperands
);
203 // Optional literal operands can fail to parse. In that case use
204 // SPV_FAILED_MATCH to avoid emitting a diagnostic. Use the following
205 // for those situations.
206 spv_result_t error_code_for_literals
=
207 spvOperandIsOptional(type
) ? SPV_FAILED_MATCH
: SPV_ERROR_INVALID_TEXT
;
210 case SPV_OPERAND_TYPE_ID
:
211 case SPV_OPERAND_TYPE_TYPE_ID
:
212 case SPV_OPERAND_TYPE_RESULT_ID
:
213 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID
:
214 case SPV_OPERAND_TYPE_SCOPE_ID
:
215 case SPV_OPERAND_TYPE_OPTIONAL_ID
: {
216 if ('%' == textValue
[0]) {
219 return context
->diagnostic() << "Expected id to start with %.";
221 if (!spvIsValidID(textValue
)) {
222 return context
->diagnostic() << "Invalid ID " << textValue
;
224 const uint32_t id
= context
->spvNamedIdAssignOrGet(textValue
);
225 if (type
== SPV_OPERAND_TYPE_TYPE_ID
) pInst
->resultTypeId
= id
;
226 spvInstructionAddWord(pInst
, id
);
228 // Set the extended instruction type.
229 // The import set id is the 3rd operand of OpExtInst.
230 if (spvIsExtendedInstruction(pInst
->opcode
) && pInst
->words
.size() == 4) {
231 auto ext_inst_type
= context
->getExtInstTypeForId(pInst
->words
[3]);
232 if (ext_inst_type
== SPV_EXT_INST_TYPE_NONE
) {
233 return context
->diagnostic()
234 << "Invalid extended instruction import Id "
237 pInst
->extInstType
= ext_inst_type
;
241 case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER
: {
242 // The assembler accepts the symbolic name for an extended instruction,
243 // and emits its corresponding number.
244 spv_ext_inst_desc extInst
;
245 if (grammar
.lookupExtInst(pInst
->extInstType
, textValue
, &extInst
) ==
247 // if we know about this extended instruction, push the numeric value
248 spvInstructionAddWord(pInst
, extInst
->ext_inst
);
250 // Prepare to parse the operands for the extended instructions.
251 spvPushOperandTypes(extInst
->operandTypes
, pExpectedOperands
);
253 // if we don't know this extended instruction and the set isn't
254 // non-semantic, we cannot process further
255 if (!spvExtInstIsNonSemantic(pInst
->extInstType
)) {
256 return context
->diagnostic()
257 << "Invalid extended instruction name '" << textValue
<< "'.";
259 // for non-semantic instruction sets, as long as the text name is an
260 // integer value we can encode it since we know the form of all such
261 // extended instructions
262 spv_literal_t extInstValue
;
263 if (spvTextToLiteral(textValue
, &extInstValue
) ||
264 extInstValue
.type
!= SPV_LITERAL_TYPE_UINT_32
) {
265 return context
->diagnostic()
266 << "Couldn't translate unknown extended instruction name '"
267 << textValue
<< "' to unsigned integer.";
270 spvInstructionAddWord(pInst
, extInstValue
.value
.u32
);
272 // opcode contains an unknown number of IDs.
273 pExpectedOperands
->push_back(SPV_OPERAND_TYPE_VARIABLE_ID
);
278 case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER
: {
279 // The assembler accepts the symbolic name for the opcode, but without
280 // the "Op" prefix. For example, "IAdd" is accepted. The number
281 // of the opcode is emitted.
283 if (grammar
.lookupSpecConstantOpcode(textValue
, &opcode
)) {
284 return context
->diagnostic() << "Invalid " << spvOperandTypeStr(type
)
285 << " '" << textValue
<< "'.";
287 spv_opcode_desc opcodeEntry
= nullptr;
288 if (grammar
.lookupOpcode(opcode
, &opcodeEntry
)) {
289 return context
->diagnostic(SPV_ERROR_INTERNAL
)
290 << "OpSpecConstant opcode table out of sync";
292 spvInstructionAddWord(pInst
, uint32_t(opcodeEntry
->opcode
));
294 // Prepare to parse the operands for the opcode. Except skip the
295 // type Id and result Id, since they've already been processed.
296 assert(opcodeEntry
->hasType
);
297 assert(opcodeEntry
->hasResult
);
298 assert(opcodeEntry
->numTypes
>= 2);
299 spvPushOperandTypes(opcodeEntry
->operandTypes
+ 2, pExpectedOperands
);
302 case SPV_OPERAND_TYPE_LITERAL_INTEGER
:
303 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER
: {
304 // The current operand is an *unsigned* 32-bit integer.
305 // That's just how the grammar works.
306 spvtools::IdType expected_type
= {
307 32, false, spvtools::IdTypeClass::kScalarIntegerType
};
308 if (auto error
= context
->binaryEncodeNumericLiteral(
309 textValue
, error_code_for_literals
, expected_type
, pInst
)) {
314 case SPV_OPERAND_TYPE_LITERAL_FLOAT
: {
315 // The current operand is a 32-bit float.
316 // That's just how the grammar works.
317 spvtools::IdType expected_type
= {
318 32, false, spvtools::IdTypeClass::kScalarFloatType
};
319 if (auto error
= context
->binaryEncodeNumericLiteral(
320 textValue
, error_code_for_literals
, expected_type
, pInst
)) {
325 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER
:
326 // This is a context-independent literal number which can be a 32-bit
327 // number of floating point value.
328 if (auto error
= context
->binaryEncodeNumericLiteral(
329 textValue
, error_code_for_literals
, spvtools::kUnknownType
,
335 case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER
:
336 case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER
: {
337 spvtools::IdType expected_type
= spvtools::kUnknownType
;
338 // The encoding for OpConstant, OpSpecConstant and OpSwitch all
339 // depend on either their own result-id or the result-id of
340 // one of their parameters.
341 if (spv::Op::OpConstant
== pInst
->opcode
||
342 spv::Op::OpSpecConstant
== pInst
->opcode
) {
343 // The type of the literal is determined by the type Id of the
346 context
->getTypeOfTypeGeneratingValue(pInst
->resultTypeId
);
347 if (!spvtools::isScalarFloating(expected_type
) &&
348 !spvtools::isScalarIntegral(expected_type
)) {
350 const char* opcode_name
= "opcode";
351 if (SPV_SUCCESS
== grammar
.lookupOpcode(pInst
->opcode
, &d
)) {
352 opcode_name
= d
->name
;
354 return context
->diagnostic()
355 << "Type for " << opcode_name
356 << " must be a scalar floating point or integer type";
358 } else if (pInst
->opcode
== spv::Op::OpSwitch
) {
359 // The type of the literal is the same as the type of the selector.
360 expected_type
= context
->getTypeOfValueInstruction(pInst
->words
[1]);
361 if (!spvtools::isScalarIntegral(expected_type
)) {
362 return context
->diagnostic()
363 << "The selector operand for OpSwitch must be the result"
364 " of an instruction that generates an integer scalar";
367 if (auto error
= context
->binaryEncodeNumericLiteral(
368 textValue
, error_code_for_literals
, expected_type
, pInst
)) {
373 case SPV_OPERAND_TYPE_LITERAL_STRING
:
374 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING
: {
375 spv_literal_t literal
= {};
376 spv_result_t error
= spvTextToLiteral(textValue
, &literal
);
377 if (error
!= SPV_SUCCESS
) {
378 if (error
== SPV_ERROR_OUT_OF_MEMORY
) return error
;
379 return context
->diagnostic(error_code_for_literals
)
380 << "Invalid literal string '" << textValue
<< "'.";
382 if (literal
.type
!= SPV_LITERAL_TYPE_STRING
) {
383 return context
->diagnostic()
384 << "Expected literal string, found literal number '" << textValue
388 // NOTE: Special case for extended instruction library import
389 if (spv::Op::OpExtInstImport
== pInst
->opcode
) {
390 const spv_ext_inst_type_t ext_inst_type
=
391 spvExtInstImportTypeGet(literal
.str
.c_str());
392 if (SPV_EXT_INST_TYPE_NONE
== ext_inst_type
) {
393 return context
->diagnostic()
394 << "Invalid extended instruction import '" << literal
.str
397 if ((error
= context
->recordIdAsExtInstImport(pInst
->words
[1],
402 if (context
->binaryEncodeString(literal
.str
.c_str(), pInst
))
403 return SPV_ERROR_INVALID_TEXT
;
407 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE
:
408 case SPV_OPERAND_TYPE_FUNCTION_CONTROL
:
409 case SPV_OPERAND_TYPE_LOOP_CONTROL
:
410 case SPV_OPERAND_TYPE_IMAGE
:
411 case SPV_OPERAND_TYPE_OPTIONAL_IMAGE
:
412 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS
:
413 case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS
:
414 case SPV_OPERAND_TYPE_SELECTION_CONTROL
:
415 case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS
:
416 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS
:
417 case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS
:
418 case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS
:
419 case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE
: {
421 if (auto error
= grammar
.parseMaskOperand(type
, textValue
, &value
)) {
422 return context
->diagnostic(error
)
423 << "Invalid " << spvOperandTypeStr(type
) << " operand '"
424 << textValue
<< "'.";
426 if (auto error
= context
->binaryEncodeU32(value
, pInst
)) return error
;
427 // Prepare to parse the operands for this logical operand.
428 grammar
.pushOperandTypesForMask(type
, value
, pExpectedOperands
);
430 case SPV_OPERAND_TYPE_OPTIONAL_CIV
: {
431 auto error
= spvTextEncodeOperand(
432 grammar
, context
, SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER
, textValue
,
433 pInst
, pExpectedOperands
);
434 if (error
== SPV_FAILED_MATCH
) {
435 // It's not a literal number -- is it a literal string?
436 error
= spvTextEncodeOperand(grammar
, context
,
437 SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING
,
438 textValue
, pInst
, pExpectedOperands
);
440 if (error
== SPV_FAILED_MATCH
) {
441 // It's not a literal -- is it an ID?
443 spvTextEncodeOperand(grammar
, context
, SPV_OPERAND_TYPE_OPTIONAL_ID
,
444 textValue
, pInst
, pExpectedOperands
);
447 return context
->diagnostic(error
)
448 << "Invalid word following !<integer>: " << textValue
;
450 if (pExpectedOperands
->empty()) {
451 pExpectedOperands
->push_back(SPV_OPERAND_TYPE_OPTIONAL_CIV
);
455 // NOTE: All non literal operands are handled here using the operand
457 spv_operand_desc entry
;
458 if (grammar
.lookupOperand(type
, textValue
, strlen(textValue
), &entry
)) {
459 return context
->diagnostic() << "Invalid " << spvOperandTypeStr(type
)
460 << " '" << textValue
<< "'.";
462 if (context
->binaryEncodeU32(entry
->value
, pInst
)) {
463 return context
->diagnostic() << "Invalid " << spvOperandTypeStr(type
)
464 << " '" << textValue
<< "'.";
467 // Prepare to parse the operands for this logical operand.
468 spvPushOperandTypes(entry
->operandTypes
, pExpectedOperands
);
476 /// Encodes an instruction started by !<integer> at the given position in text.
478 /// Puts the encoded words into *pInst. If successful, moves position past the
479 /// instruction and returns SPV_SUCCESS. Otherwise, returns an error code and
480 /// leaves position pointing to the error in text.
481 spv_result_t
encodeInstructionStartingWithImmediate(
482 const spvtools::AssemblyGrammar
& grammar
,
483 spvtools::AssemblyContext
* context
, spv_instruction_t
* pInst
) {
484 std::string firstWord
;
485 spv_position_t nextPosition
= {};
486 auto error
= context
->getWord(&firstWord
, &nextPosition
);
487 if (error
) return context
->diagnostic(error
) << "Internal Error";
489 if ((error
= encodeImmediate(context
, firstWord
.c_str(), pInst
))) {
492 while (context
->advance() != SPV_END_OF_STREAM
) {
493 // A beginning of a new instruction means we're done.
494 if (context
->isStartOfNewInst()) return SPV_SUCCESS
;
496 // Otherwise, there must be an operand that's either a literal, an ID, or
498 std::string operandValue
;
499 if ((error
= context
->getWord(&operandValue
, &nextPosition
)))
500 return context
->diagnostic(error
) << "Internal Error";
502 if (operandValue
== "=")
503 return context
->diagnostic() << firstWord
<< " not allowed before =.";
505 // Needed to pass to spvTextEncodeOpcode(), but it shouldn't ever be
507 spv_operand_pattern_t dummyExpectedOperands
;
508 error
= spvTextEncodeOperand(
509 grammar
, context
, SPV_OPERAND_TYPE_OPTIONAL_CIV
, operandValue
.c_str(),
510 pInst
, &dummyExpectedOperands
);
511 if (error
) return error
;
512 context
->setPosition(nextPosition
);
517 /// @brief Translate single Opcode and operands to binary form
519 /// @param[in] grammar the grammar to use for compilation
520 /// @param[in, out] context the dynamic compilation info
521 /// @param[in] text stream to translate
522 /// @param[out] pInst returned binary Opcode
523 /// @param[in,out] pPosition in the text stream
525 /// @return result code
526 spv_result_t
spvTextEncodeOpcode(const spvtools::AssemblyGrammar
& grammar
,
527 spvtools::AssemblyContext
* context
,
528 spv_instruction_t
* pInst
) {
529 // Check for !<integer> first.
530 if ('!' == context
->peek()) {
531 return encodeInstructionStartingWithImmediate(grammar
, context
, pInst
);
534 std::string firstWord
;
535 spv_position_t nextPosition
= {};
536 spv_result_t error
= context
->getWord(&firstWord
, &nextPosition
);
537 if (error
) return context
->diagnostic() << "Internal Error";
539 std::string opcodeName
;
540 std::string result_id
;
541 spv_position_t result_id_position
= {};
542 if (context
->startsWithOp()) {
543 opcodeName
= firstWord
;
545 result_id
= firstWord
;
546 if ('%' != result_id
.front()) {
547 return context
->diagnostic()
548 << "Expected <opcode> or <result-id> at the beginning "
549 "of an instruction, found '"
550 << result_id
<< "'.";
552 result_id_position
= context
->position();
555 context
->setPosition(nextPosition
);
556 if (context
->advance())
557 return context
->diagnostic() << "Expected '=', found end of stream.";
558 std::string equal_sign
;
559 error
= context
->getWord(&equal_sign
, &nextPosition
);
560 if ("=" != equal_sign
)
561 return context
->diagnostic() << "'=' expected after result id but found '"
562 << equal_sign
<< "'.";
564 // The <opcode> after the '=' sign.
565 context
->setPosition(nextPosition
);
566 if (context
->advance())
567 return context
->diagnostic() << "Expected opcode, found end of stream.";
568 error
= context
->getWord(&opcodeName
, &nextPosition
);
569 if (error
) return context
->diagnostic(error
) << "Internal Error";
570 if (!context
->startsWithOp()) {
571 return context
->diagnostic()
572 << "Invalid Opcode prefix '" << opcodeName
<< "'.";
576 // NOTE: The table contains Opcode names without the "Op" prefix.
577 const char* pInstName
= opcodeName
.data() + 2;
579 spv_opcode_desc opcodeEntry
;
580 error
= grammar
.lookupOpcode(pInstName
, &opcodeEntry
);
582 return context
->diagnostic(error
)
583 << "Invalid Opcode name '" << opcodeName
<< "'";
585 if (opcodeEntry
->hasResult
&& result_id
.empty()) {
586 return context
->diagnostic()
587 << "Expected <result-id> at the beginning of an instruction, found '"
588 << firstWord
<< "'.";
590 if (!opcodeEntry
->hasResult
&& !result_id
.empty()) {
591 return context
->diagnostic()
592 << "Cannot set ID " << result_id
<< " because " << opcodeName
593 << " does not produce a result ID.";
595 pInst
->opcode
= opcodeEntry
->opcode
;
596 context
->setPosition(nextPosition
);
597 // Reserve the first word for the instruction.
598 spvInstructionAddWord(pInst
, 0);
600 // Maintains the ordered list of expected operand types.
601 // For many instructions we only need the {numTypes, operandTypes}
602 // entries in opcodeEntry. However, sometimes we need to modify
603 // the list as we parse the operands. This occurs when an operand
604 // has its own logical operands (such as the LocalSize operand for
605 // ExecutionMode), or for extended instructions that may have their
606 // own operands depending on the selected extended instruction.
607 spv_operand_pattern_t expectedOperands
;
608 expectedOperands
.reserve(opcodeEntry
->numTypes
);
609 for (auto i
= 0; i
< opcodeEntry
->numTypes
; i
++)
610 expectedOperands
.push_back(
611 opcodeEntry
->operandTypes
[opcodeEntry
->numTypes
- i
- 1]);
613 while (!expectedOperands
.empty()) {
614 const spv_operand_type_t type
= expectedOperands
.back();
615 expectedOperands
.pop_back();
617 // Expand optional tuples lazily.
618 if (spvExpandOperandSequenceOnce(type
, &expectedOperands
)) continue;
620 if (type
== SPV_OPERAND_TYPE_RESULT_ID
&& !result_id
.empty()) {
621 // Handle the <result-id> for value generating instructions.
622 // We've already consumed it from the text stream. Here
623 // we inject its words into the instruction.
624 spv_position_t temp_pos
= context
->position();
625 error
= spvTextEncodeOperand(grammar
, context
, SPV_OPERAND_TYPE_RESULT_ID
,
626 result_id
.c_str(), pInst
, nullptr);
627 result_id_position
= context
->position();
628 // Because we are injecting we have to reset the position afterwards.
629 context
->setPosition(temp_pos
);
630 if (error
) return error
;
632 // Find the next word.
633 error
= context
->advance();
634 if (error
== SPV_END_OF_STREAM
) {
635 if (spvOperandIsOptional(type
)) {
636 // This would have been the last potential operand for the
638 // and we didn't find one. We're finished parsing this instruction.
641 return context
->diagnostic()
642 << "Expected operand for " << opcodeName
643 << " instruction, but found the end of the stream.";
646 assert(error
== SPV_SUCCESS
&& "Somebody added another way to fail");
648 if (context
->isStartOfNewInst()) {
649 if (spvOperandIsOptional(type
)) {
652 return context
->diagnostic()
653 << "Expected operand for " << opcodeName
654 << " instruction, but found the next instruction instead.";
658 std::string operandValue
;
659 error
= context
->getWord(&operandValue
, &nextPosition
);
660 if (error
) return context
->diagnostic(error
) << "Internal Error";
662 error
= spvTextEncodeOperand(grammar
, context
, type
, operandValue
.c_str(),
663 pInst
, &expectedOperands
);
665 if (error
== SPV_FAILED_MATCH
&& spvOperandIsOptional(type
))
668 if (error
) return error
;
670 context
->setPosition(nextPosition
);
674 if (spvOpcodeGeneratesType(pInst
->opcode
)) {
675 if (context
->recordTypeDefinition(pInst
) != SPV_SUCCESS
) {
676 return SPV_ERROR_INVALID_TEXT
;
678 } else if (opcodeEntry
->hasType
) {
679 // SPIR-V dictates that if an instruction has both a return value and a
680 // type ID then the type id is first, and the return value is second.
681 assert(opcodeEntry
->hasResult
&&
682 "Unknown opcode: has a type but no result.");
683 context
->recordTypeIdForValue(pInst
->words
[2], pInst
->words
[1]);
686 if (pInst
->words
.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX
) {
687 return context
->diagnostic()
688 << opcodeName
<< " Instruction too long: " << pInst
->words
.size()
689 << " words, but the limit is "
690 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX
;
694 spvOpcodeMake(uint16_t(pInst
->words
.size()), opcodeEntry
->opcode
);
699 enum { kAssemblerVersion
= 0 };
701 // Populates a binary stream's |header|. The target environment is specified via
702 // |env| and Id bound is via |bound|.
703 spv_result_t
SetHeader(spv_target_env env
, const uint32_t bound
,
705 if (!header
) return SPV_ERROR_INVALID_BINARY
;
707 header
[SPV_INDEX_MAGIC_NUMBER
] = spv::MagicNumber
;
708 header
[SPV_INDEX_VERSION_NUMBER
] = spvVersionForTargetEnv(env
);
709 header
[SPV_INDEX_GENERATOR_NUMBER
] =
710 SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER
, kAssemblerVersion
);
711 header
[SPV_INDEX_BOUND
] = bound
;
712 header
[SPV_INDEX_SCHEMA
] = 0; // NOTE: Reserved
717 // Collects all numeric ids in the module source into |numeric_ids|.
718 // This function is essentially a dry-run of spvTextToBinary.
719 spv_result_t
GetNumericIds(const spvtools::AssemblyGrammar
& grammar
,
720 const spvtools::MessageConsumer
& consumer
,
722 std::set
<uint32_t>* numeric_ids
) {
723 spvtools::AssemblyContext
context(text
, consumer
);
725 if (!text
->str
) return context
.diagnostic() << "Missing assembly text.";
727 if (!grammar
.isValid()) {
728 return SPV_ERROR_INVALID_TABLE
;
731 // Skip past whitespace and comments.
734 while (context
.hasText()) {
735 spv_instruction_t inst
;
737 // Operand parsing sometimes involves knowing the opcode of the instruction
738 // being parsed. A malformed input might feature such an operand *before*
739 // the opcode is known. To guard against accessing an uninitialized opcode,
740 // the instruction's opcode is initialized to a default value.
741 inst
.opcode
= spv::Op::Max
;
743 if (spvTextEncodeOpcode(grammar
, &context
, &inst
)) {
744 return SPV_ERROR_INVALID_TEXT
;
747 if (context
.advance()) break;
750 *numeric_ids
= context
.GetNumericIds();
754 // Translates a given assembly language module into binary form.
755 // If a diagnostic is generated, it is not yet marked as being
756 // for a text-based input.
757 spv_result_t
spvTextToBinaryInternal(const spvtools::AssemblyGrammar
& grammar
,
758 const spvtools::MessageConsumer
& consumer
,
760 const uint32_t options
,
761 spv_binary
* pBinary
) {
762 // The ids in this set will have the same values both in source and binary.
763 // All other ids will be generated by filling in the gaps.
764 std::set
<uint32_t> ids_to_preserve
;
766 if (options
& SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS
) {
767 // Collect all numeric ids from the source into ids_to_preserve.
768 const spv_result_t result
=
769 GetNumericIds(grammar
, consumer
, text
, &ids_to_preserve
);
770 if (result
!= SPV_SUCCESS
) return result
;
773 spvtools::AssemblyContext
context(text
, consumer
, std::move(ids_to_preserve
));
775 if (!text
->str
) return context
.diagnostic() << "Missing assembly text.";
777 if (!grammar
.isValid()) {
778 return SPV_ERROR_INVALID_TABLE
;
780 if (!pBinary
) return SPV_ERROR_INVALID_POINTER
;
782 std::vector
<spv_instruction_t
> instructions
;
784 // Skip past whitespace and comments.
787 while (context
.hasText()) {
788 instructions
.push_back({});
789 spv_instruction_t
& inst
= instructions
.back();
791 if (auto error
= spvTextEncodeOpcode(grammar
, &context
, &inst
)) {
795 if (context
.advance()) break;
798 size_t totalSize
= SPV_INDEX_INSTRUCTION
;
799 for (auto& inst
: instructions
) {
800 totalSize
+= inst
.words
.size();
803 uint32_t* data
= new uint32_t[totalSize
];
804 if (!data
) return SPV_ERROR_OUT_OF_MEMORY
;
805 uint64_t currentIndex
= SPV_INDEX_INSTRUCTION
;
806 for (auto& inst
: instructions
) {
807 memcpy(data
+ currentIndex
, inst
.words
.data(),
808 sizeof(uint32_t) * inst
.words
.size());
809 currentIndex
+= inst
.words
.size();
812 if (auto error
= SetHeader(grammar
.target_env(), context
.getBound(), data
))
815 spv_binary binary
= new spv_binary_t();
818 return SPV_ERROR_OUT_OF_MEMORY
;
821 binary
->wordCount
= totalSize
;
828 } // anonymous namespace
830 spv_result_t
spvTextToBinary(const spv_const_context context
,
831 const char* input_text
,
832 const size_t input_text_size
, spv_binary
* pBinary
,
833 spv_diagnostic
* pDiagnostic
) {
834 return spvTextToBinaryWithOptions(context
, input_text
, input_text_size
,
835 SPV_TEXT_TO_BINARY_OPTION_NONE
, pBinary
,
839 spv_result_t
spvTextToBinaryWithOptions(const spv_const_context context
,
840 const char* input_text
,
841 const size_t input_text_size
,
842 const uint32_t options
,
844 spv_diagnostic
* pDiagnostic
) {
845 spv_context_t hijack_context
= *context
;
847 *pDiagnostic
= nullptr;
848 spvtools::UseDiagnosticAsMessageConsumer(&hijack_context
, pDiagnostic
);
851 spv_text_t text
= {input_text
, input_text_size
};
852 spvtools::AssemblyGrammar
grammar(&hijack_context
);
854 spv_result_t result
= spvTextToBinaryInternal(
855 grammar
, hijack_context
.consumer
, &text
, options
, pBinary
);
856 if (pDiagnostic
&& *pDiagnostic
) (*pDiagnostic
)->isTextSource
= true;
861 void spvTextDestroy(spv_text text
) {
863 if (text
->str
) delete[] text
->str
;