blob: 128d27810e82ea24724cb07df554a58bb462045e [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
29#include <cassert>
30#include <cstring>
31#include <sstream>
32#include <unordered_map>
33
34#include <libspirv/libspirv.h>
35#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"
David Netob5dc8fc2015-10-06 16:22:00 -040039#include "instruction.h"
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010040#include "opcode.h"
41#include "operand.h"
Andrew Woloszynccc210b2015-10-16 10:23:42 -040042#include "text_handler.h"
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010043
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010044// Binary API
45
Andrew Woloszyn157e41b2015-10-16 15:11:00 -040046using id_to_type_id_map = std::unordered_map<uint32_t, uint32_t>;
47using type_id_to_type_map = std::unordered_map<uint32_t, libspirv::IdType>;
48
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010049spv_result_t spvBinaryHeaderGet(const spv_binary binary,
50 const spv_endianness_t endian,
51 spv_header_t *pHeader) {
Lei Zhang40056702015-09-11 14:31:27 -040052 if (!binary->code || !binary->wordCount) return SPV_ERROR_INVALID_BINARY;
53 if (!pHeader) return SPV_ERROR_INVALID_POINTER;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010054
55 // TODO: Validation checking?
56 pHeader->magic = spvFixWord(binary->code[SPV_INDEX_MAGIC_NUMBER], endian);
57 pHeader->version = spvFixWord(binary->code[SPV_INDEX_VERSION_NUMBER], endian);
58 pHeader->generator =
59 spvFixWord(binary->code[SPV_INDEX_GENERATOR_NUMBER], endian);
60 pHeader->bound = spvFixWord(binary->code[SPV_INDEX_BOUND], endian);
61 pHeader->schema = spvFixWord(binary->code[SPV_INDEX_SCHEMA], endian);
62 pHeader->instructions = &binary->code[SPV_INDEX_INSTRUCTION];
63
64 return SPV_SUCCESS;
65}
66
David Neto78c3b432015-08-27 13:03:52 -040067// TODO(dneto): This API is not powerful enough in the case that the
68// number and type of operands are not known until partway through parsing
69// the operation. This happens when enum operands might have different number
70// of operands, or with extended instructions.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010071spv_operand_type_t spvBinaryOperandInfo(const uint32_t word,
72 const uint16_t operandIndex,
73 const spv_opcode_desc opcodeEntry,
74 const spv_operand_table operandTable,
75 spv_operand_desc *pOperandEntry) {
76 spv_operand_type_t type;
David Neto78c3b432015-08-27 13:03:52 -040077 if (operandIndex < opcodeEntry->numTypes) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010078 // NOTE: Do operand table lookup to set operandEntry if successful
79 uint16_t index = operandIndex - 1;
80 type = opcodeEntry->operandTypes[index];
81 spv_operand_desc entry = nullptr;
82 if (!spvOperandTableValueLookup(operandTable, type, word, &entry)) {
83 if (SPV_OPERAND_TYPE_NONE != entry->operandTypes[0]) {
84 *pOperandEntry = entry;
85 }
86 }
87 } else if (*pOperandEntry) {
88 // NOTE: Use specified operand entry operand type for this word
David Neto78c3b432015-08-27 13:03:52 -040089 uint16_t index = operandIndex - opcodeEntry->numTypes;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010090 type = (*pOperandEntry)->operandTypes[index];
91 } else if (OpSwitch == opcodeEntry->opcode) {
92 // NOTE: OpSwitch is a special case which expects a list of paired extra
93 // operands
94 assert(0 &&
95 "This case is previously untested, remove this assert and ensure it "
96 "is behaving correctly!");
David Neto78c3b432015-08-27 13:03:52 -040097 uint16_t lastIndex = opcodeEntry->numTypes - 1;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010098 uint16_t index = lastIndex + ((operandIndex - lastIndex) % 2);
99 type = opcodeEntry->operandTypes[index];
100 } else {
101 // NOTE: Default to last operand type in opcode entry
David Neto78c3b432015-08-27 13:03:52 -0400102 uint16_t index = opcodeEntry->numTypes - 1;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100103 type = opcodeEntry->operandTypes[index];
104 }
105 return type;
106}
107
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400108
109/// @brief Translate a binary operand to the textual form
110///
111/// @param[in] opcode of the current instruction
112/// @param[in] type type of the operand to decode
113/// @param[in] words the binary stream of words
114/// @param[in] endian the endianness of the stream
115/// @param[in] options bitfield of spv_binary_to_text_options_t values
116/// @param[in] grammar the AssemblyGrammar to when decoding this operand
117/// @param[in,out] stream the text output stream
118/// @param[in,out] position position in the binary stream
119/// @param[out] pDiag return diagnostic on error
120///
121/// @return result code
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100122spv_result_t spvBinaryDecodeOperand(
123 const Op opcode, const spv_operand_type_t type, const uint32_t *words,
Lei Zhangb41d1502015-09-14 15:22:23 -0400124 uint16_t numWords, const spv_endianness_t endian, const uint32_t options,
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400125 const libspirv::AssemblyGrammar& grammar,
David Neto78c3b432015-08-27 13:03:52 -0400126 spv_operand_pattern_t *pExpectedOperands, spv_ext_inst_type_t *pExtInstType,
127 out_stream &stream, spv_position position, spv_diagnostic *pDiagnostic) {
Lei Zhang40056702015-09-11 14:31:27 -0400128 if (!words || !position) return SPV_ERROR_INVALID_POINTER;
129 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100130
131 bool print = spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options);
132 bool color =
133 print && spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options);
134
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100135 switch (type) {
David Netob14a7272015-09-25 13:56:09 -0400136 case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
David Neto78c3b432015-08-27 13:03:52 -0400137 case SPV_OPERAND_TYPE_ID:
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400138 case SPV_OPERAND_TYPE_TYPE_ID:
David Netob14a7272015-09-25 13:56:09 -0400139 case SPV_OPERAND_TYPE_ID_IN_OPTIONAL_TUPLE:
David Neto78c3b432015-08-27 13:03:52 -0400140 case SPV_OPERAND_TYPE_OPTIONAL_ID:
David Netob14a7272015-09-25 13:56:09 -0400141 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS:
142 case SPV_OPERAND_TYPE_RESULT_ID: {
David Neto78c3b432015-08-27 13:03:52 -0400143 if (color) {
Pyry Haulos26b3b002015-09-09 13:35:53 -0700144 if (type == SPV_OPERAND_TYPE_RESULT_ID) {
145 stream.get() << clr::blue();
146 } else {
147 stream.get() << clr::yellow();
148 }
David Neto78c3b432015-08-27 13:03:52 -0400149 }
Lei Zhang97afd5c2015-09-14 15:26:12 -0400150 stream.get() << "%" << spvFixWord(words[0], endian);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100151 stream.get() << ((color) ? clr::reset() : "");
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100152 position->index++;
153 } break;
David Neto445ce442015-10-15 15:22:06 -0400154 case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100155 if (OpExtInst == opcode) {
156 spv_ext_inst_desc extInst;
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400157 if (grammar.lookupExtInst(*pExtInstType, words[0], &extInst)) {
Lei Zhang40056702015-09-11 14:31:27 -0400158 DIAGNOSTIC << "Invalid extended instruction '" << words[0] << "'.";
159 return SPV_ERROR_INVALID_BINARY;
160 }
David Neto78c3b432015-08-27 13:03:52 -0400161 spvPrependOperandTypes(extInst->operandTypes, pExpectedOperands);
Andrew Woloszyn0d350b52015-08-21 14:23:42 -0400162 stream.get() << (color ? clr::red() : "");
163 stream.get() << extInst->name;
164 stream.get() << (color ? clr::reset() : "");
Lei Zhang41bf0732015-09-14 12:26:15 -0400165 position->index++;
David Neto445ce442015-10-15 15:22:06 -0400166 } else {
167 DIAGNOSTIC << "Internal error: grammar thinks we need an "
168 "extension instruction number for opcode "
169 << opcode;
170 return SPV_ERROR_INTERNAL;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100171 }
David Neto445ce442015-10-15 15:22:06 -0400172 } break;
173 case SPV_OPERAND_TYPE_LITERAL_INTEGER:
Lei Zhangb41d1502015-09-14 15:22:23 -0400174 case SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER:
Lei Zhang6483bd72015-10-14 17:02:39 -0400175 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
176 case SPV_OPERAND_TYPE_LITERAL_INTEGER_IN_OPTIONAL_TUPLE: {
Lei Zhang41bf0732015-09-14 12:26:15 -0400177 // TODO: Need to support multiple word literals
178 stream.get() << (color ? clr::red() : "");
Lei Zhangb41d1502015-09-14 15:22:23 -0400179 if (numWords > 2) {
180 DIAGNOSTIC << "Literal numbers larger than 64-bit not supported yet.";
181 return SPV_UNSUPPORTED;
182 } else if (numWords == 2) {
183 stream.get() << spvFixDoubleWord(words[0], words[1], endian);
184 position->index += 2;
185 } else {
186 stream.get() << spvFixWord(words[0], endian);
187 position->index++;
188 }
Lei Zhang41bf0732015-09-14 12:26:15 -0400189 stream.get() << (color ? clr::reset() : "");
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100190 } break;
David Neto78c3b432015-08-27 13:03:52 -0400191 case SPV_OPERAND_TYPE_LITERAL_STRING:
192 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: {
Lei Zhang97afd5c2015-09-14 15:26:12 -0400193 const char *string = (const char *)words;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100194 uint64_t stringOperandCount = (strlen(string) / 4) + 1;
195
196 // NOTE: Special case for extended instruction import
197 if (OpExtInstImport == opcode) {
198 *pExtInstType = spvExtInstImportTypeGet(string);
Lei Zhang40056702015-09-11 14:31:27 -0400199 if (SPV_EXT_INST_TYPE_NONE == *pExtInstType) {
200 DIAGNOSTIC << "Invalid extended instruction import'" << string
201 << "'.";
202 return SPV_ERROR_INVALID_BINARY;
203 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100204 }
205
206 stream.get() << "\"";
207 stream.get() << (color ? clr::green() : "");
David Neto980b7cb2015-10-15 16:40:04 -0400208 for (const char* p = string; *p; ++p) {
209 if(*p == '"' || *p == '\\') {
Andrew Woloszyne59e6b72015-10-14 14:18:43 -0400210 stream.get() << '\\';
211 }
David Neto980b7cb2015-10-15 16:40:04 -0400212 stream.get() << *p;
Andrew Woloszyne59e6b72015-10-14 14:18:43 -0400213 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100214 stream.get() << (color ? clr::reset() : "");
215 stream.get() << "\"";
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100216 position->index += stringOperandCount;
217 } break;
218 case SPV_OPERAND_TYPE_CAPABILITY:
219 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
220 case SPV_OPERAND_TYPE_EXECUTION_MODEL:
221 case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
222 case SPV_OPERAND_TYPE_MEMORY_MODEL:
223 case SPV_OPERAND_TYPE_EXECUTION_MODE:
David Neto78c3b432015-08-27 13:03:52 -0400224 case SPV_OPERAND_TYPE_OPTIONAL_EXECUTION_MODE:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100225 case SPV_OPERAND_TYPE_STORAGE_CLASS:
226 case SPV_OPERAND_TYPE_DIMENSIONALITY:
227 case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
228 case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100229 case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
230 case SPV_OPERAND_TYPE_LINKAGE_TYPE:
231 case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
232 case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
233 case SPV_OPERAND_TYPE_DECORATION:
234 case SPV_OPERAND_TYPE_BUILT_IN:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100235 case SPV_OPERAND_TYPE_GROUP_OPERATION:
236 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
David Neto47994822015-08-27 13:11:01 -0400237 case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100238 spv_operand_desc entry;
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400239 if (grammar.lookupOperand(type, spvFixWord(words[0], endian), &entry)) {
Lei Zhang40056702015-09-11 14:31:27 -0400240 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " operand '"
Lei Zhang97afd5c2015-09-14 15:26:12 -0400241 << words[0] << "'.";
David Neto619db262015-09-25 12:43:37 -0400242 return SPV_ERROR_INVALID_TEXT; // TODO(dneto): Surely this is invalid binary.
Lei Zhang40056702015-09-11 14:31:27 -0400243 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100244 stream.get() << entry->name;
David Neto78c3b432015-08-27 13:03:52 -0400245 // Prepare to accept operands to this operand, if needed.
246 spvPrependOperandTypes(entry->operandTypes, pExpectedOperands);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100247 position->index++;
248 } break;
David Neto619db262015-09-25 12:43:37 -0400249 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
250 case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
251 case SPV_OPERAND_TYPE_LOOP_CONTROL:
252 case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
253 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
254 case SPV_OPERAND_TYPE_SELECTION_CONTROL: {
255 // This operand is a mask.
256 // Scan it from least significant bit to most significant bit. For each
257 // set bit, emit the name of that bit and prepare to parse its operands,
258 // if any.
259 uint32_t remaining_word = spvFixWord(words[0], endian);
260 uint32_t mask;
261 int num_emitted = 0;
262 for (mask = 1; remaining_word; mask <<= 1) {
263 if (remaining_word & mask) {
264 remaining_word ^= mask;
265 spv_operand_desc entry;
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400266 if (grammar.lookupOperand(type, mask, &entry)) {
David Neto619db262015-09-25 12:43:37 -0400267 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " operand '"
268 << words[0] << "'.";
269 return SPV_ERROR_INVALID_BINARY;
270 }
271 if (num_emitted) stream.get() << "|";
272 stream.get() << entry->name;
273 num_emitted++;
274 }
275 }
276 if (!num_emitted) {
277 // An operand value of 0 was provided, so represent it by the name
278 // of the 0 value. In many cases, that's "None".
279 spv_operand_desc entry;
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400280 if (SPV_SUCCESS == grammar.lookupOperand(type, 0, &entry)) {
David Neto619db262015-09-25 12:43:37 -0400281 stream.get() << entry->name;
282 // Prepare for its operands, if any.
283 spvPrependOperandTypes(entry->operandTypes, pExpectedOperands);
284 }
285 }
286 // Prepare for subsequent operands, if any.
287 // Scan from MSB to LSB since we can only prepend operands to a pattern.
288 remaining_word = spvFixWord(words[0], endian);
289 for (mask = (1u << 31); remaining_word; mask >>= 1) {
290 if (remaining_word & mask) {
291 remaining_word ^= mask;
292 spv_operand_desc entry;
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400293 if (SPV_SUCCESS == grammar.lookupOperand(type, mask, &entry)) {
David Neto619db262015-09-25 12:43:37 -0400294 spvPrependOperandTypes(entry->operandTypes, pExpectedOperands);
295 }
296 }
297 }
298 position->index++;
299 } break;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100300 default: {
301 DIAGNOSTIC << "Invalid binary operand '" << type << "'";
302 return SPV_ERROR_INVALID_BINARY;
303 }
304 }
305
306 return SPV_SUCCESS;
307}
308
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400309
310
311/// @brief Regsiters the given instruction with the type and id tracking
312/// tables.
313///
314/// @param[in] pInst the Opcode instruction stream
315/// @param[in] pOpcodeEntry the Opcode Entry describing the instruction
316/// @param[in, out] type_map the map of Ids to Types to be filled in
317/// @param[in, out] id_map the map of Ids to type Ids to be filled in
318/// @param[in, out] position position in the stream
319/// @param[out] pDiag return diagnostic on error
320///
321/// @return result code
322spv_result_t spvRegisterIdForOpcode(const spv_instruction_t* pInst,
323 const spv_opcode_desc_t* pOpcodeEntry,
324 type_id_to_type_map* type_map,
325 id_to_type_id_map* id_map,
326 spv_position position,
327 spv_diagnostic* pDiagnostic) {
328 libspirv::IdType detected_type = libspirv::kUnknownType;
329 if (spvOpcodeIsType(pOpcodeEntry->opcode)) {
330 if (spv::OpTypeInt == pOpcodeEntry->opcode) {
331 detected_type.type_class = libspirv::IdTypeClass::kScalarIntegerType;
332 detected_type.bitwidth = pInst->words[2];
333 detected_type.isSigned = (pInst->words[3] != 0);
334 } else if (spv::OpTypeFloat == pOpcodeEntry->opcode) {
335 detected_type.type_class = libspirv::IdTypeClass::kScalarIntegerType;
336 detected_type.bitwidth = pInst->words[2];
337 detected_type.isSigned = true;
338 } else {
339 detected_type.type_class = libspirv::IdTypeClass::kOtherType;
340 }
341 }
342
343 // We do not use else-if here so that we can still catch the case where an
344 // OpType* instruction shares the same ID as a non OpType* instruction.
345 if (pOpcodeEntry->hasResult) {
346 uint32_t value_id =
347 pOpcodeEntry->hasType ? pInst->words[2] : pInst->words[1];
348 if (id_map->find(value_id) != id_map->end()) {
349 DIAGNOSTIC << "Id " << value_id << " is defined more than once";
350 return SPV_ERROR_INVALID_BINARY;
351 }
352
353 (*id_map)[value_id] = pOpcodeEntry->hasType ? pInst->words[1] : 0;
354 }
355
356 if (detected_type != libspirv::kUnknownType) {
357 // This defines a new type.
358 uint32_t id = pInst->words[1];
359 (*type_map)[id] = detected_type;
360 }
361
362 return SPV_SUCCESS;
363}
364
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400365/// @brief Translate binary Opcode stream to textual form
366///
367/// @param[in] pInst the Opcode instruction stream
368/// @param[in] endian the endianness of the stream
369/// @param[in] options bitfield of spv_binary_to_text_options_t values
370/// @param[in] grammar the AssemblyGrammar to when decoding this operand
371/// @param[in] format the assembly syntax format to decode into
372/// @param[out] stream output text stream
373/// @param[in,out] position position in the stream
374/// @param[out] pDiag return diagnostic on error
375///
376/// @return result code
377spv_result_t spvBinaryDecodeOpcode(spv_instruction_t* pInst,
378 const spv_endianness_t endian,
379 const uint32_t options,
380 const libspirv::AssemblyGrammar& grammar,
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400381 type_id_to_type_map* type_map,
382 id_to_type_id_map* id_map,
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400383 spv_assembly_syntax_format_t format,
384 out_stream &stream, spv_position position,
385 spv_diagnostic *pDiagnostic) {
Lei Zhang40056702015-09-11 14:31:27 -0400386 if (!pInst || !position) return SPV_ERROR_INVALID_POINTER;
Lei Zhang40056702015-09-11 14:31:27 -0400387 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100388
David Neto78c3b432015-08-27 13:03:52 -0400389 spv_position_t instructionStart = *position;
390
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100391 uint16_t wordCount;
392 Op opcode;
393 spvOpcodeSplit(spvFixWord(pInst->words[0], endian), &wordCount, &opcode);
394
395 spv_opcode_desc opcodeEntry;
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400396 if (grammar.lookupOpcode(opcode, &opcodeEntry)) {
Lei Zhang40056702015-09-11 14:31:27 -0400397 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
398 return SPV_ERROR_INVALID_BINARY;
399 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100400
David Neto78c3b432015-08-27 13:03:52 -0400401 // See if there are enough required words.
402 // Some operands in the operand types are optional or could be zero length.
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400403 // The optional and zero length operands must be at the end of the list.
David Neto78c3b432015-08-27 13:03:52 -0400404 if (opcodeEntry->numTypes > wordCount &&
405 !spvOperandIsOptional(opcodeEntry->operandTypes[wordCount])) {
406 uint16_t numRequired;
Lei Zhange78a7c12015-09-10 17:07:21 -0400407 for (numRequired = 0;
408 numRequired < opcodeEntry->numTypes &&
409 !spvOperandIsOptional(opcodeEntry->operandTypes[numRequired]);
410 numRequired++)
David Neto78c3b432015-08-27 13:03:52 -0400411 ;
412 DIAGNOSTIC << "Invalid instruction Op" << opcodeEntry->name
Lei Zhange78a7c12015-09-10 17:07:21 -0400413 << " word count '" << wordCount << "', expected at least '"
414 << numRequired << "'.";
David Neto78c3b432015-08-27 13:03:52 -0400415 return SPV_ERROR_INVALID_BINARY;
416 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100417
Lei Zhang29e667e2015-09-11 11:01:59 -0400418 const bool isAssigmentFormat =
419 SPV_ASSEMBLY_SYNTAX_FORMAT_ASSIGNMENT == format;
420
421 // For Canonical Assembly Format, all words are written to stream in order.
422 // For Assignment Assembly Format, <result-id> and the equal sign are written
423 // to stream first, while the rest are written to no_result_id_stream. After
424 // processing all words, all words in no_result_id_stream are transcribed to
425 // stream.
426
Lei Zhang8a375202015-08-24 15:52:26 -0400427 std::stringstream no_result_id_strstream;
428 out_stream no_result_id_stream(no_result_id_strstream);
Lei Zhang29e667e2015-09-11 11:01:59 -0400429 (isAssigmentFormat ? no_result_id_stream.get() : stream.get())
430 << "Op" << opcodeEntry->name;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100431
Lei Zhang29e667e2015-09-11 11:01:59 -0400432 const int16_t result_id_index = spvOpcodeResultIdIndex(opcodeEntry);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100433 position->index++;
434
David Neto78c3b432015-08-27 13:03:52 -0400435 // Maintains the ordered list of expected operand types.
436 // For many instructions we only need the {numTypes, operandTypes}
437 // entries in opcodeEntry. However, sometimes we need to modify
438 // the list as we parse the operands. This occurs when an operand
439 // has its own logical operands (such as the LocalSize operand for
440 // ExecutionMode), or for extended instructions that may have their
441 // own operands depending on the selected extended instruction.
442 spv_operand_pattern_t expectedOperands(
443 opcodeEntry->operandTypes,
444 opcodeEntry->operandTypes + opcodeEntry->numTypes);
445
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100446 for (uint16_t index = 1; index < wordCount; ++index) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100447 const uint64_t currentPosIndex = position->index;
Lei Zhang29e667e2015-09-11 11:01:59 -0400448 const bool currentIsResultId = result_id_index == index - 1;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100449
Lei Zhang40056702015-09-11 14:31:27 -0400450 if (expectedOperands.empty()) {
451 DIAGNOSTIC << "Invalid instruction Op" << opcodeEntry->name
452 << " starting at word " << instructionStart.index
453 << ": expected no more operands after " << index
454 << " words, but word count is " << wordCount << ".";
455 return SPV_ERROR_INVALID_BINARY;
456 }
David Neto78c3b432015-08-27 13:03:52 -0400457
458 spv_operand_type_t type = spvTakeFirstMatchableOperand(&expectedOperands);
459
Lei Zhang29e667e2015-09-11 11:01:59 -0400460 if (isAssigmentFormat) {
461 if (!currentIsResultId) no_result_id_stream.get() << " ";
462 } else {
463 stream.get() << " ";
464 }
Lei Zhangb41d1502015-09-14 15:22:23 -0400465
466 uint16_t numWords = 1;
467 if (type == SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER) {
468 // Make sure this is the last operand for this instruction.
469 if (expectedOperands.empty()) {
470 numWords = wordCount - index;
471 } else {
472 // TODO(antiagainst): This may not be an error. The exact design has not
473 // been settled yet.
474 DIAGNOSTIC << "Multiple word literal numbers can only appear as the "
475 "last operand of an instruction.";
476 return SPV_ERROR_INVALID_BINARY;
477 }
478 }
479
Lei Zhang40056702015-09-11 14:31:27 -0400480 if (spvBinaryDecodeOperand(
David Netob5dc8fc2015-10-06 16:22:00 -0400481 opcodeEntry->opcode, type, &pInst->words[index], numWords, endian,
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400482 options, grammar, &expectedOperands,
Lei Zhangb41d1502015-09-14 15:22:23 -0400483 &pInst->extInstType,
Lei Zhang29e667e2015-09-11 11:01:59 -0400484 (isAssigmentFormat && !currentIsResultId ? no_result_id_stream
485 : stream),
Lei Zhang40056702015-09-11 14:31:27 -0400486 position, pDiagnostic)) {
487 DIAGNOSTIC << "UNEXPLAINED ERROR";
488 return SPV_ERROR_INVALID_BINARY;
489 }
Lei Zhang29e667e2015-09-11 11:01:59 -0400490 if (isAssigmentFormat && currentIsResultId) stream.get() << " = ";
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100491 index += (uint16_t)(position->index - currentPosIndex - 1);
492 }
David Neto78c3b432015-08-27 13:03:52 -0400493 // TODO(dneto): There's an opportunity for a more informative message.
Lei Zhang40056702015-09-11 14:31:27 -0400494 if (!expectedOperands.empty() &&
495 !spvOperandIsOptional(expectedOperands.front())) {
496 DIAGNOSTIC << "Invalid instruction Op" << opcodeEntry->name
497 << " starting at word " << instructionStart.index
498 << ": expected more operands after " << wordCount << " words.";
499 return SPV_ERROR_INVALID_BINARY;
500 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100501
Lei Zhang8a375202015-08-24 15:52:26 -0400502 stream.get() << no_result_id_strstream.str();
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400503 if (spv_result_t error = spvRegisterIdForOpcode(
504 pInst, opcodeEntry, type_map, id_map, position, pDiagnostic)) {
505 return error;
506 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100507 return SPV_SUCCESS;
508}
509
Lei Zhange78a7c12015-09-10 17:07:21 -0400510spv_result_t spvBinaryToText(uint32_t *code, const uint64_t wordCount,
Andrew Woloszyncfeac482015-09-09 13:04:32 -0400511 const uint32_t options,
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100512 const spv_opcode_table opcodeTable,
513 const spv_operand_table operandTable,
514 const spv_ext_inst_table extInstTable,
515 spv_text *pText, spv_diagnostic *pDiagnostic) {
Lei Zhang29e667e2015-09-11 11:01:59 -0400516 return spvBinaryToTextWithFormat(
517 code, wordCount, options, opcodeTable, operandTable, extInstTable,
518 SPV_ASSEMBLY_SYNTAX_FORMAT_DEFAULT, pText, pDiagnostic);
519}
520
521spv_result_t spvBinaryToTextWithFormat(
522 uint32_t *code, const uint64_t wordCount, const uint32_t options,
523 const spv_opcode_table opcodeTable, const spv_operand_table operandTable,
524 const spv_ext_inst_table extInstTable, spv_assembly_syntax_format_t format,
525 spv_text *pText, spv_diagnostic *pDiagnostic) {
Andrew Woloszyncfeac482015-09-09 13:04:32 -0400526 spv_binary_t binary = {code, wordCount};
527
Andrew Woloszyn4b4acde2015-09-10 10:28:22 -0400528 spv_position_t position = {};
Lei Zhang40056702015-09-11 14:31:27 -0400529 if (!binary.code || !binary.wordCount) {
530 DIAGNOSTIC << "Binary stream is empty.";
531 return SPV_ERROR_INVALID_BINARY;
532 }
533 if (!opcodeTable || !operandTable || !extInstTable)
534 return SPV_ERROR_INVALID_TABLE;
535 if (pText && spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options))
536 return SPV_ERROR_INVALID_POINTER;
537 if (!pText && !spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options))
538 return SPV_ERROR_INVALID_POINTER;
539 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100540
541 spv_endianness_t endian;
Lei Zhang40056702015-09-11 14:31:27 -0400542 if (spvBinaryEndianness(&binary, &endian)) {
543 DIAGNOSTIC << "Invalid SPIR-V magic number '" << std::hex << binary.code[0]
544 << "'.";
545 return SPV_ERROR_INVALID_BINARY;
546 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100547
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400548 libspirv::AssemblyGrammar grammar(operandTable, opcodeTable, extInstTable);
549
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100550 spv_header_t header;
Lei Zhang40056702015-09-11 14:31:27 -0400551 if (spvBinaryHeaderGet(&binary, endian, &header)) {
552 DIAGNOSTIC << "Invalid SPIR-V header.";
553 return SPV_ERROR_INVALID_BINARY;
554 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100555
556 bool print = spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options);
557 bool color =
558 print && spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options);
559
560 std::stringstream sstream;
561 out_stream stream(sstream);
562 if (print) {
563 stream = out_stream();
564 }
565
566 if (color) {
567 stream.get() << clr::grey();
568 }
569 stream.get() << "; SPIR-V\n"
570 << "; Version: " << header.version << "\n"
571 << "; Generator: " << spvGeneratorStr(header.generator) << "\n"
572 << "; Bound: " << header.bound << "\n"
573 << "; Schema: " << header.schema << "\n";
574 if (color) {
575 stream.get() << clr::reset();
576 }
577
Andrew Woloszyncfeac482015-09-09 13:04:32 -0400578 const uint32_t *words = binary.code;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100579 position.index = SPV_INDEX_INSTRUCTION;
580 spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400581
582 id_to_type_id_map id_map;
583 type_id_to_type_map type_map;
584
Andrew Woloszyncfeac482015-09-09 13:04:32 -0400585 while (position.index < binary.wordCount) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100586 uint64_t index = position.index;
587 uint16_t wordCount;
588 Op opcode;
589 spvOpcodeSplit(spvFixWord(words[position.index], endian), &wordCount,
590 &opcode);
591
592 spv_instruction_t inst = {};
593 inst.extInstType = extInstType;
594 spvInstructionCopy(&words[position.index], opcode, wordCount, endian,
595 &inst);
596
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400597 if (spvBinaryDecodeOpcode(&inst, endian, options, grammar, &type_map,
598 &id_map, format, stream, &position, pDiagnostic))
Lei Zhang40056702015-09-11 14:31:27 -0400599 return SPV_ERROR_INVALID_BINARY;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100600 extInstType = inst.extInstType;
601
Lei Zhang40056702015-09-11 14:31:27 -0400602 if ((index + wordCount) != position.index) {
603 DIAGNOSTIC << "Invalid word count.";
604 return SPV_ERROR_INVALID_BINARY;
605 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100606
607 stream.get() << "\n";
608 }
609
610 if (!print) {
611 size_t length = sstream.str().size();
612 char *str = new char[length + 1];
Lei Zhang40056702015-09-11 14:31:27 -0400613 if (!str) return SPV_ERROR_OUT_OF_MEMORY;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100614 strncpy(str, sstream.str().c_str(), length + 1);
615 spv_text text = new spv_text_t();
Lei Zhang40056702015-09-11 14:31:27 -0400616 if (!text) return SPV_ERROR_OUT_OF_MEMORY;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100617 text->str = str;
618 text->length = length;
619 *pText = text;
620 }
621
622 return SPV_SUCCESS;
623}
624
625void spvBinaryDestroy(spv_binary binary) {
Lei Zhang40056702015-09-11 14:31:27 -0400626 if (!binary) return;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100627 if (binary->code) {
628 delete[] binary->code;
629 }
630 delete binary;
631}