/src/spirv-tools/source/val/validate_logicals.cpp
Line | Count | Source |
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/opcode.h" |
18 | | #include "source/val/instruction.h" |
19 | | #include "source/val/validate.h" |
20 | | #include "source/val/validation_state.h" |
21 | | |
22 | | namespace spvtools { |
23 | | namespace val { |
24 | | |
25 | 26 | spv_result_t ValidateAnyAll(ValidationState_t& _, const Instruction* inst) { |
26 | 26 | const spv::Op opcode = inst->opcode(); |
27 | 26 | const uint32_t result_type = inst->type_id(); |
28 | 26 | if (!_.IsBoolScalarType(result_type)) |
29 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
30 | 4 | << "Expected bool scalar type as Result Type: " |
31 | 4 | << spvOpcodeString(opcode); |
32 | | |
33 | 22 | const uint32_t vector_type = _.GetOperandTypeId(inst, 2); |
34 | 22 | if (!vector_type || !_.IsBoolVectorType(vector_type)) |
35 | 7 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
36 | 7 | << "Expected operand to be vector bool: " << spvOpcodeString(opcode); |
37 | 15 | return SPV_SUCCESS; |
38 | 22 | } |
39 | | |
40 | | spv_result_t ValidateSingleStatus(ValidationState_t& _, |
41 | 2.97k | const Instruction* inst) { |
42 | 2.97k | const spv::Op opcode = inst->opcode(); |
43 | 2.97k | const uint32_t result_type = inst->type_id(); |
44 | 2.97k | if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) |
45 | 29 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
46 | 29 | << "Expected bool scalar or vector type as Result Type: " |
47 | 29 | << spvOpcodeString(opcode); |
48 | | |
49 | 2.94k | const uint32_t operand_type = _.GetOperandTypeId(inst, 2); |
50 | 2.94k | if (!operand_type || (!_.IsFloatScalarType(operand_type) && |
51 | 7 | !_.IsFloatVectorType(operand_type))) |
52 | 7 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
53 | 7 | << "Expected operand to be scalar or vector float: " |
54 | 7 | << spvOpcodeString(opcode); |
55 | | |
56 | 2.93k | if (_.GetDimension(result_type) != _.GetDimension(operand_type)) |
57 | 5 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
58 | 5 | << "Expected vector sizes of Result Type and the operand to be " |
59 | 5 | "equal: " |
60 | 5 | << spvOpcodeString(opcode); |
61 | | |
62 | 2.93k | return SPV_SUCCESS; |
63 | 2.93k | } |
64 | | |
65 | | spv_result_t ValidateFloatCompare(ValidationState_t& _, |
66 | 20.0k | const Instruction* inst) { |
67 | 20.0k | const spv::Op opcode = inst->opcode(); |
68 | 20.0k | const uint32_t result_type = inst->type_id(); |
69 | 20.0k | if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) |
70 | 47 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
71 | 47 | << "Expected bool scalar or vector type as Result Type: " |
72 | 47 | << spvOpcodeString(opcode); |
73 | | |
74 | 20.0k | const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2); |
75 | 20.0k | if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) && |
76 | 31 | !_.IsFloatVectorType(left_operand_type))) |
77 | 29 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
78 | 29 | << "Expected operands to be scalar or vector float: " |
79 | 29 | << spvOpcodeString(opcode); |
80 | | |
81 | 20.0k | if (_.GetDimension(result_type) != _.GetDimension(left_operand_type)) |
82 | 5 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
83 | 5 | << "Expected vector sizes of Result Type and the operands to be " |
84 | 5 | "equal: " |
85 | 5 | << spvOpcodeString(opcode); |
86 | | |
87 | 20.0k | if (left_operand_type != _.GetOperandTypeId(inst, 3)) |
88 | 9 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
89 | 9 | << "Expected left and right operands to have the same type: " |
90 | 9 | << spvOpcodeString(opcode); |
91 | 19.9k | return SPV_SUCCESS; |
92 | 20.0k | } |
93 | | |
94 | | spv_result_t ValidateLogicalCompare(ValidationState_t& _, |
95 | | const Instruction* inst, |
96 | 4.73k | uint32_t operand_index = 2) { |
97 | 4.73k | const spv::Op opcode = inst->opcode(); |
98 | 4.73k | const uint32_t result_type = inst->type_id(); |
99 | 4.73k | if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) |
100 | 35 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
101 | 35 | << "Expected bool scalar or vector type as Result Type: " |
102 | 35 | << spvOpcodeString(opcode); |
103 | | |
104 | 4.69k | const uint32_t operand_1 = _.GetOperandTypeId(inst, operand_index); |
105 | 4.69k | const uint32_t operand_2 = _.GetOperandTypeId(inst, operand_index + 1); |
106 | 4.69k | if (result_type != operand_1 || result_type != operand_2) |
107 | 23 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
108 | 23 | << "Expected both operands to be of Result Type: " |
109 | 23 | << spvOpcodeString(opcode); |
110 | 4.67k | return SPV_SUCCESS; |
111 | 4.69k | } |
112 | | |
113 | | spv_result_t ValidateLogicalNot(ValidationState_t& _, const Instruction* inst, |
114 | 6.93k | uint32_t operand_index = 2) { |
115 | 6.93k | const spv::Op opcode = inst->opcode(); |
116 | 6.93k | const uint32_t result_type = inst->type_id(); |
117 | 6.93k | if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) |
118 | 16 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
119 | 16 | << "Expected bool scalar or vector type as Result Type: " |
120 | 16 | << spvOpcodeString(opcode); |
121 | | |
122 | 6.92k | if (result_type != _.GetOperandTypeId(inst, operand_index)) |
123 | 8 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
124 | 8 | << "Expected operand to be of Result Type: " |
125 | 8 | << spvOpcodeString(opcode); |
126 | 6.91k | return SPV_SUCCESS; |
127 | 6.92k | } |
128 | | |
129 | | spv_result_t ValidateSelect(ValidationState_t& _, const Instruction* inst, |
130 | 5.70k | uint32_t operand_index = 2) { |
131 | 5.70k | const spv::Op opcode = inst->opcode(); |
132 | 5.70k | const uint32_t result_type = inst->type_id(); |
133 | 5.70k | uint32_t dimension = 1; |
134 | 5.70k | const Instruction* type_inst = _.FindDef(result_type); |
135 | 5.70k | assert(type_inst); |
136 | | |
137 | 5.70k | const auto composites = _.features().select_between_composites; |
138 | 5.70k | auto fail = [&_, composites, inst, opcode]() -> spv_result_t { |
139 | 13 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
140 | 13 | << "Expected scalar or " << (composites ? "composite" : "vector") |
141 | 13 | << " type as Result Type: " << spvOpcodeString(opcode); |
142 | 13 | }; |
143 | | |
144 | 5.70k | const spv::Op type_opcode = type_inst->opcode(); |
145 | 5.70k | switch (type_opcode) { |
146 | 0 | case spv::Op::OpTypeUntypedPointerKHR: |
147 | 5 | case spv::Op::OpTypePointer: { |
148 | 5 | if (_.addressing_model() == spv::AddressingModel::Logical && |
149 | 3 | !_.HasCapability(spv::Capability::VariablePointersStorageBuffer)) |
150 | 3 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
151 | 3 | << "Using pointers with OpSelect requires capability " |
152 | 3 | << "VariablePointers or VariablePointersStorageBuffer"; |
153 | 2 | break; |
154 | 5 | } |
155 | | |
156 | 2 | case spv::Op::OpTypeSampledImage: |
157 | 0 | case spv::Op::OpTypeImage: |
158 | 2 | case spv::Op::OpTypeSampler: { |
159 | 2 | if (!_.HasCapability(spv::Capability::BindlessTextureNV)) |
160 | 2 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
161 | 2 | << "Using image/sampler with OpSelect requires capability " |
162 | 2 | << "BindlessTextureNV"; |
163 | 0 | break; |
164 | 2 | } |
165 | | |
166 | 5 | case spv::Op::OpTypeVector: { |
167 | 5 | dimension = type_inst->word(3); |
168 | 5 | break; |
169 | 2 | } |
170 | 0 | case spv::Op::OpTypeVectorIdEXT: { |
171 | 0 | dimension = _.GetDimension(result_type); |
172 | 0 | break; |
173 | 2 | } |
174 | | |
175 | 65 | case spv::Op::OpTypeBool: |
176 | 4.13k | case spv::Op::OpTypeInt: |
177 | 5.68k | case spv::Op::OpTypeFloat: { |
178 | 5.68k | break; |
179 | 4.13k | } |
180 | | |
181 | | // Not RuntimeArray because of other rules. |
182 | 3 | case spv::Op::OpTypeArray: |
183 | 6 | case spv::Op::OpTypeMatrix: |
184 | 9 | case spv::Op::OpTypeStruct: { |
185 | 9 | if (!composites) return fail(); |
186 | 0 | break; |
187 | 9 | } |
188 | | |
189 | 4 | default: |
190 | 4 | return fail(); |
191 | 5.70k | } |
192 | | |
193 | 5.69k | const uint32_t condition_type = _.GetOperandTypeId(inst, operand_index); |
194 | 5.69k | const uint32_t left_type = _.GetOperandTypeId(inst, operand_index + 1); |
195 | 5.69k | const uint32_t right_type = _.GetOperandTypeId(inst, operand_index + 2); |
196 | | |
197 | 5.69k | if (!condition_type || (!_.IsBoolScalarType(condition_type) && |
198 | 10 | !_.IsBoolVectorType(condition_type))) |
199 | 15 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
200 | 15 | << "Expected bool scalar or vector type as condition: " |
201 | 15 | << spvOpcodeString(opcode); |
202 | | |
203 | 5.67k | if (_.GetDimension(condition_type) != dimension) { |
204 | | // If the condition is a vector type, then the result must also be a |
205 | | // vector with matching dimensions. In SPIR-V 1.4, a scalar condition |
206 | | // can be used to select between vector types. |composites| is a |
207 | | // proxy for SPIR-V 1.4 functionality. |
208 | 4 | if (!composites || _.IsBoolVectorType(condition_type)) { |
209 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
210 | 4 | << "Expected vector sizes of Result Type and the condition " |
211 | 4 | "to be equal: " |
212 | 4 | << spvOpcodeString(opcode); |
213 | 4 | } |
214 | 4 | } |
215 | | |
216 | 5.67k | if (result_type != left_type || result_type != right_type) |
217 | 17 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
218 | 17 | << "Expected both objects to be of Result Type: " |
219 | 17 | << spvOpcodeString(opcode); |
220 | 5.65k | return SPV_SUCCESS; |
221 | 5.67k | } |
222 | | |
223 | | spv_result_t ValidateIntCompare(ValidationState_t& _, const Instruction* inst, |
224 | 77.9k | uint32_t operand_index = 2) { |
225 | 77.9k | const spv::Op opcode = inst->opcode(); |
226 | 77.9k | const uint32_t result_type = inst->type_id(); |
227 | 77.9k | if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) |
228 | 66 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
229 | 66 | << "Expected bool scalar or vector type as Result Type: " |
230 | 66 | << spvOpcodeString(opcode); |
231 | | |
232 | 77.8k | const uint32_t left_type = _.GetOperandTypeId(inst, operand_index); |
233 | 77.8k | const uint32_t right_type = _.GetOperandTypeId(inst, operand_index + 1); |
234 | | |
235 | 77.8k | if (!left_type || |
236 | 77.8k | (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type))) |
237 | 24 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
238 | 24 | << "Expected operands to be scalar or vector int: " |
239 | 24 | << spvOpcodeString(opcode); |
240 | | |
241 | 77.8k | if (_.GetDimension(result_type) != _.GetDimension(left_type)) |
242 | 7 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
243 | 7 | << "Expected vector sizes of Result Type and the operands to be" |
244 | 7 | << " equal: " << spvOpcodeString(opcode); |
245 | | |
246 | 77.8k | if (!right_type || |
247 | 77.8k | (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type))) |
248 | 8 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
249 | 8 | << "Expected operands to be scalar or vector int: " |
250 | 8 | << spvOpcodeString(opcode); |
251 | | |
252 | 77.8k | if (_.GetDimension(result_type) != _.GetDimension(right_type)) |
253 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
254 | 4 | << "Expected vector sizes of Result Type and the operands to be" |
255 | 4 | << " equal: " << spvOpcodeString(opcode); |
256 | | |
257 | 77.8k | if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type)) |
258 | 1 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
259 | 1 | << "Expected both operands to have the same component bit " |
260 | 1 | "width: " |
261 | 1 | << spvOpcodeString(opcode); |
262 | 77.8k | return SPV_SUCCESS; |
263 | 77.8k | } |
264 | | |
265 | | // Validates correctness of logical instructions. |
266 | 14.3M | spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) { |
267 | 14.3M | switch (inst->opcode()) { |
268 | 9 | case spv::Op::OpAny: |
269 | 26 | case spv::Op::OpAll: |
270 | 26 | return ValidateAnyAll(_, inst); |
271 | 1.98k | case spv::Op::OpIsNan: |
272 | 2.96k | case spv::Op::OpIsInf: |
273 | 2.96k | case spv::Op::OpIsFinite: |
274 | 2.96k | case spv::Op::OpIsNormal: |
275 | 2.97k | case spv::Op::OpSignBitSet: |
276 | 2.97k | return ValidateSingleStatus(_, inst); |
277 | 485 | case spv::Op::OpFOrdEqual: |
278 | 795 | case spv::Op::OpFUnordEqual: |
279 | 970 | case spv::Op::OpFOrdNotEqual: |
280 | 1.29k | case spv::Op::OpFUnordNotEqual: |
281 | 11.0k | case spv::Op::OpFOrdLessThan: |
282 | 11.3k | case spv::Op::OpFUnordLessThan: |
283 | 15.5k | case spv::Op::OpFOrdGreaterThan: |
284 | 15.9k | case spv::Op::OpFUnordGreaterThan: |
285 | 17.2k | case spv::Op::OpFOrdLessThanEqual: |
286 | 17.5k | case spv::Op::OpFUnordLessThanEqual: |
287 | 19.8k | case spv::Op::OpFOrdGreaterThanEqual: |
288 | 20.0k | case spv::Op::OpFUnordGreaterThanEqual: |
289 | 20.0k | case spv::Op::OpLessOrGreater: |
290 | 20.0k | case spv::Op::OpOrdered: |
291 | 20.0k | case spv::Op::OpUnordered: |
292 | 20.0k | return ValidateFloatCompare(_, inst); |
293 | 268 | case spv::Op::OpLogicalEqual: |
294 | 659 | case spv::Op::OpLogicalNotEqual: |
295 | 1.22k | case spv::Op::OpLogicalOr: |
296 | 4.71k | case spv::Op::OpLogicalAnd: |
297 | 4.71k | return ValidateLogicalCompare(_, inst); |
298 | 6.92k | case spv::Op::OpLogicalNot: |
299 | 6.92k | return ValidateLogicalNot(_, inst); |
300 | 5.70k | case spv::Op::OpSelect: |
301 | 5.70k | return ValidateSelect(_, inst); |
302 | 17.9k | case spv::Op::OpIEqual: |
303 | 24.3k | case spv::Op::OpINotEqual: |
304 | 26.6k | case spv::Op::OpUGreaterThan: |
305 | 28.0k | case spv::Op::OpUGreaterThanEqual: |
306 | 29.5k | case spv::Op::OpULessThan: |
307 | 30.9k | case spv::Op::OpULessThanEqual: |
308 | 39.6k | case spv::Op::OpSGreaterThan: |
309 | 43.6k | case spv::Op::OpSGreaterThanEqual: |
310 | 66.8k | case spv::Op::OpSLessThan: |
311 | 77.9k | case spv::Op::OpSLessThanEqual: |
312 | 77.9k | return ValidateIntCompare(_, inst); |
313 | | |
314 | 283 | case spv::Op::OpSpecConstantOp: { |
315 | 283 | switch (inst->GetOperandAs<spv::Op>(2u)) { |
316 | 3 | case spv::Op::OpLogicalEqual: |
317 | 6 | case spv::Op::OpLogicalNotEqual: |
318 | 9 | case spv::Op::OpLogicalOr: |
319 | 15 | case spv::Op::OpLogicalAnd: |
320 | 15 | return ValidateLogicalCompare(_, inst, 3); |
321 | 11 | case spv::Op::OpLogicalNot: |
322 | 11 | return ValidateLogicalNot(_, inst, 3); |
323 | 0 | case spv::Op::OpSelect: |
324 | 0 | return ValidateSelect(_, inst, 3); |
325 | 3 | case spv::Op::OpIEqual: |
326 | 6 | case spv::Op::OpINotEqual: |
327 | 9 | case spv::Op::OpUGreaterThan: |
328 | 12 | case spv::Op::OpUGreaterThanEqual: |
329 | 17 | case spv::Op::OpULessThan: |
330 | 20 | case spv::Op::OpULessThanEqual: |
331 | 23 | case spv::Op::OpSGreaterThan: |
332 | 26 | case spv::Op::OpSGreaterThanEqual: |
333 | 29 | case spv::Op::OpSLessThan: |
334 | 31 | case spv::Op::OpSLessThanEqual: |
335 | 31 | return ValidateIntCompare(_, inst, 3); |
336 | 226 | default: |
337 | 226 | break; |
338 | 283 | } |
339 | 226 | break; |
340 | 283 | } |
341 | | |
342 | 14.2M | default: |
343 | 14.2M | break; |
344 | 14.3M | } |
345 | | |
346 | 14.2M | return SPV_SUCCESS; |
347 | 14.3M | } |
348 | | |
349 | | } // namespace val |
350 | | } // namespace spvtools |