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