blob: 08282bb4f1e9b9d6ba505cd5d880fdc86f6b84de [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
27#include <libspirv/libspirv.h>
28#include "binary.h"
29#include "diagnostic.h"
30#include "ext_inst.h"
31#include "opcode.h"
32#include "operand.h"
33
34#include <assert.h>
35#include <string.h>
36
37#include <sstream>
38
39// Binary API
40
41enum {
42 I32_ENDIAN_LITTLE = 0x03020100ul,
43 I32_ENDIAN_BIG = 0x00010203ul,
44};
45
46static const union {
47 unsigned char bytes[4];
48 uint32_t value;
49} o32_host_order = {{0, 1, 2, 3}};
50
51#define I32_ENDIAN_HOST (o32_host_order.value)
52
53spv_result_t spvBinaryEndianness(const spv_binary binary,
54 spv_endianness_t *pEndian) {
55 spvCheck(!binary->code || !binary->wordCount,
56 return SPV_ERROR_INVALID_BINARY);
57 spvCheck(!pEndian, return SPV_ERROR_INVALID_POINTER);
58
59 uint8_t bytes[4];
60 memcpy(bytes, binary->code, sizeof(uint32_t));
61
62 if (0x03 == bytes[0] && 0x02 == bytes[1] && 0x23 == bytes[2] &&
63 0x07 == bytes[3]) {
64 *pEndian = SPV_ENDIANNESS_LITTLE;
65 return SPV_SUCCESS;
66 }
67
68 if (0x07 == bytes[0] && 0x23 == bytes[1] && 0x02 == bytes[2] &&
69 0x03 == bytes[3]) {
70 *pEndian = SPV_ENDIANNESS_BIG;
71 return SPV_SUCCESS;
72 }
73
74 return SPV_ERROR_INVALID_BINARY;
75}
76
77uint32_t spvFixWord(const uint32_t word, const spv_endianness_t endian) {
78 if ((SPV_ENDIANNESS_LITTLE == endian && I32_ENDIAN_HOST == I32_ENDIAN_BIG) ||
79 (SPV_ENDIANNESS_BIG == endian && I32_ENDIAN_HOST == I32_ENDIAN_LITTLE)) {
80 return (word & 0x000000ff) << 24 | (word & 0x0000ff00) << 8 |
81 (word & 0x00ff0000) >> 8 | (word & 0xff000000) >> 24;
82 }
83
84 return word;
85}
86
87spv_result_t spvBinaryHeaderGet(const spv_binary binary,
88 const spv_endianness_t endian,
89 spv_header_t *pHeader) {
90 spvCheck(!binary->code || !binary->wordCount,
91 return SPV_ERROR_INVALID_BINARY);
92 spvCheck(!pHeader, return SPV_ERROR_INVALID_POINTER);
93
94 // TODO: Validation checking?
95 pHeader->magic = spvFixWord(binary->code[SPV_INDEX_MAGIC_NUMBER], endian);
96 pHeader->version = spvFixWord(binary->code[SPV_INDEX_VERSION_NUMBER], endian);
97 pHeader->generator =
98 spvFixWord(binary->code[SPV_INDEX_GENERATOR_NUMBER], endian);
99 pHeader->bound = spvFixWord(binary->code[SPV_INDEX_BOUND], endian);
100 pHeader->schema = spvFixWord(binary->code[SPV_INDEX_SCHEMA], endian);
101 pHeader->instructions = &binary->code[SPV_INDEX_INSTRUCTION];
102
103 return SPV_SUCCESS;
104}
105
106spv_result_t spvBinaryHeaderSet(spv_binary_t *binary, const uint32_t bound) {
107 spvCheck(!binary, return SPV_ERROR_INVALID_BINARY);
108 spvCheck(!binary->code || !binary->wordCount,
109 return SPV_ERROR_INVALID_BINARY);
110
111 binary->code[SPV_INDEX_MAGIC_NUMBER] = SPV_MAGIC_NUMBER;
112 binary->code[SPV_INDEX_VERSION_NUMBER] = SPV_VERSION_NUMBER;
Kenneth Benzie (Benie)81d7d492015-06-01 09:50:46 -0700113 binary->code[SPV_INDEX_GENERATOR_NUMBER] = SPV_GENERATOR_KHRONOS;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100114 binary->code[SPV_INDEX_BOUND] = bound;
115 binary->code[SPV_INDEX_SCHEMA] = 0; // NOTE: Reserved
116
117 return SPV_SUCCESS;
118}
119
120spv_result_t spvBinaryEncodeU32(const uint32_t value, spv_instruction_t *pInst,
121 const spv_position position,
122 spv_diagnostic *pDiagnostic) {
123 spvCheck(pInst->wordCount + 1 > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX,
124 DIAGNOSTIC << "Instruction word count '"
125 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << "' exceeded.";
126 return SPV_ERROR_INVALID_TEXT);
127
128 pInst->words[pInst->wordCount++] = (uint32_t)value;
129 return SPV_SUCCESS;
130}
131
132spv_result_t spvBinaryEncodeU64(const uint64_t value, spv_instruction_t *pInst,
133 const spv_position position,
134 spv_diagnostic *pDiagnostic) {
135 spvCheck(pInst->wordCount + 2 > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX,
136 DIAGNOSTIC << "Instruction word count '"
137 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << "' exceeded.";
138 return SPV_ERROR_INVALID_TEXT);
139
140 uint32_t low = (uint32_t)(0x00000000ffffffff & value);
141 uint32_t high = (uint32_t)((0xffffffff00000000 & value) >> 32);
142 pInst->words[pInst->wordCount++] = low;
143 pInst->words[pInst->wordCount++] = high;
144 return SPV_SUCCESS;
145}
146
147spv_result_t spvBinaryEncodeString(const char *str, spv_instruction_t *pInst,
148 const spv_position position,
149 spv_diagnostic *pDiagnostic) {
150 size_t length = strlen(str);
151 size_t wordCount = (length / 4) + 1;
152 spvCheck((sizeof(uint32_t) * pInst->wordCount) + length >
153 sizeof(uint32_t) * SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX,
154 DIAGNOSTIC << "Instruction word count '"
155 << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << "'exceeded.";
156 return SPV_ERROR_INVALID_TEXT);
157
158 char *dest = (char *)&pInst->words[pInst->wordCount];
159 strncpy(dest, str, length);
160 pInst->wordCount += (uint16_t)wordCount;
161
162 return SPV_SUCCESS;
163}
164
165spv_operand_type_t spvBinaryOperandInfo(const uint32_t word,
166 const uint16_t operandIndex,
167 const spv_opcode_desc opcodeEntry,
168 const spv_operand_table operandTable,
169 spv_operand_desc *pOperandEntry) {
170 spv_operand_type_t type;
171 if (operandIndex < opcodeEntry->wordCount) {
172 // NOTE: Do operand table lookup to set operandEntry if successful
173 uint16_t index = operandIndex - 1;
174 type = opcodeEntry->operandTypes[index];
175 spv_operand_desc entry = nullptr;
176 if (!spvOperandTableValueLookup(operandTable, type, word, &entry)) {
177 if (SPV_OPERAND_TYPE_NONE != entry->operandTypes[0]) {
178 *pOperandEntry = entry;
179 }
180 }
181 } else if (*pOperandEntry) {
182 // NOTE: Use specified operand entry operand type for this word
183 uint16_t index = operandIndex - opcodeEntry->wordCount;
184 type = (*pOperandEntry)->operandTypes[index];
185 } else if (OpSwitch == opcodeEntry->opcode) {
186 // NOTE: OpSwitch is a special case which expects a list of paired extra
187 // operands
188 assert(0 &&
189 "This case is previously untested, remove this assert and ensure it "
190 "is behaving correctly!");
191 uint16_t lastIndex = opcodeEntry->wordCount - 1;
192 uint16_t index = lastIndex + ((operandIndex - lastIndex) % 2);
193 type = opcodeEntry->operandTypes[index];
194 } else {
195 // NOTE: Default to last operand type in opcode entry
196 uint16_t index = opcodeEntry->wordCount - 1;
197 type = opcodeEntry->operandTypes[index];
198 }
199 return type;
200}
201
202spv_result_t spvBinaryDecodeOperand(
203 const Op opcode, const spv_operand_type_t type, const uint32_t *words,
204 const spv_endianness_t endian, const uint32_t options,
205 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
206 spv_ext_inst_type_t *pExtInstType, out_stream &stream,
207 spv_position position, spv_diagnostic *pDiagnostic) {
208 spvCheck(!words || !position, return SPV_ERROR_INVALID_POINTER);
209 spvCheck(!pDiagnostic, return SPV_ERROR_INVALID_DIAGNOSTIC);
210
211 bool print = spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options);
212 bool color =
213 print && spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options);
214
215 uint64_t index = 0;
216 switch (type) {
217 case SPV_OPERAND_TYPE_ID: {
218 stream.get() << ((color) ? clr::yellow() : "");
219 stream.get() << "$" << spvFixWord(words[index], endian);
220 stream.get() << ((color) ? clr::reset() : "");
221 index++;
222 position->index++;
223 } break;
224 case SPV_OPERAND_TYPE_RESULT_ID: {
225 stream.get() << (color ? clr::blue() : "");
226 stream.get() << "%" << spvFixWord(words[index], endian);
227 stream.get() << (color ? clr::reset() : "");
228 index++;
229 position->index++;
230 } break;
231 case SPV_OPERAND_TYPE_LITERAL: {
232 // TODO: Need to support multiple word literals
233 stream.get() << (color ? clr::red() : "");
234 stream.get() << spvFixWord(words[index], endian);
235 stream.get() << (color ? clr::reset() : "");
236 index++;
237 position->index++;
238 } break;
239 case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
240 // NOTE: Special case for extended instruction use
241 if (OpExtInst == opcode) {
242 spv_ext_inst_desc extInst;
243 spvCheck(spvExtInstTableValueLookup(extInstTable, *pExtInstType,
244 words[0], &extInst),
245 DIAGNOSTIC << "Invalid extended instruction '" << words[0]
246 << "'.";
247 return SPV_ERROR_INVALID_BINARY);
248 }
249
250 stream.get() << (color ? clr::red() : "");
251 stream.get() << spvFixWord(words[index], endian);
252 stream.get() << (color ? clr::reset() : "");
253 index++;
254 position->index++;
255 } break;
256 case SPV_OPERAND_TYPE_LITERAL_STRING: {
257 const char *string = (const char *)&words[index];
258 uint64_t stringOperandCount = (strlen(string) / 4) + 1;
259
260 // NOTE: Special case for extended instruction import
261 if (OpExtInstImport == opcode) {
262 *pExtInstType = spvExtInstImportTypeGet(string);
263 spvCheck(SPV_EXT_INST_TYPE_NONE == *pExtInstType,
264 DIAGNOSTIC << "Invalid extended instruction import'" << string
265 << "'.";
266 return SPV_ERROR_INVALID_BINARY);
267 }
268
269 stream.get() << "\"";
270 stream.get() << (color ? clr::green() : "");
271 stream.get() << string;
272 stream.get() << (color ? clr::reset() : "");
273 stream.get() << "\"";
274 index += stringOperandCount;
275 position->index += stringOperandCount;
276 } break;
277 case SPV_OPERAND_TYPE_CAPABILITY:
278 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
279 case SPV_OPERAND_TYPE_EXECUTION_MODEL:
280 case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
281 case SPV_OPERAND_TYPE_MEMORY_MODEL:
282 case SPV_OPERAND_TYPE_EXECUTION_MODE:
283 case SPV_OPERAND_TYPE_STORAGE_CLASS:
284 case SPV_OPERAND_TYPE_DIMENSIONALITY:
285 case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
286 case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
287 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
288 case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
289 case SPV_OPERAND_TYPE_LINKAGE_TYPE:
290 case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
291 case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
292 case SPV_OPERAND_TYPE_DECORATION:
293 case SPV_OPERAND_TYPE_BUILT_IN:
294 case SPV_OPERAND_TYPE_SELECTION_CONTROL:
295 case SPV_OPERAND_TYPE_LOOP_CONTROL:
296 case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
297 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS:
298 case SPV_OPERAND_TYPE_MEMORY_ACCESS:
299 case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
300 case SPV_OPERAND_TYPE_GROUP_OPERATION:
301 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
302 case SPV_OPERAND_TYPE_KERENL_PROFILING_INFO: {
303 spv_operand_desc entry;
304 spvCheck(
305 spvOperandTableValueLookup(operandTable, type,
306 spvFixWord(words[index], endian), &entry),
307 DIAGNOSTIC << "Invalid " << spvOperandTypeStr(type) << " operand '"
308 << words[index] << "'.";
309 return SPV_ERROR_INVALID_TEXT);
310 stream.get() << entry->name;
311 index++;
312 position->index++;
313 } break;
314 default: {
315 DIAGNOSTIC << "Invalid binary operand '" << type << "'";
316 return SPV_ERROR_INVALID_BINARY;
317 }
318 }
319
320 return SPV_SUCCESS;
321}
322
323spv_result_t spvBinaryDecodeOpcode(
324 spv_instruction_t *pInst, const spv_endianness_t endian,
325 const uint32_t options, const spv_opcode_table opcodeTable,
326 const spv_operand_table operandTable, const spv_ext_inst_table extInstTable,
327 out_stream &stream, spv_position position, spv_diagnostic *pDiagnostic) {
328 spvCheck(!pInst || !position, return SPV_ERROR_INVALID_POINTER);
329 spvCheck(!opcodeTable || !operandTable || !extInstTable,
330 return SPV_ERROR_INVALID_TABLE);
331 spvCheck(!pDiagnostic, return SPV_ERROR_INVALID_DIAGNOSTIC);
332
333 uint16_t wordCount;
334 Op opcode;
335 spvOpcodeSplit(spvFixWord(pInst->words[0], endian), &wordCount, &opcode);
336
337 spv_opcode_desc opcodeEntry;
338 spvCheck(spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry),
339 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
340 return SPV_ERROR_INVALID_BINARY);
341
342 spvCheck(opcodeEntry->wordCount > wordCount,
343 DIAGNOSTIC << "Invalid instruction word count '" << wordCount
344 << "', expected at least '" << opcodeEntry->wordCount
345 << "'.";
346 return SPV_ERROR_INVALID_BINARY);
347
348 stream.get() << "Op" << opcodeEntry->name;
349
350 position->index++;
351
352 spv_operand_desc operandEntry = nullptr;
353 for (uint16_t index = 1; index < wordCount; ++index) {
354 const uint32_t word = spvFixWord(pInst->words[index], endian);
355 const uint64_t currentPosIndex = position->index;
356
357 stream.get() << " ";
358 spv_operand_type_t type = spvBinaryOperandInfo(word, index, opcodeEntry,
359 operandTable, &operandEntry);
360 spvCheck(spvBinaryDecodeOperand(
361 opcodeEntry->opcode, type, pInst->words + index, endian,
362 options, operandTable, extInstTable, &pInst->extInstType,
363 stream, position, pDiagnostic),
364 return SPV_ERROR_INVALID_BINARY);
365 index += (uint16_t)(position->index - currentPosIndex - 1);
366 }
367
368 return SPV_SUCCESS;
369}
370
371spv_result_t spvBinaryToText(const spv_binary binary, const uint32_t options,
372 const spv_opcode_table opcodeTable,
373 const spv_operand_table operandTable,
374 const spv_ext_inst_table extInstTable,
375 spv_text *pText, spv_diagnostic *pDiagnostic) {
376 spvCheck(!binary->code || !binary->wordCount,
377 return SPV_ERROR_INVALID_BINARY);
378 spvCheck(!opcodeTable || !operandTable || !extInstTable,
379 return SPV_ERROR_INVALID_TABLE);
380 spvCheck(pText && spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options),
381 return SPV_ERROR_INVALID_POINTER);
382 spvCheck(!pText && !spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options),
383 return SPV_ERROR_INVALID_POINTER);
384 spvCheck(!pDiagnostic, return SPV_ERROR_INVALID_DIAGNOSTIC);
385
386 spv_endianness_t endian;
387 spv_position_t position = {};
388 spvCheck(spvBinaryEndianness(binary, &endian),
389 DIAGNOSTIC << "Invalid SPIR-V magic number '" << std::hex
390 << binary->code[0] << "'.";
391 return SPV_ERROR_INVALID_BINARY);
392
393 spv_header_t header;
394 spvCheck(spvBinaryHeaderGet(binary, endian, &header),
395 DIAGNOSTIC << "Invalid SPIR-V header.";
396 return SPV_ERROR_INVALID_BINARY);
397
398 bool print = spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options);
399 bool color =
400 print && spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options);
401
402 std::stringstream sstream;
403 out_stream stream(sstream);
404 if (print) {
405 stream = out_stream();
406 }
407
408 if (color) {
409 stream.get() << clr::grey();
410 }
411 stream.get() << "; SPIR-V\n"
412 << "; Version: " << header.version << "\n"
413 << "; Generator: " << spvGeneratorStr(header.generator) << "\n"
414 << "; Bound: " << header.bound << "\n"
415 << "; Schema: " << header.schema << "\n";
416 if (color) {
417 stream.get() << clr::reset();
418 }
419
420 const uint32_t *words = binary->code;
421 position.index = SPV_INDEX_INSTRUCTION;
422 spv_ext_inst_type_t extInstType = SPV_EXT_INST_TYPE_NONE;
423 while (position.index < binary->wordCount) {
424 uint64_t index = position.index;
425 uint16_t wordCount;
426 Op opcode;
427 spvOpcodeSplit(spvFixWord(words[position.index], endian), &wordCount,
428 &opcode);
429
430 spv_instruction_t inst = {};
431 inst.extInstType = extInstType;
432 spvInstructionCopy(&words[position.index], opcode, wordCount, endian,
433 &inst);
434
435 spvCheck(
436 spvBinaryDecodeOpcode(&inst, endian, options, opcodeTable, operandTable,
437 extInstTable, stream, &position, pDiagnostic),
438 return SPV_ERROR_INVALID_BINARY);
439 extInstType = inst.extInstType;
440
441 spvCheck((index + wordCount) != position.index,
442 DIAGNOSTIC << "Invalid word count.";
443 return SPV_ERROR_INVALID_BINARY);
444
445 stream.get() << "\n";
446 }
447
448 if (!print) {
449 size_t length = sstream.str().size();
450 char *str = new char[length + 1];
451 spvCheck(!str, return SPV_ERROR_OUT_OF_MEMORY);
452 strncpy(str, sstream.str().c_str(), length + 1);
453 spv_text text = new spv_text_t();
454 spvCheck(!text, return SPV_ERROR_OUT_OF_MEMORY);
455 text->str = str;
456 text->length = length;
457 *pText = text;
458 }
459
460 return SPV_SUCCESS;
461}
462
463void spvBinaryDestroy(spv_binary binary) {
464 spvCheck(!binary, return );
465 if (binary->code) {
466 delete[] binary->code;
467 }
468 delete binary;
469}