1// Copyright (c) 2015-2020 The Khronos Group Inc.
2// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
3// reserved.
4//
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
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
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.
16
17#include "source/binary.h"
18
19#include <algorithm>
20#include <cassert>
21#include <cstring>
22#include <iterator>
23#include <limits>
24#include <string>
25#include <unordered_map>
26#include <vector>
27
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
37spv_result_t spvBinaryHeaderGet(const spv_const_binary binary,
38 const spv_endianness_t endian,
39 spv_header_t* pHeader) {
40 if (!binary->code) return SPV_ERROR_INVALID_BINARY;
41 if (binary->wordCount < SPV_INDEX_INSTRUCTION)
42 return SPV_ERROR_INVALID_BINARY;
43 if (!pHeader) return SPV_ERROR_INVALID_POINTER;
44
45 // TODO: Validation checking?
46 pHeader->magic = spvFixWord(binary->code[SPV_INDEX_MAGIC_NUMBER], endian);
47 pHeader->version = spvFixWord(binary->code[SPV_INDEX_VERSION_NUMBER], endian);
48 pHeader->generator =
49 spvFixWord(binary->code[SPV_INDEX_GENERATOR_NUMBER], endian);
50 pHeader->bound = spvFixWord(binary->code[SPV_INDEX_BOUND], endian);
51 pHeader->schema = spvFixWord(binary->code[SPV_INDEX_SCHEMA], endian);
52 pHeader->instructions = &binary->code[SPV_INDEX_INSTRUCTION];
53
54 return SPV_SUCCESS;
55}
56
57namespace {
58
59// A SPIR-V binary parser. A parser instance communicates detailed parse
60// results via callbacks.
61class Parser {
62 public:
63 // The user_data value is provided to the callbacks as context.
64 Parser(const spv_const_context context, void* user_data,
65 spv_parsed_header_fn_t parsed_header_fn,
66 spv_parsed_instruction_fn_t parsed_instruction_fn)
67 : grammar_(context),
68 consumer_(context->consumer),
69 user_data_(user_data),
70 parsed_header_fn_(parsed_header_fn),
71 parsed_instruction_fn_(parsed_instruction_fn) {}
72
73 // Parses the specified binary SPIR-V module, issuing callbacks on a parsed
74 // header and for each parsed instruction. Returns SPV_SUCCESS on success.
75 // Otherwise returns an error code and issues a diagnostic.
76 spv_result_t parse(const uint32_t* words, size_t num_words,
77 spv_diagnostic* diagnostic);
78
79 private:
80 // All remaining methods work on the current module parse state.
81
82 // Like the parse method, but works on the current module parse state.
83 spv_result_t parseModule();
84
85 // Parses an instruction at the current position of the binary. Assumes
86 // the header has been parsed, the endian has been set, and the word index is
87 // still in range. Advances the parsing position past the instruction, and
88 // updates other parsing state for the current module.
89 // On success, returns SPV_SUCCESS and issues the parsed-instruction callback.
90 // On failure, returns an error code and issues a diagnostic.
91 spv_result_t parseInstruction();
92
93 // Parses an instruction operand with the given type, for an instruction
94 // starting at inst_offset words into the SPIR-V binary.
95 // If the SPIR-V binary is the same endianness as the host, then the
96 // endian_converted_inst_words parameter is ignored. Otherwise, this method
97 // appends the words for this operand, converted to host native endianness,
98 // to the end of endian_converted_inst_words. This method also updates the
99 // expected_operands parameter, and the scalar members of the inst parameter.
100 // On success, returns SPV_SUCCESS, advances past the operand, and pushes a
101 // new entry on to the operands vector. Otherwise returns an error code and
102 // issues a diagnostic.
103 spv_result_t parseOperand(size_t inst_offset, spv_parsed_instruction_t* inst,
104 const spv_operand_type_t type,
105 std::vector<uint32_t>* endian_converted_inst_words,
106 std::vector<spv_parsed_operand_t>* operands,
107 spv_operand_pattern_t* expected_operands);
108
109 // Records the numeric type for an operand according to the type information
110 // associated with the given non-zero type Id. This can fail if the type Id
111 // is not a type Id, or if the type Id does not reference a scalar numeric
112 // type. On success, return SPV_SUCCESS and populates the num_words,
113 // number_kind, and number_bit_width fields of parsed_operand.
114 spv_result_t setNumericTypeInfoForType(spv_parsed_operand_t* parsed_operand,
115 uint32_t type_id);
116
117 // Records the number type for an instruction at the given offset, if that
118 // instruction generates a type. For types that aren't scalar numbers,
119 // record something with number kind SPV_NUMBER_NONE.
120 void recordNumberType(size_t inst_offset,
121 const spv_parsed_instruction_t* inst);
122
123 // Returns a diagnostic stream object initialized with current position in
124 // the input stream, and for the given error code. Any data written to the
125 // returned object will be propagated to the current parse's diagnostic
126 // object.
127 spvtools::DiagnosticStream diagnostic(spv_result_t error) {
128 return spvtools::DiagnosticStream({0, 0, _.instruction_count}, consumer_,
129 "", error);
130 }
131
132 // Returns a diagnostic stream object with the default parse error code.
133 spvtools::DiagnosticStream diagnostic() {
134 // The default failure for parsing is invalid binary.
135 return diagnostic(SPV_ERROR_INVALID_BINARY);
136 }
137
138 // Issues a diagnostic describing an exhaustion of input condition when
139 // trying to decode an instruction operand, and returns
140 // SPV_ERROR_INVALID_BINARY.
141 spv_result_t exhaustedInputDiagnostic(size_t inst_offset, SpvOp opcode,
142 spv_operand_type_t type) {
143 return diagnostic() << "End of input reached while decoding Op"
144 << spvOpcodeString(opcode) << " starting at word "
145 << inst_offset
146 << ((_.word_index < _.num_words) ? ": truncated "
147 : ": missing ")
148 << spvOperandTypeStr(type) << " operand at word offset "
149 << _.word_index - inst_offset << ".";
150 }
151
152 // Returns the endian-corrected word at the current position.
153 uint32_t peek() const { return peekAt(_.word_index); }
154
155 // Returns the endian-corrected word at the given position.
156 uint32_t peekAt(size_t index) const {
157 assert(index < _.num_words);
158 return spvFixWord(_.words[index], _.endian);
159 }
160
161 // Data members
162
163 const spvtools::AssemblyGrammar grammar_; // SPIR-V syntax utility.
164 const spvtools::MessageConsumer& consumer_; // Message consumer callback.
165 void* const user_data_; // Context for the callbacks
166 const spv_parsed_header_fn_t parsed_header_fn_; // Parsed header callback
167 const spv_parsed_instruction_fn_t
168 parsed_instruction_fn_; // Parsed instruction callback
169
170 // Describes the format of a typed literal number.
171 struct NumberType {
172 spv_number_kind_t type;
173 uint32_t bit_width;
174 };
175
176 // The state used to parse a single SPIR-V binary module.
177 struct State {
178 State(const uint32_t* words_arg, size_t num_words_arg,
179 spv_diagnostic* diagnostic_arg)
180 : words(words_arg),
181 num_words(num_words_arg),
182 diagnostic(diagnostic_arg),
183 word_index(0),
184 instruction_count(0),
185 endian(),
186 requires_endian_conversion(false) {
187 // Temporary storage for parser state within a single instruction.
188 // Most instructions require fewer than 25 words or operands.
189 operands.reserve(25);
190 endian_converted_words.reserve(25);
191 expected_operands.reserve(25);
192 }
193 State() : State(0, 0, nullptr) {}
194 const uint32_t* words; // Words in the binary SPIR-V module.
195 size_t num_words; // Number of words in the module.
196 spv_diagnostic* diagnostic; // Where diagnostics go.
197 size_t word_index; // The current position in words.
198 size_t instruction_count; // The count of processed instructions
199 spv_endianness_t endian; // The endianness of the binary.
200 // Is the SPIR-V binary in a different endiannes from the host native
201 // endianness?
202 bool requires_endian_conversion;
203
204 // Maps a result ID to its type ID. By convention:
205 // - a result ID that is a type definition maps to itself.
206 // - a result ID without a type maps to 0. (E.g. for OpLabel)
207 std::unordered_map<uint32_t, uint32_t> id_to_type_id;
208 // Maps a type ID to its number type description.
209 std::unordered_map<uint32_t, NumberType> type_id_to_number_type_info;
210 // Maps an ExtInstImport id to the extended instruction type.
211 std::unordered_map<uint32_t, spv_ext_inst_type_t>
212 import_id_to_ext_inst_type;
213
214 // Used by parseOperand
215 std::vector<spv_parsed_operand_t> operands;
216 std::vector<uint32_t> endian_converted_words;
217 spv_operand_pattern_t expected_operands;
218 } _;
219};
220
221spv_result_t Parser::parse(const uint32_t* words, size_t num_words,
222 spv_diagnostic* diagnostic_arg) {
223 _ = State(words, num_words, diagnostic_arg);
224
225 const spv_result_t result = parseModule();
226
227 // Clear the module state. The tables might be big.
228 _ = State();
229
230 return result;
231}
232
233spv_result_t Parser::parseModule() {
234 if (!_.words) return diagnostic() << "Missing module.";
235
236 if (_.num_words < SPV_INDEX_INSTRUCTION)
237 return diagnostic() << "Module has incomplete header: only " << _.num_words
238 << " words instead of " << SPV_INDEX_INSTRUCTION;
239
240 // Check the magic number and detect the module's endianness.
241 spv_const_binary_t binary{_.words, _.num_words};
242 if (spvBinaryEndianness(&binary, &_.endian)) {
243 return diagnostic() << "Invalid SPIR-V magic number '" << std::hex
244 << _.words[0] << "'.";
245 }
246 _.requires_endian_conversion = !spvIsHostEndian(_.endian);
247
248 // Process the header.
249 spv_header_t header;
250 if (spvBinaryHeaderGet(&binary, _.endian, &header)) {
251 // It turns out there is no way to trigger this error since the only
252 // failure cases are already handled above, with better messages.
253 return diagnostic(SPV_ERROR_INTERNAL)
254 << "Internal error: unhandled header parse failure";
255 }
256 if (parsed_header_fn_) {
257 if (auto error = parsed_header_fn_(user_data_, _.endian, header.magic,
258 header.version, header.generator,
259 header.bound, header.schema)) {
260 return error;
261 }
262 }
263
264 // Process the instructions.
265 _.word_index = SPV_INDEX_INSTRUCTION;
266 while (_.word_index < _.num_words)
267 if (auto error = parseInstruction()) return error;
268
269 // Running off the end should already have been reported earlier.
270 assert(_.word_index == _.num_words);
271
272 return SPV_SUCCESS;
273}
274
275spv_result_t Parser::parseInstruction() {
276 _.instruction_count++;
277
278 // The zero values for all members except for opcode are the
279 // correct initial values.
280 spv_parsed_instruction_t inst = {};
281
282 const uint32_t first_word = peek();
283
284 // If the module's endianness is different from the host native endianness,
285 // then converted_words contains the the endian-translated words in the
286 // instruction.
287 _.endian_converted_words.clear();
288 _.endian_converted_words.push_back(first_word);
289
290 // After a successful parse of the instruction, the inst.operands member
291 // will point to this vector's storage.
292 _.operands.clear();
293
294 assert(_.word_index < _.num_words);
295 // Decompose and check the first word.
296 uint16_t inst_word_count = 0;
297 spvOpcodeSplit(first_word, &inst_word_count, &inst.opcode);
298 if (inst_word_count < 1) {
299 return diagnostic() << "Invalid instruction word count: "
300 << inst_word_count;
301 }
302 spv_opcode_desc opcode_desc;
303 if (grammar_.lookupOpcode(static_cast<SpvOp>(inst.opcode), &opcode_desc))
304 return diagnostic() << "Invalid opcode: " << inst.opcode;
305
306 // Advance past the opcode word. But remember the of the start
307 // of the instruction.
308 const size_t inst_offset = _.word_index;
309 _.word_index++;
310
311 // Maintains the ordered list of expected operand types.
312 // For many instructions we only need the {numTypes, operandTypes}
313 // entries in opcode_desc. However, sometimes we need to modify
314 // the list as we parse the operands. This occurs when an operand
315 // has its own logical operands (such as the LocalSize operand for
316 // ExecutionMode), or for extended instructions that may have their
317 // own operands depending on the selected extended instruction.
318 _.expected_operands.clear();
319 for (auto i = 0; i < opcode_desc->numTypes; i++)
320 _.expected_operands.push_back(
321 opcode_desc->operandTypes[opcode_desc->numTypes - i - 1]);
322
323 while (_.word_index < inst_offset + inst_word_count) {
324 const uint16_t inst_word_index = uint16_t(_.word_index - inst_offset);
325 if (_.expected_operands.empty()) {
326 return diagnostic() << "Invalid instruction Op" << opcode_desc->name
327 << " starting at word " << inst_offset
328 << ": expected no more operands after "
329 << inst_word_index
330 << " words, but stated word count is "
331 << inst_word_count << ".";
332 }
333
334 spv_operand_type_t type =
335 spvTakeFirstMatchableOperand(&_.expected_operands);
336
337 if (auto error =
338 parseOperand(inst_offset, &inst, type, &_.endian_converted_words,
339 &_.operands, &_.expected_operands)) {
340 return error;
341 }
342 }
343
344 if (!_.expected_operands.empty() &&
345 !spvOperandIsOptional(_.expected_operands.back())) {
346 return diagnostic() << "End of input reached while decoding Op"
347 << opcode_desc->name << " starting at word "
348 << inst_offset << ": expected more operands after "
349 << inst_word_count << " words.";
350 }
351
352 if ((inst_offset + inst_word_count) != _.word_index) {
353 return diagnostic() << "Invalid word count: Op" << opcode_desc->name
354 << " starting at word " << inst_offset
355 << " says it has " << inst_word_count
356 << " words, but found " << _.word_index - inst_offset
357 << " words instead.";
358 }
359
360 // Check the computed length of the endian-converted words vector against
361 // the declared number of words in the instruction. If endian conversion
362 // is required, then they should match. If no endian conversion was
363 // performed, then the vector only contains the initial opcode/word-count
364 // word.
365 assert(!_.requires_endian_conversion ||
366 (inst_word_count == _.endian_converted_words.size()));
367 assert(_.requires_endian_conversion ||
368 (_.endian_converted_words.size() == 1));
369
370 recordNumberType(inst_offset, &inst);
371
372 if (_.requires_endian_conversion) {
373 // We must wait until here to set this pointer, because the vector might
374 // have been be resized while we accumulated its elements.
375 inst.words = _.endian_converted_words.data();
376 } else {
377 // If no conversion is required, then just point to the underlying binary.
378 // This saves time and space.
379 inst.words = _.words + inst_offset;
380 }
381 inst.num_words = inst_word_count;
382
383 // We must wait until here to set this pointer, because the vector might
384 // have been be resized while we accumulated its elements.
385 inst.operands = _.operands.data();
386 inst.num_operands = uint16_t(_.operands.size());
387
388 // Issue the callback. The callee should know that all the storage in inst
389 // is transient, and will disappear immediately afterward.
390 if (parsed_instruction_fn_) {
391 if (auto error = parsed_instruction_fn_(user_data_, &inst)) return error;
392 }
393
394 return SPV_SUCCESS;
395}
396
397spv_result_t Parser::parseOperand(size_t inst_offset,
398 spv_parsed_instruction_t* inst,
399 const spv_operand_type_t type,
400 std::vector<uint32_t>* words,
401 std::vector<spv_parsed_operand_t>* operands,
402 spv_operand_pattern_t* expected_operands) {
403 const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
404 // We'll fill in this result as we go along.
405 spv_parsed_operand_t parsed_operand;
406 parsed_operand.offset = uint16_t(_.word_index - inst_offset);
407 // Most operands occupy one word. This might be be adjusted later.
408 parsed_operand.num_words = 1;
409 // The type argument is the one used by the grammar to parse the instruction.
410 // But it can exposes internal parser details such as whether an operand is
411 // optional or actually represents a variable-length sequence of operands.
412 // The resulting type should be adjusted to avoid those internal details.
413 // In most cases, the resulting operand type is the same as the grammar type.
414 parsed_operand.type = type;
415
416 // Assume non-numeric values. This will be updated for literal numbers.
417 parsed_operand.number_kind = SPV_NUMBER_NONE;
418 parsed_operand.number_bit_width = 0;
419
420 if (_.word_index >= _.num_words)
421 return exhaustedInputDiagnostic(inst_offset, opcode, type);
422
423 const uint32_t word = peek();
424
425 // Do the words in this operand have to be converted to native endianness?
426 // True for all but literal strings.
427 bool convert_operand_endianness = true;
428
429 switch (type) {
430 case SPV_OPERAND_TYPE_TYPE_ID:
431 if (!word)
432 return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Type Id is 0";
433 inst->type_id = word;
434 break;
435
436 case SPV_OPERAND_TYPE_RESULT_ID:
437 if (!word)
438 return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Result Id is 0";
439 inst->result_id = word;
440 // Save the result ID to type ID mapping.
441 // In the grammar, type ID always appears before result ID.
442 if (_.id_to_type_id.find(inst->result_id) != _.id_to_type_id.end())
443 return diagnostic(SPV_ERROR_INVALID_ID)
444 << "Id " << inst->result_id << " is defined more than once";
445 // Record it.
446 // A regular value maps to its type. Some instructions (e.g. OpLabel)
447 // have no type Id, and will map to 0. The result Id for a
448 // type-generating instruction (e.g. OpTypeInt) maps to itself.
449 _.id_to_type_id[inst->result_id] =
450 spvOpcodeGeneratesType(opcode) ? inst->result_id : inst->type_id;
451 break;
452
453 case SPV_OPERAND_TYPE_ID:
454 case SPV_OPERAND_TYPE_OPTIONAL_ID:
455 if (!word) return diagnostic(SPV_ERROR_INVALID_ID) << "Id is 0";
456 parsed_operand.type = SPV_OPERAND_TYPE_ID;
457
458 if (opcode == SpvOpExtInst && parsed_operand.offset == 3) {
459 // The current word is the extended instruction set Id.
460 // Set the extended instruction set type for the current instruction.
461 auto ext_inst_type_iter = _.import_id_to_ext_inst_type.find(word);
462 if (ext_inst_type_iter == _.import_id_to_ext_inst_type.end()) {
463 return diagnostic(SPV_ERROR_INVALID_ID)
464 << "OpExtInst set Id " << word
465 << " does not reference an OpExtInstImport result Id";
466 }
467 inst->ext_inst_type = ext_inst_type_iter->second;
468 }
469 break;
470
471 case SPV_OPERAND_TYPE_SCOPE_ID:
472 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
473 // Check for trivially invalid values. The operand descriptions already
474 // have the word "ID" in them.
475 if (!word) return diagnostic() << spvOperandTypeStr(type) << " is 0";
476 break;
477
478 case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
479 assert(SpvOpExtInst == opcode);
480 assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE);
481 spv_ext_inst_desc ext_inst;
482 if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) ==
483 SPV_SUCCESS) {
484 // if we know about this ext inst, push the expected operands
485 spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
486 } else {
487 // if we don't know this extended instruction and the set isn't
488 // non-semantic, we cannot process further
489 if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) {
490 return diagnostic()
491 << "Invalid extended instruction number: " << word;
492 } else {
493 // for non-semantic instruction sets, we know the form of all such
494 // extended instructions contains a series of IDs as parameters
495 expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
496 }
497 }
498 } break;
499
500 case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
501 assert(SpvOpSpecConstantOp == opcode);
502 if (grammar_.lookupSpecConstantOpcode(SpvOp(word))) {
503 return diagnostic()
504 << "Invalid " << spvOperandTypeStr(type) << ": " << word;
505 }
506 spv_opcode_desc opcode_entry = nullptr;
507 if (grammar_.lookupOpcode(SpvOp(word), &opcode_entry)) {
508 return diagnostic(SPV_ERROR_INTERNAL)
509 << "OpSpecConstant opcode table out of sync";
510 }
511 // OpSpecConstant opcodes must have a type and result. We've already
512 // processed them, so skip them when preparing to parse the other
513 // operants for the opcode.
514 assert(opcode_entry->hasType);
515 assert(opcode_entry->hasResult);
516 assert(opcode_entry->numTypes >= 2);
517 spvPushOperandTypes(opcode_entry->operandTypes + 2, expected_operands);
518 } break;
519
520 case SPV_OPERAND_TYPE_LITERAL_INTEGER:
521 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
522 // These are regular single-word literal integer operands.
523 // Post-parsing validation should check the range of the parsed value.
524 parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_INTEGER;
525 // It turns out they are always unsigned integers!
526 parsed_operand.number_kind = SPV_NUMBER_UNSIGNED_INT;
527 parsed_operand.number_bit_width = 32;
528 break;
529
530 case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
531 case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
532 parsed_operand.type = SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER;
533 if (opcode == SpvOpSwitch) {
534 // The literal operands have the same type as the value
535 // referenced by the selector Id.
536 const uint32_t selector_id = peekAt(inst_offset + 1);
537 const auto type_id_iter = _.id_to_type_id.find(selector_id);
538 if (type_id_iter == _.id_to_type_id.end() ||
539 type_id_iter->second == 0) {
540 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
541 << " has no type";
542 }
543 uint32_t type_id = type_id_iter->second;
544
545 if (selector_id == type_id) {
546 // Recall that by convention, a result ID that is a type definition
547 // maps to itself.
548 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
549 << " is a type, not a value";
550 }
551 if (auto error = setNumericTypeInfoForType(&parsed_operand, type_id))
552 return error;
553 if (parsed_operand.number_kind != SPV_NUMBER_UNSIGNED_INT &&
554 parsed_operand.number_kind != SPV_NUMBER_SIGNED_INT) {
555 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
556 << " is not a scalar integer";
557 }
558 } else {
559 assert(opcode == SpvOpConstant || opcode == SpvOpSpecConstant);
560 // The literal number type is determined by the type Id for the
561 // constant.
562 assert(inst->type_id);
563 if (auto error =
564 setNumericTypeInfoForType(&parsed_operand, inst->type_id))
565 return error;
566 }
567 break;
568
569 case SPV_OPERAND_TYPE_LITERAL_STRING:
570 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: {
571 convert_operand_endianness = false;
572 const char* string =
573 reinterpret_cast<const char*>(_.words + _.word_index);
574 // Compute the length of the string, but make sure we don't run off the
575 // end of the input.
576 const size_t remaining_input_bytes =
577 sizeof(uint32_t) * (_.num_words - _.word_index);
578 const size_t string_num_content_bytes =
579 spv_strnlen_s(string, remaining_input_bytes);
580 // If there was no terminating null byte, then that's an end-of-input
581 // error.
582 if (string_num_content_bytes == remaining_input_bytes)
583 return exhaustedInputDiagnostic(inst_offset, opcode, type);
584 // Account for null in the word length, so add 1 for null, then add 3 to
585 // make sure we round up. The following is equivalent to:
586 // (string_num_content_bytes + 1 + 3) / 4
587 const size_t string_num_words = string_num_content_bytes / 4 + 1;
588 // Make sure we can record the word count without overflow.
589 //
590 // This error can't currently be triggered because of validity
591 // checks elsewhere.
592 if (string_num_words > std::numeric_limits<uint16_t>::max()) {
593 return diagnostic() << "Literal string is longer than "
594 << std::numeric_limits<uint16_t>::max()
595 << " words: " << string_num_words << " words long";
596 }
597 parsed_operand.num_words = uint16_t(string_num_words);
598 parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_STRING;
599
600 if (SpvOpExtInstImport == opcode) {
601 // Record the extended instruction type for the ID for this import.
602 // There is only one string literal argument to OpExtInstImport,
603 // so it's sufficient to guard this just on the opcode.
604 const spv_ext_inst_type_t ext_inst_type =
605 spvExtInstImportTypeGet(string);
606 if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) {
607 return diagnostic()
608 << "Invalid extended instruction import '" << string << "'";
609 }
610 // We must have parsed a valid result ID. It's a condition
611 // of the grammar, and we only accept non-zero result Ids.
612 assert(inst->result_id);
613 _.import_id_to_ext_inst_type[inst->result_id] = ext_inst_type;
614 }
615 } break;
616
617 case SPV_OPERAND_TYPE_CAPABILITY:
618 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
619 case SPV_OPERAND_TYPE_EXECUTION_MODEL:
620 case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
621 case SPV_OPERAND_TYPE_MEMORY_MODEL:
622 case SPV_OPERAND_TYPE_EXECUTION_MODE:
623 case SPV_OPERAND_TYPE_STORAGE_CLASS:
624 case SPV_OPERAND_TYPE_DIMENSIONALITY:
625 case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
626 case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
627 case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT:
628 case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
629 case SPV_OPERAND_TYPE_LINKAGE_TYPE:
630 case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
631 case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER:
632 case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
633 case SPV_OPERAND_TYPE_DECORATION:
634 case SPV_OPERAND_TYPE_BUILT_IN:
635 case SPV_OPERAND_TYPE_GROUP_OPERATION:
636 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
637 case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
638 case SPV_OPERAND_TYPE_RAY_FLAGS:
639 case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
640 case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
641 case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
642 case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
643 case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
644 case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
645 case SPV_OPERAND_TYPE_DEBUG_OPERATION:
646 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
647 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
648 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
649 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
650 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: {
651 // A single word that is a plain enum value.
652
653 // Map an optional operand type to its corresponding concrete type.
654 if (type == SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER)
655 parsed_operand.type = SPV_OPERAND_TYPE_ACCESS_QUALIFIER;
656
657 spv_operand_desc entry;
658 if (grammar_.lookupOperand(type, word, &entry)) {
659 return diagnostic()
660 << "Invalid " << spvOperandTypeStr(parsed_operand.type)
661 << " operand: " << word;
662 }
663 // Prepare to accept operands to this operand, if needed.
664 spvPushOperandTypes(entry->operandTypes, expected_operands);
665 } break;
666
667 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
668 case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
669 case SPV_OPERAND_TYPE_LOOP_CONTROL:
670 case SPV_OPERAND_TYPE_IMAGE:
671 case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
672 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
673 case SPV_OPERAND_TYPE_SELECTION_CONTROL:
674 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
675 case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: {
676 // This operand is a mask.
677
678 // Map an optional operand type to its corresponding concrete type.
679 if (type == SPV_OPERAND_TYPE_OPTIONAL_IMAGE)
680 parsed_operand.type = SPV_OPERAND_TYPE_IMAGE;
681 else if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS)
682 parsed_operand.type = SPV_OPERAND_TYPE_MEMORY_ACCESS;
683
684 // Check validity of set mask bits. Also prepare for operands for those
685 // masks if they have any. To get operand order correct, scan from
686 // MSB to LSB since we can only prepend operands to a pattern.
687 // The only case in the grammar where you have more than one mask bit
688 // having an operand is for image operands. See SPIR-V 3.14 Image
689 // Operands.
690 uint32_t remaining_word = word;
691 for (uint32_t mask = (1u << 31); remaining_word; mask >>= 1) {
692 if (remaining_word & mask) {
693 spv_operand_desc entry;
694 if (grammar_.lookupOperand(type, mask, &entry)) {
695 return diagnostic()
696 << "Invalid " << spvOperandTypeStr(parsed_operand.type)
697 << " operand: " << word << " has invalid mask component "
698 << mask;
699 }
700 remaining_word ^= mask;
701 spvPushOperandTypes(entry->operandTypes, expected_operands);
702 }
703 }
704 if (word == 0) {
705 // An all-zeroes mask *might* also be valid.
706 spv_operand_desc entry;
707 if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) {
708 // Prepare for its operands, if any.
709 spvPushOperandTypes(entry->operandTypes, expected_operands);
710 }
711 }
712 } break;
713 default:
714 return diagnostic() << "Internal error: Unhandled operand type: " << type;
715 }
716
717 assert(spvOperandIsConcrete(parsed_operand.type));
718
719 operands->push_back(parsed_operand);
720
721 const size_t index_after_operand = _.word_index + parsed_operand.num_words;
722
723 // Avoid buffer overrun for the cases where the operand has more than one
724 // word, and where it isn't a string. (Those other cases have already been
725 // handled earlier.) For example, this error can occur for a multi-word
726 // argument to OpConstant, or a multi-word case literal operand for OpSwitch.
727 if (_.num_words < index_after_operand)
728 return exhaustedInputDiagnostic(inst_offset, opcode, type);
729
730 if (_.requires_endian_conversion) {
731 // Copy instruction words. Translate to native endianness as needed.
732 if (convert_operand_endianness) {
733 const spv_endianness_t endianness = _.endian;
734 std::transform(_.words + _.word_index, _.words + index_after_operand,
735 std::back_inserter(*words),
736 [endianness](const uint32_t raw_word) {
737 return spvFixWord(raw_word, endianness);
738 });
739 } else {
740 words->insert(words->end(), _.words + _.word_index,
741 _.words + index_after_operand);
742 }
743 }
744
745 // Advance past the operand.
746 _.word_index = index_after_operand;
747
748 return SPV_SUCCESS;
749}
750
751spv_result_t Parser::setNumericTypeInfoForType(
752 spv_parsed_operand_t* parsed_operand, uint32_t type_id) {
753 assert(type_id != 0);
754 auto type_info_iter = _.type_id_to_number_type_info.find(type_id);
755 if (type_info_iter == _.type_id_to_number_type_info.end()) {
756 return diagnostic() << "Type Id " << type_id << " is not a type";
757 }
758 const NumberType& info = type_info_iter->second;
759 if (info.type == SPV_NUMBER_NONE) {
760 // This is a valid type, but for something other than a scalar number.
761 return diagnostic() << "Type Id " << type_id
762 << " is not a scalar numeric type";
763 }
764
765 parsed_operand->number_kind = info.type;
766 parsed_operand->number_bit_width = info.bit_width;
767 // Round up the word count.
768 parsed_operand->num_words = static_cast<uint16_t>((info.bit_width + 31) / 32);
769 return SPV_SUCCESS;
770}
771
772void Parser::recordNumberType(size_t inst_offset,
773 const spv_parsed_instruction_t* inst) {
774 const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
775 if (spvOpcodeGeneratesType(opcode)) {
776 NumberType info = {SPV_NUMBER_NONE, 0};
777 if (SpvOpTypeInt == opcode) {
778 const bool is_signed = peekAt(inst_offset + 3) != 0;
779 info.type = is_signed ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT;
780 info.bit_width = peekAt(inst_offset + 2);
781 } else if (SpvOpTypeFloat == opcode) {
782 info.type = SPV_NUMBER_FLOATING;
783 info.bit_width = peekAt(inst_offset + 2);
784 }
785 // The *result* Id of a type generating instruction is the type Id.
786 _.type_id_to_number_type_info[inst->result_id] = info;
787 }
788}
789
790} // anonymous namespace
791
792spv_result_t spvBinaryParse(const spv_const_context context, void* user_data,
793 const uint32_t* code, const size_t num_words,
794 spv_parsed_header_fn_t parsed_header,
795 spv_parsed_instruction_fn_t parsed_instruction,
796 spv_diagnostic* diagnostic) {
797 spv_context_t hijack_context = *context;
798 if (diagnostic) {
799 *diagnostic = nullptr;
800 spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, diagnostic);
801 }
802 Parser parser(&hijack_context, user_data, parsed_header, parsed_instruction);
803 return parser.parse(code, num_words, diagnostic);
804}
805
806// TODO(dneto): This probably belongs in text.cpp since that's the only place
807// that a spv_binary_t value is created.
808void spvBinaryDestroy(spv_binary binary) {
809 if (binary) {
810 if (binary->code) delete[] binary->code;
811 delete binary;
812 }
813}
814
815size_t spv_strnlen_s(const char* str, size_t strsz) {
816 if (!str) return 0;
817 for (size_t i = 0; i < strsz; i++) {
818 if (!str[i]) return i;
819 }
820 return strsz;
821}
822