• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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