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 return _.diag(SPV_ERROR_INVALID_DATA, inst)
168 << "Using pointers with OpSelect requires capability "
169 << "VariablePointers or VariablePointersStorageBuffer";
170 break;
171 }
172
173 case SpvOpTypeVector: {
174 dimension = type_inst->word(3);
175 break;
176 }
177
178 case SpvOpTypeBool:
179 case SpvOpTypeInt:
180 case SpvOpTypeFloat: {
181 break;
182 }
183
184 // Not RuntimeArray because of other rules.
185 case SpvOpTypeArray:
186 case SpvOpTypeMatrix:
187 case SpvOpTypeStruct: {
188 if (!composites) return fail();
189 break;
190 }
191
192 default:
193 return fail();
194 }
195
196 const uint32_t condition_type = _.GetOperandTypeId(inst, 2);
197 const uint32_t left_type = _.GetOperandTypeId(inst, 3);
198 const uint32_t right_type = _.GetOperandTypeId(inst, 4);
199
200 if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
201 !_.IsBoolVectorType(condition_type)))
202 return _.diag(SPV_ERROR_INVALID_DATA, inst)
203 << "Expected bool scalar or vector type as condition: "
204 << spvOpcodeString(opcode);
205
206 if (_.GetDimension(condition_type) != dimension) {
207 // If the condition is a vector type, then the result must also be a
208 // vector with matching dimensions. In SPIR-V 1.4, a scalar condition
209 // can be used to select between vector types. |composites| is a
210 // proxy for SPIR-V 1.4 functionality.
211 if (!composites || _.IsBoolVectorType(condition_type)) {
212 return _.diag(SPV_ERROR_INVALID_DATA, inst)
213 << "Expected vector sizes of Result Type and the condition "
214 "to be equal: "
215 << spvOpcodeString(opcode);
216 }
217 }
218
219 if (result_type != left_type || result_type != right_type)
220 return _.diag(SPV_ERROR_INVALID_DATA, inst)
221 << "Expected both objects to be of Result Type: "
222 << spvOpcodeString(opcode);
223
224 break;
225 }
226 }
227
228 case SpvOpIEqual:
229 case SpvOpINotEqual:
230 case SpvOpUGreaterThan:
231 case SpvOpUGreaterThanEqual:
232 case SpvOpULessThan:
233 case SpvOpULessThanEqual:
234 case SpvOpSGreaterThan:
235 case SpvOpSGreaterThanEqual:
236 case SpvOpSLessThan:
237 case SpvOpSLessThanEqual: {
238 if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
239 return _.diag(SPV_ERROR_INVALID_DATA, inst)
240 << "Expected bool scalar or vector type as Result Type: "
241 << spvOpcodeString(opcode);
242
243 const uint32_t left_type = _.GetOperandTypeId(inst, 2);
244 const uint32_t right_type = _.GetOperandTypeId(inst, 3);
245
246 if (!left_type ||
247 (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type)))
248 return _.diag(SPV_ERROR_INVALID_DATA, inst)
249 << "Expected operands to be scalar or vector int: "
250 << spvOpcodeString(opcode);
251
252 if (_.GetDimension(result_type) != _.GetDimension(left_type))
253 return _.diag(SPV_ERROR_INVALID_DATA, inst)
254 << "Expected vector sizes of Result Type and the operands to be"
255 << " equal: " << spvOpcodeString(opcode);
256
257 if (!right_type ||
258 (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type)))
259 return _.diag(SPV_ERROR_INVALID_DATA, inst)
260 << "Expected operands to be scalar or vector int: "
261 << spvOpcodeString(opcode);
262
263 if (_.GetDimension(result_type) != _.GetDimension(right_type))
264 return _.diag(SPV_ERROR_INVALID_DATA, inst)
265 << "Expected vector sizes of Result Type and the operands to be"
266 << " equal: " << spvOpcodeString(opcode);
267
268 if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type))
269 return _.diag(SPV_ERROR_INVALID_DATA, inst)
270 << "Expected both operands to have the same component bit "
271 "width: "
272 << spvOpcodeString(opcode);
273
274 break;
275 }
276
277 default:
278 break;
279 }
280
281 return SPV_SUCCESS;
282 }
283
284 } // namespace val
285 } // namespace spvtools
286