blob: 8698b27d585a315ea293d06ccb642d5c4e7ed533 [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,
Lei Zhang1a0334e2015-11-02 09:41:20 -050051 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,
Lei Zhang1a0334e2015-11-02 09:41:20 -050075 spv_operand_desc* pOperandEntry) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010076 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];
Lei Zhangb36e7042015-10-28 13:40:52 -040091 } else if (SpvOpSwitch == opcodeEntry->opcode) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010092 // 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/// @brief Translate a binary operand to the textual form
109///
110/// @param[in] opcode of the current instruction
111/// @param[in] type type of the operand to decode
112/// @param[in] words the binary stream of words
113/// @param[in] endian the endianness of the stream
114/// @param[in] options bitfield of spv_binary_to_text_options_t values
115/// @param[in] grammar the AssemblyGrammar to when decoding this operand
116/// @param[in,out] stream the text output stream
117/// @param[in,out] position position in the binary stream
118/// @param[out] pDiag return diagnostic on error
119///
120/// @return result code
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100121spv_result_t spvBinaryDecodeOperand(
Lei Zhang1a0334e2015-11-02 09:41:20 -0500122 const SpvOp opcode, const spv_operand_type_t type, const uint32_t* words,
Lei Zhangb41d1502015-09-14 15:22:23 -0400123 uint16_t numWords, const spv_endianness_t endian, const uint32_t options,
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400124 const libspirv::AssemblyGrammar& grammar,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500125 spv_operand_pattern_t* pExpectedOperands, spv_ext_inst_type_t* pExtInstType,
126 out_stream& stream, spv_position position, spv_diagnostic* pDiagnostic) {
Lei Zhang40056702015-09-11 14:31:27 -0400127 if (!words || !position) return SPV_ERROR_INVALID_POINTER;
128 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100129
130 bool print = spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options);
131 bool color =
132 print && spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options);
133
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100134 switch (type) {
David Netob14a7272015-09-25 13:56:09 -0400135 case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
David Neto78c3b432015-08-27 13:03:52 -0400136 case SPV_OPERAND_TYPE_ID:
Andrew Woloszyn537e7762015-09-29 11:28:34 -0400137 case SPV_OPERAND_TYPE_TYPE_ID:
David Netob14a7272015-09-25 13:56:09 -0400138 case SPV_OPERAND_TYPE_ID_IN_OPTIONAL_TUPLE:
David Neto78c3b432015-08-27 13:03:52 -0400139 case SPV_OPERAND_TYPE_OPTIONAL_ID:
David Netob14a7272015-09-25 13:56:09 -0400140 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS:
141 case SPV_OPERAND_TYPE_RESULT_ID: {
David Neto78c3b432015-08-27 13:03:52 -0400142 if (color) {
Pyry Haulos26b3b002015-09-09 13:35:53 -0700143 if (type == SPV_OPERAND_TYPE_RESULT_ID) {
144 stream.get() << clr::blue();
145 } else {
146 stream.get() << clr::yellow();
147 }
David Neto78c3b432015-08-27 13:03:52 -0400148 }
Lei Zhang97afd5c2015-09-14 15:26:12 -0400149 stream.get() << "%" << spvFixWord(words[0], endian);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100150 stream.get() << ((color) ? clr::reset() : "");
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100151 position->index++;
152 } break;
David Neto445ce442015-10-15 15:22:06 -0400153 case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: {
Lei Zhangb36e7042015-10-28 13:40:52 -0400154 if (SpvOpExtInst == opcode) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100155 spv_ext_inst_desc extInst;
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400156 if (grammar.lookupExtInst(*pExtInstType, words[0], &extInst)) {
Lei Zhang40056702015-09-11 14:31:27 -0400157 DIAGNOSTIC << "Invalid extended instruction '" << words[0] << "'.";
158 return SPV_ERROR_INVALID_BINARY;
159 }
David Neto78c3b432015-08-27 13:03:52 -0400160 spvPrependOperandTypes(extInst->operandTypes, pExpectedOperands);
Andrew Woloszyn0d350b52015-08-21 14:23:42 -0400161 stream.get() << (color ? clr::red() : "");
162 stream.get() << extInst->name;
163 stream.get() << (color ? clr::reset() : "");
Lei Zhang41bf0732015-09-14 12:26:15 -0400164 position->index++;
David Neto445ce442015-10-15 15:22:06 -0400165 } else {
166 DIAGNOSTIC << "Internal error: grammar thinks we need an "
167 "extension instruction number for opcode "
168 << opcode;
169 return SPV_ERROR_INTERNAL;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100170 }
David Neto445ce442015-10-15 15:22:06 -0400171 } break;
172 case SPV_OPERAND_TYPE_LITERAL_INTEGER:
Lei Zhangb41d1502015-09-14 15:22:23 -0400173 case SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER:
Lei Zhang6483bd72015-10-14 17:02:39 -0400174 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
175 case SPV_OPERAND_TYPE_LITERAL_INTEGER_IN_OPTIONAL_TUPLE: {
Lei Zhang41bf0732015-09-14 12:26:15 -0400176 // TODO: Need to support multiple word literals
177 stream.get() << (color ? clr::red() : "");
Lei Zhangb41d1502015-09-14 15:22:23 -0400178 if (numWords > 2) {
179 DIAGNOSTIC << "Literal numbers larger than 64-bit not supported yet.";
180 return SPV_UNSUPPORTED;
181 } else if (numWords == 2) {
182 stream.get() << spvFixDoubleWord(words[0], words[1], endian);
183 position->index += 2;
184 } else {
185 stream.get() << spvFixWord(words[0], endian);
186 position->index++;
187 }
Lei Zhang41bf0732015-09-14 12:26:15 -0400188 stream.get() << (color ? clr::reset() : "");
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100189 } break;
David Neto78c3b432015-08-27 13:03:52 -0400190 case SPV_OPERAND_TYPE_LITERAL_STRING:
191 case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: {
Lei Zhang1a0334e2015-11-02 09:41:20 -0500192 const char* string = (const char*)words;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100193 uint64_t stringOperandCount = (strlen(string) / 4) + 1;
194
195 // NOTE: Special case for extended instruction import
Lei Zhangb36e7042015-10-28 13:40:52 -0400196 if (SpvOpExtInstImport == opcode) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100197 *pExtInstType = spvExtInstImportTypeGet(string);
Lei Zhang40056702015-09-11 14:31:27 -0400198 if (SPV_EXT_INST_TYPE_NONE == *pExtInstType) {
199 DIAGNOSTIC << "Invalid extended instruction import'" << string
200 << "'.";
201 return SPV_ERROR_INVALID_BINARY;
202 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100203 }
204
205 stream.get() << "\"";
206 stream.get() << (color ? clr::green() : "");
David Neto980b7cb2015-10-15 16:40:04 -0400207 for (const char* p = string; *p; ++p) {
Lei Zhang1a0334e2015-11-02 09:41:20 -0500208 if (*p == '"' || *p == '\\') {
Andrew Woloszyne59e6b72015-10-14 14:18:43 -0400209 stream.get() << '\\';
210 }
David Neto980b7cb2015-10-15 16:40:04 -0400211 stream.get() << *p;
Andrew Woloszyne59e6b72015-10-14 14:18:43 -0400212 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100213 stream.get() << (color ? clr::reset() : "");
214 stream.get() << "\"";
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100215 position->index += stringOperandCount;
216 } break;
217 case SPV_OPERAND_TYPE_CAPABILITY:
218 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
219 case SPV_OPERAND_TYPE_EXECUTION_MODEL:
220 case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
221 case SPV_OPERAND_TYPE_MEMORY_MODEL:
222 case SPV_OPERAND_TYPE_EXECUTION_MODE:
David Neto78c3b432015-08-27 13:03:52 -0400223 case SPV_OPERAND_TYPE_OPTIONAL_EXECUTION_MODE:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100224 case SPV_OPERAND_TYPE_STORAGE_CLASS:
225 case SPV_OPERAND_TYPE_DIMENSIONALITY:
226 case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
227 case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100228 case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
229 case SPV_OPERAND_TYPE_LINKAGE_TYPE:
230 case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
231 case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
232 case SPV_OPERAND_TYPE_DECORATION:
233 case SPV_OPERAND_TYPE_BUILT_IN:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100234 case SPV_OPERAND_TYPE_GROUP_OPERATION:
235 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
David Neto47994822015-08-27 13:11:01 -0400236 case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100237 spv_operand_desc entry;
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400238 if (grammar.lookupOperand(type, spvFixWord(words[0], endian), &entry)) {
Lei Zhang40056702015-09-11 14:31:27 -0400239 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " operand '"
Lei Zhang97afd5c2015-09-14 15:26:12 -0400240 << words[0] << "'.";
Lei Zhang1a0334e2015-11-02 09:41:20 -0500241 return SPV_ERROR_INVALID_TEXT; // TODO(dneto): Surely this is invalid
242 // 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/// @brief Regsiters the given instruction with the type and id tracking
310/// tables.
311///
312/// @param[in] pInst the Opcode instruction stream
313/// @param[in] pOpcodeEntry the Opcode Entry describing the instruction
314/// @param[in, out] type_map the map of Ids to Types to be filled in
315/// @param[in, out] id_map the map of Ids to type Ids to be filled in
316/// @param[in, out] position position in the stream
317/// @param[out] pDiag return diagnostic on error
318///
319/// @return result code
320spv_result_t spvRegisterIdForOpcode(const spv_instruction_t* pInst,
321 const spv_opcode_desc_t* pOpcodeEntry,
322 type_id_to_type_map* type_map,
323 id_to_type_id_map* id_map,
324 spv_position position,
325 spv_diagnostic* pDiagnostic) {
326 libspirv::IdType detected_type = libspirv::kUnknownType;
David Netoaef608c2015-11-02 14:59:02 -0500327 if (spvOpcodeGeneratesType(pOpcodeEntry->opcode)) {
Lei Zhangb36e7042015-10-28 13:40:52 -0400328 if (SpvOpTypeInt == pOpcodeEntry->opcode) {
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400329 detected_type.type_class = libspirv::IdTypeClass::kScalarIntegerType;
330 detected_type.bitwidth = pInst->words[2];
331 detected_type.isSigned = (pInst->words[3] != 0);
Lei Zhangb36e7042015-10-28 13:40:52 -0400332 } else if (SpvOpTypeFloat == pOpcodeEntry->opcode) {
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400333 detected_type.type_class = libspirv::IdTypeClass::kScalarIntegerType;
334 detected_type.bitwidth = pInst->words[2];
335 detected_type.isSigned = true;
336 } else {
337 detected_type.type_class = libspirv::IdTypeClass::kOtherType;
338 }
339 }
340
341 // We do not use else-if here so that we can still catch the case where an
342 // OpType* instruction shares the same ID as a non OpType* instruction.
343 if (pOpcodeEntry->hasResult) {
344 uint32_t value_id =
345 pOpcodeEntry->hasType ? pInst->words[2] : pInst->words[1];
346 if (id_map->find(value_id) != id_map->end()) {
347 DIAGNOSTIC << "Id " << value_id << " is defined more than once";
348 return SPV_ERROR_INVALID_BINARY;
349 }
350
351 (*id_map)[value_id] = pOpcodeEntry->hasType ? pInst->words[1] : 0;
352 }
353
354 if (detected_type != libspirv::kUnknownType) {
355 // This defines a new type.
356 uint32_t id = pInst->words[1];
357 (*type_map)[id] = detected_type;
358 }
359
360 return SPV_SUCCESS;
361}
362
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400363/// @brief Translate binary Opcode stream to textual form
364///
365/// @param[in] pInst the Opcode instruction stream
366/// @param[in] endian the endianness of the stream
367/// @param[in] options bitfield of spv_binary_to_text_options_t values
368/// @param[in] grammar the AssemblyGrammar to when decoding this operand
369/// @param[in] format the assembly syntax format to decode into
370/// @param[out] stream output text stream
371/// @param[in,out] position position in the stream
372/// @param[out] pDiag return diagnostic on error
373///
374/// @return result code
Lei Zhang1a0334e2015-11-02 09:41:20 -0500375spv_result_t spvBinaryDecodeOpcode(
376 spv_instruction_t* pInst, const spv_endianness_t endian,
377 const uint32_t options, const libspirv::AssemblyGrammar& grammar,
378 type_id_to_type_map* type_map, id_to_type_id_map* id_map,
379 spv_assembly_syntax_format_t format, out_stream& stream,
380 spv_position position, spv_diagnostic* pDiagnostic) {
Lei Zhang40056702015-09-11 14:31:27 -0400381 if (!pInst || !position) return SPV_ERROR_INVALID_POINTER;
Lei Zhang40056702015-09-11 14:31:27 -0400382 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100383
David Neto78c3b432015-08-27 13:03:52 -0400384 spv_position_t instructionStart = *position;
385
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100386 uint16_t wordCount;
Lei Zhangb36e7042015-10-28 13:40:52 -0400387 SpvOp opcode;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100388 spvOpcodeSplit(spvFixWord(pInst->words[0], endian), &wordCount, &opcode);
389
390 spv_opcode_desc opcodeEntry;
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400391 if (grammar.lookupOpcode(opcode, &opcodeEntry)) {
Lei Zhang40056702015-09-11 14:31:27 -0400392 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
393 return SPV_ERROR_INVALID_BINARY;
394 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100395
David Neto78c3b432015-08-27 13:03:52 -0400396 // See if there are enough required words.
397 // Some operands in the operand types are optional or could be zero length.
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400398 // The optional and zero length operands must be at the end of the list.
David Neto78c3b432015-08-27 13:03:52 -0400399 if (opcodeEntry->numTypes > wordCount &&
400 !spvOperandIsOptional(opcodeEntry->operandTypes[wordCount])) {
401 uint16_t numRequired;
Lei Zhange78a7c12015-09-10 17:07:21 -0400402 for (numRequired = 0;
403 numRequired < opcodeEntry->numTypes &&
404 !spvOperandIsOptional(opcodeEntry->operandTypes[numRequired]);
405 numRequired++)
David Neto78c3b432015-08-27 13:03:52 -0400406 ;
407 DIAGNOSTIC << "Invalid instruction Op" << opcodeEntry->name
Lei Zhange78a7c12015-09-10 17:07:21 -0400408 << " word count '" << wordCount << "', expected at least '"
409 << numRequired << "'.";
David Neto78c3b432015-08-27 13:03:52 -0400410 return SPV_ERROR_INVALID_BINARY;
411 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100412
Lei Zhang29e667e2015-09-11 11:01:59 -0400413 const bool isAssigmentFormat =
414 SPV_ASSEMBLY_SYNTAX_FORMAT_ASSIGNMENT == format;
415
416 // For Canonical Assembly Format, all words are written to stream in order.
417 // For Assignment Assembly Format, <result-id> and the equal sign are written
418 // to stream first, while the rest are written to no_result_id_stream. After
419 // processing all words, all words in no_result_id_stream are transcribed to
420 // stream.
421
Lei Zhang8a375202015-08-24 15:52:26 -0400422 std::stringstream no_result_id_strstream;
423 out_stream no_result_id_stream(no_result_id_strstream);
Lei Zhang29e667e2015-09-11 11:01:59 -0400424 (isAssigmentFormat ? no_result_id_stream.get() : stream.get())
425 << "Op" << opcodeEntry->name;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100426
Lei Zhang29e667e2015-09-11 11:01:59 -0400427 const int16_t result_id_index = spvOpcodeResultIdIndex(opcodeEntry);
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100428 position->index++;
429
David Neto78c3b432015-08-27 13:03:52 -0400430 // Maintains the ordered list of expected operand types.
431 // For many instructions we only need the {numTypes, operandTypes}
432 // entries in opcodeEntry. However, sometimes we need to modify
433 // the list as we parse the operands. This occurs when an operand
434 // has its own logical operands (such as the LocalSize operand for
435 // ExecutionMode), or for extended instructions that may have their
436 // own operands depending on the selected extended instruction.
437 spv_operand_pattern_t expectedOperands(
438 opcodeEntry->operandTypes,
439 opcodeEntry->operandTypes + opcodeEntry->numTypes);
440
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100441 for (uint16_t index = 1; index < wordCount; ++index) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100442 const uint64_t currentPosIndex = position->index;
Lei Zhang29e667e2015-09-11 11:01:59 -0400443 const bool currentIsResultId = result_id_index == index - 1;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100444
Lei Zhang40056702015-09-11 14:31:27 -0400445 if (expectedOperands.empty()) {
446 DIAGNOSTIC << "Invalid instruction Op" << opcodeEntry->name
447 << " starting at word " << instructionStart.index
448 << ": expected no more operands after " << index
449 << " words, but word count is " << wordCount << ".";
450 return SPV_ERROR_INVALID_BINARY;
451 }
David Neto78c3b432015-08-27 13:03:52 -0400452
453 spv_operand_type_t type = spvTakeFirstMatchableOperand(&expectedOperands);
454
Lei Zhang29e667e2015-09-11 11:01:59 -0400455 if (isAssigmentFormat) {
456 if (!currentIsResultId) no_result_id_stream.get() << " ";
457 } else {
458 stream.get() << " ";
459 }
Lei Zhangb41d1502015-09-14 15:22:23 -0400460
461 uint16_t numWords = 1;
462 if (type == SPV_OPERAND_TYPE_MULTIWORD_LITERAL_NUMBER) {
463 // Make sure this is the last operand for this instruction.
464 if (expectedOperands.empty()) {
465 numWords = wordCount - index;
466 } else {
467 // TODO(antiagainst): This may not be an error. The exact design has not
468 // been settled yet.
469 DIAGNOSTIC << "Multiple word literal numbers can only appear as the "
470 "last operand of an instruction.";
471 return SPV_ERROR_INVALID_BINARY;
472 }
473 }
474
Lei Zhang40056702015-09-11 14:31:27 -0400475 if (spvBinaryDecodeOperand(
David Netob5dc8fc2015-10-06 16:22:00 -0400476 opcodeEntry->opcode, type, &pInst->words[index], numWords, endian,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500477 options, grammar, &expectedOperands, &pInst->extInstType,
Lei Zhang29e667e2015-09-11 11:01:59 -0400478 (isAssigmentFormat && !currentIsResultId ? no_result_id_stream
479 : stream),
Lei Zhang40056702015-09-11 14:31:27 -0400480 position, pDiagnostic)) {
481 DIAGNOSTIC << "UNEXPLAINED ERROR";
482 return SPV_ERROR_INVALID_BINARY;
483 }
Lei Zhang29e667e2015-09-11 11:01:59 -0400484 if (isAssigmentFormat && currentIsResultId) stream.get() << " = ";
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100485 index += (uint16_t)(position->index - currentPosIndex - 1);
486 }
David Neto78c3b432015-08-27 13:03:52 -0400487 // TODO(dneto): There's an opportunity for a more informative message.
Lei Zhang40056702015-09-11 14:31:27 -0400488 if (!expectedOperands.empty() &&
489 !spvOperandIsOptional(expectedOperands.front())) {
490 DIAGNOSTIC << "Invalid instruction Op" << opcodeEntry->name
491 << " starting at word " << instructionStart.index
492 << ": expected more operands after " << wordCount << " words.";
493 return SPV_ERROR_INVALID_BINARY;
494 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100495
Lei Zhang8a375202015-08-24 15:52:26 -0400496 stream.get() << no_result_id_strstream.str();
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400497 if (spv_result_t error = spvRegisterIdForOpcode(
498 pInst, opcodeEntry, type_map, id_map, position, pDiagnostic)) {
499 return error;
500 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100501 return SPV_SUCCESS;
502}
503
Lei Zhang29e667e2015-09-11 11:01:59 -0400504spv_result_t spvBinaryToTextWithFormat(
David Netoe4945de2015-10-28 13:50:32 -0400505 uint32_t const* code, const uint64_t wordCount, const uint32_t options,
Lei Zhang29e667e2015-09-11 11:01:59 -0400506 const spv_opcode_table opcodeTable, const spv_operand_table operandTable,
507 const spv_ext_inst_table extInstTable, spv_assembly_syntax_format_t format,
Lei Zhang1a0334e2015-11-02 09:41:20 -0500508 spv_text* pText, spv_diagnostic* pDiagnostic) {
Andrew Woloszyncfeac482015-09-09 13:04:32 -0400509 spv_binary_t binary = {code, wordCount};
510
Andrew Woloszyn4b4acde2015-09-10 10:28:22 -0400511 spv_position_t position = {};
Lei Zhang40056702015-09-11 14:31:27 -0400512 if (!binary.code || !binary.wordCount) {
513 DIAGNOSTIC << "Binary stream is empty.";
514 return SPV_ERROR_INVALID_BINARY;
515 }
516 if (!opcodeTable || !operandTable || !extInstTable)
517 return SPV_ERROR_INVALID_TABLE;
518 if (pText && spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options))
519 return SPV_ERROR_INVALID_POINTER;
520 if (!pText && !spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options))
521 return SPV_ERROR_INVALID_POINTER;
522 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100523
524 spv_endianness_t endian;
Lei Zhang40056702015-09-11 14:31:27 -0400525 if (spvBinaryEndianness(&binary, &endian)) {
526 DIAGNOSTIC << "Invalid SPIR-V magic number '" << std::hex << binary.code[0]
527 << "'.";
528 return SPV_ERROR_INVALID_BINARY;
529 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100530
Andrew Woloszynccc210b2015-10-16 10:23:42 -0400531 libspirv::AssemblyGrammar grammar(operandTable, opcodeTable, extInstTable);
532
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100533 spv_header_t header;
Lei Zhang40056702015-09-11 14:31:27 -0400534 if (spvBinaryHeaderGet(&binary, endian, &header)) {
535 DIAGNOSTIC << "Invalid SPIR-V header.";
536 return SPV_ERROR_INVALID_BINARY;
537 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100538
539 bool print = spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options);
540 bool color =
541 print && spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options);
542
543 std::stringstream sstream;
544 out_stream stream(sstream);
545 if (print) {
546 stream = out_stream();
547 }
548
549 if (color) {
550 stream.get() << clr::grey();
551 }
552 stream.get() << "; SPIR-V\n"
553 << "; Version: " << header.version << "\n"
554 << "; Generator: " << spvGeneratorStr(header.generator) << "\n"
555 << "; Bound: " << header.bound << "\n"
556 << "; Schema: " << header.schema << "\n";
557 if (color) {
558 stream.get() << clr::reset();
559 }
560
Lei Zhang1a0334e2015-11-02 09:41:20 -0500561 const uint32_t* words = binary.code;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100562 position.index = SPV_INDEX_INSTRUCTION;
563 spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400564
565 id_to_type_id_map id_map;
566 type_id_to_type_map type_map;
567
Andrew Woloszyncfeac482015-09-09 13:04:32 -0400568 while (position.index < binary.wordCount) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100569 uint64_t index = position.index;
570 uint16_t wordCount;
Lei Zhangb36e7042015-10-28 13:40:52 -0400571 SpvOp opcode;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100572 spvOpcodeSplit(spvFixWord(words[position.index], endian), &wordCount,
573 &opcode);
574
575 spv_instruction_t inst = {};
576 inst.extInstType = extInstType;
577 spvInstructionCopy(&words[position.index], opcode, wordCount, endian,
578 &inst);
579
Andrew Woloszyn157e41b2015-10-16 15:11:00 -0400580 if (spvBinaryDecodeOpcode(&inst, endian, options, grammar, &type_map,
581 &id_map, format, stream, &position, pDiagnostic))
Lei Zhang40056702015-09-11 14:31:27 -0400582 return SPV_ERROR_INVALID_BINARY;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100583 extInstType = inst.extInstType;
584
Lei Zhang40056702015-09-11 14:31:27 -0400585 if ((index + wordCount) != position.index) {
586 DIAGNOSTIC << "Invalid word count.";
587 return SPV_ERROR_INVALID_BINARY;
588 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100589
590 stream.get() << "\n";
591 }
592
593 if (!print) {
594 size_t length = sstream.str().size();
Lei Zhang1a0334e2015-11-02 09:41:20 -0500595 char* str = new char[length + 1];
Lei Zhang40056702015-09-11 14:31:27 -0400596 if (!str) return SPV_ERROR_OUT_OF_MEMORY;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100597 strncpy(str, sstream.str().c_str(), length + 1);
598 spv_text text = new spv_text_t();
Lei Zhang40056702015-09-11 14:31:27 -0400599 if (!text) return SPV_ERROR_OUT_OF_MEMORY;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100600 text->str = str;
601 text->length = length;
602 *pText = text;
603 }
604
605 return SPV_SUCCESS;
606}
607
608void spvBinaryDestroy(spv_binary binary) {
Lei Zhang40056702015-09-11 14:31:27 -0400609 if (!binary) return;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100610 if (binary->code) {
611 delete[] binary->code;
612 }
613 delete binary;
614}