1 // Copyright (c) 2017 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 // Validates correctness of logical SPIR-V instructions.
16 
17 #include "source/val/validate.h"
18 
19 #include "source/diagnostic.h"
20 #include "source/opcode.h"
21 #include "source/val/instruction.h"
22 #include "source/val/validation_state.h"
23 
24 namespace spvtools {
25 namespace val {
26 
27 // Validates correctness of logical instructions.
LogicalsPass(ValidationState_t & _,const Instruction * inst)28 spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
29   const SpvOp opcode = inst->opcode();
30   const uint32_t result_type = inst->type_id();
31 
32   switch (opcode) {
33     case SpvOpAny:
34     case SpvOpAll: {
35       if (!_.IsBoolScalarType(result_type))
36         return _.diag(SPV_ERROR_INVALID_DATA, inst)
37                << "Expected bool scalar type as Result Type: "
38                << spvOpcodeString(opcode);
39 
40       const uint32_t vector_type = _.GetOperandTypeId(inst, 2);
41       if (!vector_type || !_.IsBoolVectorType(vector_type))
42         return _.diag(SPV_ERROR_INVALID_DATA, inst)
43                << "Expected operand to be vector bool: "
44                << spvOpcodeString(opcode);
45 
46       break;
47     }
48 
49     case SpvOpIsNan:
50     case SpvOpIsInf:
51     case SpvOpIsFinite:
52     case SpvOpIsNormal:
53     case SpvOpSignBitSet: {
54       if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
55         return _.diag(SPV_ERROR_INVALID_DATA, inst)
56                << "Expected bool scalar or vector type as Result Type: "
57                << spvOpcodeString(opcode);
58 
59       const uint32_t operand_type = _.GetOperandTypeId(inst, 2);
60       if (!operand_type || (!_.IsFloatScalarType(operand_type) &&
61                             !_.IsFloatVectorType(operand_type)))
62         return _.diag(SPV_ERROR_INVALID_DATA, inst)
63                << "Expected operand to be scalar or vector float: "
64                << spvOpcodeString(opcode);
65 
66       if (_.GetDimension(result_type) != _.GetDimension(operand_type))
67         return _.diag(SPV_ERROR_INVALID_DATA, inst)
68                << "Expected vector sizes of Result Type and the operand to be "
69                   "equal: "
70                << spvOpcodeString(opcode);
71 
72       break;
73     }
74 
75     case SpvOpFOrdEqual:
76     case SpvOpFUnordEqual:
77     case SpvOpFOrdNotEqual:
78     case SpvOpFUnordNotEqual:
79     case SpvOpFOrdLessThan:
80     case SpvOpFUnordLessThan:
81     case SpvOpFOrdGreaterThan:
82     case SpvOpFUnordGreaterThan:
83     case SpvOpFOrdLessThanEqual:
84     case SpvOpFUnordLessThanEqual:
85     case SpvOpFOrdGreaterThanEqual:
86     case SpvOpFUnordGreaterThanEqual:
87     case SpvOpLessOrGreater:
88     case SpvOpOrdered:
89     case SpvOpUnordered: {
90       if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
91         return _.diag(SPV_ERROR_INVALID_DATA, inst)
92                << "Expected bool scalar or vector type as Result Type: "
93                << spvOpcodeString(opcode);
94 
95       const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2);
96       if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) &&
97                                  !_.IsFloatVectorType(left_operand_type)))
98         return _.diag(SPV_ERROR_INVALID_DATA, inst)
99                << "Expected operands to be scalar or vector float: "
100                << spvOpcodeString(opcode);
101 
102       if (_.GetDimension(result_type) != _.GetDimension(left_operand_type))
103         return _.diag(SPV_ERROR_INVALID_DATA, inst)
104                << "Expected vector sizes of Result Type and the operands to be "
105                   "equal: "
106                << spvOpcodeString(opcode);
107 
108       if (left_operand_type != _.GetOperandTypeId(inst, 3))
109         return _.diag(SPV_ERROR_INVALID_DATA, inst)
110                << "Expected left and right operands to have the same type: "
111                << spvOpcodeString(opcode);
112 
113       break;
114     }
115 
116     case SpvOpLogicalEqual:
117     case SpvOpLogicalNotEqual:
118     case SpvOpLogicalOr:
119     case SpvOpLogicalAnd: {
120       if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
121         return _.diag(SPV_ERROR_INVALID_DATA, inst)
122                << "Expected bool scalar or vector type as Result Type: "
123                << spvOpcodeString(opcode);
124 
125       if (result_type != _.GetOperandTypeId(inst, 2) ||
126           result_type != _.GetOperandTypeId(inst, 3))
127         return _.diag(SPV_ERROR_INVALID_DATA, inst)
128                << "Expected both operands to be of Result Type: "
129                << spvOpcodeString(opcode);
130 
131       break;
132     }
133 
134     case SpvOpLogicalNot: {
135       if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
136         return _.diag(SPV_ERROR_INVALID_DATA, inst)
137                << "Expected bool scalar or vector type as Result Type: "
138                << spvOpcodeString(opcode);
139 
140       if (result_type != _.GetOperandTypeId(inst, 2))
141         return _.diag(SPV_ERROR_INVALID_DATA, inst)
142                << "Expected operand to be of Result Type: "
143                << spvOpcodeString(opcode);
144 
145       break;
146     }
147 
148     case SpvOpSelect: {
149       uint32_t dimension = 1;
150       {
151         const Instruction* type_inst = _.FindDef(result_type);
152         assert(type_inst);
153 
154         const auto composites = _.features().select_between_composites;
155         auto fail = [&_, composites, inst, opcode]() -> spv_result_t {
156           return _.diag(SPV_ERROR_INVALID_DATA, inst)
157                  << "Expected scalar or "
158                  << (composites ? "composite" : "vector")
159                  << " type as Result Type: " << spvOpcodeString(opcode);
160         };
161 
162         const SpvOp type_opcode = type_inst->opcode();
163         switch (type_opcode) {
164           case SpvOpTypePointer: {
165             if (_.addressing_model() == SpvAddressingModelLogical &&
166                 !_.features().variable_pointers &&
167                 !_.features().variable_pointers_storage_buffer)
168               return _.diag(SPV_ERROR_INVALID_DATA, inst)
169                      << "Using pointers with OpSelect requires capability "
170                      << "VariablePointers or VariablePointersStorageBuffer";
171             break;
172           }
173 
174           case SpvOpTypeVector: {
175             dimension = type_inst->word(3);
176             break;
177           }
178 
179           case SpvOpTypeBool:
180           case SpvOpTypeInt:
181           case SpvOpTypeFloat: {
182             break;
183           }
184 
185           // Not RuntimeArray because of other rules.
186           case SpvOpTypeArray:
187           case SpvOpTypeMatrix:
188           case SpvOpTypeStruct: {
189             if (!composites) return fail();
190             break;
191           };
192 
193           default:
194             return fail();
195         }
196 
197         const uint32_t condition_type = _.GetOperandTypeId(inst, 2);
198         const uint32_t left_type = _.GetOperandTypeId(inst, 3);
199         const uint32_t right_type = _.GetOperandTypeId(inst, 4);
200 
201         if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
202                                 !_.IsBoolVectorType(condition_type)))
203           return _.diag(SPV_ERROR_INVALID_DATA, inst)
204                  << "Expected bool scalar or vector type as condition: "
205                  << spvOpcodeString(opcode);
206 
207         if (_.GetDimension(condition_type) != dimension) {
208           // If the condition is a vector type, then the result must also be a
209           // vector with matching dimensions. In SPIR-V 1.4, a scalar condition
210           // can be used to select between vector types. |composites| is a
211           // proxy for SPIR-V 1.4 functionality.
212           if (!composites || _.IsBoolVectorType(condition_type)) {
213             return _.diag(SPV_ERROR_INVALID_DATA, inst)
214                    << "Expected vector sizes of Result Type and the condition "
215                       "to be equal: "
216                    << spvOpcodeString(opcode);
217           }
218         }
219 
220         if (result_type != left_type || result_type != right_type)
221           return _.diag(SPV_ERROR_INVALID_DATA, inst)
222                  << "Expected both objects to be of Result Type: "
223                  << spvOpcodeString(opcode);
224 
225         break;
226       }
227     }
228 
229     case SpvOpIEqual:
230     case SpvOpINotEqual:
231     case SpvOpUGreaterThan:
232     case SpvOpUGreaterThanEqual:
233     case SpvOpULessThan:
234     case SpvOpULessThanEqual:
235     case SpvOpSGreaterThan:
236     case SpvOpSGreaterThanEqual:
237     case SpvOpSLessThan:
238     case SpvOpSLessThanEqual: {
239       if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
240         return _.diag(SPV_ERROR_INVALID_DATA, inst)
241                << "Expected bool scalar or vector type as Result Type: "
242                << spvOpcodeString(opcode);
243 
244       const uint32_t left_type = _.GetOperandTypeId(inst, 2);
245       const uint32_t right_type = _.GetOperandTypeId(inst, 3);
246 
247       if (!left_type ||
248           (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type)))
249         return _.diag(SPV_ERROR_INVALID_DATA, inst)
250                << "Expected operands to be scalar or vector int: "
251                << spvOpcodeString(opcode);
252 
253       if (_.GetDimension(result_type) != _.GetDimension(left_type))
254         return _.diag(SPV_ERROR_INVALID_DATA, inst)
255                << "Expected vector sizes of Result Type and the operands to be"
256                << " equal: " << spvOpcodeString(opcode);
257 
258       if (!right_type ||
259           (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type)))
260         return _.diag(SPV_ERROR_INVALID_DATA, inst)
261                << "Expected operands to be scalar or vector int: "
262                << spvOpcodeString(opcode);
263 
264       if (_.GetDimension(result_type) != _.GetDimension(right_type))
265         return _.diag(SPV_ERROR_INVALID_DATA, inst)
266                << "Expected vector sizes of Result Type and the operands to be"
267                << " equal: " << spvOpcodeString(opcode);
268 
269       if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type))
270         return _.diag(SPV_ERROR_INVALID_DATA, inst)
271                << "Expected both operands to have the same component bit "
272                   "width: "
273                << spvOpcodeString(opcode);
274 
275       break;
276     }
277 
278     default:
279       break;
280   }
281 
282   return SPV_SUCCESS;
283 }
284 
285 }  // namespace val
286 }  // namespace spvtools
287