1 // Copyright (c) 2015-2020 The Khronos Group Inc.
2 // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
17 #include "source/binary.h"
25 #include <unordered_map>
28 #include "source/assembly_grammar.h"
29 #include "source/diagnostic.h"
30 #include "source/ext_inst.h"
31 #include "source/latest_version_spirv_header.h"
32 #include "source/opcode.h"
33 #include "source/operand.h"
34 #include "source/spirv_constant.h"
35 #include "source/spirv_endian.h"
36 #include "source/util/string_utils.h"
38 spv_result_t
spvBinaryHeaderGet(const spv_const_binary binary
,
39 const spv_endianness_t endian
,
40 spv_header_t
* pHeader
) {
41 if (!binary
->code
) return SPV_ERROR_INVALID_BINARY
;
42 if (binary
->wordCount
< SPV_INDEX_INSTRUCTION
)
43 return SPV_ERROR_INVALID_BINARY
;
44 if (!pHeader
) return SPV_ERROR_INVALID_POINTER
;
46 // TODO: Validation checking?
47 pHeader
->magic
= spvFixWord(binary
->code
[SPV_INDEX_MAGIC_NUMBER
], endian
);
48 pHeader
->version
= spvFixWord(binary
->code
[SPV_INDEX_VERSION_NUMBER
], endian
);
49 // Per 2.3.1 version's high and low bytes are 0
50 if ((pHeader
->version
& 0x000000ff) || pHeader
->version
& 0xff000000)
51 return SPV_ERROR_INVALID_BINARY
;
52 // Minimum version was 1.0 and max version is defined by SPV_VERSION.
53 if (pHeader
->version
< SPV_SPIRV_VERSION_WORD(1, 0) ||
54 pHeader
->version
> SPV_VERSION
)
55 return SPV_ERROR_INVALID_BINARY
;
58 spvFixWord(binary
->code
[SPV_INDEX_GENERATOR_NUMBER
], endian
);
59 pHeader
->bound
= spvFixWord(binary
->code
[SPV_INDEX_BOUND
], endian
);
60 pHeader
->schema
= spvFixWord(binary
->code
[SPV_INDEX_SCHEMA
], endian
);
61 pHeader
->instructions
= &binary
->code
[SPV_INDEX_INSTRUCTION
];
66 std::string
spvDecodeLiteralStringOperand(const spv_parsed_instruction_t
& inst
,
67 const uint16_t operand_index
) {
68 assert(operand_index
< inst
.num_operands
);
69 const spv_parsed_operand_t
& operand
= inst
.operands
[operand_index
];
71 return spvtools::utils::MakeString(inst
.words
+ operand
.offset
,
77 // A SPIR-V binary parser. A parser instance communicates detailed parse
78 // results via callbacks.
81 // The user_data value is provided to the callbacks as context.
82 Parser(const spv_const_context context
, void* user_data
,
83 spv_parsed_header_fn_t parsed_header_fn
,
84 spv_parsed_instruction_fn_t parsed_instruction_fn
)
86 consumer_(context
->consumer
),
87 user_data_(user_data
),
88 parsed_header_fn_(parsed_header_fn
),
89 parsed_instruction_fn_(parsed_instruction_fn
) {}
91 // Parses the specified binary SPIR-V module, issuing callbacks on a parsed
92 // header and for each parsed instruction. Returns SPV_SUCCESS on success.
93 // Otherwise returns an error code and issues a diagnostic.
94 spv_result_t
parse(const uint32_t* words
, size_t num_words
,
95 spv_diagnostic
* diagnostic
);
98 // All remaining methods work on the current module parse state.
100 // Like the parse method, but works on the current module parse state.
101 spv_result_t
parseModule();
103 // Parses an instruction at the current position of the binary. Assumes
104 // the header has been parsed, the endian has been set, and the word index is
105 // still in range. Advances the parsing position past the instruction, and
106 // updates other parsing state for the current module.
107 // On success, returns SPV_SUCCESS and issues the parsed-instruction callback.
108 // On failure, returns an error code and issues a diagnostic.
109 spv_result_t
parseInstruction();
111 // Parses an instruction operand with the given type, for an instruction
112 // starting at inst_offset words into the SPIR-V binary.
113 // If the SPIR-V binary is the same endianness as the host, then the
114 // endian_converted_inst_words parameter is ignored. Otherwise, this method
115 // appends the words for this operand, converted to host native endianness,
116 // to the end of endian_converted_inst_words. This method also updates the
117 // expected_operands parameter, and the scalar members of the inst parameter.
118 // On success, returns SPV_SUCCESS, advances past the operand, and pushes a
119 // new entry on to the operands vector. Otherwise returns an error code and
120 // issues a diagnostic.
121 spv_result_t
parseOperand(size_t inst_offset
, spv_parsed_instruction_t
* inst
,
122 const spv_operand_type_t type
,
123 std::vector
<uint32_t>* endian_converted_inst_words
,
124 std::vector
<spv_parsed_operand_t
>* operands
,
125 spv_operand_pattern_t
* expected_operands
);
127 // Records the numeric type for an operand according to the type information
128 // associated with the given non-zero type Id. This can fail if the type Id
129 // is not a type Id, or if the type Id does not reference a scalar numeric
130 // type. On success, return SPV_SUCCESS and populates the num_words,
131 // number_kind, and number_bit_width fields of parsed_operand.
132 spv_result_t
setNumericTypeInfoForType(spv_parsed_operand_t
* parsed_operand
,
135 // Records the number type for an instruction at the given offset, if that
136 // instruction generates a type. For types that aren't scalar numbers,
137 // record something with number kind SPV_NUMBER_NONE.
138 void recordNumberType(size_t inst_offset
,
139 const spv_parsed_instruction_t
* inst
);
141 // Returns a diagnostic stream object initialized with current position in
142 // the input stream, and for the given error code. Any data written to the
143 // returned object will be propagated to the current parse's diagnostic
145 spvtools::DiagnosticStream
diagnostic(spv_result_t error
) {
146 return spvtools::DiagnosticStream({0, 0, _
.instruction_count
}, consumer_
,
150 // Returns a diagnostic stream object with the default parse error code.
151 spvtools::DiagnosticStream
diagnostic() {
152 // The default failure for parsing is invalid binary.
153 return diagnostic(SPV_ERROR_INVALID_BINARY
);
156 // Issues a diagnostic describing an exhaustion of input condition when
157 // trying to decode an instruction operand, and returns
158 // SPV_ERROR_INVALID_BINARY.
159 spv_result_t
exhaustedInputDiagnostic(size_t inst_offset
, spv::Op opcode
,
160 spv_operand_type_t type
) {
161 return diagnostic() << "End of input reached while decoding Op"
162 << spvOpcodeString(opcode
) << " starting at word "
164 << ((_
.word_index
< _
.num_words
) ? ": truncated "
166 << spvOperandTypeStr(type
) << " operand at word offset "
167 << _
.word_index
- inst_offset
<< ".";
170 // Returns the endian-corrected word at the current position.
171 uint32_t peek() const { return peekAt(_
.word_index
); }
173 // Returns the endian-corrected word at the given position.
174 uint32_t peekAt(size_t index
) const {
175 assert(index
< _
.num_words
);
176 return spvFixWord(_
.words
[index
], _
.endian
);
181 const spvtools::AssemblyGrammar grammar_
; // SPIR-V syntax utility.
182 const spvtools::MessageConsumer
& consumer_
; // Message consumer callback.
183 void* const user_data_
; // Context for the callbacks
184 const spv_parsed_header_fn_t parsed_header_fn_
; // Parsed header callback
185 const spv_parsed_instruction_fn_t
186 parsed_instruction_fn_
; // Parsed instruction callback
188 // Describes the format of a typed literal number.
190 spv_number_kind_t type
;
194 // The state used to parse a single SPIR-V binary module.
196 State(const uint32_t* words_arg
, size_t num_words_arg
,
197 spv_diagnostic
* diagnostic_arg
)
199 num_words(num_words_arg
),
200 diagnostic(diagnostic_arg
),
202 instruction_count(0),
204 requires_endian_conversion(false) {
205 // Temporary storage for parser state within a single instruction.
206 // Most instructions require fewer than 25 words or operands.
207 operands
.reserve(25);
208 endian_converted_words
.reserve(25);
209 expected_operands
.reserve(25);
211 State() : State(0, 0, nullptr) {}
212 const uint32_t* words
; // Words in the binary SPIR-V module.
213 size_t num_words
; // Number of words in the module.
214 spv_diagnostic
* diagnostic
; // Where diagnostics go.
215 size_t word_index
; // The current position in words.
216 size_t instruction_count
; // The count of processed instructions
217 spv_endianness_t endian
; // The endianness of the binary.
218 // Is the SPIR-V binary in a different endianness from the host native
220 bool requires_endian_conversion
;
222 // Maps a result ID to its type ID. By convention:
223 // - a result ID that is a type definition maps to itself.
224 // - a result ID without a type maps to 0. (E.g. for OpLabel)
225 std::unordered_map
<uint32_t, uint32_t> id_to_type_id
;
226 // Maps a type ID to its number type description.
227 std::unordered_map
<uint32_t, NumberType
> type_id_to_number_type_info
;
228 // Maps an ExtInstImport id to the extended instruction type.
229 std::unordered_map
<uint32_t, spv_ext_inst_type_t
>
230 import_id_to_ext_inst_type
;
232 // Used by parseOperand
233 std::vector
<spv_parsed_operand_t
> operands
;
234 std::vector
<uint32_t> endian_converted_words
;
235 spv_operand_pattern_t expected_operands
;
239 spv_result_t
Parser::parse(const uint32_t* words
, size_t num_words
,
240 spv_diagnostic
* diagnostic_arg
) {
241 _
= State(words
, num_words
, diagnostic_arg
);
243 const spv_result_t result
= parseModule();
245 // Clear the module state. The tables might be big.
251 spv_result_t
Parser::parseModule() {
252 if (!_
.words
) return diagnostic() << "Missing module.";
254 if (_
.num_words
< SPV_INDEX_INSTRUCTION
)
255 return diagnostic() << "Module has incomplete header: only " << _
.num_words
256 << " words instead of " << SPV_INDEX_INSTRUCTION
;
258 // Check the magic number and detect the module's endianness.
259 spv_const_binary_t binary
{_
.words
, _
.num_words
};
260 if (spvBinaryEndianness(&binary
, &_
.endian
)) {
261 return diagnostic() << "Invalid SPIR-V magic number '" << std::hex
262 << _
.words
[0] << "'.";
264 _
.requires_endian_conversion
= !spvIsHostEndian(_
.endian
);
266 // Process the header.
268 if (spvBinaryHeaderGet(&binary
, _
.endian
, &header
)) {
269 // It turns out there is no way to trigger this error since the only
270 // failure cases are already handled above, with better messages.
271 return diagnostic(SPV_ERROR_INTERNAL
)
272 << "Internal error: unhandled header parse failure";
274 if (parsed_header_fn_
) {
275 if (auto error
= parsed_header_fn_(user_data_
, _
.endian
, header
.magic
,
276 header
.version
, header
.generator
,
277 header
.bound
, header
.schema
)) {
282 // Process the instructions.
283 _
.word_index
= SPV_INDEX_INSTRUCTION
;
284 while (_
.word_index
< _
.num_words
)
285 if (auto error
= parseInstruction()) return error
;
287 // Running off the end should already have been reported earlier.
288 assert(_
.word_index
== _
.num_words
);
293 spv_result_t
Parser::parseInstruction() {
294 _
.instruction_count
++;
296 // The zero values for all members except for opcode are the
297 // correct initial values.
298 spv_parsed_instruction_t inst
= {};
300 const uint32_t first_word
= peek();
302 // If the module's endianness is different from the host native endianness,
303 // then converted_words contains the endian-translated words in the
305 _
.endian_converted_words
.clear();
306 _
.endian_converted_words
.push_back(first_word
);
308 // After a successful parse of the instruction, the inst.operands member
309 // will point to this vector's storage.
312 assert(_
.word_index
< _
.num_words
);
313 // Decompose and check the first word.
314 uint16_t inst_word_count
= 0;
315 spvOpcodeSplit(first_word
, &inst_word_count
, &inst
.opcode
);
316 if (inst_word_count
< 1) {
317 return diagnostic() << "Invalid instruction word count: "
320 spv_opcode_desc opcode_desc
;
321 if (grammar_
.lookupOpcode(static_cast<spv::Op
>(inst
.opcode
), &opcode_desc
))
322 return diagnostic() << "Invalid opcode: " << inst
.opcode
;
324 // Advance past the opcode word. But remember the of the start
325 // of the instruction.
326 const size_t inst_offset
= _
.word_index
;
329 // Maintains the ordered list of expected operand types.
330 // For many instructions we only need the {numTypes, operandTypes}
331 // entries in opcode_desc. However, sometimes we need to modify
332 // the list as we parse the operands. This occurs when an operand
333 // has its own logical operands (such as the LocalSize operand for
334 // ExecutionMode), or for extended instructions that may have their
335 // own operands depending on the selected extended instruction.
336 _
.expected_operands
.clear();
337 for (auto i
= 0; i
< opcode_desc
->numTypes
; i
++)
338 _
.expected_operands
.push_back(
339 opcode_desc
->operandTypes
[opcode_desc
->numTypes
- i
- 1]);
341 while (_
.word_index
< inst_offset
+ inst_word_count
) {
342 const uint16_t inst_word_index
= uint16_t(_
.word_index
- inst_offset
);
343 if (_
.expected_operands
.empty()) {
344 return diagnostic() << "Invalid instruction Op" << opcode_desc
->name
345 << " starting at word " << inst_offset
346 << ": expected no more operands after "
348 << " words, but stated word count is "
349 << inst_word_count
<< ".";
352 spv_operand_type_t type
=
353 spvTakeFirstMatchableOperand(&_
.expected_operands
);
356 parseOperand(inst_offset
, &inst
, type
, &_
.endian_converted_words
,
357 &_
.operands
, &_
.expected_operands
)) {
362 if (!_
.expected_operands
.empty() &&
363 !spvOperandIsOptional(_
.expected_operands
.back())) {
364 return diagnostic() << "End of input reached while decoding Op"
365 << opcode_desc
->name
<< " starting at word "
366 << inst_offset
<< ": expected more operands after "
367 << inst_word_count
<< " words.";
370 if ((inst_offset
+ inst_word_count
) != _
.word_index
) {
371 return diagnostic() << "Invalid word count: Op" << opcode_desc
->name
372 << " starting at word " << inst_offset
373 << " says it has " << inst_word_count
374 << " words, but found " << _
.word_index
- inst_offset
375 << " words instead.";
378 // Check the computed length of the endian-converted words vector against
379 // the declared number of words in the instruction. If endian conversion
380 // is required, then they should match. If no endian conversion was
381 // performed, then the vector only contains the initial opcode/word-count
383 assert(!_
.requires_endian_conversion
||
384 (inst_word_count
== _
.endian_converted_words
.size()));
385 assert(_
.requires_endian_conversion
||
386 (_
.endian_converted_words
.size() == 1));
388 recordNumberType(inst_offset
, &inst
);
390 if (_
.requires_endian_conversion
) {
391 // We must wait until here to set this pointer, because the vector might
392 // have been be resized while we accumulated its elements.
393 inst
.words
= _
.endian_converted_words
.data();
395 // If no conversion is required, then just point to the underlying binary.
396 // This saves time and space.
397 inst
.words
= _
.words
+ inst_offset
;
399 inst
.num_words
= inst_word_count
;
401 // We must wait until here to set this pointer, because the vector might
402 // have been be resized while we accumulated its elements.
403 inst
.operands
= _
.operands
.data();
404 inst
.num_operands
= uint16_t(_
.operands
.size());
406 // Issue the callback. The callee should know that all the storage in inst
407 // is transient, and will disappear immediately afterward.
408 if (parsed_instruction_fn_
) {
409 if (auto error
= parsed_instruction_fn_(user_data_
, &inst
)) return error
;
415 spv_result_t
Parser::parseOperand(size_t inst_offset
,
416 spv_parsed_instruction_t
* inst
,
417 const spv_operand_type_t type
,
418 std::vector
<uint32_t>* words
,
419 std::vector
<spv_parsed_operand_t
>* operands
,
420 spv_operand_pattern_t
* expected_operands
) {
421 const spv::Op opcode
= static_cast<spv::Op
>(inst
->opcode
);
422 // We'll fill in this result as we go along.
423 spv_parsed_operand_t parsed_operand
;
424 parsed_operand
.offset
= uint16_t(_
.word_index
- inst_offset
);
425 // Most operands occupy one word. This might be be adjusted later.
426 parsed_operand
.num_words
= 1;
427 // The type argument is the one used by the grammar to parse the instruction.
428 // But it can exposes internal parser details such as whether an operand is
429 // optional or actually represents a variable-length sequence of operands.
430 // The resulting type should be adjusted to avoid those internal details.
431 // In most cases, the resulting operand type is the same as the grammar type.
432 parsed_operand
.type
= type
;
434 // Assume non-numeric values. This will be updated for literal numbers.
435 parsed_operand
.number_kind
= SPV_NUMBER_NONE
;
436 parsed_operand
.number_bit_width
= 0;
438 if (_
.word_index
>= _
.num_words
)
439 return exhaustedInputDiagnostic(inst_offset
, opcode
, type
);
441 const uint32_t word
= peek();
443 // Do the words in this operand have to be converted to native endianness?
444 // True for all but literal strings.
445 bool convert_operand_endianness
= true;
448 case SPV_OPERAND_TYPE_TYPE_ID
:
450 return diagnostic(SPV_ERROR_INVALID_ID
) << "Error: Type Id is 0";
451 inst
->type_id
= word
;
454 case SPV_OPERAND_TYPE_RESULT_ID
:
456 return diagnostic(SPV_ERROR_INVALID_ID
) << "Error: Result Id is 0";
457 inst
->result_id
= word
;
458 // Save the result ID to type ID mapping.
459 // In the grammar, type ID always appears before result ID.
460 if (_
.id_to_type_id
.find(inst
->result_id
) != _
.id_to_type_id
.end())
461 return diagnostic(SPV_ERROR_INVALID_ID
)
462 << "Id " << inst
->result_id
<< " is defined more than once";
464 // A regular value maps to its type. Some instructions (e.g. OpLabel)
465 // have no type Id, and will map to 0. The result Id for a
466 // type-generating instruction (e.g. OpTypeInt) maps to itself.
467 _
.id_to_type_id
[inst
->result_id
] =
468 spvOpcodeGeneratesType(opcode
) ? inst
->result_id
: inst
->type_id
;
471 case SPV_OPERAND_TYPE_ID
:
472 case SPV_OPERAND_TYPE_OPTIONAL_ID
:
473 if (!word
) return diagnostic(SPV_ERROR_INVALID_ID
) << "Id is 0";
474 parsed_operand
.type
= SPV_OPERAND_TYPE_ID
;
476 if (spvIsExtendedInstruction(opcode
) && parsed_operand
.offset
== 3) {
477 // The current word is the extended instruction set Id.
478 // Set the extended instruction set type for the current instruction.
479 auto ext_inst_type_iter
= _
.import_id_to_ext_inst_type
.find(word
);
480 if (ext_inst_type_iter
== _
.import_id_to_ext_inst_type
.end()) {
481 return diagnostic(SPV_ERROR_INVALID_ID
)
482 << "OpExtInst set Id " << word
483 << " does not reference an OpExtInstImport result Id";
485 inst
->ext_inst_type
= ext_inst_type_iter
->second
;
489 case SPV_OPERAND_TYPE_SCOPE_ID
:
490 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID
:
491 // Check for trivially invalid values. The operand descriptions already
492 // have the word "ID" in them.
493 if (!word
) return diagnostic() << spvOperandTypeStr(type
) << " is 0";
496 case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER
: {
497 assert(spvIsExtendedInstruction(opcode
));
498 assert(inst
->ext_inst_type
!= SPV_EXT_INST_TYPE_NONE
);
499 spv_ext_inst_desc ext_inst
;
500 if (grammar_
.lookupExtInst(inst
->ext_inst_type
, word
, &ext_inst
) ==
502 // if we know about this ext inst, push the expected operands
503 spvPushOperandTypes(ext_inst
->operandTypes
, expected_operands
);
505 // if we don't know this extended instruction and the set isn't
506 // non-semantic, we cannot process further
507 if (!spvExtInstIsNonSemantic(inst
->ext_inst_type
)) {
509 << "Invalid extended instruction number: " << word
;
511 // for non-semantic instruction sets, we know the form of all such
512 // extended instructions contains a series of IDs as parameters
513 expected_operands
->push_back(SPV_OPERAND_TYPE_VARIABLE_ID
);
518 case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER
: {
519 assert(spv::Op::OpSpecConstantOp
== opcode
);
520 if (word
> static_cast<uint32_t>(spv::Op::Max
) ||
521 grammar_
.lookupSpecConstantOpcode(spv::Op(word
))) {
523 << "Invalid " << spvOperandTypeStr(type
) << ": " << word
;
525 spv_opcode_desc opcode_entry
= nullptr;
526 if (grammar_
.lookupOpcode(spv::Op(word
), &opcode_entry
)) {
527 return diagnostic(SPV_ERROR_INTERNAL
)
528 << "OpSpecConstant opcode table out of sync";
530 // OpSpecConstant opcodes must have a type and result. We've already
531 // processed them, so skip them when preparing to parse the other
532 // operants for the opcode.
533 assert(opcode_entry
->hasType
);
534 assert(opcode_entry
->hasResult
);
535 assert(opcode_entry
->numTypes
>= 2);
536 spvPushOperandTypes(opcode_entry
->operandTypes
+ 2, expected_operands
);
539 case SPV_OPERAND_TYPE_LITERAL_INTEGER
:
540 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER
:
541 // These are regular single-word literal integer operands.
542 // Post-parsing validation should check the range of the parsed value.
543 parsed_operand
.type
= SPV_OPERAND_TYPE_LITERAL_INTEGER
;
544 // It turns out they are always unsigned integers!
545 parsed_operand
.number_kind
= SPV_NUMBER_UNSIGNED_INT
;
546 parsed_operand
.number_bit_width
= 32;
549 case SPV_OPERAND_TYPE_LITERAL_FLOAT
:
550 // These are regular single-word literal float operands.
551 parsed_operand
.type
= SPV_OPERAND_TYPE_LITERAL_FLOAT
;
552 parsed_operand
.number_kind
= SPV_NUMBER_FLOATING
;
553 parsed_operand
.number_bit_width
= 32;
556 case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER
:
557 case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER
:
558 parsed_operand
.type
= SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER
;
559 if (opcode
== spv::Op::OpSwitch
) {
560 // The literal operands have the same type as the value
561 // referenced by the selector Id.
562 const uint32_t selector_id
= peekAt(inst_offset
+ 1);
563 const auto type_id_iter
= _
.id_to_type_id
.find(selector_id
);
564 if (type_id_iter
== _
.id_to_type_id
.end() ||
565 type_id_iter
->second
== 0) {
566 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
569 uint32_t type_id
= type_id_iter
->second
;
571 if (selector_id
== type_id
) {
572 // Recall that by convention, a result ID that is a type definition
574 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
575 << " is a type, not a value";
577 if (auto error
= setNumericTypeInfoForType(&parsed_operand
, type_id
))
579 if (parsed_operand
.number_kind
!= SPV_NUMBER_UNSIGNED_INT
&&
580 parsed_operand
.number_kind
!= SPV_NUMBER_SIGNED_INT
) {
581 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
582 << " is not a scalar integer";
585 assert(opcode
== spv::Op::OpConstant
||
586 opcode
== spv::Op::OpSpecConstant
);
587 // The literal number type is determined by the type Id for the
589 assert(inst
->type_id
);
591 setNumericTypeInfoForType(&parsed_operand
, inst
->type_id
))
596 case SPV_OPERAND_TYPE_LITERAL_STRING
:
597 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING
: {
598 const size_t max_words
= _
.num_words
- _
.word_index
;
600 spvtools::utils::MakeString(_
.words
+ _
.word_index
, max_words
, false);
602 if (string
.length() == max_words
* 4)
603 return exhaustedInputDiagnostic(inst_offset
, opcode
, type
);
605 // Make sure we can record the word count without overflow.
607 // This error can't currently be triggered because of validity
609 const size_t string_num_words
= string
.length() / 4 + 1;
610 if (string_num_words
> std::numeric_limits
<uint16_t>::max()) {
611 return diagnostic() << "Literal string is longer than "
612 << std::numeric_limits
<uint16_t>::max()
613 << " words: " << string_num_words
<< " words long";
615 parsed_operand
.num_words
= uint16_t(string_num_words
);
616 parsed_operand
.type
= SPV_OPERAND_TYPE_LITERAL_STRING
;
618 if (spv::Op::OpExtInstImport
== opcode
) {
619 // Record the extended instruction type for the ID for this import.
620 // There is only one string literal argument to OpExtInstImport,
621 // so it's sufficient to guard this just on the opcode.
622 const spv_ext_inst_type_t ext_inst_type
=
623 spvExtInstImportTypeGet(string
.c_str());
624 if (SPV_EXT_INST_TYPE_NONE
== ext_inst_type
) {
626 << "Invalid extended instruction import '" << string
<< "'";
628 // We must have parsed a valid result ID. It's a condition
629 // of the grammar, and we only accept non-zero result Ids.
630 assert(inst
->result_id
);
631 _
.import_id_to_ext_inst_type
[inst
->result_id
] = ext_inst_type
;
635 case SPV_OPERAND_TYPE_CAPABILITY
:
636 case SPV_OPERAND_TYPE_EXECUTION_MODEL
:
637 case SPV_OPERAND_TYPE_ADDRESSING_MODEL
:
638 case SPV_OPERAND_TYPE_MEMORY_MODEL
:
639 case SPV_OPERAND_TYPE_EXECUTION_MODE
:
640 case SPV_OPERAND_TYPE_STORAGE_CLASS
:
641 case SPV_OPERAND_TYPE_DIMENSIONALITY
:
642 case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE
:
643 case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE
:
644 case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT
:
645 case SPV_OPERAND_TYPE_FP_ROUNDING_MODE
:
646 case SPV_OPERAND_TYPE_LINKAGE_TYPE
:
647 case SPV_OPERAND_TYPE_ACCESS_QUALIFIER
:
648 case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER
:
649 case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE
:
650 case SPV_OPERAND_TYPE_DECORATION
:
651 case SPV_OPERAND_TYPE_BUILT_IN
:
652 case SPV_OPERAND_TYPE_GROUP_OPERATION
:
653 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS
:
654 case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO
:
655 case SPV_OPERAND_TYPE_RAY_FLAGS
:
656 case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION
:
657 case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE
:
658 case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE
:
659 case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING
:
660 case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE
:
661 case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER
:
662 case SPV_OPERAND_TYPE_DEBUG_OPERATION
:
663 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING
:
664 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE
:
665 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER
:
666 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION
:
667 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY
:
668 case SPV_OPERAND_TYPE_FPDENORM_MODE
:
669 case SPV_OPERAND_TYPE_FPOPERATION_MODE
:
670 case SPV_OPERAND_TYPE_QUANTIZATION_MODES
:
671 case SPV_OPERAND_TYPE_OVERFLOW_MODES
:
672 case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT
:
673 case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT
:
674 case SPV_OPERAND_TYPE_FPENCODING
:
675 case SPV_OPERAND_TYPE_OPTIONAL_FPENCODING
:
676 case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER
:
677 case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL
:
678 case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL
:
679 case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS
: {
680 // A single word that is a plain enum value.
682 // Map an optional operand type to its corresponding concrete type.
683 if (type
== SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER
)
684 parsed_operand
.type
= SPV_OPERAND_TYPE_ACCESS_QUALIFIER
;
685 if (type
== SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT
)
686 parsed_operand
.type
= SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT
;
687 if (type
== SPV_OPERAND_TYPE_OPTIONAL_FPENCODING
)
688 parsed_operand
.type
= SPV_OPERAND_TYPE_FPENCODING
;
690 spv_operand_desc entry
;
691 if (grammar_
.lookupOperand(type
, word
, &entry
)) {
693 << "Invalid " << spvOperandTypeStr(parsed_operand
.type
)
694 << " operand: " << word
;
696 // Prepare to accept operands to this operand, if needed.
697 spvPushOperandTypes(entry
->operandTypes
, expected_operands
);
700 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE
: {
701 spv_operand_desc entry
;
702 if (grammar_
.lookupOperand(type
, word
, &entry
)) {
704 << "Invalid " << spvOperandTypeStr(parsed_operand
.type
)
705 << " operand: " << word
706 << ", if you are creating a new source language please use "
708 "(Unknown) and when ready, add your source language to "
711 // Prepare to accept operands to this operand, if needed.
712 spvPushOperandTypes(entry
->operandTypes
, expected_operands
);
715 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE
:
716 case SPV_OPERAND_TYPE_FUNCTION_CONTROL
:
717 case SPV_OPERAND_TYPE_LOOP_CONTROL
:
718 case SPV_OPERAND_TYPE_IMAGE
:
719 case SPV_OPERAND_TYPE_OPTIONAL_IMAGE
:
720 case SPV_OPERAND_TYPE_MEMORY_ACCESS
:
721 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS
:
722 case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS
:
723 case SPV_OPERAND_TYPE_SELECTION_CONTROL
:
724 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS
:
725 case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS
:
726 case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS
:
727 case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS
:
728 case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE
:
729 case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS
: {
730 // This operand is a mask.
732 // Map an optional operand type to its corresponding concrete type.
733 if (type
== SPV_OPERAND_TYPE_OPTIONAL_IMAGE
)
734 parsed_operand
.type
= SPV_OPERAND_TYPE_IMAGE
;
735 if (type
== SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS
)
736 parsed_operand
.type
= SPV_OPERAND_TYPE_MEMORY_ACCESS
;
737 if (type
== SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS
)
738 parsed_operand
.type
= SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS
;
739 if (type
== SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS
)
740 parsed_operand
.type
= SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS
;
742 // Check validity of set mask bits. Also prepare for operands for those
743 // masks if they have any. To get operand order correct, scan from
744 // MSB to LSB since we can only prepend operands to a pattern.
745 // The only case in the grammar where you have more than one mask bit
746 // having an operand is for image operands. See SPIR-V 3.14 Image
748 uint32_t remaining_word
= word
;
749 for (uint32_t mask
= (1u << 31); remaining_word
; mask
>>= 1) {
750 if (remaining_word
& mask
) {
751 spv_operand_desc entry
;
752 if (grammar_
.lookupOperand(type
, mask
, &entry
)) {
754 << "Invalid " << spvOperandTypeStr(parsed_operand
.type
)
755 << " operand: " << word
<< " has invalid mask component "
758 remaining_word
^= mask
;
759 spvPushOperandTypes(entry
->operandTypes
, expected_operands
);
763 // An all-zeroes mask *might* also be valid.
764 spv_operand_desc entry
;
765 if (SPV_SUCCESS
== grammar_
.lookupOperand(type
, 0, &entry
)) {
766 // Prepare for its operands, if any.
767 spvPushOperandTypes(entry
->operandTypes
, expected_operands
);
772 return diagnostic() << "Internal error: Unhandled operand type: " << type
;
775 assert(spvOperandIsConcrete(parsed_operand
.type
));
777 operands
->push_back(parsed_operand
);
779 const size_t index_after_operand
= _
.word_index
+ parsed_operand
.num_words
;
781 // Avoid buffer overrun for the cases where the operand has more than one
782 // word, and where it isn't a string. (Those other cases have already been
783 // handled earlier.) For example, this error can occur for a multi-word
784 // argument to OpConstant, or a multi-word case literal operand for OpSwitch.
785 if (_
.num_words
< index_after_operand
)
786 return exhaustedInputDiagnostic(inst_offset
, opcode
, type
);
788 if (_
.requires_endian_conversion
) {
789 // Copy instruction words. Translate to native endianness as needed.
790 if (convert_operand_endianness
) {
791 const spv_endianness_t endianness
= _
.endian
;
792 std::transform(_
.words
+ _
.word_index
, _
.words
+ index_after_operand
,
793 std::back_inserter(*words
),
794 [endianness
](const uint32_t raw_word
) {
795 return spvFixWord(raw_word
, endianness
);
798 words
->insert(words
->end(), _
.words
+ _
.word_index
,
799 _
.words
+ index_after_operand
);
803 // Advance past the operand.
804 _
.word_index
= index_after_operand
;
809 spv_result_t
Parser::setNumericTypeInfoForType(
810 spv_parsed_operand_t
* parsed_operand
, uint32_t type_id
) {
811 assert(type_id
!= 0);
812 auto type_info_iter
= _
.type_id_to_number_type_info
.find(type_id
);
813 if (type_info_iter
== _
.type_id_to_number_type_info
.end()) {
814 return diagnostic() << "Type Id " << type_id
<< " is not a type";
816 const NumberType
& info
= type_info_iter
->second
;
817 if (info
.type
== SPV_NUMBER_NONE
) {
818 // This is a valid type, but for something other than a scalar number.
819 return diagnostic() << "Type Id " << type_id
820 << " is not a scalar numeric type";
823 parsed_operand
->number_kind
= info
.type
;
824 parsed_operand
->number_bit_width
= info
.bit_width
;
825 // Round up the word count.
826 parsed_operand
->num_words
= static_cast<uint16_t>((info
.bit_width
+ 31) / 32);
830 void Parser::recordNumberType(size_t inst_offset
,
831 const spv_parsed_instruction_t
* inst
) {
832 const spv::Op opcode
= static_cast<spv::Op
>(inst
->opcode
);
833 if (spvOpcodeGeneratesType(opcode
)) {
834 NumberType info
= {SPV_NUMBER_NONE
, 0};
835 if (spv::Op::OpTypeInt
== opcode
) {
836 const bool is_signed
= peekAt(inst_offset
+ 3) != 0;
837 info
.type
= is_signed
? SPV_NUMBER_SIGNED_INT
: SPV_NUMBER_UNSIGNED_INT
;
838 info
.bit_width
= peekAt(inst_offset
+ 2);
839 } else if (spv::Op::OpTypeFloat
== opcode
) {
840 info
.type
= SPV_NUMBER_FLOATING
;
841 info
.bit_width
= peekAt(inst_offset
+ 2);
843 // The *result* Id of a type generating instruction is the type Id.
844 _
.type_id_to_number_type_info
[inst
->result_id
] = info
;
848 } // anonymous namespace
850 spv_result_t
spvBinaryParse(const spv_const_context context
, void* user_data
,
851 const uint32_t* code
, const size_t num_words
,
852 spv_parsed_header_fn_t parsed_header
,
853 spv_parsed_instruction_fn_t parsed_instruction
,
854 spv_diagnostic
* diagnostic
) {
855 spv_context_t hijack_context
= *context
;
857 *diagnostic
= nullptr;
858 spvtools::UseDiagnosticAsMessageConsumer(&hijack_context
, diagnostic
);
860 Parser
parser(&hijack_context
, user_data
, parsed_header
, parsed_instruction
);
861 return parser
.parse(code
, num_words
, diagnostic
);
864 // TODO(dneto): This probably belongs in text.cpp since that's the only place
865 // that a spv_binary_t value is created.
866 void spvBinaryDestroy(spv_binary binary
) {
868 if (binary
->code
) delete[] binary
->code
;
873 size_t spv_strnlen_s(const char* str
, size_t strsz
) {
875 for (size_t i
= 0; i
< strsz
; i
++) {
876 if (!str
[i
]) return i
;