1 // Copyright (c) 2016 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Ensures Data Rules are followed according to the specifications.
16
17 #include "validate.h"
18
19 #include <cassert>
20 #include <sstream>
21 #include <string>
22
23 #include "diagnostic.h"
24 #include "opcode.h"
25 #include "operand.h"
26 #include "val/instruction.h"
27 #include "val/validation_state.h"
28
29 using libspirv::CapabilitySet;
30 using libspirv::DiagnosticStream;
31 using libspirv::ValidationState_t;
32
33 namespace {
34
35 // Validates that the number of components in the vector is valid.
36 // Vector types can only be parameterized as having 2, 3, or 4 components.
37 // If the Vector16 capability is added, 8 and 16 components are also allowed.
ValidateVecNumComponents(ValidationState_t & _,const spv_parsed_instruction_t * inst)38 spv_result_t ValidateVecNumComponents(ValidationState_t& _,
39 const spv_parsed_instruction_t* inst) {
40 // Operand 2 specifies the number of components in the vector.
41 const uint32_t num_components = inst->words[inst->operands[2].offset];
42 if (num_components == 2 || num_components == 3 || num_components == 4) {
43 return SPV_SUCCESS;
44 }
45 if (num_components == 8 || num_components == 16) {
46 if (_.HasCapability(SpvCapabilityVector16)) {
47 return SPV_SUCCESS;
48 }
49 return _.diag(SPV_ERROR_INVALID_DATA)
50 << "Having " << num_components << " components for "
51 << spvOpcodeString(static_cast<SpvOp>(inst->opcode))
52 << " requires the Vector16 capability";
53 }
54 return _.diag(SPV_ERROR_INVALID_DATA)
55 << "Illegal number of components (" << num_components << ") for "
56 << spvOpcodeString(static_cast<SpvOp>(inst->opcode));
57 }
58
59 // Validates that the number of bits specifed for a float type is valid.
60 // Scalar floating-point types can be parameterized only with 32-bits.
61 // Float16 capability allows using a 16-bit OpTypeFloat.
62 // Float16Buffer capability allows creation of a 16-bit OpTypeFloat.
63 // Float64 capability allows using a 64-bit OpTypeFloat.
ValidateFloatSize(ValidationState_t & _,const spv_parsed_instruction_t * inst)64 spv_result_t ValidateFloatSize(ValidationState_t& _,
65 const spv_parsed_instruction_t* inst) {
66 // Operand 1 is the number of bits for this float
67 const uint32_t num_bits = inst->words[inst->operands[1].offset];
68 if (num_bits == 32) {
69 return SPV_SUCCESS;
70 }
71 if (num_bits == 16) {
72 if (_.features().declare_float16_type) {
73 return SPV_SUCCESS;
74 }
75 return _.diag(SPV_ERROR_INVALID_DATA)
76 << "Using a 16-bit floating point "
77 << "type requires the Float16 or Float16Buffer capability,"
78 " or an extension that explicitly enables 16-bit floating point.";
79 }
80 if (num_bits == 64) {
81 if (_.HasCapability(SpvCapabilityFloat64)) {
82 return SPV_SUCCESS;
83 }
84 return _.diag(SPV_ERROR_INVALID_DATA)
85 << "Using a 64-bit floating point "
86 << "type requires the Float64 capability.";
87 }
88 return _.diag(SPV_ERROR_INVALID_DATA)
89 << "Invalid number of bits (" << num_bits << ") used for OpTypeFloat.";
90 }
91
92 // Validates that the number of bits specified for an Int type is valid.
93 // Scalar integer types can be parameterized only with 32-bits.
94 // Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit
95 // integers, respectively.
ValidateIntSize(ValidationState_t & _,const spv_parsed_instruction_t * inst)96 spv_result_t ValidateIntSize(ValidationState_t& _,
97 const spv_parsed_instruction_t* inst) {
98 // Operand 1 is the number of bits for this integer.
99 const uint32_t num_bits = inst->words[inst->operands[1].offset];
100 if (num_bits == 32) {
101 return SPV_SUCCESS;
102 }
103 if (num_bits == 8) {
104 if (_.HasCapability(SpvCapabilityInt8)) {
105 return SPV_SUCCESS;
106 }
107 return _.diag(SPV_ERROR_INVALID_DATA)
108 << "Using an 8-bit integer type requires the Int8 capability.";
109 }
110 if (num_bits == 16) {
111 if (_.features().declare_int16_type) {
112 return SPV_SUCCESS;
113 }
114 return _.diag(SPV_ERROR_INVALID_DATA)
115 << "Using a 16-bit integer type requires the Int16 capability,"
116 " or an extension that explicitly enables 16-bit integers.";
117 }
118 if (num_bits == 64) {
119 if (_.HasCapability(SpvCapabilityInt64)) {
120 return SPV_SUCCESS;
121 }
122 return _.diag(SPV_ERROR_INVALID_DATA)
123 << "Using a 64-bit integer type requires the Int64 capability.";
124 }
125 return _.diag(SPV_ERROR_INVALID_DATA) << "Invalid number of bits ("
126 << num_bits << ") used for OpTypeInt.";
127 }
128
129 // Validates that the matrix is parameterized with floating-point types.
ValidateMatrixColumnType(ValidationState_t & _,const spv_parsed_instruction_t * inst)130 spv_result_t ValidateMatrixColumnType(ValidationState_t& _,
131 const spv_parsed_instruction_t* inst) {
132 // Find the component type of matrix columns (must be vector).
133 // Operand 1 is the <id> of the type specified for matrix columns.
134 auto type_id = inst->words[inst->operands[1].offset];
135 auto col_type_instr = _.FindDef(type_id);
136 if (col_type_instr->opcode() != SpvOpTypeVector) {
137 return _.diag(SPV_ERROR_INVALID_ID)
138 << "Columns in a matrix must be of type vector.";
139 }
140
141 // Trace back once more to find out the type of components in the vector.
142 // Operand 1 is the <id> of the type of data in the vector.
143 auto comp_type_id =
144 col_type_instr->words()[col_type_instr->operands()[1].offset];
145 auto comp_type_instruction = _.FindDef(comp_type_id);
146 if (comp_type_instruction->opcode() != SpvOpTypeFloat) {
147 return _.diag(SPV_ERROR_INVALID_DATA) << "Matrix types can only be "
148 "parameterized with "
149 "floating-point types.";
150 }
151 return SPV_SUCCESS;
152 }
153
154 // Validates that the matrix has 2,3, or 4 columns.
ValidateMatrixNumCols(ValidationState_t & _,const spv_parsed_instruction_t * inst)155 spv_result_t ValidateMatrixNumCols(ValidationState_t& _,
156 const spv_parsed_instruction_t* inst) {
157 // Operand 2 is the number of columns in the matrix.
158 const uint32_t num_cols = inst->words[inst->operands[2].offset];
159 if (num_cols != 2 && num_cols != 3 && num_cols != 4) {
160 return _.diag(SPV_ERROR_INVALID_DATA) << "Matrix types can only be "
161 "parameterized as having only 2, "
162 "3, or 4 columns.";
163 }
164 return SPV_SUCCESS;
165 }
166
167 // Validates that OpSpecConstant specializes to either int or float type.
ValidateSpecConstNumerical(ValidationState_t & _,const spv_parsed_instruction_t * inst)168 spv_result_t ValidateSpecConstNumerical(ValidationState_t& _,
169 const spv_parsed_instruction_t* inst) {
170 // Operand 0 is the <id> of the type that we're specializing to.
171 auto type_id = inst->words[inst->operands[0].offset];
172 auto type_instruction = _.FindDef(type_id);
173 auto type_opcode = type_instruction->opcode();
174 if (type_opcode != SpvOpTypeInt && type_opcode != SpvOpTypeFloat) {
175 return _.diag(SPV_ERROR_INVALID_DATA) << "Specialization constant must be "
176 "an integer or floating-point "
177 "number.";
178 }
179 return SPV_SUCCESS;
180 }
181
182 // Validates that OpSpecConstantTrue and OpSpecConstantFalse specialize to bool.
ValidateSpecConstBoolean(ValidationState_t & _,const spv_parsed_instruction_t * inst)183 spv_result_t ValidateSpecConstBoolean(ValidationState_t& _,
184 const spv_parsed_instruction_t* inst) {
185 // Find out the type that we're specializing to.
186 auto type_instruction = _.FindDef(inst->type_id);
187 if (type_instruction->opcode() != SpvOpTypeBool) {
188 return _.diag(SPV_ERROR_INVALID_ID) << "Specialization constant must be "
189 "a boolean type.";
190 }
191 return SPV_SUCCESS;
192 }
193
194 // Records the <id> of the forward pointer to be used for validation.
ValidateForwardPointer(ValidationState_t & _,const spv_parsed_instruction_t * inst)195 spv_result_t ValidateForwardPointer(ValidationState_t& _,
196 const spv_parsed_instruction_t* inst) {
197 // Record the <id> (which is operand 0) to ensure it's used properly.
198 // OpTypeStruct can only include undefined pointers that are
199 // previously declared as a ForwardPointer
200 return (_.RegisterForwardPointer(inst->words[inst->operands[0].offset]));
201 }
202
203 // Validates that any undefined component of the struct is a forward pointer.
204 // It is valid to declare a forward pointer, and use its <id> as one of the
205 // components of a struct.
ValidateStruct(ValidationState_t & _,const spv_parsed_instruction_t * inst)206 spv_result_t ValidateStruct(ValidationState_t& _,
207 const spv_parsed_instruction_t* inst) {
208 // Struct components are operands 1, 2, etc.
209 for (unsigned i = 1; i < inst->num_operands; i++) {
210 auto type_id = inst->words[inst->operands[i].offset];
211 auto type_instruction = _.FindDef(type_id);
212 if (type_instruction == nullptr && !_.IsForwardPointer(type_id)) {
213 return _.diag(SPV_ERROR_INVALID_ID)
214 << "Forward reference operands in an OpTypeStruct must first be "
215 "declared using OpTypeForwardPointer.";
216 }
217 }
218 return SPV_SUCCESS;
219 }
220
221 } // anonymous namespace
222
223 namespace libspirv {
224
225 // Validates that Data Rules are followed according to the specifications.
226 // (Data Rules subsection of 2.16.1 Universal Validation Rules)
DataRulesPass(ValidationState_t & _,const spv_parsed_instruction_t * inst)227 spv_result_t DataRulesPass(ValidationState_t& _,
228 const spv_parsed_instruction_t* inst) {
229 switch (inst->opcode) {
230 case SpvOpTypeVector: {
231 if (auto error = ValidateVecNumComponents(_, inst)) return error;
232 break;
233 }
234 case SpvOpTypeFloat: {
235 if (auto error = ValidateFloatSize(_, inst)) return error;
236 break;
237 }
238 case SpvOpTypeInt: {
239 if (auto error = ValidateIntSize(_, inst)) return error;
240 break;
241 }
242 case SpvOpTypeMatrix: {
243 if (auto error = ValidateMatrixColumnType(_, inst)) return error;
244 if (auto error = ValidateMatrixNumCols(_, inst)) return error;
245 break;
246 }
247 // TODO(ehsan): Add OpSpecConstantComposite validation code.
248 // TODO(ehsan): Add OpSpecConstantOp validation code (if any).
249 case SpvOpSpecConstant: {
250 if (auto error = ValidateSpecConstNumerical(_, inst)) return error;
251 break;
252 }
253 case SpvOpSpecConstantFalse:
254 case SpvOpSpecConstantTrue: {
255 if (auto error = ValidateSpecConstBoolean(_, inst)) return error;
256 break;
257 }
258 case SpvOpTypeForwardPointer: {
259 if (auto error = ValidateForwardPointer(_, inst)) return error;
260 break;
261 }
262 case SpvOpTypeStruct: {
263 if (auto error = ValidateStruct(_, inst)) return error;
264 break;
265 }
266 // TODO(ehsan): add more data rules validation here.
267 default: { break; }
268 }
269
270 return SPV_SUCCESS;
271 }
272
273 } // namespace libspirv
274