blob: 07ecb79fa47f4c83016636bf7c8f2b5da8915d26 [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:
102 case SPV_OPERAND_TYPE_MEMORY_ACCESS:
103 case SPV_OPERAND_TYPE_EXECUTION_SCOPE:
104 case SPV_OPERAND_TYPE_GROUP_OPERATION:
105 case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
106 case SPV_OPERAND_TYPE_KERENL_PROFILING_INFO: {
107 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
138 spvCheck(opcodeEntry->wordCount > wordCount,
139 DIAGNOSTIC << "Instruction word count '" << wordCount
140 << "' is not small, expected at least '"
141 << opcodeEntry->wordCount << "'.";
142 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];
148 spv_operand_type_t type = spvBinaryOperandInfo(
149 word, index, opcodeEntry, operandTable, &operandEntry);
150 if (SPV_OPERAND_TYPE_LITERAL_STRING == type) {
151 spvCheckReturn(spvValidateOperandsString(
152 words + index, wordCount - index, position, pDiagnostic));
153 // NOTE: String literals are always at the end of Opcodes
154 break;
155 } else if (SPV_OPERAND_TYPE_LITERAL == type) {
156 spvCheckReturn(spvValidateOperandsLiteral(
157 words + index, wordCount - index, 2, position, pDiagnostic));
158 } else {
159 spvCheckReturn(spvValidateOperandValue(type, word, operandTable,
160 position, pDiagnostic));
161 }
162 }
163 }
164
165 return SPV_SUCCESS;
166}
167
168spv_result_t spvValidateIDs(const spv_instruction_t *pInsts,
169 const uint64_t count, const uint32_t bound,
170 const spv_opcode_table opcodeTable,
171 const spv_operand_table operandTable,
172 const spv_ext_inst_table extInstTable,
173 spv_position position,
174 spv_diagnostic *pDiagnostic) {
175 std::vector<spv_id_info_t> idUses;
176 std::vector<spv_id_info_t> idDefs;
177
178 for (uint64_t instIndex = 0; instIndex < count; ++instIndex) {
179 const uint32_t *words = pInsts[instIndex].words;
180 Op opcode;
181 spvOpcodeSplit(words[0], nullptr, &opcode);
182
183 spv_opcode_desc opcodeEntry = nullptr;
184 spvCheck(spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry),
185 DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
186 return SPV_ERROR_INVALID_BINARY);
187
188 spv_operand_desc operandEntry = nullptr;
189 position->index++; // NOTE: Account for Opcode word
190 for (uint16_t index = 1; index < pInsts[instIndex].wordCount;
191 ++index, position->index++) {
192 const uint32_t word = words[index];
193
194 spv_operand_type_t type = spvBinaryOperandInfo(
195 word, index, opcodeEntry, operandTable, &operandEntry);
196
197 if (SPV_OPERAND_TYPE_RESULT_ID == type || SPV_OPERAND_TYPE_ID == type) {
198 spvCheck(0 == word, DIAGNOSTIC << "Invalid ID of '0' is not allowed.";
199 return SPV_ERROR_INVALID_ID);
200 spvCheck(bound < word, DIAGNOSTIC << "Invalid ID '" << word
201 << "' exceeds the bound '" << bound
202 << "'.";
203 return SPV_ERROR_INVALID_ID);
204 }
205
206 if (SPV_OPERAND_TYPE_RESULT_ID == type) {
207 idDefs.push_back(
208 {word, opcodeEntry->opcode, &pInsts[instIndex], *position});
209 }
210
211 if (SPV_OPERAND_TYPE_ID == type) {
212 idUses.push_back({word, opcodeEntry->opcode, nullptr, *position});
213 }
214 }
215 }
216
217 // NOTE: Error on redefined ID
218 for (size_t outerIndex = 0; outerIndex < idDefs.size(); ++outerIndex) {
219 for (size_t innerIndex = 0; innerIndex < idDefs.size(); ++innerIndex) {
220 if (outerIndex == innerIndex) {
221 continue;
222 }
223 if (idDefs[outerIndex].id == idDefs[innerIndex].id) {
224 DIAGNOSTIC << "Multiply defined ID '" << idDefs[outerIndex].id << "'.";
225 return SPV_ERROR_INVALID_ID;
226 }
227 }
228 }
229
230 // NOTE: Validate ID usage, including use of undefined ID's
231 position->index = SPV_INDEX_INSTRUCTION;
232 spvCheck(spvValidateInstructionIDs(pInsts, count, idUses.data(),
233 idUses.size(), idDefs.data(),
234 idDefs.size(), opcodeTable, operandTable,
235 extInstTable, position, pDiagnostic),
236 return SPV_ERROR_INVALID_ID);
237
238 return SPV_SUCCESS;
239}
240
241spv_result_t spvValidate(const spv_binary binary,
242 const spv_opcode_table opcodeTable,
243 const spv_operand_table operandTable,
244 const spv_ext_inst_table extInstTable,
245 const uint32_t options, spv_diagnostic *pDiagnostic) {
246 spvCheck(!opcodeTable || !operandTable, return SPV_ERROR_INVALID_TABLE);
247 spvCheck(!pDiagnostic, return SPV_ERROR_INVALID_DIAGNOSTIC);
248
249 spv_endianness_t endian;
250 spv_position_t position = {};
251 spvCheck(spvBinaryEndianness(binary, &endian),
252 DIAGNOSTIC << "Invalid SPIR-V magic number.";
253 return SPV_ERROR_INVALID_BINARY);
254
255 spv_header_t header;
256 spvCheck(spvBinaryHeaderGet(binary, endian, &header),
257 DIAGNOSTIC << "Invalid SPIR-V header.";
258 return SPV_ERROR_INVALID_BINARY);
259
260 // NOTE: Copy each instruction for easier processing
261 std::vector<spv_instruction_t> instructions;
262 uint64_t index = SPV_INDEX_INSTRUCTION;
263 while (index < binary->wordCount) {
264 uint16_t wordCount;
265 Op opcode;
266 spvOpcodeSplit(spvFixWord(binary->code[index], endian), &wordCount,
267 &opcode);
268 spv_instruction_t inst;
269 spvInstructionCopy(&binary->code[index], opcode, wordCount, endian, &inst);
270 instructions.push_back(inst);
271 index += wordCount;
272 }
273
274 if (spvIsInBitfield(SPV_VALIDATE_BASIC_BIT, options)) {
275 position.index = SPV_INDEX_INSTRUCTION;
276 // TODO: Imcomplete implementation
277 spvCheckReturn(spvValidateBasic(instructions.data(), instructions.size(),
278 opcodeTable, operandTable, &position,
279 pDiagnostic));
280 }
281
282 if (spvIsInBitfield(SPV_VALIDATE_LAYOUT_BIT, options)) {
283 position.index = SPV_INDEX_INSTRUCTION;
284 // TODO: spvBinaryValidateLayout
285 }
286
287 if (spvIsInBitfield(SPV_VALIDATE_ID_BIT, options)) {
288 position.index = SPV_INDEX_INSTRUCTION;
289 spvCheckReturn(spvValidateIDs(instructions.data(), instructions.size(),
290 header.bound, opcodeTable, operandTable,
291 extInstTable, &position, pDiagnostic));
292 }
293
294 if (spvIsInBitfield(SPV_VALIDATE_RULES_BIT, options)) {
295 position.index = SPV_INDEX_INSTRUCTION;
296 // TODO: Specified validation rules...
297 }
298
299 return SPV_SUCCESS;
300}