blob: ff66308c56d31e7d358ddcb44db58cd83c2f788c [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"
David Netodb901b62015-10-27 16:14:40 -040030#include "endian.h"
David Netob5dc8fc2015-10-06 16:22:00 -040031#include "instruction.h"
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010032#include "opcode.h"
33#include "operand.h"
34#include "validate.h"
35
36#include <assert.h>
37#include <string.h>
38#include <stdio.h>
39
40#include <vector>
41
Lei Zhang40056702015-09-11 14:31:27 -040042#define spvCheckReturn(expression) \
43 if (spv_result_t error = (expression)) return error;
44
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010045spv_result_t spvValidateOperandsString(const uint32_t *words,
46 const uint16_t wordCount,
47 spv_position position,
48 spv_diagnostic *pDiagnostic) {
49 const char *str = (const char *)words;
50 uint64_t strWordCount = strlen(str) / sizeof(uint32_t) + 1;
Lei Zhang40056702015-09-11 14:31:27 -040051 if (strWordCount < wordCount) {
52 DIAGNOSTIC << "Instruction word count is too short, string extends past "
53 "end of instruction.";
54 return SPV_WARNING;
55 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010056 return SPV_SUCCESS;
57}
58
59spv_result_t spvValidateOperandsLiteral(const uint32_t *words,
60 const uint32_t length,
61 const uint16_t maxLength,
62 spv_position position,
63 spv_diagnostic *pDiagnostic) {
64 // NOTE: A literal could either be a number consuming up to 2 words or a
65 // null terminated string.
66 (void)words;
67 (void)length;
68 (void)maxLength;
69 (void)position;
70 (void)pDiagnostic;
71 return SPV_UNSUPPORTED;
72}
73
74spv_result_t spvValidateOperandValue(const spv_operand_type_t type,
75 const uint32_t word,
76 const spv_operand_table operandTable,
77 spv_position position,
78 spv_diagnostic *pDiagnostic) {
79 switch (type) {
80 case SPV_OPERAND_TYPE_ID:
81 case SPV_OPERAND_TYPE_RESULT_ID: {
82 // NOTE: ID's are validated in SPV_VALIDATION_LEVEL_1, this is
83 // SPV_VALIDATION_LEVEL_0
84 } break;
Lei Zhang6483bd72015-10-14 17:02:39 -040085 case SPV_OPERAND_TYPE_LITERAL_INTEGER: {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010086 // NOTE: Implicitly valid as they are encoded as 32 bit value
87 } break;
88 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
89 case SPV_OPERAND_TYPE_EXECUTION_MODEL:
90 case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
91 case SPV_OPERAND_TYPE_MEMORY_MODEL:
92 case SPV_OPERAND_TYPE_EXECUTION_MODE:
93 case SPV_OPERAND_TYPE_STORAGE_CLASS:
94 case SPV_OPERAND_TYPE_DIMENSIONALITY:
95 case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
96 case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
97 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
98 case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
99 case SPV_OPERAND_TYPE_LINKAGE_TYPE:
100 case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
101 case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
102 case SPV_OPERAND_TYPE_DECORATION:
103 case SPV_OPERAND_TYPE_BUILT_IN:
104 case SPV_OPERAND_TYPE_SELECTION_CONTROL:
105 case SPV_OPERAND_TYPE_LOOP_CONTROL:
106 case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
107 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS:
David Neto78c3b432015-08-27 13:03:52 -0400108 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100109 case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
110 case SPV_OPERAND_TYPE_GROUP_OPERATION:
111 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
David Neto47994822015-08-27 13:11:01 -0400112 case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100113 spv_operand_desc operandEntry = nullptr;
114 spv_result_t error =
115 spvOperandTableValueLookup(operandTable, type, word, &operandEntry);
Lei Zhang40056702015-09-11 14:31:27 -0400116 if (error) {
117 DIAGNOSTIC << "Invalid '" << spvOperandTypeStr(type) << "' operand '"
118 << word << "'.";
119 return error;
120 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100121 } break;
122 default:
123 assert(0 && "Invalid operand types should already have been caught!");
124 }
125 return SPV_SUCCESS;
126}
127
128spv_result_t spvValidateBasic(const spv_instruction_t *pInsts,
129 const uint64_t instCount,
130 const spv_opcode_table opcodeTable,
131 const spv_operand_table operandTable,
132 spv_position position,
133 spv_diagnostic *pDiagnostic) {
134 for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
David Netob5dc8fc2015-10-06 16:22:00 -0400135 const uint32_t *words = pInsts[instIndex].words.data();
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100136 uint16_t wordCount;
137 Op opcode;
138 spvOpcodeSplit(words[0], &wordCount, &opcode);
139
140 spv_opcode_desc opcodeEntry = nullptr;
Lei Zhang40056702015-09-11 14:31:27 -0400141 if (spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry)) {
142 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
143 return SPV_ERROR_INVALID_BINARY;
144 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100145 position->index++;
146
Lei Zhang40056702015-09-11 14:31:27 -0400147 if (opcodeEntry->numTypes > wordCount) {
148 DIAGNOSTIC << "Instruction word count '" << wordCount
149 << "' is not small, expected at least '"
150 << opcodeEntry->numTypes << "'.";
151 return SPV_ERROR_INVALID_BINARY;
152 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100153
154 spv_operand_desc operandEntry = nullptr;
David Netob5dc8fc2015-10-06 16:22:00 -0400155 for (uint16_t index = 1; index < pInsts[instIndex].words.size();
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100156 ++index, position->index++) {
157 const uint32_t word = words[index];
David Neto78c3b432015-08-27 13:03:52 -0400158
159 // TODO(dneto): This strategy is inadequate for dealing with operations
160 // with varying kinds or numbers of logical operands. See the definition
161 // of spvBinaryOperandInfo for more.
162 // We should really parse the instruction and capture and use
163 // the elaborated list of logical operands generated as a side effect
164 // of the parse.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100165 spv_operand_type_t type = spvBinaryOperandInfo(
166 word, index, opcodeEntry, operandTable, &operandEntry);
167 if (SPV_OPERAND_TYPE_LITERAL_STRING == type) {
168 spvCheckReturn(spvValidateOperandsString(
169 words + index, wordCount - index, position, pDiagnostic));
170 // NOTE: String literals are always at the end of Opcodes
171 break;
Lei Zhang6483bd72015-10-14 17:02:39 -0400172 } else if (SPV_OPERAND_TYPE_LITERAL_INTEGER == type) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100173 spvCheckReturn(spvValidateOperandsLiteral(
174 words + index, wordCount - index, 2, position, pDiagnostic));
175 } else {
176 spvCheckReturn(spvValidateOperandValue(type, word, operandTable,
177 position, pDiagnostic));
178 }
179 }
180 }
181
182 return SPV_SUCCESS;
183}
184
185spv_result_t spvValidateIDs(const spv_instruction_t *pInsts,
186 const uint64_t count, const uint32_t bound,
187 const spv_opcode_table opcodeTable,
188 const spv_operand_table operandTable,
189 const spv_ext_inst_table extInstTable,
190 spv_position position,
191 spv_diagnostic *pDiagnostic) {
192 std::vector<spv_id_info_t> idUses;
193 std::vector<spv_id_info_t> idDefs;
194
195 for (uint64_t instIndex = 0; instIndex < count; ++instIndex) {
David Netob5dc8fc2015-10-06 16:22:00 -0400196 const uint32_t *words = pInsts[instIndex].words.data();
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100197 Op opcode;
198 spvOpcodeSplit(words[0], nullptr, &opcode);
199
200 spv_opcode_desc opcodeEntry = nullptr;
Lei Zhang40056702015-09-11 14:31:27 -0400201 if (spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry)) {
202 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
203 return SPV_ERROR_INVALID_BINARY;
204 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100205
206 spv_operand_desc operandEntry = nullptr;
207 position->index++; // NOTE: Account for Opcode word
David Netob5dc8fc2015-10-06 16:22:00 -0400208 for (uint16_t index = 1; index < pInsts[instIndex].words.size();
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100209 ++index, position->index++) {
210 const uint32_t word = words[index];
211
212 spv_operand_type_t type = spvBinaryOperandInfo(
213 word, index, opcodeEntry, operandTable, &operandEntry);
214
215 if (SPV_OPERAND_TYPE_RESULT_ID == type || SPV_OPERAND_TYPE_ID == type) {
Lei Zhang40056702015-09-11 14:31:27 -0400216 if (0 == word) {
217 DIAGNOSTIC << "Invalid ID of '0' is not allowed.";
218 return SPV_ERROR_INVALID_ID;
219 }
220 if (bound < word) {
221 DIAGNOSTIC << "Invalid ID '" << word << "' exceeds the bound '"
222 << bound << "'.";
223 return SPV_ERROR_INVALID_ID;
224 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100225 }
226
227 if (SPV_OPERAND_TYPE_RESULT_ID == type) {
228 idDefs.push_back(
229 {word, opcodeEntry->opcode, &pInsts[instIndex], *position});
230 }
231
232 if (SPV_OPERAND_TYPE_ID == type) {
233 idUses.push_back({word, opcodeEntry->opcode, nullptr, *position});
234 }
235 }
236 }
237
238 // NOTE: Error on redefined ID
239 for (size_t outerIndex = 0; outerIndex < idDefs.size(); ++outerIndex) {
240 for (size_t innerIndex = 0; innerIndex < idDefs.size(); ++innerIndex) {
241 if (outerIndex == innerIndex) {
242 continue;
243 }
244 if (idDefs[outerIndex].id == idDefs[innerIndex].id) {
245 DIAGNOSTIC << "Multiply defined ID '" << idDefs[outerIndex].id << "'.";
246 return SPV_ERROR_INVALID_ID;
247 }
248 }
249 }
250
251 // NOTE: Validate ID usage, including use of undefined ID's
252 position->index = SPV_INDEX_INSTRUCTION;
Lei Zhang40056702015-09-11 14:31:27 -0400253 if (spvValidateInstructionIDs(pInsts, count, idUses.data(), idUses.size(),
254 idDefs.data(), idDefs.size(), opcodeTable,
255 operandTable, extInstTable, position,
256 pDiagnostic))
257 return SPV_ERROR_INVALID_ID;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100258
259 return SPV_SUCCESS;
260}
261
262spv_result_t spvValidate(const spv_binary binary,
263 const spv_opcode_table opcodeTable,
264 const spv_operand_table operandTable,
265 const spv_ext_inst_table extInstTable,
266 const uint32_t options, spv_diagnostic *pDiagnostic) {
Lei Zhang40056702015-09-11 14:31:27 -0400267 if (!opcodeTable || !operandTable) return SPV_ERROR_INVALID_TABLE;
268 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100269
270 spv_endianness_t endian;
271 spv_position_t position = {};
Lei Zhang40056702015-09-11 14:31:27 -0400272 if (spvBinaryEndianness(binary, &endian)) {
273 DIAGNOSTIC << "Invalid SPIR-V magic number.";
274 return SPV_ERROR_INVALID_BINARY;
275 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100276
277 spv_header_t header;
Lei Zhang40056702015-09-11 14:31:27 -0400278 if (spvBinaryHeaderGet(binary, endian, &header)) {
279 DIAGNOSTIC << "Invalid SPIR-V header.";
280 return SPV_ERROR_INVALID_BINARY;
281 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100282
283 // NOTE: Copy each instruction for easier processing
284 std::vector<spv_instruction_t> instructions;
285 uint64_t index = SPV_INDEX_INSTRUCTION;
286 while (index < binary->wordCount) {
287 uint16_t wordCount;
288 Op opcode;
289 spvOpcodeSplit(spvFixWord(binary->code[index], endian), &wordCount,
290 &opcode);
291 spv_instruction_t inst;
292 spvInstructionCopy(&binary->code[index], opcode, wordCount, endian, &inst);
293 instructions.push_back(inst);
294 index += wordCount;
295 }
296
297 if (spvIsInBitfield(SPV_VALIDATE_BASIC_BIT, options)) {
298 position.index = SPV_INDEX_INSTRUCTION;
299 // TODO: Imcomplete implementation
300 spvCheckReturn(spvValidateBasic(instructions.data(), instructions.size(),
301 opcodeTable, operandTable, &position,
302 pDiagnostic));
303 }
304
305 if (spvIsInBitfield(SPV_VALIDATE_LAYOUT_BIT, options)) {
306 position.index = SPV_INDEX_INSTRUCTION;
307 // TODO: spvBinaryValidateLayout
308 }
309
310 if (spvIsInBitfield(SPV_VALIDATE_ID_BIT, options)) {
311 position.index = SPV_INDEX_INSTRUCTION;
312 spvCheckReturn(spvValidateIDs(instructions.data(), instructions.size(),
313 header.bound, opcodeTable, operandTable,
314 extInstTable, &position, pDiagnostic));
315 }
316
317 if (spvIsInBitfield(SPV_VALIDATE_RULES_BIT, options)) {
318 position.index = SPV_INDEX_INSTRUCTION;
319 // TODO: Specified validation rules...
320 }
321
322 return SPV_SUCCESS;
323}