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 #include "source/util/string_utils.h"
37
spvBinaryHeaderGet(const spv_const_binary binary,const spv_endianness_t endian,spv_header_t * pHeader)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;
45
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;
56
57 pHeader->generator =
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];
62
63 return SPV_SUCCESS;
64 }
65
spvDecodeLiteralStringOperand(const spv_parsed_instruction_t & inst,const uint16_t operand_index)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];
70
71 return spvtools::utils::MakeString(inst.words + operand.offset,
72 operand.num_words);
73 }
74
75 namespace {
76
77 // A SPIR-V binary parser. A parser instance communicates detailed parse
78 // results via callbacks.
79 class Parser {
80 public:
81 // The user_data value is provided to the callbacks as context.
Parser(const spv_const_context context,void * user_data,spv_parsed_header_fn_t parsed_header_fn,spv_parsed_instruction_fn_t parsed_instruction_fn)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)
85 : grammar_(context),
86 consumer_(context->consumer),
87 user_data_(user_data),
88 parsed_header_fn_(parsed_header_fn),
89 parsed_instruction_fn_(parsed_instruction_fn) {}
90
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);
96
97 private:
98 // All remaining methods work on the current module parse state.
99
100 // Like the parse method, but works on the current module parse state.
101 spv_result_t parseModule();
102
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();
110
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);
126
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,
133 uint32_t type_id);
134
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);
140
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
144 // object.
diagnostic(spv_result_t error)145 spvtools::DiagnosticStream diagnostic(spv_result_t error) {
146 return spvtools::DiagnosticStream({0, 0, _.instruction_count}, consumer_,
147 "", error);
148 }
149
150 // Returns a diagnostic stream object with the default parse error code.
diagnostic()151 spvtools::DiagnosticStream diagnostic() {
152 // The default failure for parsing is invalid binary.
153 return diagnostic(SPV_ERROR_INVALID_BINARY);
154 }
155
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.
exhaustedInputDiagnostic(size_t inst_offset,spv::Op opcode,spv_operand_type_t type)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 "
163 << inst_offset
164 << ((_.word_index < _.num_words) ? ": truncated "
165 : ": missing ")
166 << spvOperandTypeStr(type) << " operand at word offset "
167 << _.word_index - inst_offset << ".";
168 }
169
170 // Returns the endian-corrected word at the current position.
peek() const171 uint32_t peek() const { return peekAt(_.word_index); }
172
173 // Returns the endian-corrected word at the given position.
peekAt(size_t index) const174 uint32_t peekAt(size_t index) const {
175 assert(index < _.num_words);
176 return spvFixWord(_.words[index], _.endian);
177 }
178
179 // Data members
180
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
187
188 // Describes the format of a typed literal number.
189 struct NumberType {
190 spv_number_kind_t type;
191 uint32_t bit_width;
192 };
193
194 // The state used to parse a single SPIR-V binary module.
195 struct State {
State__anon9062b3f00111::Parser::State196 State(const uint32_t* words_arg, size_t num_words_arg,
197 spv_diagnostic* diagnostic_arg)
198 : words(words_arg),
199 num_words(num_words_arg),
200 diagnostic(diagnostic_arg),
201 word_index(0),
202 instruction_count(0),
203 endian(),
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);
210 }
State__anon9062b3f00111::Parser::State211 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
219 // endianness?
220 bool requires_endian_conversion;
221
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;
231
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;
236 } _;
237 };
238
parse(const uint32_t * words,size_t num_words,spv_diagnostic * diagnostic_arg)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);
242
243 const spv_result_t result = parseModule();
244
245 // Clear the module state. The tables might be big.
246 _ = State();
247
248 return result;
249 }
250
parseModule()251 spv_result_t Parser::parseModule() {
252 if (!_.words) return diagnostic() << "Missing module.";
253
254 if (_.num_words < SPV_INDEX_INSTRUCTION)
255 return diagnostic() << "Module has incomplete header: only " << _.num_words
256 << " words instead of " << SPV_INDEX_INSTRUCTION;
257
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] << "'.";
263 }
264 _.requires_endian_conversion = !spvIsHostEndian(_.endian);
265
266 // Process the header.
267 spv_header_t 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";
273 }
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)) {
278 return error;
279 }
280 }
281
282 // Process the instructions.
283 _.word_index = SPV_INDEX_INSTRUCTION;
284 while (_.word_index < _.num_words)
285 if (auto error = parseInstruction()) return error;
286
287 // Running off the end should already have been reported earlier.
288 assert(_.word_index == _.num_words);
289
290 return SPV_SUCCESS;
291 }
292
parseInstruction()293 spv_result_t Parser::parseInstruction() {
294 _.instruction_count++;
295
296 // The zero values for all members except for opcode are the
297 // correct initial values.
298 spv_parsed_instruction_t inst = {};
299
300 const uint32_t first_word = peek();
301
302 // If the module's endianness is different from the host native endianness,
303 // then converted_words contains the endian-translated words in the
304 // instruction.
305 _.endian_converted_words.clear();
306 _.endian_converted_words.push_back(first_word);
307
308 // After a successful parse of the instruction, the inst.operands member
309 // will point to this vector's storage.
310 _.operands.clear();
311
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: "
318 << inst_word_count;
319 }
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;
323
324 // Advance past the opcode word. But remember the of the start
325 // of the instruction.
326 const size_t inst_offset = _.word_index;
327 _.word_index++;
328
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]);
340
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 "
347 << inst_word_index
348 << " words, but stated word count is "
349 << inst_word_count << ".";
350 }
351
352 spv_operand_type_t type =
353 spvTakeFirstMatchableOperand(&_.expected_operands);
354
355 if (auto error =
356 parseOperand(inst_offset, &inst, type, &_.endian_converted_words,
357 &_.operands, &_.expected_operands)) {
358 return error;
359 }
360 }
361
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.";
368 }
369
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.";
376 }
377
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
382 // word.
383 assert(!_.requires_endian_conversion ||
384 (inst_word_count == _.endian_converted_words.size()));
385 assert(_.requires_endian_conversion ||
386 (_.endian_converted_words.size() == 1));
387
388 recordNumberType(inst_offset, &inst);
389
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();
394 } else {
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;
398 }
399 inst.num_words = inst_word_count;
400
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());
405
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;
410 }
411
412 return SPV_SUCCESS;
413 }
414
parseOperand(size_t inst_offset,spv_parsed_instruction_t * inst,const spv_operand_type_t type,std::vector<uint32_t> * words,std::vector<spv_parsed_operand_t> * operands,spv_operand_pattern_t * expected_operands)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;
433
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;
437
438 if (_.word_index >= _.num_words)
439 return exhaustedInputDiagnostic(inst_offset, opcode, type);
440
441 const uint32_t word = peek();
442
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;
446
447 switch (type) {
448 case SPV_OPERAND_TYPE_TYPE_ID:
449 if (!word)
450 return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Type Id is 0";
451 inst->type_id = word;
452 break;
453
454 case SPV_OPERAND_TYPE_RESULT_ID:
455 if (!word)
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";
463 // Record it.
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;
469 break;
470
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;
475
476 if (opcode == spv::Op::OpExtInst && 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";
484 }
485 inst->ext_inst_type = ext_inst_type_iter->second;
486 }
487 break;
488
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";
494 break;
495
496 case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
497 assert(spv::Op::OpExtInst == 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) ==
501 SPV_SUCCESS) {
502 // if we know about this ext inst, push the expected operands
503 spvPushOperandTypes(ext_inst->operandTypes, expected_operands);
504 } else {
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)) {
508 return diagnostic()
509 << "Invalid extended instruction number: " << word;
510 } else {
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);
514 }
515 }
516 } break;
517
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))) {
522 return diagnostic()
523 << "Invalid " << spvOperandTypeStr(type) << ": " << word;
524 }
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";
529 }
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);
537 } break;
538
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;
547 break;
548
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;
554 break;
555
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
567 << " has no type";
568 }
569 uint32_t type_id = type_id_iter->second;
570
571 if (selector_id == type_id) {
572 // Recall that by convention, a result ID that is a type definition
573 // maps to itself.
574 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
575 << " is a type, not a value";
576 }
577 if (auto error = setNumericTypeInfoForType(&parsed_operand, type_id))
578 return error;
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";
583 }
584 } else {
585 assert(opcode == spv::Op::OpConstant ||
586 opcode == spv::Op::OpSpecConstant);
587 // The literal number type is determined by the type Id for the
588 // constant.
589 assert(inst->type_id);
590 if (auto error =
591 setNumericTypeInfoForType(&parsed_operand, inst->type_id))
592 return error;
593 }
594 break;
595
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;
599 std::string string =
600 spvtools::utils::MakeString(_.words + _.word_index, max_words, false);
601
602 if (string.length() == max_words * 4)
603 return exhaustedInputDiagnostic(inst_offset, opcode, type);
604
605 // Make sure we can record the word count without overflow.
606 //
607 // This error can't currently be triggered because of validity
608 // checks elsewhere.
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";
614 }
615 parsed_operand.num_words = uint16_t(string_num_words);
616 parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_STRING;
617
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) {
625 return diagnostic()
626 << "Invalid extended instruction import '" << string << "'";
627 }
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;
632 }
633 } break;
634
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 // A single word that is a plain enum value.
675
676 // Map an optional operand type to its corresponding concrete type.
677 if (type == SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER)
678 parsed_operand.type = SPV_OPERAND_TYPE_ACCESS_QUALIFIER;
679 if (type == SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT)
680 parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT;
681
682 spv_operand_desc entry;
683 if (grammar_.lookupOperand(type, word, &entry)) {
684 return diagnostic()
685 << "Invalid " << spvOperandTypeStr(parsed_operand.type)
686 << " operand: " << word;
687 }
688 // Prepare to accept operands to this operand, if needed.
689 spvPushOperandTypes(entry->operandTypes, expected_operands);
690 } break;
691
692 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: {
693 spv_operand_desc entry;
694 if (grammar_.lookupOperand(type, word, &entry)) {
695 return diagnostic()
696 << "Invalid " << spvOperandTypeStr(parsed_operand.type)
697 << " operand: " << word
698 << ", if you are creating a new source language please use "
699 "value 0 "
700 "(Unknown) and when ready, add your source language to "
701 "SPRIV-Headers";
702 }
703 // Prepare to accept operands to this operand, if needed.
704 spvPushOperandTypes(entry->operandTypes, expected_operands);
705 } break;
706
707 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
708 case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
709 case SPV_OPERAND_TYPE_LOOP_CONTROL:
710 case SPV_OPERAND_TYPE_IMAGE:
711 case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
712 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
713 case SPV_OPERAND_TYPE_SELECTION_CONTROL:
714 case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
715 case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
716 case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS:
717 case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS: {
718 // This operand is a mask.
719
720 // Map an optional operand type to its corresponding concrete type.
721 if (type == SPV_OPERAND_TYPE_OPTIONAL_IMAGE)
722 parsed_operand.type = SPV_OPERAND_TYPE_IMAGE;
723 else if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS)
724 parsed_operand.type = SPV_OPERAND_TYPE_MEMORY_ACCESS;
725 if (type == SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS)
726 parsed_operand.type = SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS;
727
728 // Check validity of set mask bits. Also prepare for operands for those
729 // masks if they have any. To get operand order correct, scan from
730 // MSB to LSB since we can only prepend operands to a pattern.
731 // The only case in the grammar where you have more than one mask bit
732 // having an operand is for image operands. See SPIR-V 3.14 Image
733 // Operands.
734 uint32_t remaining_word = word;
735 for (uint32_t mask = (1u << 31); remaining_word; mask >>= 1) {
736 if (remaining_word & mask) {
737 spv_operand_desc entry;
738 if (grammar_.lookupOperand(type, mask, &entry)) {
739 return diagnostic()
740 << "Invalid " << spvOperandTypeStr(parsed_operand.type)
741 << " operand: " << word << " has invalid mask component "
742 << mask;
743 }
744 remaining_word ^= mask;
745 spvPushOperandTypes(entry->operandTypes, expected_operands);
746 }
747 }
748 if (word == 0) {
749 // An all-zeroes mask *might* also be valid.
750 spv_operand_desc entry;
751 if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) {
752 // Prepare for its operands, if any.
753 spvPushOperandTypes(entry->operandTypes, expected_operands);
754 }
755 }
756 } break;
757 default:
758 return diagnostic() << "Internal error: Unhandled operand type: " << type;
759 }
760
761 assert(spvOperandIsConcrete(parsed_operand.type));
762
763 operands->push_back(parsed_operand);
764
765 const size_t index_after_operand = _.word_index + parsed_operand.num_words;
766
767 // Avoid buffer overrun for the cases where the operand has more than one
768 // word, and where it isn't a string. (Those other cases have already been
769 // handled earlier.) For example, this error can occur for a multi-word
770 // argument to OpConstant, or a multi-word case literal operand for OpSwitch.
771 if (_.num_words < index_after_operand)
772 return exhaustedInputDiagnostic(inst_offset, opcode, type);
773
774 if (_.requires_endian_conversion) {
775 // Copy instruction words. Translate to native endianness as needed.
776 if (convert_operand_endianness) {
777 const spv_endianness_t endianness = _.endian;
778 std::transform(_.words + _.word_index, _.words + index_after_operand,
779 std::back_inserter(*words),
780 [endianness](const uint32_t raw_word) {
781 return spvFixWord(raw_word, endianness);
782 });
783 } else {
784 words->insert(words->end(), _.words + _.word_index,
785 _.words + index_after_operand);
786 }
787 }
788
789 // Advance past the operand.
790 _.word_index = index_after_operand;
791
792 return SPV_SUCCESS;
793 }
794
setNumericTypeInfoForType(spv_parsed_operand_t * parsed_operand,uint32_t type_id)795 spv_result_t Parser::setNumericTypeInfoForType(
796 spv_parsed_operand_t* parsed_operand, uint32_t type_id) {
797 assert(type_id != 0);
798 auto type_info_iter = _.type_id_to_number_type_info.find(type_id);
799 if (type_info_iter == _.type_id_to_number_type_info.end()) {
800 return diagnostic() << "Type Id " << type_id << " is not a type";
801 }
802 const NumberType& info = type_info_iter->second;
803 if (info.type == SPV_NUMBER_NONE) {
804 // This is a valid type, but for something other than a scalar number.
805 return diagnostic() << "Type Id " << type_id
806 << " is not a scalar numeric type";
807 }
808
809 parsed_operand->number_kind = info.type;
810 parsed_operand->number_bit_width = info.bit_width;
811 // Round up the word count.
812 parsed_operand->num_words = static_cast<uint16_t>((info.bit_width + 31) / 32);
813 return SPV_SUCCESS;
814 }
815
recordNumberType(size_t inst_offset,const spv_parsed_instruction_t * inst)816 void Parser::recordNumberType(size_t inst_offset,
817 const spv_parsed_instruction_t* inst) {
818 const spv::Op opcode = static_cast<spv::Op>(inst->opcode);
819 if (spvOpcodeGeneratesType(opcode)) {
820 NumberType info = {SPV_NUMBER_NONE, 0};
821 if (spv::Op::OpTypeInt == opcode) {
822 const bool is_signed = peekAt(inst_offset + 3) != 0;
823 info.type = is_signed ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT;
824 info.bit_width = peekAt(inst_offset + 2);
825 } else if (spv::Op::OpTypeFloat == opcode) {
826 info.type = SPV_NUMBER_FLOATING;
827 info.bit_width = peekAt(inst_offset + 2);
828 }
829 // The *result* Id of a type generating instruction is the type Id.
830 _.type_id_to_number_type_info[inst->result_id] = info;
831 }
832 }
833
834 } // anonymous namespace
835
spvBinaryParse(const spv_const_context context,void * user_data,const uint32_t * code,const size_t num_words,spv_parsed_header_fn_t parsed_header,spv_parsed_instruction_fn_t parsed_instruction,spv_diagnostic * diagnostic)836 spv_result_t spvBinaryParse(const spv_const_context context, void* user_data,
837 const uint32_t* code, const size_t num_words,
838 spv_parsed_header_fn_t parsed_header,
839 spv_parsed_instruction_fn_t parsed_instruction,
840 spv_diagnostic* diagnostic) {
841 spv_context_t hijack_context = *context;
842 if (diagnostic) {
843 *diagnostic = nullptr;
844 spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, diagnostic);
845 }
846 Parser parser(&hijack_context, user_data, parsed_header, parsed_instruction);
847 return parser.parse(code, num_words, diagnostic);
848 }
849
850 // TODO(dneto): This probably belongs in text.cpp since that's the only place
851 // that a spv_binary_t value is created.
spvBinaryDestroy(spv_binary binary)852 void spvBinaryDestroy(spv_binary binary) {
853 if (binary) {
854 if (binary->code) delete[] binary->code;
855 delete binary;
856 }
857 }
858
spv_strnlen_s(const char * str,size_t strsz)859 size_t spv_strnlen_s(const char* str, size_t strsz) {
860 if (!str) return 0;
861 for (size_t i = 0; i < strsz; i++) {
862 if (!str[i]) return i;
863 }
864 return strsz;
865 }
866