blob: 99e868ed8ed9dd9d086b2ea46b46427842621011 [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 Netob5dc8fc2015-10-06 16:22:00 -040030#include "instruction.h"
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010031#include "opcode.h"
32#include "operand.h"
33#include "validate.h"
34
35#include <assert.h>
36#include <string.h>
37#include <stdio.h>
38
39#include <vector>
40
Lei Zhang40056702015-09-11 14:31:27 -040041#define spvCheckReturn(expression) \
42 if (spv_result_t error = (expression)) return error;
43
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010044spv_result_t spvValidateOperandsString(const uint32_t *words,
45 const uint16_t wordCount,
46 spv_position position,
47 spv_diagnostic *pDiagnostic) {
48 const char *str = (const char *)words;
49 uint64_t strWordCount = strlen(str) / sizeof(uint32_t) + 1;
Lei Zhang40056702015-09-11 14:31:27 -040050 if (strWordCount < wordCount) {
51 DIAGNOSTIC << "Instruction word count is too short, string extends past "
52 "end of instruction.";
53 return SPV_WARNING;
54 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010055 return SPV_SUCCESS;
56}
57
58spv_result_t spvValidateOperandsLiteral(const uint32_t *words,
59 const uint32_t length,
60 const uint16_t maxLength,
61 spv_position position,
62 spv_diagnostic *pDiagnostic) {
63 // NOTE: A literal could either be a number consuming up to 2 words or a
64 // null terminated string.
65 (void)words;
66 (void)length;
67 (void)maxLength;
68 (void)position;
69 (void)pDiagnostic;
70 return SPV_UNSUPPORTED;
71}
72
73spv_result_t spvValidateOperandValue(const spv_operand_type_t type,
74 const uint32_t word,
75 const spv_operand_table operandTable,
76 spv_position position,
77 spv_diagnostic *pDiagnostic) {
78 switch (type) {
79 case SPV_OPERAND_TYPE_ID:
80 case SPV_OPERAND_TYPE_RESULT_ID: {
81 // NOTE: ID's are validated in SPV_VALIDATION_LEVEL_1, this is
82 // SPV_VALIDATION_LEVEL_0
83 } break;
84 case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
85 // NOTE: Implicitly valid as they are encoded as 32 bit value
86 } break;
87 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
88 case SPV_OPERAND_TYPE_EXECUTION_MODEL:
89 case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
90 case SPV_OPERAND_TYPE_MEMORY_MODEL:
91 case SPV_OPERAND_TYPE_EXECUTION_MODE:
92 case SPV_OPERAND_TYPE_STORAGE_CLASS:
93 case SPV_OPERAND_TYPE_DIMENSIONALITY:
94 case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
95 case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
96 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
97 case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
98 case SPV_OPERAND_TYPE_LINKAGE_TYPE:
99 case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
100 case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
101 case SPV_OPERAND_TYPE_DECORATION:
102 case SPV_OPERAND_TYPE_BUILT_IN:
103 case SPV_OPERAND_TYPE_SELECTION_CONTROL:
104 case SPV_OPERAND_TYPE_LOOP_CONTROL:
105 case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
106 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS:
David Neto78c3b432015-08-27 13:03:52 -0400107 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100108 case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
109 case SPV_OPERAND_TYPE_GROUP_OPERATION:
110 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
David Neto47994822015-08-27 13:11:01 -0400111 case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100112 spv_operand_desc operandEntry = nullptr;
113 spv_result_t error =
114 spvOperandTableValueLookup(operandTable, type, word, &operandEntry);
Lei Zhang40056702015-09-11 14:31:27 -0400115 if (error) {
116 DIAGNOSTIC << "Invalid '" << spvOperandTypeStr(type) << "' operand '"
117 << word << "'.";
118 return error;
119 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100120 } break;
121 default:
122 assert(0 && "Invalid operand types should already have been caught!");
123 }
124 return SPV_SUCCESS;
125}
126
127spv_result_t spvValidateBasic(const spv_instruction_t *pInsts,
128 const uint64_t instCount,
129 const spv_opcode_table opcodeTable,
130 const spv_operand_table operandTable,
131 spv_position position,
132 spv_diagnostic *pDiagnostic) {
133 for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
David Netob5dc8fc2015-10-06 16:22:00 -0400134 const uint32_t *words = pInsts[instIndex].words.data();
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100135 uint16_t wordCount;
136 Op opcode;
137 spvOpcodeSplit(words[0], &wordCount, &opcode);
138
139 spv_opcode_desc opcodeEntry = nullptr;
Lei Zhang40056702015-09-11 14:31:27 -0400140 if (spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry)) {
141 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
142 return SPV_ERROR_INVALID_BINARY;
143 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100144 position->index++;
145
Lei Zhang40056702015-09-11 14:31:27 -0400146 if (opcodeEntry->numTypes > wordCount) {
147 DIAGNOSTIC << "Instruction word count '" << wordCount
148 << "' is not small, expected at least '"
149 << opcodeEntry->numTypes << "'.";
150 return SPV_ERROR_INVALID_BINARY;
151 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100152
153 spv_operand_desc operandEntry = nullptr;
David Netob5dc8fc2015-10-06 16:22:00 -0400154 for (uint16_t index = 1; index < pInsts[instIndex].words.size();
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100155 ++index, position->index++) {
156 const uint32_t word = words[index];
David Neto78c3b432015-08-27 13:03:52 -0400157
158 // TODO(dneto): This strategy is inadequate for dealing with operations
159 // with varying kinds or numbers of logical operands. See the definition
160 // of spvBinaryOperandInfo for more.
161 // We should really parse the instruction and capture and use
162 // the elaborated list of logical operands generated as a side effect
163 // of the parse.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100164 spv_operand_type_t type = spvBinaryOperandInfo(
165 word, index, opcodeEntry, operandTable, &operandEntry);
166 if (SPV_OPERAND_TYPE_LITERAL_STRING == type) {
167 spvCheckReturn(spvValidateOperandsString(
168 words + index, wordCount - index, position, pDiagnostic));
169 // NOTE: String literals are always at the end of Opcodes
170 break;
David Neto561dc4e2015-09-25 14:23:29 -0400171 } else if (SPV_OPERAND_TYPE_LITERAL_NUMBER == type) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100172 spvCheckReturn(spvValidateOperandsLiteral(
173 words + index, wordCount - index, 2, position, pDiagnostic));
174 } else {
175 spvCheckReturn(spvValidateOperandValue(type, word, operandTable,
176 position, pDiagnostic));
177 }
178 }
179 }
180
181 return SPV_SUCCESS;
182}
183
184spv_result_t spvValidateIDs(const spv_instruction_t *pInsts,
185 const uint64_t count, const uint32_t bound,
186 const spv_opcode_table opcodeTable,
187 const spv_operand_table operandTable,
188 const spv_ext_inst_table extInstTable,
189 spv_position position,
190 spv_diagnostic *pDiagnostic) {
191 std::vector<spv_id_info_t> idUses;
192 std::vector<spv_id_info_t> idDefs;
193
194 for (uint64_t instIndex = 0; instIndex < count; ++instIndex) {
David Netob5dc8fc2015-10-06 16:22:00 -0400195 const uint32_t *words = pInsts[instIndex].words.data();
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100196 Op opcode;
197 spvOpcodeSplit(words[0], nullptr, &opcode);
198
199 spv_opcode_desc opcodeEntry = nullptr;
Lei Zhang40056702015-09-11 14:31:27 -0400200 if (spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry)) {
201 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
202 return SPV_ERROR_INVALID_BINARY;
203 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100204
205 spv_operand_desc operandEntry = nullptr;
206 position->index++; // NOTE: Account for Opcode word
David Netob5dc8fc2015-10-06 16:22:00 -0400207 for (uint16_t index = 1; index < pInsts[instIndex].words.size();
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100208 ++index, position->index++) {
209 const uint32_t word = words[index];
210
211 spv_operand_type_t type = spvBinaryOperandInfo(
212 word, index, opcodeEntry, operandTable, &operandEntry);
213
214 if (SPV_OPERAND_TYPE_RESULT_ID == type || SPV_OPERAND_TYPE_ID == type) {
Lei Zhang40056702015-09-11 14:31:27 -0400215 if (0 == word) {
216 DIAGNOSTIC << "Invalid ID of '0' is not allowed.";
217 return SPV_ERROR_INVALID_ID;
218 }
219 if (bound < word) {
220 DIAGNOSTIC << "Invalid ID '" << word << "' exceeds the bound '"
221 << bound << "'.";
222 return SPV_ERROR_INVALID_ID;
223 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100224 }
225
226 if (SPV_OPERAND_TYPE_RESULT_ID == type) {
227 idDefs.push_back(
228 {word, opcodeEntry->opcode, &pInsts[instIndex], *position});
229 }
230
231 if (SPV_OPERAND_TYPE_ID == type) {
232 idUses.push_back({word, opcodeEntry->opcode, nullptr, *position});
233 }
234 }
235 }
236
237 // NOTE: Error on redefined ID
238 for (size_t outerIndex = 0; outerIndex < idDefs.size(); ++outerIndex) {
239 for (size_t innerIndex = 0; innerIndex < idDefs.size(); ++innerIndex) {
240 if (outerIndex == innerIndex) {
241 continue;
242 }
243 if (idDefs[outerIndex].id == idDefs[innerIndex].id) {
244 DIAGNOSTIC << "Multiply defined ID '" << idDefs[outerIndex].id << "'.";
245 return SPV_ERROR_INVALID_ID;
246 }
247 }
248 }
249
250 // NOTE: Validate ID usage, including use of undefined ID's
251 position->index = SPV_INDEX_INSTRUCTION;
Lei Zhang40056702015-09-11 14:31:27 -0400252 if (spvValidateInstructionIDs(pInsts, count, idUses.data(), idUses.size(),
253 idDefs.data(), idDefs.size(), opcodeTable,
254 operandTable, extInstTable, position,
255 pDiagnostic))
256 return SPV_ERROR_INVALID_ID;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100257
258 return SPV_SUCCESS;
259}
260
261spv_result_t spvValidate(const spv_binary binary,
262 const spv_opcode_table opcodeTable,
263 const spv_operand_table operandTable,
264 const spv_ext_inst_table extInstTable,
265 const uint32_t options, spv_diagnostic *pDiagnostic) {
Lei Zhang40056702015-09-11 14:31:27 -0400266 if (!opcodeTable || !operandTable) return SPV_ERROR_INVALID_TABLE;
267 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100268
269 spv_endianness_t endian;
270 spv_position_t position = {};
Lei Zhang40056702015-09-11 14:31:27 -0400271 if (spvBinaryEndianness(binary, &endian)) {
272 DIAGNOSTIC << "Invalid SPIR-V magic number.";
273 return SPV_ERROR_INVALID_BINARY;
274 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100275
276 spv_header_t header;
Lei Zhang40056702015-09-11 14:31:27 -0400277 if (spvBinaryHeaderGet(binary, endian, &header)) {
278 DIAGNOSTIC << "Invalid SPIR-V header.";
279 return SPV_ERROR_INVALID_BINARY;
280 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100281
282 // NOTE: Copy each instruction for easier processing
283 std::vector<spv_instruction_t> instructions;
284 uint64_t index = SPV_INDEX_INSTRUCTION;
285 while (index < binary->wordCount) {
286 uint16_t wordCount;
287 Op opcode;
288 spvOpcodeSplit(spvFixWord(binary->code[index], endian), &wordCount,
289 &opcode);
290 spv_instruction_t inst;
291 spvInstructionCopy(&binary->code[index], opcode, wordCount, endian, &inst);
292 instructions.push_back(inst);
293 index += wordCount;
294 }
295
296 if (spvIsInBitfield(SPV_VALIDATE_BASIC_BIT, options)) {
297 position.index = SPV_INDEX_INSTRUCTION;
298 // TODO: Imcomplete implementation
299 spvCheckReturn(spvValidateBasic(instructions.data(), instructions.size(),
300 opcodeTable, operandTable, &position,
301 pDiagnostic));
302 }
303
304 if (spvIsInBitfield(SPV_VALIDATE_LAYOUT_BIT, options)) {
305 position.index = SPV_INDEX_INSTRUCTION;
306 // TODO: spvBinaryValidateLayout
307 }
308
309 if (spvIsInBitfield(SPV_VALIDATE_ID_BIT, options)) {
310 position.index = SPV_INDEX_INSTRUCTION;
311 spvCheckReturn(spvValidateIDs(instructions.data(), instructions.size(),
312 header.bound, opcodeTable, operandTable,
313 extInstTable, &position, pDiagnostic));
314 }
315
316 if (spvIsInBitfield(SPV_VALIDATE_RULES_BIT, options)) {
317 position.index = SPV_INDEX_INSTRUCTION;
318 // TODO: Specified validation rules...
319 }
320
321 return SPV_SUCCESS;
322}