blob: c589b6b41d8f15cad0f342efa3de9fe8e41a673d [file] [log] [blame]
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +01001// Copyright (c) 2015 The Khronos Group Inc.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and/or associated documentation files (the
5// "Materials"), to deal in the Materials without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Materials, and to
8// permit persons to whom the Materials are furnished to do so, subject to
9// the following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Materials.
13//
14// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
15// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
16// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
17// https://www.khronos.org/registry/
18//
19// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
26
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010027#include "binary.h"
David Netofcc7d582015-10-27 15:31:10 -040028
David Neto7bff3eb2015-11-20 14:21:10 -050029#include <algorithm>
David Netofcc7d582015-10-27 15:31:10 -040030#include <cassert>
31#include <cstring>
David Neto0ca6b592015-10-30 16:06:15 -040032#include <limits>
David Netofcc7d582015-10-27 15:31:10 -040033#include <unordered_map>
34
David Netofcc7d582015-10-27 15:31:10 -040035#include "assembly_grammar.h"
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010036#include "diagnostic.h"
David Netodb901b62015-10-27 16:14:40 -040037#include "endian.h"
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010038#include "ext_inst.h"
Lei Zhang923f6c12015-11-11 12:45:23 -050039#include "libspirv/libspirv.h"
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010040#include "opcode.h"
41#include "operand.h"
Lei Zhangaa056cd2015-11-11 14:24:04 -050042#include "spirv_constant.h"
Andrew Woloszyn157e41b2015-10-16 15:11:00 -040043
Andrew Woloszyn55ecc2e2015-11-11 11:05:07 -050044spv_result_t spvBinaryHeaderGet(const spv_const_binary binary,
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010045 const spv_endianness_t endian,
Lei Zhang1a0334e2015-11-02 09:41:20 -050046 spv_header_t* pHeader) {
David Neto0ca6b592015-10-30 16:06:15 -040047 if (!binary->code) return SPV_ERROR_INVALID_BINARY;
48 if (binary->wordCount < SPV_INDEX_INSTRUCTION)
49 return SPV_ERROR_INVALID_BINARY;
Lei Zhang40056702015-09-11 14:31:27 -040050 if (!pHeader) return SPV_ERROR_INVALID_POINTER;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010051
52 // TODO: Validation checking?
53 pHeader->magic = spvFixWord(binary->code[SPV_INDEX_MAGIC_NUMBER], endian);
54 pHeader->version = spvFixWord(binary->code[SPV_INDEX_VERSION_NUMBER], endian);
55 pHeader->generator =
56 spvFixWord(binary->code[SPV_INDEX_GENERATOR_NUMBER], endian);
57 pHeader->bound = spvFixWord(binary->code[SPV_INDEX_BOUND], endian);
58 pHeader->schema = spvFixWord(binary->code[SPV_INDEX_SCHEMA], endian);
59 pHeader->instructions = &binary->code[SPV_INDEX_INSTRUCTION];
60
61 return SPV_SUCCESS;
62}
63
David Neto78c3b432015-08-27 13:03:52 -040064// TODO(dneto): This API is not powerful enough in the case that the
65// number and type of operands are not known until partway through parsing
66// the operation. This happens when enum operands might have different number
67// of operands, or with extended instructions.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010068spv_operand_type_t spvBinaryOperandInfo(const uint32_t word,
69 const uint16_t operandIndex,
70 const spv_opcode_desc opcodeEntry,
71 const spv_operand_table operandTable,
Lei Zhang1a0334e2015-11-02 09:41:20 -050072 spv_operand_desc* pOperandEntry) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010073 spv_operand_type_t type;
David Neto78c3b432015-08-27 13:03:52 -040074 if (operandIndex < opcodeEntry->numTypes) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010075 // NOTE: Do operand table lookup to set operandEntry if successful
76 uint16_t index = operandIndex - 1;
77 type = opcodeEntry->operandTypes[index];
78 spv_operand_desc entry = nullptr;
79 if (!spvOperandTableValueLookup(operandTable, type, word, &entry)) {
80 if (SPV_OPERAND_TYPE_NONE != entry->operandTypes[0]) {
81 *pOperandEntry = entry;
82 }
83 }
84 } else if (*pOperandEntry) {
85 // NOTE: Use specified operand entry operand type for this word
David Neto78c3b432015-08-27 13:03:52 -040086 uint16_t index = operandIndex - opcodeEntry->numTypes;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010087 type = (*pOperandEntry)->operandTypes[index];
Lei Zhangb36e7042015-10-28 13:40:52 -040088 } else if (SpvOpSwitch == opcodeEntry->opcode) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010089 // NOTE: OpSwitch is a special case which expects a list of paired extra
90 // operands
91 assert(0 &&
92 "This case is previously untested, remove this assert and ensure it "
93 "is behaving correctly!");
David Neto78c3b432015-08-27 13:03:52 -040094 uint16_t lastIndex = opcodeEntry->numTypes - 1;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010095 uint16_t index = lastIndex + ((operandIndex - lastIndex) % 2);
96 type = opcodeEntry->operandTypes[index];
97 } else {
98 // NOTE: Default to last operand type in opcode entry
David Neto78c3b432015-08-27 13:03:52 -040099 uint16_t index = opcodeEntry->numTypes - 1;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100100 type = opcodeEntry->operandTypes[index];
101 }
102 return type;
103}
104
David Neto0ca6b592015-10-30 16:06:15 -0400105namespace {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100106
David Neto0ca6b592015-10-30 16:06:15 -0400107// A SPIR-V binary parser. A parser instance communicates detailed parse
108// results via callbacks.
109class Parser {
110 public:
111 // The user_data value is provided to the callbacks as context.
Lei Zhang972788b2015-11-12 13:48:30 -0500112 Parser(const spv_const_context context, void* user_data,
113 spv_parsed_header_fn_t parsed_header_fn,
David Neto0ca6b592015-10-30 16:06:15 -0400114 spv_parsed_instruction_fn_t parsed_instruction_fn)
Lei Zhang972788b2015-11-12 13:48:30 -0500115 : grammar_(context),
116 user_data_(user_data),
David Neto0ca6b592015-10-30 16:06:15 -0400117 parsed_header_fn_(parsed_header_fn),
118 parsed_instruction_fn_(parsed_instruction_fn) {}
119
120 // Parses the specified binary SPIR-V module, issuing callbacks on a parsed
121 // header and for each parsed instruction. Returns SPV_SUCCESS on success.
122 // Otherwise returns an error code and issues a diagnostic.
123 spv_result_t parse(const uint32_t* words, size_t num_words,
124 spv_diagnostic* diagnostic);
125
126 private:
127 // All remaining methods work on the current module parse state.
128
129 // Like the parse method, but works on the current module parse state.
130 spv_result_t parseModule();
131
132 // Parses an instruction at the current position of the binary. Assumes
133 // the header has been parsed, the endian has been set, and the word index is
134 // still in range. Advances the parsing position past the instruction, and
135 // updates other parsing state for the current module.
136 // On success, returns SPV_SUCCESS and issues the parsed-instruction callback.
137 // On failure, returns an error code and issues a diagnostic.
138 spv_result_t parseInstruction();
139
David Neto7bff3eb2015-11-20 14:21:10 -0500140 // Parses an instruction operand with the given type, for an instruction
141 // starting at inst_offset words into the SPIR-V binary.
142 // If the SPIR-V binary is the same endianness as the host, then the
143 // endian_converted_inst_words parameter is ignored. Otherwise, this method
144 // appends the words for this operand, converted to host native endianness,
145 // to the end of endian_converted_inst_words. This method also updates the
146 // expected_operands parameter, and the scalar members of the inst parameter.
147 // On success, returns SPV_SUCCESS, advances past the operand, and pushes a
148 // new entry on to the operands vector. Otherwise returns an error code and
149 // issues a diagnostic.
150 spv_result_t parseOperand(size_t inst_offset, spv_parsed_instruction_t* inst,
David Neto0ca6b592015-10-30 16:06:15 -0400151 const spv_operand_type_t type,
David Neto7bff3eb2015-11-20 14:21:10 -0500152 std::vector<uint32_t>* endian_converted_inst_words,
David Neto0ca6b592015-10-30 16:06:15 -0400153 std::vector<spv_parsed_operand_t>* operands,
154 spv_operand_pattern_t* expected_operands);
155
156 // Records the numeric type for an operand according to the type information
157 // associated with the given non-zero type Id. This can fail if the type Id
158 // is not a type Id, or if the type Id does not reference a scalar numeric
159 // type. On success, return SPV_SUCCESS and populates the num_words,
160 // number_kind, and number_bit_width fields of parsed_operand.
161 spv_result_t setNumericTypeInfoForType(spv_parsed_operand_t* parsed_operand,
162 uint32_t type_id);
163
David Neto7bff3eb2015-11-20 14:21:10 -0500164 // Records the number type for an instruction at the given offset, if that
165 // instruction generates a type. For types that aren't scalar numbers,
166 // record something with number kind SPV_NUMBER_NONE.
167 void recordNumberType(size_t inst_offset,
168 const spv_parsed_instruction_t* inst);
David Neto0ca6b592015-10-30 16:06:15 -0400169
170 // Returns a diagnostic stream object initialized with current position in
171 // the input stream, and for the given error code. Any data written to the
172 // returned object will be propagated to the current parse's diagnostic
173 // object.
David Neto01656362015-11-20 10:44:41 -0500174 libspirv::DiagnosticStream diagnostic(spv_result_t error) {
David Neto7bff3eb2015-11-20 14:21:10 -0500175 return libspirv::DiagnosticStream({0, 0, _.word_index}, _.diagnostic,
176 error);
David Neto0ca6b592015-10-30 16:06:15 -0400177 }
178
179 // Returns a diagnostic stream object with the default parse error code.
David Neto01656362015-11-20 10:44:41 -0500180 libspirv::DiagnosticStream diagnostic() {
David Neto0ca6b592015-10-30 16:06:15 -0400181 // The default failure for parsing is invalid binary.
182 return diagnostic(SPV_ERROR_INVALID_BINARY);
183 }
184
185 // Returns the endian-corrected word at the current position.
186 uint32_t peek() const { return peekAt(_.word_index); }
187
188 // Returns the endian-corrected word at the given position.
189 uint32_t peekAt(size_t index) const {
190 assert(index < _.num_words);
191 return spvFixWord(_.words[index], _.endian);
192 }
193
194 // Data members
195
196 const libspirv::AssemblyGrammar grammar_; // SPIR-V syntax utility.
197 void* const user_data_; // Context for the callbacks
198 const spv_parsed_header_fn_t parsed_header_fn_; // Parsed header callback
199 const spv_parsed_instruction_fn_t
200 parsed_instruction_fn_; // Parsed instruction callback
201
202 // Describes the format of a typed literal number.
203 struct NumberType {
204 spv_number_kind_t type;
205 uint32_t bit_width;
206 };
207
208 // The state used to parse a single SPIR-V binary module.
209 struct State {
210 State(const uint32_t* words_arg, size_t num_words_arg,
211 spv_diagnostic* diagnostic_arg)
212 : words(words_arg),
213 num_words(num_words_arg),
214 diagnostic(diagnostic_arg),
215 word_index(0),
216 endian() {}
217 State() : State(0, 0, nullptr) {}
218 const uint32_t* words; // Words in the binary SPIR-V module.
219 size_t num_words; // Number of words in the module.
220 spv_diagnostic* diagnostic; // Where diagnostics go.
221 size_t word_index; // The current position in words.
222 spv_endianness_t endian; // The endianness of the binary.
David Neto7bff3eb2015-11-20 14:21:10 -0500223 // Is the SPIR-V binary in a different endiannes from the host native
224 // endianness?
225 bool requires_endian_conversion;
David Neto0ca6b592015-10-30 16:06:15 -0400226
227 // Maps a result ID to its type ID. By convention:
228 // - a result ID that is a type definition maps to itself.
229 // - a result ID without a type maps to 0. (E.g. for OpLabel)
230 std::unordered_map<uint32_t, uint32_t> id_to_type_id;
231 // Maps a type ID to its number type description.
232 std::unordered_map<uint32_t, NumberType> type_id_to_number_type_info;
233 // Maps an ExtInstImport id to the extended instruction type.
234 std::unordered_map<uint32_t, spv_ext_inst_type_t>
235 import_id_to_ext_inst_type;
236 } _;
237};
238
239spv_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
251spv_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.
Andrew Woloszyn55ecc2e2015-11-11 11:05:07 -0500259 spv_const_binary_t binary{_.words, _.num_words};
David Neto0ca6b592015-10-30 16:06:15 -0400260 if (spvBinaryEndianness(&binary, &_.endian)) {
261 return diagnostic() << "Invalid SPIR-V magic number '" << std::hex
262 << _.words[0] << "'.";
263 }
David Neto7bff3eb2015-11-20 14:21:10 -0500264 _.requires_endian_conversion = !spvIsHostEndian(_.endian);
David Neto0ca6b592015-10-30 16:06:15 -0400265
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
293spv_result_t Parser::parseInstruction() {
294 // The zero values for all members except for opcode are the
295 // correct initial values.
296 spv_parsed_instruction_t inst = {};
David Neto7bff3eb2015-11-20 14:21:10 -0500297
298 const uint32_t first_word = peek();
299
300 // TODO(dneto): If it's too expensive to construct the following "words"
301 // and "operands" vectors for each instruction, each instruction, then make
302 // them class data members instead, and clear them here.
303
304 // If the module's endianness is different from the host native endianness,
305 // then converted_words contains the the endian-translated words in the
306 // instruction.
307 std::vector<uint32_t> endian_converted_words = {first_word};
308 if (_.requires_endian_conversion) {
309 // Most instructions have fewer than 25 words.
310 endian_converted_words.reserve(25);
311 }
David Neto0ca6b592015-10-30 16:06:15 -0400312
313 // After a successful parse of the instruction, the inst.operands member
314 // will point to this vector's storage.
David Neto0ca6b592015-10-30 16:06:15 -0400315 std::vector<spv_parsed_operand_t> operands;
316 // Most instructions have fewer than 25 logical operands.
317 operands.reserve(25);
318
319 assert(_.word_index < _.num_words);
320 // Decompose and check the first word.
321 uint16_t inst_word_count = 0;
David Neto7bff3eb2015-11-20 14:21:10 -0500322 spvOpcodeSplit(first_word, &inst_word_count, &inst.opcode);
David Neto0ca6b592015-10-30 16:06:15 -0400323 if (inst_word_count < 1) {
324 return diagnostic() << "Invalid instruction word count: "
325 << inst_word_count;
326 }
327 spv_opcode_desc opcode_desc;
328 if (grammar_.lookupOpcode(inst.opcode, &opcode_desc))
329 return diagnostic() << "Invalid opcode: " << int(inst.opcode);
330
David Neto7bff3eb2015-11-20 14:21:10 -0500331 // Advance past the opcode word. But remember the of the start
332 // of the instruction.
333 const size_t inst_offset = _.word_index;
David Neto0ca6b592015-10-30 16:06:15 -0400334 _.word_index++;
335
336 // Maintains the ordered list of expected operand types.
337 // For many instructions we only need the {numTypes, operandTypes}
338 // entries in opcode_desc. However, sometimes we need to modify
339 // the list as we parse the operands. This occurs when an operand
340 // has its own logical operands (such as the LocalSize operand for
341 // ExecutionMode), or for extended instructions that may have their
342 // own operands depending on the selected extended instruction.
343 spv_operand_pattern_t expected_operands(
344 opcode_desc->operandTypes,
345 opcode_desc->operandTypes + opcode_desc->numTypes);
346
David Neto7bff3eb2015-11-20 14:21:10 -0500347 while (_.word_index < inst_offset + inst_word_count) {
348 const uint16_t inst_word_index = uint16_t(_.word_index - inst_offset);
David Neto0ca6b592015-10-30 16:06:15 -0400349 if (expected_operands.empty()) {
350 return diagnostic() << "Invalid instruction Op" << opcode_desc->name
David Neto7bff3eb2015-11-20 14:21:10 -0500351 << " starting at word " << inst_offset
David Neto0ca6b592015-10-30 16:06:15 -0400352 << ": expected no more operands after "
353 << inst_word_index
354 << " words, but stated word count is "
355 << inst_word_count << ".";
356 }
357
358 spv_operand_type_t type = spvTakeFirstMatchableOperand(&expected_operands);
359
David Neto7bff3eb2015-11-20 14:21:10 -0500360 if (auto error =
361 parseOperand(inst_offset, &inst, type, &endian_converted_words,
362 &operands, &expected_operands))
David Neto0ca6b592015-10-30 16:06:15 -0400363 return error;
364 }
365
366 if (!expected_operands.empty() &&
367 !spvOperandIsOptional(expected_operands.front())) {
368 return diagnostic() << "End of input reached while decoding Op"
369 << opcode_desc->name << " starting at word "
David Neto7bff3eb2015-11-20 14:21:10 -0500370 << inst_offset << ": expected more operands after "
David Neto0ca6b592015-10-30 16:06:15 -0400371 << inst_word_count << " words.";
372 }
373
David Neto7bff3eb2015-11-20 14:21:10 -0500374 if ((inst_offset + inst_word_count) != _.word_index) {
David Neto0ca6b592015-10-30 16:06:15 -0400375 return diagnostic() << "Invalid word count: Instruction starting at word "
David Neto7bff3eb2015-11-20 14:21:10 -0500376 << inst_offset << " says it has " << inst_word_count
377 << " words, but found " << _.word_index - inst_offset
David Neto0ca6b592015-10-30 16:06:15 -0400378 << " words instead.";
379 }
David Neto7bff3eb2015-11-20 14:21:10 -0500380 assert(inst_word_count == words.size());
David Neto0ca6b592015-10-30 16:06:15 -0400381
David Neto7bff3eb2015-11-20 14:21:10 -0500382 recordNumberType(inst_offset, &inst);
David Neto0ca6b592015-10-30 16:06:15 -0400383
David Neto7bff3eb2015-11-20 14:21:10 -0500384 if (_.requires_endian_conversion) {
385 // We must wait until here to set this pointer, because the vector might
386 // have been be resized while we accumulated its elements.
387 inst.words = endian_converted_words.data();
388 } else {
389 // If no conversion is required, then just point to the underlying binary.
390 // This saves time and space.
391 inst.words = _.words + inst_offset;
392 }
393 inst.num_words = inst_word_count;
394
395 // We must wait until here to set this pointer, because the vector might
396 // have been be resized while we accumulated its elements.
David Neto0ca6b592015-10-30 16:06:15 -0400397 inst.operands = operands.data();
Andrew Woloszyn3a4bc7e2015-11-19 09:22:53 -0500398 inst.num_operands = uint16_t(operands.size());
David Neto0ca6b592015-10-30 16:06:15 -0400399
400 // Issue the callback. The callee should know that all the storage in inst
401 // is transient, and will disappear immediately afterward.
402 if (parsed_instruction_fn_) {
403 if (auto error = parsed_instruction_fn_(user_data_, &inst)) return error;
404 }
405
406 return SPV_SUCCESS;
407}
408
David Neto7bff3eb2015-11-20 14:21:10 -0500409spv_result_t Parser::parseOperand(size_t inst_offset,
410 spv_parsed_instruction_t* inst,
David Neto0ca6b592015-10-30 16:06:15 -0400411 const spv_operand_type_t type,
David Neto7bff3eb2015-11-20 14:21:10 -0500412 std::vector<uint32_t>* words,
David Neto0ca6b592015-10-30 16:06:15 -0400413 std::vector<spv_parsed_operand_t>* operands,
414 spv_operand_pattern_t* expected_operands) {
415 // We'll fill in this result as we go along.
416 spv_parsed_operand_t parsed_operand;
David Neto7bff3eb2015-11-20 14:21:10 -0500417 parsed_operand.offset = uint16_t(_.word_index - inst_offset);
David Neto0ca6b592015-10-30 16:06:15 -0400418 // Most operands occupy one word. This might be be adjusted later.
419 parsed_operand.num_words = 1;
420 // The type argument is the one used by the grammar to parse the instruction.
421 // But it can exposes internal parser details such as whether an operand is
422 // optional or actually represents a variable-length sequence of operands.
423 // The resulting type should be adjusted to avoid those internal details.
424 // In most cases, the resulting operand type is the same as the grammar type.
425 parsed_operand.type = type;
426
427 // Assume non-numeric values. This will be updated for literal numbers.
428 parsed_operand.number_kind = SPV_NUMBER_NONE;
429 parsed_operand.number_bit_width = 0;
430
431 const uint32_t word = peek();
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100432
David Neto7bff3eb2015-11-20 14:21:10 -0500433 // 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
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100437 switch (type) {
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400438 case SPV_OPERAND_TYPE_TYPE_ID:
David Neto0ca6b592015-10-30 16:06:15 -0400439 if (!word) return diagnostic() << "Error: Type Id is 0";
440 inst->type_id = word;
441 break;
442
443 case SPV_OPERAND_TYPE_RESULT_ID:
444 if (!word) return diagnostic() << "Error: Result Id is 0";
445 inst->result_id = word;
446 // Save the result ID to type ID mapping.
447 // In the grammar, type ID always appears before result ID.
448 if (_.id_to_type_id.find(inst->result_id) != _.id_to_type_id.end())
449 return diagnostic() << "Id " << inst->result_id
450 << " is defined more than once";
451 // Record it.
452 // A regular value maps to its type. Some instructions (e.g. OpLabel)
453 // have no type Id, and will map to 0. The result Id for a
454 // type-generating instruction (e.g. OpTypeInt) maps to itself.
455 _.id_to_type_id[inst->result_id] = spvOpcodeGeneratesType(inst->opcode)
456 ? inst->result_id
457 : inst->type_id;
458 break;
459
460 case SPV_OPERAND_TYPE_ID:
David Neto78c3b432015-08-27 13:03:52 -0400461 case SPV_OPERAND_TYPE_OPTIONAL_ID:
David Neto0ca6b592015-10-30 16:06:15 -0400462 if (!word) return diagnostic() << "Id is 0";
463 parsed_operand.type = SPV_OPERAND_TYPE_ID;
464
465 if (inst->opcode == SpvOpExtInst && parsed_operand.offset == 3) {
466 // The current word is the extended instruction set Id.
467 // Set the extended instruction set type for the current instruction.
468 auto ext_inst_type_iter = _.import_id_to_ext_inst_type.find(word);
469 if (ext_inst_type_iter == _.import_id_to_ext_inst_type.end()) {
470 return diagnostic()
471 << "OpExtInst set Id " << word
472 << " does not reference an OpExtInstImport result Id";
473 }
474 inst->ext_inst_type = ext_inst_type_iter->second;
475 }
476 break;
477
David Neto64a9be92015-11-18 15:48:32 -0500478 case SPV_OPERAND_TYPE_SCOPE_ID:
479 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
David Neto0ca6b592015-10-30 16:06:15 -0400480 if (!word) return diagnostic() << spvOperandTypeStr(type) << " Id is 0";
481 break;
482
David Neto445ce442015-10-15 15:22:06 -0400483 case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
David Neto0ca6b592015-10-30 16:06:15 -0400484 assert(SpvOpExtInst == inst->opcode);
485 assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE);
486 spv_ext_inst_desc ext_inst;
487 if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst))
488 return diagnostic() << "Invalid extended instruction number: " << word;
489 spvPrependOperandTypes(ext_inst->operandTypes, expected_operands);
David Neto445ce442015-10-15 15:22:06 -0400490 } break;
David Neto0ca6b592015-10-30 16:06:15 -0400491
David Neto21196942015-11-11 02:45:45 -0500492 case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: {
493 assert(SpvOpSpecConstantOp == inst->opcode);
494 if (grammar_.lookupSpecConstantOpcode(SpvOp(word))) {
495 return diagnostic() << "Invalid " << spvOperandTypeStr(type) << ": "
496 << word;
497 }
498 spv_opcode_desc opcode_entry = nullptr;
499 if (grammar_.lookupOpcode(SpvOp(word), &opcode_entry)) {
500 return diagnostic(SPV_ERROR_INTERNAL)
501 << "OpSpecConstant opcode table out of sync";
502 }
503 // OpSpecConstant opcodes must have a type and result. We've already
504 // processed them, so skip them when preparing to parse the other
505 // operants for the opcode.
506 assert(opcode_entry->hasType);
507 assert(opcode_entry->hasResult);
508 assert(opcode_entry->numTypes >= 2);
509 spvPrependOperandTypes(opcode_entry->operandTypes + 2, expected_operands);
510 } break;
511
David Neto445ce442015-10-15 15:22:06 -0400512 case SPV_OPERAND_TYPE_LITERAL_INTEGER:
Lei Zhang6483bd72015-10-14 17:02:39 -0400513 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
David Neto201caf72015-11-04 17:38:17 -0500514 // These are regular single-word literal integer operands.
515 // Post-parsing validation should check the range of the parsed value.
David Neto0ca6b592015-10-30 16:06:15 -0400516 parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_INTEGER;
David Neto201caf72015-11-04 17:38:17 -0500517 // It turns out they are always unsigned integers!
518 parsed_operand.number_kind = SPV_NUMBER_UNSIGNED_INT;
519 parsed_operand.number_bit_width = 32;
520 break;
521
522 case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER:
523 case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
Lei Zhangaa3cd5a2015-11-10 14:29:35 -0500524 parsed_operand.type = SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER;
David Neto0ca6b592015-10-30 16:06:15 -0400525 if (inst->opcode == SpvOpSwitch) {
526 // The literal operands have the same type as the value
527 // referenced by the selector Id.
David Neto7bff3eb2015-11-20 14:21:10 -0500528 const uint32_t selector_id = peekAt(inst_offset + 1);
David Neto0ca6b592015-10-30 16:06:15 -0400529 auto type_id_iter = _.id_to_type_id.find(selector_id);
530 if (type_id_iter == _.id_to_type_id.end()) {
531 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
532 << " has no type";
533 }
534 uint32_t type_id = type_id_iter->second;
535
536 if (selector_id == type_id) {
537 // Recall that by convention, a result ID that is a type definition
538 // maps to itself.
539 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
540 << " is a type, not a value";
541 }
542 if (auto error = setNumericTypeInfoForType(&parsed_operand, type_id))
543 return error;
544 if (parsed_operand.number_kind != SPV_NUMBER_UNSIGNED_INT &&
545 parsed_operand.number_kind != SPV_NUMBER_SIGNED_INT) {
546 return diagnostic() << "Invalid OpSwitch: selector id " << selector_id
547 << " is not a scalar integer";
548 }
Lei Zhangb41d1502015-09-14 15:22:23 -0400549 } else {
David Neto201caf72015-11-04 17:38:17 -0500550 assert(inst->opcode == SpvOpConstant ||
551 inst->opcode == SpvOpSpecConstant);
552 // The literal number type is determined by the type Id for the
553 // constant.
554 assert(inst->type_id);
555 if (auto error =
556 setNumericTypeInfoForType(&parsed_operand, inst->type_id))
557 return error;
Lei Zhangb41d1502015-09-14 15:22:23 -0400558 }
David Neto0ca6b592015-10-30 16:06:15 -0400559 break;
560
David Neto78c3b432015-08-27 13:03:52 -0400561 case SPV_OPERAND_TYPE_LITERAL_STRING:
562 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: {
David Neto7bff3eb2015-11-20 14:21:10 -0500563 convert_operand_endianness = false;
David Neto0ca6b592015-10-30 16:06:15 -0400564 const char* string =
565 reinterpret_cast<const char*>(_.words + _.word_index);
566 size_t string_num_words = (strlen(string) / 4) + 1; // Account for null.
567 // Make sure we can record the word count without overflow.
568 // We still might have a string that's 64K words, but would still
569 // make the instruction too long because of earlier operands.
570 // That will be caught later at the end of the instruciton.
571 if (string_num_words > std::numeric_limits<uint16_t>::max()) {
572 return diagnostic() << "Literal string is longer than "
573 << std::numeric_limits<uint16_t>::max()
574 << " words: " << string_num_words << " words long";
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100575 }
Andrew Woloszyn3a4bc7e2015-11-19 09:22:53 -0500576 parsed_operand.num_words = uint16_t(string_num_words);
David Neto0ca6b592015-10-30 16:06:15 -0400577 parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_STRING;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100578
David Neto0ca6b592015-10-30 16:06:15 -0400579 if (SpvOpExtInstImport == inst->opcode) {
580 // Record the extended instruction type for the ID for this import.
581 // There is only one string literal argument to OpExtInstImport,
582 // so it's sufficient to guard this just on the opcode.
583 const spv_ext_inst_type_t ext_inst_type =
584 spvExtInstImportTypeGet(string);
585 if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) {
586 return diagnostic() << "Invalid extended instruction import '"
587 << string << "'";
Andrew Woloszyne59e6b72015-10-14 14:18:43 -0400588 }
David Neto0ca6b592015-10-30 16:06:15 -0400589 // We must have parsed a valid result ID. It's a condition
590 // of the grammar, and we only accept non-zero result Ids.
591 assert(inst->result_id);
592 _.import_id_to_ext_inst_type[inst->result_id] = ext_inst_type;
Andrew Woloszyne59e6b72015-10-14 14:18:43 -0400593 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100594 } break;
David Neto0ca6b592015-10-30 16:06:15 -0400595
596 case SPV_OPERAND_TYPE_OPTIONAL_EXECUTION_MODE:
597 parsed_operand.type = SPV_OPERAND_TYPE_EXECUTION_MODE;
598 // Fall through
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100599 case SPV_OPERAND_TYPE_CAPABILITY:
600 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
601 case SPV_OPERAND_TYPE_EXECUTION_MODEL:
602 case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
603 case SPV_OPERAND_TYPE_MEMORY_MODEL:
604 case SPV_OPERAND_TYPE_EXECUTION_MODE:
605 case SPV_OPERAND_TYPE_STORAGE_CLASS:
606 case SPV_OPERAND_TYPE_DIMENSIONALITY:
607 case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
608 case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100609 case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
610 case SPV_OPERAND_TYPE_LINKAGE_TYPE:
611 case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
612 case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
613 case SPV_OPERAND_TYPE_DECORATION:
614 case SPV_OPERAND_TYPE_BUILT_IN:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100615 case SPV_OPERAND_TYPE_GROUP_OPERATION:
616 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
David Neto47994822015-08-27 13:11:01 -0400617 case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: {
David Neto0ca6b592015-10-30 16:06:15 -0400618 // A single word that is a plain enum value.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100619 spv_operand_desc entry;
David Neto0ca6b592015-10-30 16:06:15 -0400620 if (grammar_.lookupOperand(type, word, &entry)) {
David Neto201caf72015-11-04 17:38:17 -0500621 return diagnostic() << "Invalid "
622 << spvOperandTypeStr(parsed_operand.type)
David Neto0ca6b592015-10-30 16:06:15 -0400623 << " operand: " << word;
Lei Zhang40056702015-09-11 14:31:27 -0400624 }
David Neto78c3b432015-08-27 13:03:52 -0400625 // Prepare to accept operands to this operand, if needed.
David Neto0ca6b592015-10-30 16:06:15 -0400626 spvPrependOperandTypes(entry->operandTypes, expected_operands);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100627 } break;
David Neto0ca6b592015-10-30 16:06:15 -0400628
David Neto619db262015-09-25 12:43:37 -0400629 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
630 case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
631 case SPV_OPERAND_TYPE_LOOP_CONTROL:
632 case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
633 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
634 case SPV_OPERAND_TYPE_SELECTION_CONTROL: {
635 // This operand is a mask.
David Neto201caf72015-11-04 17:38:17 -0500636
637 // Map an optional operand type to its corresponding concrete type.
638 if (type == SPV_OPERAND_TYPE_OPTIONAL_IMAGE)
639 parsed_operand.type = SPV_OPERAND_TYPE_IMAGE;
640 else if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS)
641 parsed_operand.type = SPV_OPERAND_TYPE_MEMORY_ACCESS;
642
David Neto0ca6b592015-10-30 16:06:15 -0400643 // Check validity of set mask bits. Also prepare for operands for those
644 // masks if they have any. To get operand order correct, scan from
645 // MSB to LSB since we can only prepend operands to a pattern.
646 // The only case in the grammar where you have more than one mask bit
647 // having an operand is for image operands. See SPIR-V 3.14 Image
648 // Operands.
649 uint32_t remaining_word = word;
650 for (uint32_t mask = (1u << 31); remaining_word; mask >>= 1) {
David Neto619db262015-09-25 12:43:37 -0400651 if (remaining_word & mask) {
David Neto619db262015-09-25 12:43:37 -0400652 spv_operand_desc entry;
David Neto0ca6b592015-10-30 16:06:15 -0400653 if (grammar_.lookupOperand(type, mask, &entry)) {
David Neto201caf72015-11-04 17:38:17 -0500654 return diagnostic()
655 << "Invalid " << spvOperandTypeStr(parsed_operand.type)
656 << " operand: " << word << " has invalid mask component "
657 << mask;
David Neto619db262015-09-25 12:43:37 -0400658 }
David Neto0ca6b592015-10-30 16:06:15 -0400659 remaining_word ^= mask;
660 spvPrependOperandTypes(entry->operandTypes, expected_operands);
David Neto619db262015-09-25 12:43:37 -0400661 }
662 }
David Neto0ca6b592015-10-30 16:06:15 -0400663 if (word == 0) {
664 // An all-zeroes mask *might* also be valid.
David Neto619db262015-09-25 12:43:37 -0400665 spv_operand_desc entry;
David Neto0ca6b592015-10-30 16:06:15 -0400666 if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) {
David Neto619db262015-09-25 12:43:37 -0400667 // Prepare for its operands, if any.
David Neto0ca6b592015-10-30 16:06:15 -0400668 spvPrependOperandTypes(entry->operandTypes, expected_operands);
David Neto619db262015-09-25 12:43:37 -0400669 }
670 }
David Neto619db262015-09-25 12:43:37 -0400671 } break;
David Neto0ca6b592015-10-30 16:06:15 -0400672 default:
673 return diagnostic() << "Internal error: Unhandled operand type: " << type;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100674 }
675
David Neto0ca6b592015-10-30 16:06:15 -0400676 assert(int(SPV_OPERAND_TYPE_FIRST_CONCRETE_TYPE) <= int(parsed_operand.type));
677 assert(int(SPV_OPERAND_TYPE_LAST_CONCRETE_TYPE) >= int(parsed_operand.type));
678
679 operands->push_back(parsed_operand);
680
David Neto7bff3eb2015-11-20 14:21:10 -0500681 const size_t index_after_operand = _.word_index + parsed_operand.num_words;
682 if (_.requires_endian_conversion) {
683 // Copy instruction words. Translate to native endianness as needed.
684 if (convert_operand_endianness) {
685 const spv_endianness_t endianness = _.endian;
686 std::transform(_.words + _.word_index, _.words + index_after_operand,
687 words->end(), [endianness](const uint32_t word) {
688 return spvFixWord(word, endianness);
689 });
690 } else {
691 words->insert(words->end(), _.words + _.word_index,
692 _.words + index_after_operand);
693 }
694 }
695
696 // Advance past the operand.
697 _.word_index = index_after_operand;
David Neto0ca6b592015-10-30 16:06:15 -0400698
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100699 return SPV_SUCCESS;
700}
701
David Neto0ca6b592015-10-30 16:06:15 -0400702spv_result_t Parser::setNumericTypeInfoForType(
703 spv_parsed_operand_t* parsed_operand, uint32_t type_id) {
David Neto201caf72015-11-04 17:38:17 -0500704 assert(type_id != 0);
David Neto0ca6b592015-10-30 16:06:15 -0400705 auto type_info_iter = _.type_id_to_number_type_info.find(type_id);
706 if (type_info_iter == _.type_id_to_number_type_info.end()) {
707 return diagnostic() << "Type Id " << type_id << " is not a type";
708 }
709 const NumberType& info = type_info_iter->second;
710 if (info.type == SPV_NUMBER_NONE) {
711 // This is a valid type, but for something other than a scalar number.
712 return diagnostic() << "Type Id " << type_id
713 << " is not a scalar numeric type";
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400714 }
715
David Neto0ca6b592015-10-30 16:06:15 -0400716 parsed_operand->number_kind = info.type;
717 parsed_operand->number_bit_width = info.bit_width;
David Neto229b90f2015-11-06 11:23:57 -0500718 parsed_operand->num_words = (info.bit_width + 31) / 32; // Round up
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400719 return SPV_SUCCESS;
720}
721
David Neto7bff3eb2015-11-20 14:21:10 -0500722void Parser::recordNumberType(size_t inst_offset,
723 const spv_parsed_instruction_t* inst) {
David Neto0ca6b592015-10-30 16:06:15 -0400724 if (spvOpcodeGeneratesType(inst->opcode)) {
725 NumberType info = {SPV_NUMBER_NONE, 0};
726 if (SpvOpTypeInt == inst->opcode) {
David Neto7bff3eb2015-11-20 14:21:10 -0500727 const bool is_signed = peekAt(inst_offset + 3) != 0;
David Neto0ca6b592015-10-30 16:06:15 -0400728 info.type = is_signed ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT;
David Neto7bff3eb2015-11-20 14:21:10 -0500729 info.bit_width = peekAt(inst_offset + 2);
David Neto0ca6b592015-10-30 16:06:15 -0400730 } else if (SpvOpTypeFloat == inst->opcode) {
731 info.type = SPV_NUMBER_FLOATING;
David Neto7bff3eb2015-11-20 14:21:10 -0500732 info.bit_width = peekAt(inst_offset + 2);
Lei Zhang40056702015-09-11 14:31:27 -0400733 }
David Neto0ca6b592015-10-30 16:06:15 -0400734 // The *result* Id of a type generating instruction is the type Id.
735 _.type_id_to_number_type_info[inst->result_id] = info;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100736 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100737}
738
David Neto0ca6b592015-10-30 16:06:15 -0400739} // anonymous namespace
Andrew Woloszyncfeac482015-09-09 13:04:32 -0400740
Lei Zhang972788b2015-11-12 13:48:30 -0500741spv_result_t spvBinaryParse(const spv_const_context context, void* user_data,
742 const uint32_t* code, const size_t num_words,
David Neto0ca6b592015-10-30 16:06:15 -0400743 spv_parsed_header_fn_t parsed_header,
744 spv_parsed_instruction_fn_t parsed_instruction,
745 spv_diagnostic* diagnostic) {
Lei Zhang972788b2015-11-12 13:48:30 -0500746 Parser parser(context, user_data, parsed_header, parsed_instruction);
David Neto0ca6b592015-10-30 16:06:15 -0400747 return parser.parse(code, num_words, diagnostic);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100748}
749
David Neto0ca6b592015-10-30 16:06:15 -0400750// TODO(dneto): This probably belongs in text.cpp since that's the only place
751// that a spv_binary_t value is created.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100752void spvBinaryDestroy(spv_binary binary) {
Lei Zhang40056702015-09-11 14:31:27 -0400753 if (!binary) return;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100754 if (binary->code) {
755 delete[] binary->code;
756 }
757 delete binary;
758}