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