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 // Validates correctness of logical instructions.
LogicalsPass(ValidationState_t & _,const Instruction * inst)26 spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
27 const spv::Op opcode = inst->opcode();
28 const uint32_t result_type = inst->type_id();
29
30 switch (opcode) {
31 case spv::Op::OpAny:
32 case spv::Op::OpAll: {
33 if (!_.IsBoolScalarType(result_type))
34 return _.diag(SPV_ERROR_INVALID_DATA, inst)
35 << "Expected bool scalar type as Result Type: "
36 << spvOpcodeString(opcode);
37
38 const uint32_t vector_type = _.GetOperandTypeId(inst, 2);
39 if (!vector_type || !_.IsBoolVectorType(vector_type))
40 return _.diag(SPV_ERROR_INVALID_DATA, inst)
41 << "Expected operand to be vector bool: "
42 << spvOpcodeString(opcode);
43
44 break;
45 }
46
47 case spv::Op::OpIsNan:
48 case spv::Op::OpIsInf:
49 case spv::Op::OpIsFinite:
50 case spv::Op::OpIsNormal:
51 case spv::Op::OpSignBitSet: {
52 if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
53 return _.diag(SPV_ERROR_INVALID_DATA, inst)
54 << "Expected bool scalar or vector type as Result Type: "
55 << spvOpcodeString(opcode);
56
57 const uint32_t operand_type = _.GetOperandTypeId(inst, 2);
58 if (!operand_type || (!_.IsFloatScalarType(operand_type) &&
59 !_.IsFloatVectorType(operand_type)))
60 return _.diag(SPV_ERROR_INVALID_DATA, inst)
61 << "Expected operand to be scalar or vector float: "
62 << spvOpcodeString(opcode);
63
64 if (_.GetDimension(result_type) != _.GetDimension(operand_type))
65 return _.diag(SPV_ERROR_INVALID_DATA, inst)
66 << "Expected vector sizes of Result Type and the operand to be "
67 "equal: "
68 << spvOpcodeString(opcode);
69
70 break;
71 }
72
73 case spv::Op::OpFOrdEqual:
74 case spv::Op::OpFUnordEqual:
75 case spv::Op::OpFOrdNotEqual:
76 case spv::Op::OpFUnordNotEqual:
77 case spv::Op::OpFOrdLessThan:
78 case spv::Op::OpFUnordLessThan:
79 case spv::Op::OpFOrdGreaterThan:
80 case spv::Op::OpFUnordGreaterThan:
81 case spv::Op::OpFOrdLessThanEqual:
82 case spv::Op::OpFUnordLessThanEqual:
83 case spv::Op::OpFOrdGreaterThanEqual:
84 case spv::Op::OpFUnordGreaterThanEqual:
85 case spv::Op::OpLessOrGreater:
86 case spv::Op::OpOrdered:
87 case spv::Op::OpUnordered: {
88 if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
89 return _.diag(SPV_ERROR_INVALID_DATA, inst)
90 << "Expected bool scalar or vector type as Result Type: "
91 << spvOpcodeString(opcode);
92
93 const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2);
94 if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) &&
95 !_.IsFloatVectorType(left_operand_type)))
96 return _.diag(SPV_ERROR_INVALID_DATA, inst)
97 << "Expected operands to be scalar or vector float: "
98 << spvOpcodeString(opcode);
99
100 if (_.GetDimension(result_type) != _.GetDimension(left_operand_type))
101 return _.diag(SPV_ERROR_INVALID_DATA, inst)
102 << "Expected vector sizes of Result Type and the operands to be "
103 "equal: "
104 << spvOpcodeString(opcode);
105
106 if (left_operand_type != _.GetOperandTypeId(inst, 3))
107 return _.diag(SPV_ERROR_INVALID_DATA, inst)
108 << "Expected left and right operands to have the same type: "
109 << spvOpcodeString(opcode);
110
111 break;
112 }
113
114 case spv::Op::OpLogicalEqual:
115 case spv::Op::OpLogicalNotEqual:
116 case spv::Op::OpLogicalOr:
117 case spv::Op::OpLogicalAnd: {
118 if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
119 return _.diag(SPV_ERROR_INVALID_DATA, inst)
120 << "Expected bool scalar or vector type as Result Type: "
121 << spvOpcodeString(opcode);
122
123 if (result_type != _.GetOperandTypeId(inst, 2) ||
124 result_type != _.GetOperandTypeId(inst, 3))
125 return _.diag(SPV_ERROR_INVALID_DATA, inst)
126 << "Expected both operands to be of Result Type: "
127 << spvOpcodeString(opcode);
128
129 break;
130 }
131
132 case spv::Op::OpLogicalNot: {
133 if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
134 return _.diag(SPV_ERROR_INVALID_DATA, inst)
135 << "Expected bool scalar or vector type as Result Type: "
136 << spvOpcodeString(opcode);
137
138 if (result_type != _.GetOperandTypeId(inst, 2))
139 return _.diag(SPV_ERROR_INVALID_DATA, inst)
140 << "Expected operand to be of Result Type: "
141 << spvOpcodeString(opcode);
142
143 break;
144 }
145
146 case spv::Op::OpSelect: {
147 uint32_t dimension = 1;
148 {
149 const Instruction* type_inst = _.FindDef(result_type);
150 assert(type_inst);
151
152 const auto composites = _.features().select_between_composites;
153 auto fail = [&_, composites, inst, opcode]() -> spv_result_t {
154 return _.diag(SPV_ERROR_INVALID_DATA, inst)
155 << "Expected scalar or "
156 << (composites ? "composite" : "vector")
157 << " type as Result Type: " << spvOpcodeString(opcode);
158 };
159
160 const spv::Op type_opcode = type_inst->opcode();
161 switch (type_opcode) {
162 case spv::Op::OpTypePointer: {
163 if (_.addressing_model() == spv::AddressingModel::Logical &&
164 !_.features().variable_pointers)
165 return _.diag(SPV_ERROR_INVALID_DATA, inst)
166 << "Using pointers with OpSelect requires capability "
167 << "VariablePointers or VariablePointersStorageBuffer";
168 break;
169 }
170
171 case spv::Op::OpTypeSampledImage:
172 case spv::Op::OpTypeImage:
173 case spv::Op::OpTypeSampler: {
174 if (!_.HasCapability(spv::Capability::BindlessTextureNV))
175 return _.diag(SPV_ERROR_INVALID_DATA, inst)
176 << "Using image/sampler with OpSelect requires capability "
177 << "BindlessTextureNV";
178 break;
179 }
180
181 case spv::Op::OpTypeVector: {
182 dimension = type_inst->word(3);
183 break;
184 }
185
186 case spv::Op::OpTypeBool:
187 case spv::Op::OpTypeInt:
188 case spv::Op::OpTypeFloat: {
189 break;
190 }
191
192 // Not RuntimeArray because of other rules.
193 case spv::Op::OpTypeArray:
194 case spv::Op::OpTypeMatrix:
195 case spv::Op::OpTypeStruct: {
196 if (!composites) return fail();
197 break;
198 }
199
200 default:
201 return fail();
202 }
203
204 const uint32_t condition_type = _.GetOperandTypeId(inst, 2);
205 const uint32_t left_type = _.GetOperandTypeId(inst, 3);
206 const uint32_t right_type = _.GetOperandTypeId(inst, 4);
207
208 if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
209 !_.IsBoolVectorType(condition_type)))
210 return _.diag(SPV_ERROR_INVALID_DATA, inst)
211 << "Expected bool scalar or vector type as condition: "
212 << spvOpcodeString(opcode);
213
214 if (_.GetDimension(condition_type) != dimension) {
215 // If the condition is a vector type, then the result must also be a
216 // vector with matching dimensions. In SPIR-V 1.4, a scalar condition
217 // can be used to select between vector types. |composites| is a
218 // proxy for SPIR-V 1.4 functionality.
219 if (!composites || _.IsBoolVectorType(condition_type)) {
220 return _.diag(SPV_ERROR_INVALID_DATA, inst)
221 << "Expected vector sizes of Result Type and the condition "
222 "to be equal: "
223 << spvOpcodeString(opcode);
224 }
225 }
226
227 if (result_type != left_type || result_type != right_type)
228 return _.diag(SPV_ERROR_INVALID_DATA, inst)
229 << "Expected both objects to be of Result Type: "
230 << spvOpcodeString(opcode);
231
232 break;
233 }
234 }
235
236 case spv::Op::OpIEqual:
237 case spv::Op::OpINotEqual:
238 case spv::Op::OpUGreaterThan:
239 case spv::Op::OpUGreaterThanEqual:
240 case spv::Op::OpULessThan:
241 case spv::Op::OpULessThanEqual:
242 case spv::Op::OpSGreaterThan:
243 case spv::Op::OpSGreaterThanEqual:
244 case spv::Op::OpSLessThan:
245 case spv::Op::OpSLessThanEqual: {
246 if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
247 return _.diag(SPV_ERROR_INVALID_DATA, inst)
248 << "Expected bool scalar or vector type as Result Type: "
249 << spvOpcodeString(opcode);
250
251 const uint32_t left_type = _.GetOperandTypeId(inst, 2);
252 const uint32_t right_type = _.GetOperandTypeId(inst, 3);
253
254 if (!left_type ||
255 (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type)))
256 return _.diag(SPV_ERROR_INVALID_DATA, inst)
257 << "Expected operands to be scalar or vector int: "
258 << spvOpcodeString(opcode);
259
260 if (_.GetDimension(result_type) != _.GetDimension(left_type))
261 return _.diag(SPV_ERROR_INVALID_DATA, inst)
262 << "Expected vector sizes of Result Type and the operands to be"
263 << " equal: " << spvOpcodeString(opcode);
264
265 if (!right_type ||
266 (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type)))
267 return _.diag(SPV_ERROR_INVALID_DATA, inst)
268 << "Expected operands to be scalar or vector int: "
269 << spvOpcodeString(opcode);
270
271 if (_.GetDimension(result_type) != _.GetDimension(right_type))
272 return _.diag(SPV_ERROR_INVALID_DATA, inst)
273 << "Expected vector sizes of Result Type and the operands to be"
274 << " equal: " << spvOpcodeString(opcode);
275
276 if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type))
277 return _.diag(SPV_ERROR_INVALID_DATA, inst)
278 << "Expected both operands to have the same component bit "
279 "width: "
280 << spvOpcodeString(opcode);
281
282 break;
283 }
284
285 default:
286 break;
287 }
288
289 return SPV_SUCCESS;
290 }
291
292 } // namespace val
293 } // namespace spvtools
294