blob: 4419a7260524aad56cade06290b38d1a137fd30d [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 "opcode.h"
31#include "operand.h"
32#include "validate.h"
33
34#include <assert.h>
35#include <string.h>
36#include <stdio.h>
37
38#include <vector>
39
Lei Zhang40056702015-09-11 14:31:27 -040040#define spvCheckReturn(expression) \
41 if (spv_result_t error = (expression)) return error;
42
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010043spv_result_t spvValidateOperandsString(const uint32_t *words,
44 const uint16_t wordCount,
45 spv_position position,
46 spv_diagnostic *pDiagnostic) {
47 const char *str = (const char *)words;
48 uint64_t strWordCount = strlen(str) / sizeof(uint32_t) + 1;
Lei Zhang40056702015-09-11 14:31:27 -040049 if (strWordCount < wordCount) {
50 DIAGNOSTIC << "Instruction word count is too short, string extends past "
51 "end of instruction.";
52 return SPV_WARNING;
53 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +010054 return SPV_SUCCESS;
55}
56
57spv_result_t spvValidateOperandsLiteral(const uint32_t *words,
58 const uint32_t length,
59 const uint16_t maxLength,
60 spv_position position,
61 spv_diagnostic *pDiagnostic) {
62 // NOTE: A literal could either be a number consuming up to 2 words or a
63 // null terminated string.
64 (void)words;
65 (void)length;
66 (void)maxLength;
67 (void)position;
68 (void)pDiagnostic;
69 return SPV_UNSUPPORTED;
70}
71
72spv_result_t spvValidateOperandValue(const spv_operand_type_t type,
73 const uint32_t word,
74 const spv_operand_table operandTable,
75 spv_position position,
76 spv_diagnostic *pDiagnostic) {
77 switch (type) {
78 case SPV_OPERAND_TYPE_ID:
79 case SPV_OPERAND_TYPE_RESULT_ID: {
80 // NOTE: ID's are validated in SPV_VALIDATION_LEVEL_1, this is
81 // SPV_VALIDATION_LEVEL_0
82 } break;
83 case SPV_OPERAND_TYPE_LITERAL_NUMBER: {
84 // NOTE: Implicitly valid as they are encoded as 32 bit value
85 } break;
86 case SPV_OPERAND_TYPE_SOURCE_LANGUAGE:
87 case SPV_OPERAND_TYPE_EXECUTION_MODEL:
88 case SPV_OPERAND_TYPE_ADDRESSING_MODEL:
89 case SPV_OPERAND_TYPE_MEMORY_MODEL:
90 case SPV_OPERAND_TYPE_EXECUTION_MODE:
91 case SPV_OPERAND_TYPE_STORAGE_CLASS:
92 case SPV_OPERAND_TYPE_DIMENSIONALITY:
93 case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE:
94 case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE:
95 case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
96 case SPV_OPERAND_TYPE_FP_ROUNDING_MODE:
97 case SPV_OPERAND_TYPE_LINKAGE_TYPE:
98 case SPV_OPERAND_TYPE_ACCESS_QUALIFIER:
99 case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE:
100 case SPV_OPERAND_TYPE_DECORATION:
101 case SPV_OPERAND_TYPE_BUILT_IN:
102 case SPV_OPERAND_TYPE_SELECTION_CONTROL:
103 case SPV_OPERAND_TYPE_LOOP_CONTROL:
104 case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
105 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS:
David Neto78c3b432015-08-27 13:03:52 -0400106 case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100107 case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
108 case SPV_OPERAND_TYPE_GROUP_OPERATION:
109 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
David Neto47994822015-08-27 13:11:01 -0400110 case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100111 spv_operand_desc operandEntry = nullptr;
112 spv_result_t error =
113 spvOperandTableValueLookup(operandTable, type, word, &operandEntry);
Lei Zhang40056702015-09-11 14:31:27 -0400114 if (error) {
115 DIAGNOSTIC << "Invalid '" << spvOperandTypeStr(type) << "' operand '"
116 << word << "'.";
117 return error;
118 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100119 } break;
120 default:
121 assert(0 && "Invalid operand types should already have been caught!");
122 }
123 return SPV_SUCCESS;
124}
125
126spv_result_t spvValidateBasic(const spv_instruction_t *pInsts,
127 const uint64_t instCount,
128 const spv_opcode_table opcodeTable,
129 const spv_operand_table operandTable,
130 spv_position position,
131 spv_diagnostic *pDiagnostic) {
132 for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
133 const uint32_t *words = pInsts[instIndex].words;
134 uint16_t wordCount;
135 Op opcode;
136 spvOpcodeSplit(words[0], &wordCount, &opcode);
137
138 spv_opcode_desc opcodeEntry = nullptr;
Lei Zhang40056702015-09-11 14:31:27 -0400139 if (spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry)) {
140 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
141 return SPV_ERROR_INVALID_BINARY;
142 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100143 position->index++;
144
Lei Zhang40056702015-09-11 14:31:27 -0400145 if (opcodeEntry->numTypes > wordCount) {
146 DIAGNOSTIC << "Instruction word count '" << wordCount
147 << "' is not small, expected at least '"
148 << opcodeEntry->numTypes << "'.";
149 return SPV_ERROR_INVALID_BINARY;
150 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100151
152 spv_operand_desc operandEntry = nullptr;
153 for (uint16_t index = 1; index < pInsts[instIndex].wordCount;
154 ++index, position->index++) {
155 const uint32_t word = words[index];
David Neto78c3b432015-08-27 13:03:52 -0400156
157 // TODO(dneto): This strategy is inadequate for dealing with operations
158 // with varying kinds or numbers of logical operands. See the definition
159 // of spvBinaryOperandInfo for more.
160 // We should really parse the instruction and capture and use
161 // the elaborated list of logical operands generated as a side effect
162 // of the parse.
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100163 spv_operand_type_t type = spvBinaryOperandInfo(
164 word, index, opcodeEntry, operandTable, &operandEntry);
165 if (SPV_OPERAND_TYPE_LITERAL_STRING == type) {
166 spvCheckReturn(spvValidateOperandsString(
167 words + index, wordCount - index, position, pDiagnostic));
168 // NOTE: String literals are always at the end of Opcodes
169 break;
David Neto561dc4e2015-09-25 14:23:29 -0400170 } else if (SPV_OPERAND_TYPE_LITERAL_NUMBER == type) {
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100171 spvCheckReturn(spvValidateOperandsLiteral(
172 words + index, wordCount - index, 2, position, pDiagnostic));
173 } else {
174 spvCheckReturn(spvValidateOperandValue(type, word, operandTable,
175 position, pDiagnostic));
176 }
177 }
178 }
179
180 return SPV_SUCCESS;
181}
182
183spv_result_t spvValidateIDs(const spv_instruction_t *pInsts,
184 const uint64_t count, const uint32_t bound,
185 const spv_opcode_table opcodeTable,
186 const spv_operand_table operandTable,
187 const spv_ext_inst_table extInstTable,
188 spv_position position,
189 spv_diagnostic *pDiagnostic) {
190 std::vector<spv_id_info_t> idUses;
191 std::vector<spv_id_info_t> idDefs;
192
193 for (uint64_t instIndex = 0; instIndex < count; ++instIndex) {
194 const uint32_t *words = pInsts[instIndex].words;
195 Op opcode;
196 spvOpcodeSplit(words[0], nullptr, &opcode);
197
198 spv_opcode_desc opcodeEntry = nullptr;
Lei Zhang40056702015-09-11 14:31:27 -0400199 if (spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry)) {
200 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
201 return SPV_ERROR_INVALID_BINARY;
202 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100203
204 spv_operand_desc operandEntry = nullptr;
205 position->index++; // NOTE: Account for Opcode word
206 for (uint16_t index = 1; index < pInsts[instIndex].wordCount;
207 ++index, position->index++) {
208 const uint32_t word = words[index];
209
210 spv_operand_type_t type = spvBinaryOperandInfo(
211 word, index, opcodeEntry, operandTable, &operandEntry);
212
213 if (SPV_OPERAND_TYPE_RESULT_ID == type || SPV_OPERAND_TYPE_ID == type) {
Lei Zhang40056702015-09-11 14:31:27 -0400214 if (0 == word) {
215 DIAGNOSTIC << "Invalid ID of '0' is not allowed.";
216 return SPV_ERROR_INVALID_ID;
217 }
218 if (bound < word) {
219 DIAGNOSTIC << "Invalid ID '" << word << "' exceeds the bound '"
220 << bound << "'.";
221 return SPV_ERROR_INVALID_ID;
222 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100223 }
224
225 if (SPV_OPERAND_TYPE_RESULT_ID == type) {
226 idDefs.push_back(
227 {word, opcodeEntry->opcode, &pInsts[instIndex], *position});
228 }
229
230 if (SPV_OPERAND_TYPE_ID == type) {
231 idUses.push_back({word, opcodeEntry->opcode, nullptr, *position});
232 }
233 }
234 }
235
236 // NOTE: Error on redefined ID
237 for (size_t outerIndex = 0; outerIndex < idDefs.size(); ++outerIndex) {
238 for (size_t innerIndex = 0; innerIndex < idDefs.size(); ++innerIndex) {
239 if (outerIndex == innerIndex) {
240 continue;
241 }
242 if (idDefs[outerIndex].id == idDefs[innerIndex].id) {
243 DIAGNOSTIC << "Multiply defined ID '" << idDefs[outerIndex].id << "'.";
244 return SPV_ERROR_INVALID_ID;
245 }
246 }
247 }
248
249 // NOTE: Validate ID usage, including use of undefined ID's
250 position->index = SPV_INDEX_INSTRUCTION;
Lei Zhang40056702015-09-11 14:31:27 -0400251 if (spvValidateInstructionIDs(pInsts, count, idUses.data(), idUses.size(),
252 idDefs.data(), idDefs.size(), opcodeTable,
253 operandTable, extInstTable, position,
254 pDiagnostic))
255 return SPV_ERROR_INVALID_ID;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100256
257 return SPV_SUCCESS;
258}
259
260spv_result_t spvValidate(const spv_binary binary,
261 const spv_opcode_table opcodeTable,
262 const spv_operand_table operandTable,
263 const spv_ext_inst_table extInstTable,
264 const uint32_t options, spv_diagnostic *pDiagnostic) {
Lei Zhang40056702015-09-11 14:31:27 -0400265 if (!opcodeTable || !operandTable) return SPV_ERROR_INVALID_TABLE;
266 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC;
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100267
268 spv_endianness_t endian;
269 spv_position_t position = {};
Lei Zhang40056702015-09-11 14:31:27 -0400270 if (spvBinaryEndianness(binary, &endian)) {
271 DIAGNOSTIC << "Invalid SPIR-V magic number.";
272 return SPV_ERROR_INVALID_BINARY;
273 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100274
275 spv_header_t header;
Lei Zhang40056702015-09-11 14:31:27 -0400276 if (spvBinaryHeaderGet(binary, endian, &header)) {
277 DIAGNOSTIC << "Invalid SPIR-V header.";
278 return SPV_ERROR_INVALID_BINARY;
279 }
Kenneth Benzie (Benie)83e5a292015-05-22 18:26:19 +0100280
281 // NOTE: Copy each instruction for easier processing
282 std::vector<spv_instruction_t> instructions;
283 uint64_t index = SPV_INDEX_INSTRUCTION;
284 while (index < binary->wordCount) {
285 uint16_t wordCount;
286 Op opcode;
287 spvOpcodeSplit(spvFixWord(binary->code[index], endian), &wordCount,
288 &opcode);
289 spv_instruction_t inst;
290 spvInstructionCopy(&binary->code[index], opcode, wordCount, endian, &inst);
291 instructions.push_back(inst);
292 index += wordCount;
293 }
294
295 if (spvIsInBitfield(SPV_VALIDATE_BASIC_BIT, options)) {
296 position.index = SPV_INDEX_INSTRUCTION;
297 // TODO: Imcomplete implementation
298 spvCheckReturn(spvValidateBasic(instructions.data(), instructions.size(),
299 opcodeTable, operandTable, &position,
300 pDiagnostic));
301 }
302
303 if (spvIsInBitfield(SPV_VALIDATE_LAYOUT_BIT, options)) {
304 position.index = SPV_INDEX_INSTRUCTION;
305 // TODO: spvBinaryValidateLayout
306 }
307
308 if (spvIsInBitfield(SPV_VALIDATE_ID_BIT, options)) {
309 position.index = SPV_INDEX_INSTRUCTION;
310 spvCheckReturn(spvValidateIDs(instructions.data(), instructions.size(),
311 header.bound, opcodeTable, operandTable,
312 extInstTable, &position, pDiagnostic));
313 }
314
315 if (spvIsInBitfield(SPV_VALIDATE_RULES_BIT, options)) {
316 position.index = SPV_INDEX_INSTRUCTION;
317 // TODO: Specified validation rules...
318 }
319
320 return SPV_SUCCESS;
321}