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