Coverage Report

Created: 2026-08-14 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/binary.cpp
Line
Count
Source
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/table2.h"
37
#include "source/util/string_utils.h"
38
39
spv_result_t spvBinaryHeaderGet(const spv_const_binary binary,
40
                                const spv_endianness_t endian,
41
398k
                                spv_header_t* pHeader) {
42
398k
  if (!binary->code) return SPV_ERROR_INVALID_BINARY;
43
398k
  if (binary->wordCount < SPV_INDEX_INSTRUCTION)
44
6
    return SPV_ERROR_INVALID_BINARY;
45
398k
  if (!pHeader) return SPV_ERROR_INVALID_POINTER;
46
47
  // TODO: Validation checking?
48
398k
  pHeader->magic = spvFixWord(binary->code[SPV_INDEX_MAGIC_NUMBER], endian);
49
398k
  pHeader->version = spvFixWord(binary->code[SPV_INDEX_VERSION_NUMBER], endian);
50
  // Per 2.3.1 version's high and low bytes are 0
51
398k
  if ((pHeader->version & 0x000000ff) || pHeader->version & 0xff000000)
52
128
    return SPV_ERROR_INVALID_BINARY;
53
  // Minimum version was 1.0 and max version is defined by SPV_VERSION.
54
398k
  if (pHeader->version < SPV_SPIRV_VERSION_WORD(1, 0) ||
55
397k
      pHeader->version > SPV_VERSION)
56
202
    return SPV_ERROR_INVALID_BINARY;
57
58
397k
  pHeader->generator =
59
397k
      spvFixWord(binary->code[SPV_INDEX_GENERATOR_NUMBER], endian);
60
397k
  pHeader->bound = spvFixWord(binary->code[SPV_INDEX_BOUND], endian);
61
397k
  pHeader->schema = spvFixWord(binary->code[SPV_INDEX_SCHEMA], endian);
62
397k
  pHeader->instructions = &binary->code[SPV_INDEX_INSTRUCTION];
63
64
397k
  return SPV_SUCCESS;
65
398k
}
66
67
std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst,
68
198k
                                          const uint16_t operand_index) {
69
198k
  assert(operand_index < inst.num_operands);
70
198k
  const spv_parsed_operand_t& operand = inst.operands[operand_index];
71
72
198k
  return spvtools::utils::MakeString(inst.words + operand.offset,
73
198k
                                     operand.num_words);
74
198k
}
75
76
namespace {
77
78
// A SPIR-V binary parser.  A parser instance communicates detailed parse
79
// results via callbacks.
80
class Parser {
81
 public:
82
  // The user_data value is provided to the callbacks as context.
83
  Parser(const spv_const_context context, void* user_data,
84
         spv_parsed_header_fn_t parsed_header_fn,
85
         spv_parsed_instruction_fn_t parsed_instruction_fn)
86
341k
      : grammar_(context),
87
341k
        consumer_(context->consumer),
88
341k
        user_data_(user_data),
89
341k
        parsed_header_fn_(parsed_header_fn),
90
341k
        parsed_instruction_fn_(parsed_instruction_fn) {}
91
92
  // Parses the specified binary SPIR-V module, issuing callbacks on a parsed
93
  // header and for each parsed instruction.  Returns SPV_SUCCESS on success.
94
  // Otherwise returns an error code and issues a diagnostic.
95
  spv_result_t parse(const uint32_t* words, size_t num_words,
96
                     spv_diagnostic* diagnostic);
97
98
  // Sets whether to handle, rather than reject, unrecognized content:
99
  // unknown opcodes, unknown extended instruction numbers in semantic sets,
100
  // and known opcodes with unknown enum operands.  When set, unknown
101
  // instructions are re-emitted as raw OpUnknown data instead of returning
102
  // an error.
103
0
  void SetHandleUnknownOpcodes(bool value) { handle_unknown_opcodes_ = value; }
104
105
 private:
106
  // All remaining methods work on the current module parse state.
107
108
  // Like the parse method, but works on the current module parse state.
109
  spv_result_t parseModule();
110
111
  // Parses an instruction at the current position of the binary.  Assumes
112
  // the header has been parsed, the endian has been set, and the word index is
113
  // still in range.  Advances the parsing position past the instruction, and
114
  // updates other parsing state for the current module.
115
  // On success, returns SPV_SUCCESS and issues the parsed-instruction callback.
116
  // On failure, returns an error code and issues a diagnostic.
117
  spv_result_t parseInstruction();
118
119
  // Parses an instruction operand with the given type, for an instruction
120
  // starting at inst_offset words into the SPIR-V binary.
121
  // If the SPIR-V binary is the same endianness as the host, then the
122
  // endian_converted_inst_words parameter is ignored.  Otherwise, this method
123
  // appends the words for this operand, converted to host native endianness,
124
  // to the end of endian_converted_inst_words.  This method also updates the
125
  // expected_operands parameter, and the scalar members of the inst parameter.
126
  // On success, returns SPV_SUCCESS, advances past the operand, and pushes a
127
  // new entry on to the operands vector.  Otherwise returns an error code and
128
  // issues a diagnostic.
129
  spv_result_t parseOperand(size_t inst_offset, spv_parsed_instruction_t* inst,
130
                            const spv_operand_type_t type,
131
                            std::vector<uint32_t>* endian_converted_inst_words,
132
                            std::vector<spv_parsed_operand_t>* operands,
133
                            spv_operand_pattern_t* expected_operands);
134
135
  // Records the numeric type for an operand according to the type information
136
  // associated with the given non-zero type Id.  This can fail if the type Id
137
  // is not a type Id, or if the type Id does not reference a scalar numeric
138
  // type.  On success, return SPV_SUCCESS and populates the num_words,
139
  // number_kind, and number_bit_width fields of parsed_operand.
140
  spv_result_t setNumericTypeInfoForType(spv_parsed_operand_t* parsed_operand,
141
                                         uint32_t type_id);
142
143
  // Records the number type for an instruction at the given offset, if that
144
  // instruction generates a type.  For types that aren't scalar numbers,
145
  // record something with number kind SPV_NUMBER_NONE.
146
  void recordNumberType(size_t inst_offset,
147
                        const spv_parsed_instruction_t* inst);
148
149
  // Returns a diagnostic stream object initialized with current position in
150
  // the input stream, and for the given error code. Any data written to the
151
  // returned object will be propagated to the current parse's diagnostic
152
  // object.
153
14.2k
  spvtools::DiagnosticStream diagnostic(spv_result_t error) {
154
14.2k
    return spvtools::DiagnosticStream({0, 0, _.instruction_count}, consumer_,
155
14.2k
                                      "", error);
156
14.2k
  }
157
158
  // Returns a diagnostic stream object with the default parse error code.
159
13.3k
  spvtools::DiagnosticStream diagnostic() {
160
    // The default failure for parsing is invalid binary.
161
13.3k
    return diagnostic(SPV_ERROR_INVALID_BINARY);
162
13.3k
  }
163
164
  // Issues a diagnostic describing an exhaustion of input condition when
165
  // trying to decode an instruction operand, and returns
166
  // SPV_ERROR_INVALID_BINARY.
167
  spv_result_t exhaustedInputDiagnostic(size_t inst_offset, spv::Op opcode,
168
2.58k
                                        spv_operand_type_t type) {
169
2.58k
    return diagnostic() << "End of input reached while decoding Op"
170
2.58k
                        << spvOpcodeString(opcode) << " starting at word "
171
2.58k
                        << inst_offset
172
2.58k
                        << ((_.word_index < _.num_words) ? ": truncated "
173
2.58k
                                                         : ": missing ")
174
2.58k
                        << spvOperandTypeStr(type) << " operand at word offset "
175
2.58k
                        << _.word_index - inst_offset << ".";
176
2.58k
  }
177
178
  // Returns the endian-corrected word at the current position.
179
214M
  uint32_t peek() const { return peekAt(_.word_index); }
180
181
  // Returns the endian-corrected word at the given position.
182
215M
  uint32_t peekAt(size_t index) const {
183
215M
    assert(index < _.num_words);
184
215M
    return spvFixWord(_.words[index], _.endian);
185
215M
  }
186
187
  // Data members
188
189
  const spvtools::AssemblyGrammar grammar_;        // SPIR-V syntax utility.
190
  const spvtools::MessageConsumer& consumer_;      // Message consumer callback.
191
  void* const user_data_;                          // Context for the callbacks
192
  const spv_parsed_header_fn_t parsed_header_fn_;  // Parsed header callback
193
  const spv_parsed_instruction_fn_t
194
      parsed_instruction_fn_;  // Parsed instruction callback
195
  // When true, unrecognized opcodes, ext inst numbers, and enum operands are
196
  // passed to the callback as raw OpUnknown data instead of returning an error.
197
  bool handle_unknown_opcodes_ = false;
198
199
  // Describes the format of a typed literal number.
200
  struct NumberType {
201
    spv_number_kind_t type;
202
    uint32_t bit_width;
203
    spv_fp_encoding_t encoding;
204
  };
205
206
  // The state used to parse a single SPIR-V binary module.
207
  struct State {
208
    State(const uint32_t* words_arg, size_t num_words_arg,
209
          spv_diagnostic* diagnostic_arg)
210
1.02M
        : words(words_arg),
211
1.02M
          num_words(num_words_arg),
212
1.02M
          diagnostic(diagnostic_arg),
213
1.02M
          word_index(0),
214
1.02M
          instruction_count(0),
215
          endian(),
216
1.02M
          requires_endian_conversion(false) {
217
      // Temporary storage for parser state within a single instruction.
218
      // Most instructions require fewer than 25 words or operands.
219
1.02M
      operands.reserve(25);
220
1.02M
      endian_converted_words.reserve(25);
221
1.02M
      expected_operands.reserve(25);
222
1.02M
    }
223
683k
    State() : State(0, 0, nullptr) {}
224
    const uint32_t* words;       // Words in the binary SPIR-V module.
225
    size_t num_words;            // Number of words in the module.
226
    spv_diagnostic* diagnostic;  // Where diagnostics go.
227
    size_t word_index;           // The current position in words.
228
    size_t instruction_count;    // The count of processed instructions
229
    spv_endianness_t endian;     // The endianness of the binary.
230
    // Is the SPIR-V binary in a different endianness from the host native
231
    // endianness?
232
    bool requires_endian_conversion;
233
    // Set by parseOperand when LookupOperand fails for an enum operand and
234
    // handle_unknown_opcodes_ is set.  Signals parseInstruction to discard
235
    // the partially-decoded instruction and re-emit it as raw OpUnknown data.
236
    // Cleared by parseInstruction immediately before calling emitAsUnknown.
237
    bool retry_instruction_as_unknown_ = false;
238
239
    // Maps a result ID to its type ID.  By convention:
240
    //  - a result ID that is a type definition maps to itself.
241
    //  - a result ID without a type maps to 0.  (E.g. for OpLabel)
242
    std::unordered_map<uint32_t, uint32_t> id_to_type_id;
243
    // Maps a type ID to its number type description.
244
    std::unordered_map<uint32_t, NumberType> type_id_to_number_type_info;
245
    // Maps an ExtInstImport id to the extended instruction type.
246
    std::unordered_map<uint32_t, spv_ext_inst_type_t>
247
        import_id_to_ext_inst_type;
248
249
    // Used by parseOperand
250
    std::vector<spv_parsed_operand_t> operands;
251
    std::vector<uint32_t> endian_converted_words;
252
    spv_operand_pattern_t expected_operands;
253
  } _;
254
};
255
256
spv_result_t Parser::parse(const uint32_t* words, size_t num_words,
257
341k
                           spv_diagnostic* diagnostic_arg) {
258
341k
  _ = State(words, num_words, diagnostic_arg);
259
260
341k
  const spv_result_t result = parseModule();
261
262
  // Clear the module state.  The tables might be big.
263
341k
  _ = State();
264
265
341k
  return result;
266
341k
}
267
268
341k
spv_result_t Parser::parseModule() {
269
341k
  if (!_.words) return diagnostic() << "Missing module.";
270
271
341k
  if (_.num_words < SPV_INDEX_INSTRUCTION)
272
219
    return diagnostic() << "Module has incomplete header: only " << _.num_words
273
219
                        << " words instead of " << SPV_INDEX_INSTRUCTION;
274
275
  // Check the magic number and detect the module's endianness.
276
341k
  spv_const_binary_t binary{_.words, _.num_words};
277
341k
  if (spvBinaryEndianness(&binary, &_.endian)) {
278
225
    return diagnostic() << "Invalid SPIR-V magic number '" << std::hex
279
225
                        << _.words[0] << "'.";
280
225
  }
281
341k
  _.requires_endian_conversion = !spvIsHostEndian(_.endian);
282
283
  // Process the header.
284
341k
  spv_header_t header;
285
341k
  if (spvBinaryHeaderGet(&binary, _.endian, &header)) {
286
    // It turns out there is no way to trigger this error since the only
287
    // failure cases are already handled above, with better messages.
288
272
    return diagnostic(SPV_ERROR_INTERNAL)
289
272
           << "Internal error: unhandled header parse failure";
290
272
  }
291
340k
  if (parsed_header_fn_) {
292
143k
    if (auto error = parsed_header_fn_(user_data_, _.endian, header.magic,
293
143k
                                       header.version, header.generator,
294
143k
                                       header.bound, header.schema)) {
295
0
      return error;
296
0
    }
297
143k
  }
298
299
  // Process the instructions.
300
340k
  _.word_index = SPV_INDEX_INSTRUCTION;
301
76.6M
  while (_.word_index < _.num_words)
302
76.4M
    if (auto error = parseInstruction()) return error;
303
304
  // Running off the end should already have been reported earlier.
305
340k
  assert(_.word_index == _.num_words);
306
307
248k
  return SPV_SUCCESS;
308
248k
}
309
310
76.4M
spv_result_t Parser::parseInstruction() {
311
76.4M
  _.instruction_count++;
312
313
  // The zero values for all members except for opcode are the
314
  // correct initial values.
315
76.4M
  spv_parsed_instruction_t inst = {};
316
317
76.4M
  const uint32_t first_word = peek();
318
319
  // If the module's endianness is different from the host native endianness,
320
  // then converted_words contains the endian-translated words in the
321
  // instruction.
322
76.4M
  _.endian_converted_words.clear();
323
76.4M
  _.endian_converted_words.push_back(first_word);
324
325
  // After a successful parse of the instruction, the inst.operands member
326
  // will point to this vector's storage.
327
76.4M
  _.operands.clear();
328
329
76.4M
  assert(_.word_index < _.num_words);
330
  // Decompose and check the first word.
331
76.4M
  uint16_t inst_word_count = 0;
332
76.4M
  spvOpcodeSplit(first_word, &inst_word_count, &inst.opcode);
333
76.4M
  if (inst_word_count < 1) {
334
307
    return diagnostic() << "Invalid instruction word count: "
335
307
                        << inst_word_count;
336
307
  }
337
76.4M
  const spvtools::InstructionDesc* opcode_desc = nullptr;
338
76.4M
  const bool opcode_known =
339
76.4M
      spvtools::LookupOpcode(static_cast<spv::Op>(inst.opcode), &opcode_desc) ==
340
76.4M
      SPV_SUCCESS;
341
76.4M
  if (!opcode_known && !handle_unknown_opcodes_)
342
617
    return diagnostic() << "Invalid opcode: " << inst.opcode;
343
344
  // Advance past the opcode word.  But remember the start of the instruction.
345
76.4M
  const size_t inst_offset = _.word_index;
346
76.4M
  _.word_index++;
347
348
  // Emits the instruction at inst_offset as raw data with no decoded operands.
349
76.4M
  auto emitAsUnknown = [&]() -> spv_result_t {
350
0
    if (inst_offset + inst_word_count > _.num_words) {
351
0
      return diagnostic() << "Truncated binary: instruction at word "
352
0
                          << inst_offset << " claims " << inst_word_count
353
0
                          << " words but binary ends at " << _.num_words;
354
0
    }
355
    // Repopulate endian_converted_words from scratch.  The operand loop may
356
    // have partially filled it before the unknown enum was detected.
357
0
    _.endian_converted_words.clear();
358
0
    _.endian_converted_words.push_back(first_word);
359
0
    if (_.requires_endian_conversion) {
360
0
      for (uint16_t i = 1; i < inst_word_count; i++) {
361
0
        _.endian_converted_words.push_back(peekAt(inst_offset + i));
362
0
      }
363
0
    }
364
0
    _.word_index = inst_offset + inst_word_count;
365
0
    inst.words = _.requires_endian_conversion ? _.endian_converted_words.data()
366
0
                                              : _.words + inst_offset;
367
0
    inst.num_words = inst_word_count;
368
0
    _.operands.clear();
369
0
    inst.operands = _.operands.data();
370
0
    inst.num_operands = 0;
371
0
    if (parsed_instruction_fn_) {
372
0
      if (auto error = parsed_instruction_fn_(user_data_, &inst)) return error;
373
0
    }
374
0
    return SPV_SUCCESS;
375
0
  };
376
377
76.4M
  if (!opcode_known) {
378
0
    return emitAsUnknown();
379
0
  }
380
381
  // Maintains the ordered list of expected operand types.
382
  // For many instructions we only need the {numTypes, operandTypes}
383
  // entries in opcode_desc.  However, sometimes we need to modify
384
  // the list as we parse the operands. This occurs when an operand
385
  // has its own logical operands (such as the LocalSize operand for
386
  // ExecutionMode), or for extended instructions that may have their
387
  // own operands depending on the selected extended instruction.
388
76.4M
  _.expected_operands.clear();
389
390
76.4M
  spvPushOperandTypes(opcode_desc->operands(), &_.expected_operands);
391
392
214M
  while (_.word_index < inst_offset + inst_word_count) {
393
137M
    const uint16_t inst_word_index = uint16_t(_.word_index - inst_offset);
394
137M
    if (_.expected_operands.empty()) {
395
1.59k
      return diagnostic() << "Invalid instruction Op"
396
1.59k
                          << opcode_desc->name().data() << " starting at word "
397
1.59k
                          << inst_offset << ": expected no more operands after "
398
1.59k
                          << inst_word_index
399
1.59k
                          << " words, but stated word count is "
400
1.59k
                          << inst_word_count << ".";
401
1.59k
    }
402
403
137M
    spv_operand_type_t type =
404
137M
        spvTakeFirstMatchableOperand(&_.expected_operands);
405
406
137M
    if (auto error =
407
137M
            parseOperand(inst_offset, &inst, type, &_.endian_converted_words,
408
137M
                         &_.operands, &_.expected_operands)) {
409
10.3k
      if (_.retry_instruction_as_unknown_) {
410
0
        _.retry_instruction_as_unknown_ = false;
411
0
        return emitAsUnknown();
412
0
      }
413
10.3k
      return error;
414
10.3k
    }
415
137M
  }
416
417
76.4M
  if (!_.expected_operands.empty() &&
418
60.5M
      !spvOperandIsOptional(_.expected_operands.back())) {
419
251
    return diagnostic() << "End of input reached while decoding Op"
420
251
                        << opcode_desc->name().data() << " starting at word "
421
251
                        << inst_offset << ": expected more operands after "
422
251
                        << inst_word_count << " words.";
423
251
  }
424
425
76.4M
  if ((inst_offset + inst_word_count) != _.word_index) {
426
412
    return diagnostic() << "Invalid word count: Op"
427
412
                        << opcode_desc->name().data() << " starting at word "
428
412
                        << inst_offset << " says it has " << inst_word_count
429
412
                        << " words, but found " << _.word_index - inst_offset
430
412
                        << " words instead.";
431
412
  }
432
433
  // Check the computed length of the endian-converted words vector against
434
  // the declared number of words in the instruction.  If endian conversion
435
  // is required, then they should match.  If no endian conversion was
436
  // performed, then the vector only contains the initial opcode/word-count
437
  // word.
438
76.4M
  assert(!_.requires_endian_conversion ||
439
76.4M
         (inst_word_count == _.endian_converted_words.size()));
440
76.4M
  assert(_.requires_endian_conversion ||
441
76.4M
         (_.endian_converted_words.size() == 1));
442
443
76.4M
  if (_.requires_endian_conversion) {
444
    // We must wait until here to set this pointer, because the vector might
445
    // have been be resized while we accumulated its elements.
446
274k
    inst.words = _.endian_converted_words.data();
447
76.1M
  } else {
448
    // If no conversion is required, then just point to the underlying binary.
449
    // This saves time and space.
450
76.1M
    inst.words = _.words + inst_offset;
451
76.1M
  }
452
76.4M
  inst.num_words = inst_word_count;
453
454
76.4M
  recordNumberType(inst_offset, &inst);
455
456
  // We must wait until here to set this pointer, because the vector might
457
  // have been be resized while we accumulated its elements.
458
76.4M
  inst.operands = _.operands.data();
459
76.4M
  inst.num_operands = uint16_t(_.operands.size());
460
461
  // Issue the callback.  The callee should know that all the storage in inst
462
  // is transient, and will disappear immediately afterward.
463
76.4M
  if (parsed_instruction_fn_) {
464
76.2M
    if (auto error = parsed_instruction_fn_(user_data_, &inst)) return error;
465
76.2M
  }
466
467
76.3M
  return SPV_SUCCESS;
468
76.4M
}
469
470
spv_result_t Parser::parseOperand(size_t inst_offset,
471
                                  spv_parsed_instruction_t* inst,
472
                                  const spv_operand_type_t type,
473
                                  std::vector<uint32_t>* words,
474
                                  std::vector<spv_parsed_operand_t>* operands,
475
137M
                                  spv_operand_pattern_t* expected_operands) {
476
137M
  const spv::Op opcode = static_cast<spv::Op>(inst->opcode);
477
  // We'll fill in this result as we go along.
478
137M
  spv_parsed_operand_t parsed_operand;
479
137M
  parsed_operand.offset = uint16_t(_.word_index - inst_offset);
480
  // Most operands occupy one word.  This might be be adjusted later.
481
137M
  parsed_operand.num_words = 1;
482
  // The type argument is the one used by the grammar to parse the instruction.
483
  // But it can exposes internal parser details such as whether an operand is
484
  // optional or actually represents a variable-length sequence of operands.
485
  // The resulting type should be adjusted to avoid those internal details.
486
  // In most cases, the resulting operand type is the same as the grammar type.
487
137M
  parsed_operand.type = type;
488
489
  // Assume non-numeric values.  This will be updated for literal numbers.
490
137M
  parsed_operand.number_kind = SPV_NUMBER_NONE;
491
137M
  parsed_operand.number_bit_width = 0;
492
493
137M
  if (_.word_index >= _.num_words)
494
2.48k
    return exhaustedInputDiagnostic(inst_offset, opcode, type);
495
496
137M
  const uint32_t word = peek();
497
498
  // Do the words in this operand have to be converted to native endianness?
499
  // True for all but literal strings.
500
137M
  bool convert_operand_endianness = true;
501
502
137M
  switch (type) {
503
9.77M
    case SPV_OPERAND_TYPE_TYPE_ID:
504
9.77M
      if (!word)
505
18
        return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Type Id is 0";
506
9.77M
      inst->type_id = word;
507
9.77M
      break;
508
509
14.2M
    case SPV_OPERAND_TYPE_RESULT_ID:
510
14.2M
      if (!word)
511
19
        return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Result Id is 0";
512
14.2M
      inst->result_id = word;
513
      // Save the result ID to type ID mapping.
514
      // In the grammar, type ID always appears before result ID.
515
14.2M
      if (_.id_to_type_id.find(inst->result_id) != _.id_to_type_id.end())
516
25
        return diagnostic(SPV_ERROR_INVALID_ID)
517
25
               << "Id " << inst->result_id << " is defined more than once";
518
      // Record it.
519
      // A regular value maps to its type.  Some instructions (e.g. OpLabel)
520
      // have no type Id, and will map to 0.  The result Id for a
521
      // type-generating instruction (e.g. OpTypeInt) maps to itself.
522
14.2M
      _.id_to_type_id[inst->result_id] =
523
14.2M
          spvOpcodeGeneratesType(opcode) ? inst->result_id : inst->type_id;
524
14.2M
      break;
525
526
74.7M
    case SPV_OPERAND_TYPE_ID:
527
100M
    case SPV_OPERAND_TYPE_OPTIONAL_ID:
528
100M
      if (!word) return diagnostic(SPV_ERROR_INVALID_ID) << "Id is 0";
529
100M
      parsed_operand.type = SPV_OPERAND_TYPE_ID;
530
531
100M
      if (spvIsExtendedInstruction(opcode) && parsed_operand.offset == 3) {
532
        // The current word is the extended instruction set Id.
533
        // Set the extended instruction set type for the current instruction.
534
323k
        auto ext_inst_type_iter = _.import_id_to_ext_inst_type.find(word);
535
323k
        if (ext_inst_type_iter == _.import_id_to_ext_inst_type.end()) {
536
457
          return diagnostic(SPV_ERROR_INVALID_ID)
537
457
                 << "OpExtInst set Id " << word
538
457
                 << " does not reference an OpExtInstImport result Id";
539
457
        }
540
323k
        inst->ext_inst_type = ext_inst_type_iter->second;
541
323k
      }
542
100M
      break;
543
544
100M
    case SPV_OPERAND_TYPE_SCOPE_ID:
545
45.1k
    case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
546
      // Check for trivially invalid values.  The operand descriptions already
547
      // have the word "ID" in them.
548
45.1k
      if (!word) return diagnostic() << spvOperandTypeStr(type) << " is 0";
549
45.1k
      break;
550
551
323k
    case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
552
323k
      assert(spvIsExtendedInstruction(opcode));
553
323k
      assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE);
554
555
323k
      const spvtools::ExtInstDesc* desc = nullptr;
556
323k
      if (spvtools::LookupExtInst(inst->ext_inst_type, word, &desc) ==
557
323k
          SPV_SUCCESS) {
558
        // Push VARIABLE_ID so extra trailing operands from future NSDI
559
        // versions are silently absorbed after the instruction-specific ones.
560
317k
        if (spvExtInstIsNonSemantic(inst->ext_inst_type)) {
561
239
          expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
562
239
        }
563
564
        // if we know about this ext inst, push the expected operands
565
317k
        spvPushOperandTypes(desc->operands(), expected_operands);
566
317k
      } else {
567
        // If we don't know this extended instruction and the set is semantic,
568
        // fail unless handle_unknown_opcodes_ is set.  For non-semantic sets,
569
        // always continue regardless of the flag. In both non-error cases the
570
        // remaining operands are exposed as variable IDs. For non-semantic
571
        // sets the disassembler emits the instruction via its normal operand
572
        // loop; for semantic sets with handle_unknown_opcodes_ set, the
573
        // disassembler independently detects the unknown number via
574
        // LookupExtInst and emits the entire instruction as OpUnknown.
575
5.18k
        if (!spvExtInstIsNonSemantic(inst->ext_inst_type) &&
576
193
            !handle_unknown_opcodes_) {
577
193
          return diagnostic()
578
193
                 << "Invalid extended instruction number: " << word;
579
193
        }
580
4.98k
        expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID);
581
4.98k
      }
582
323k
    } break;
583
584
322k
    case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
585
2.43k
      assert(spv::Op::OpSpecConstantOp == opcode);
586
2.43k
      if (word > static_cast<uint32_t>(spv::Op::Max) ||
587
2.22k
          grammar_.lookupSpecConstantOpcode(spv::Op(word))) {
588
325
        return diagnostic()
589
325
               << "Invalid " << spvOperandTypeStr(type) << ": " << word;
590
325
      }
591
2.10k
      const spvtools::InstructionDesc* opcode_entry = nullptr;
592
2.10k
      if (spvtools::LookupOpcode(spv::Op(word), &opcode_entry)) {
593
0
        return diagnostic(SPV_ERROR_INTERNAL)
594
0
               << "OpSpecConstant opcode table out of sync";
595
0
      }
596
      // OpSpecConstant opcodes must have a type and result. We've already
597
      // processed them, so skip them when preparing to parse the other
598
      // operants for the opcode.
599
2.10k
      assert(opcode_entry->hasType);
600
2.10k
      assert(opcode_entry->hasResult);
601
2.10k
      assert(opcode_entry->operands().size() >= 2);
602
2.10k
      spvPushOperandTypes(opcode_entry->operands().subspan(2),
603
2.10k
                          expected_operands);
604
2.10k
    } break;
605
606
2.13M
    case SPV_OPERAND_TYPE_LITERAL_INTEGER:
607
3.05M
    case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
608
      // These are regular single-word literal integer operands.
609
      // Post-parsing validation should check the range of the parsed value.
610
3.05M
      parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_INTEGER;
611
      // It turns out they are always unsigned integers!
612
3.05M
      parsed_operand.number_kind = SPV_NUMBER_UNSIGNED_INT;
613
3.05M
      parsed_operand.number_bit_width = 32;
614
3.05M
      break;
615
616
1.01k
    case SPV_OPERAND_TYPE_LITERAL_FLOAT:
617
      // These are regular single-word literal float operands.
618
1.01k
      parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_FLOAT;
619
1.01k
      parsed_operand.number_kind = SPV_NUMBER_FLOATING;
620
1.01k
      parsed_operand.number_bit_width = 32;
621
1.01k
      break;
622
623
1.23M
    case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
624
1.74M
    case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
625
1.74M
      parsed_operand.type = SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER;
626
1.74M
      if (opcode == spv::Op::OpSwitch) {
627
        // The literal operands have the same type as the value
628
        // referenced by the selector Id.
629
509k
        const uint32_t selector_id = peekAt(inst_offset + 1);
630
509k
        const auto type_id_iter = _.id_to_type_id.find(selector_id);
631
509k
        if (type_id_iter == _.id_to_type_id.end() ||
632
508k
            type_id_iter->second == 0) {
633
25
          return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
634
25
                              << " has no type";
635
25
        }
636
508k
        uint32_t type_id = type_id_iter->second;
637
638
508k
        if (selector_id == type_id) {
639
          // Recall that by convention, a result ID that is a type definition
640
          // maps to itself.
641
32
          return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
642
32
                              << " is a type, not a value";
643
32
        }
644
508k
        if (auto error = setNumericTypeInfoForType(&parsed_operand, type_id))
645
186
          return error;
646
508k
        if (parsed_operand.number_kind != SPV_NUMBER_UNSIGNED_INT &&
647
465k
            parsed_operand.number_kind != SPV_NUMBER_SIGNED_INT) {
648
10
          return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
649
10
                              << " is not a scalar integer";
650
10
        }
651
1.23M
      } else {
652
1.23M
        assert(opcode == spv::Op::OpConstant ||
653
1.23M
               opcode == spv::Op::OpSpecConstant);
654
        // The literal number type is determined by the type Id for the
655
        // constant.
656
1.23M
        assert(inst->type_id);
657
1.23M
        if (auto error =
658
1.23M
                setNumericTypeInfoForType(&parsed_operand, inst->type_id))
659
755
          return error;
660
1.23M
      }
661
1.74M
      break;
662
663
1.74M
    case SPV_OPERAND_TYPE_LITERAL_STRING:
664
1.18M
    case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: {
665
1.18M
      const size_t max_words = _.num_words - _.word_index;
666
1.18M
      std::string string =
667
1.18M
          spvtools::utils::MakeString(_.words + _.word_index, max_words, false);
668
669
1.18M
      if (string.length() == max_words * 4)
670
57
        return exhaustedInputDiagnostic(inst_offset, opcode, type);
671
672
      // Make sure we can record the word count without overflow.
673
      //
674
      // This error can't currently be triggered because of validity
675
      // checks elsewhere.
676
1.18M
      const size_t string_num_words = string.length() / 4 + 1;
677
1.18M
      if (string_num_words > std::numeric_limits<uint16_t>::max()) {
678
26
        return diagnostic() << "Literal string is longer than "
679
26
                            << std::numeric_limits<uint16_t>::max()
680
26
                            << " words: " << string_num_words << " words long";
681
26
      }
682
1.18M
      parsed_operand.num_words = uint16_t(string_num_words);
683
1.18M
      parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_STRING;
684
685
1.18M
      if (spv::Op::OpExtInstImport == opcode) {
686
        // Record the extended instruction type for the ID for this import.
687
        // There is only one string literal argument to OpExtInstImport,
688
        // so it's sufficient to guard this just on the opcode.
689
114k
        const spv_ext_inst_type_t ext_inst_type =
690
114k
            spvExtInstImportTypeGet(string.c_str());
691
114k
        if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) {
692
4.76k
          return diagnostic()
693
4.76k
                 << "Invalid extended instruction import '" << string << "'";
694
4.76k
        }
695
        // We must have parsed a valid result ID.  It's a condition
696
        // of the grammar, and we only accept non-zero result Ids.
697
114k
        assert(inst->result_id);
698
109k
        _.import_id_to_ext_inst_type[inst->result_id] = ext_inst_type;
699
109k
      }
700
1.18M
    } break;
701
702
1.17M
    case SPV_OPERAND_TYPE_CAPABILITY:
703
375k
    case SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY:
704
531k
    case SPV_OPERAND_TYPE_EXECUTION_MODEL:
705
786k
    case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
706
1.04M
    case SPV_OPERAND_TYPE_MEMORY_MODEL:
707
1.25M
    case SPV_OPERAND_TYPE_EXECUTION_MODE:
708
2.91M
    case SPV_OPERAND_TYPE_STORAGE_CLASS:
709
2.93M
    case SPV_OPERAND_TYPE_DIMENSIONALITY:
710
2.93M
    case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
711
2.93M
    case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
712
2.95M
    case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT:
713
2.95M
    case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
714
2.95M
    case SPV_OPERAND_TYPE_LINKAGE_TYPE:
715
2.95M
    case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
716
2.95M
    case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER:
717
2.96M
    case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
718
5.40M
    case SPV_OPERAND_TYPE_DECORATION:
719
5.60M
    case SPV_OPERAND_TYPE_BUILT_IN:
720
5.60M
    case SPV_OPERAND_TYPE_GROUP_OPERATION:
721
5.60M
    case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
722
5.60M
    case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
723
5.60M
    case SPV_OPERAND_TYPE_RAY_FLAGS:
724
5.60M
    case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
725
5.60M
    case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
726
5.60M
    case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
727
5.60M
    case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
728
5.60M
    case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
729
5.60M
    case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
730
5.60M
    case SPV_OPERAND_TYPE_DEBUG_OPERATION:
731
5.60M
    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
732
5.60M
    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE:
733
5.60M
    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER:
734
5.60M
    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION:
735
5.60M
    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY:
736
5.60M
    case SPV_OPERAND_TYPE_FPDENORM_MODE:
737
5.60M
    case SPV_OPERAND_TYPE_FPOPERATION_MODE:
738
5.60M
    case SPV_OPERAND_TYPE_QUANTIZATION_MODES:
739
5.60M
    case SPV_OPERAND_TYPE_OVERFLOW_MODES:
740
5.60M
    case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT:
741
5.60M
    case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT:
742
5.60M
    case SPV_OPERAND_TYPE_FPENCODING:
743
5.60M
    case SPV_OPERAND_TYPE_OPTIONAL_FPENCODING:
744
5.61M
    case SPV_OPERAND_TYPE_HOST_ACCESS_QUALIFIER:
745
5.61M
    case SPV_OPERAND_TYPE_LOAD_CACHE_CONTROL:
746
5.61M
    case SPV_OPERAND_TYPE_STORE_CACHE_CONTROL:
747
5.61M
    case SPV_OPERAND_TYPE_NAMED_MAXIMUM_NUMBER_OF_REGISTERS:
748
5.61M
    case SPV_OPERAND_TYPE_GATHER_MODES: {
749
      // A single word that is a plain enum value.
750
751
      // Map an optional operand type to its corresponding concrete type.
752
5.61M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER)
753
90
        parsed_operand.type = SPV_OPERAND_TYPE_ACCESS_QUALIFIER;
754
5.61M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT)
755
42
        parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT;
756
5.61M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_FPENCODING)
757
4.42k
        parsed_operand.type = SPV_OPERAND_TYPE_FPENCODING;
758
5.61M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_CAPABILITY)
759
1.23k
        parsed_operand.type = SPV_OPERAND_TYPE_CAPABILITY;
760
761
5.61M
      const spvtools::OperandDesc* entry = nullptr;
762
5.61M
      if (spvtools::LookupOperand(type, word, &entry)) {
763
397
        if (handle_unknown_opcodes_) _.retry_instruction_as_unknown_ = true;
764
397
        return diagnostic()
765
397
               << "Invalid " << spvOperandTypeStr(parsed_operand.type)
766
397
               << " operand: " << word;
767
397
      }
768
      // Prepare to accept operands to this operand, if needed.
769
5.61M
      spvPushOperandTypes(entry->operands(), expected_operands);
770
5.61M
    } break;
771
772
62.1k
    case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: {
773
62.1k
      const spvtools::OperandDesc* entry = nullptr;
774
62.1k
      if (spvtools::LookupOperand(type, word, &entry)) {
775
53
        if (handle_unknown_opcodes_) _.retry_instruction_as_unknown_ = true;
776
53
        return diagnostic()
777
53
               << "Invalid " << spvOperandTypeStr(parsed_operand.type)
778
53
               << " operand: " << word
779
53
               << ", if you are creating a new source language please use "
780
53
                  "value 0 "
781
53
                  "(Unknown) and when ready, add your source language to "
782
53
                  "SPIRV-Headers";
783
53
      }
784
      // Prepare to accept operands to this operand, if needed.
785
62.1k
      spvPushOperandTypes(entry->operands(), expected_operands);
786
62.1k
    } break;
787
788
21.3k
    case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
789
287k
    case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
790
656k
    case SPV_OPERAND_TYPE_LOOP_CONTROL:
791
657k
    case SPV_OPERAND_TYPE_IMAGE:
792
658k
    case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
793
659k
    case SPV_OPERAND_TYPE_MEMORY_ACCESS:
794
659k
    case SPV_OPERAND_TYPE_TENSOR_OPERANDS:
795
659k
    case SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS:
796
900k
    case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
797
900k
    case SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS:
798
1.34M
    case SPV_OPERAND_TYPE_SELECTION_CONTROL:
799
1.34M
    case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS:
800
1.34M
    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
801
1.34M
    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS:
802
1.34M
    case SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS:
803
1.34M
    case SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_REDUCE:
804
1.34M
    case SPV_OPERAND_TYPE_TENSOR_ADDRESSING_OPERANDS:
805
1.34M
    case SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS:
806
1.34M
    case SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS: {
807
      // This operand is a mask.
808
809
      // Map an optional operand type to its corresponding concrete type.
810
1.34M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_IMAGE)
811
1.12k
        parsed_operand.type = SPV_OPERAND_TYPE_IMAGE;
812
1.34M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS)
813
240k
        parsed_operand.type = SPV_OPERAND_TYPE_MEMORY_ACCESS;
814
1.34M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_COOPERATIVE_MATRIX_OPERANDS)
815
80
        parsed_operand.type = SPV_OPERAND_TYPE_COOPERATIVE_MATRIX_OPERANDS;
816
1.34M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_RAW_ACCESS_CHAIN_OPERANDS)
817
45
        parsed_operand.type = SPV_OPERAND_TYPE_RAW_ACCESS_CHAIN_OPERANDS;
818
1.34M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS)
819
172
        parsed_operand.type =
820
172
            SPV_OPERAND_TYPE_MATRIX_MULTIPLY_ACCUMULATE_OPERANDS;
821
1.34M
      if (type == SPV_OPERAND_TYPE_OPTIONAL_TENSOR_OPERANDS)
822
376
        parsed_operand.type = SPV_OPERAND_TYPE_TENSOR_OPERANDS;
823
824
      // Check validity of set mask bits. Also prepare for operands for those
825
      // masks if they have any.  To get operand order correct, scan from
826
      // MSB to LSB since we can only prepend operands to a pattern.
827
      // The only case in the grammar where you have more than one mask bit
828
      // having an operand is for image operands.  See SPIR-V 3.14 Image
829
      // Operands.
830
1.34M
      uint32_t remaining_word = word;
831
15.7M
      for (uint32_t mask = (1u << 31); remaining_word; mask >>= 1) {
832
14.3M
        if (remaining_word & mask) {
833
725k
          const spvtools::OperandDesc* entry = nullptr;
834
725k
          if (spvtools::LookupOperand(type, mask, &entry)) {
835
374
            if (handle_unknown_opcodes_) _.retry_instruction_as_unknown_ = true;
836
374
            return diagnostic()
837
374
                   << "Invalid " << spvOperandTypeStr(parsed_operand.type)
838
374
                   << " operand: " << word << " has invalid mask component "
839
374
                   << mask;
840
374
          }
841
725k
          remaining_word ^= mask;
842
725k
          spvPushOperandTypes(entry->operands(), expected_operands);
843
725k
        }
844
14.3M
      }
845
1.34M
      if (word == 0) {
846
        // An all-zeroes mask *might* also be valid.
847
887k
        const spvtools::OperandDesc* entry = nullptr;
848
887k
        if (SPV_SUCCESS == spvtools::LookupOperand(type, 0, &entry)) {
849
          // Prepare for its operands, if any.
850
887k
          spvPushOperandTypes(entry->operands(), expected_operands);
851
887k
        }
852
887k
      }
853
1.34M
    } break;
854
12
    default:
855
12
      return diagnostic() << "Internal error: Unhandled operand type: " << type;
856
137M
  }
857
858
137M
  assert(spvOperandIsConcrete(parsed_operand.type));
859
860
137M
  operands->push_back(parsed_operand);
861
862
137M
  const size_t index_after_operand = _.word_index + parsed_operand.num_words;
863
864
  // Avoid buffer overrun for the cases where the operand has more than one
865
  // word, and where it isn't a string.  (Those other cases have already been
866
  // handled earlier.)  For example, this error can occur for a multi-word
867
  // argument to OpConstant, or a multi-word case literal operand for OpSwitch.
868
137M
  if (_.num_words < index_after_operand)
869
47
    return exhaustedInputDiagnostic(inst_offset, opcode, type);
870
871
137M
  if (_.requires_endian_conversion) {
872
    // Copy instruction words.  Translate to native endianness as needed.
873
11.0M
    if (convert_operand_endianness) {
874
11.0M
      const spv_endianness_t endianness = _.endian;
875
11.0M
      std::transform(_.words + _.word_index, _.words + index_after_operand,
876
11.0M
                     std::back_inserter(*words),
877
18.8M
                     [endianness](const uint32_t raw_word) {
878
18.8M
                       return spvFixWord(raw_word, endianness);
879
18.8M
                     });
880
11.0M
    } else {
881
0
      words->insert(words->end(), _.words + _.word_index,
882
0
                    _.words + index_after_operand);
883
0
    }
884
11.0M
  }
885
886
  // Advance past the operand.
887
137M
  _.word_index = index_after_operand;
888
889
137M
  return SPV_SUCCESS;
890
137M
}
891
892
spv_result_t Parser::setNumericTypeInfoForType(
893
1.74M
    spv_parsed_operand_t* parsed_operand, uint32_t type_id) {
894
1.74M
  assert(type_id != 0);
895
1.74M
  auto type_info_iter = _.type_id_to_number_type_info.find(type_id);
896
1.74M
  if (type_info_iter == _.type_id_to_number_type_info.end()) {
897
931
    return diagnostic() << "Type Id " << type_id << " is not a type";
898
931
  }
899
1.74M
  const NumberType& info = type_info_iter->second;
900
1.74M
  if (info.type == SPV_NUMBER_NONE) {
901
    // This is a valid type, but for something other than a scalar number.
902
10
    return diagnostic() << "Type Id " << type_id
903
10
                        << " is not a scalar numeric type";
904
10
  }
905
906
1.74M
  parsed_operand->number_kind = info.type;
907
1.74M
  parsed_operand->number_bit_width = info.bit_width;
908
1.74M
  parsed_operand->fp_encoding = info.encoding;
909
  // Round up the word count.
910
1.74M
  parsed_operand->num_words = static_cast<uint16_t>((info.bit_width + 31) / 32);
911
1.74M
  return SPV_SUCCESS;
912
1.74M
}
913
914
void Parser::recordNumberType(size_t inst_offset,
915
76.4M
                              const spv_parsed_instruction_t* inst) {
916
76.4M
  const spv::Op opcode = static_cast<spv::Op>(inst->opcode);
917
76.4M
  if (spvOpcodeGeneratesType(opcode)) {
918
1.90M
    NumberType info = {SPV_NUMBER_NONE, 0};
919
1.90M
    if (spv::Op::OpTypeInt == opcode) {
920
189k
      const bool is_signed = peekAt(inst_offset + 3) != 0;
921
189k
      info.type = is_signed ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT;
922
189k
      info.bit_width = peekAt(inst_offset + 2);
923
1.71M
    } else if (spv::Op::OpTypeFloat == opcode) {
924
123k
      info.type = SPV_NUMBER_FLOATING;
925
123k
      info.bit_width = peekAt(inst_offset + 2);
926
123k
      if (inst->num_words >= 4) {
927
4.41k
        const spvtools::OperandDesc* desc = nullptr;
928
4.41k
        spv_result_t status = spvtools::LookupOperand(
929
4.41k
            SPV_OPERAND_TYPE_FPENCODING, peekAt(inst_offset + 3), &desc);
930
4.41k
        if (status == SPV_SUCCESS) {
931
4.41k
          info.encoding = spvFPEncodingFromOperandFPEncoding(
932
4.41k
              static_cast<spv::FPEncoding>(desc->value));
933
4.41k
        } else {
934
0
          info.encoding = SPV_FP_ENCODING_UNKNOWN;
935
0
        }
936
4.41k
      }
937
123k
    }
938
    // The *result* Id of a type generating instruction is the type Id.
939
1.90M
    _.type_id_to_number_type_info[inst->result_id] = info;
940
1.90M
  }
941
76.4M
}
942
943
}  // anonymous namespace
944
945
spv_result_t spvBinaryParse(const spv_const_context context, void* user_data,
946
                            const uint32_t* code, const size_t num_words,
947
                            spv_parsed_header_fn_t parsed_header,
948
                            spv_parsed_instruction_fn_t parsed_instruction,
949
231k
                            spv_diagnostic* diagnostic) {
950
231k
  return spvBinaryParseWithOptions(context, user_data, code, num_words,
951
231k
                                   parsed_header, parsed_instruction,
952
231k
                                   diagnostic, 0);
953
231k
}
954
955
spv_result_t spvBinaryParseWithOptions(
956
    const spv_const_context context, void* user_data, const uint32_t* code,
957
    const size_t num_words, spv_parsed_header_fn_t parsed_header,
958
    spv_parsed_instruction_fn_t parsed_instruction, spv_diagnostic* diagnostic,
959
341k
    uint32_t options) {
960
341k
  spv_context_t hijack_context = *context;
961
341k
  if (diagnostic) {
962
119k
    *diagnostic = nullptr;
963
119k
    spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, diagnostic);
964
119k
  }
965
341k
  Parser parser(&hijack_context, user_data, parsed_header, parsed_instruction);
966
341k
  if (options & SPV_BINARY_TO_TEXT_OPTION_HANDLE_UNKNOWN_OPCODES) {
967
0
    parser.SetHandleUnknownOpcodes(true);
968
0
  }
969
341k
  return parser.parse(code, num_words, diagnostic);
970
341k
}
971
972
// TODO(dneto): This probably belongs in text.cpp since that's the only place
973
// that a spv_binary_t value is created.
974
17.0k
void spvBinaryDestroy(spv_binary binary) {
975
17.0k
  if (binary) {
976
17.0k
    if (binary->code) delete[] binary->code;
977
17.0k
    delete binary;
978
17.0k
  }
979
17.0k
}
980
981
0
size_t spv_strnlen_s(const char* str, size_t strsz) {
982
0
  if (!str) return 0;
983
0
  for (size_t i = 0; i < strsz; i++) {
984
0
    if (!str[i]) return i;
985
0
  }
986
0
  return strsz;
987
0
}