/src/spirv-tools/source/val/validate_conversion.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 conversion instructions. |
16 | | |
17 | | #include <climits> |
18 | | |
19 | | #include "source/opcode.h" |
20 | | #include "source/spirv_constant.h" |
21 | | #include "source/spirv_target_env.h" |
22 | | #include "source/val/instruction.h" |
23 | | #include "source/val/validate.h" |
24 | | #include "source/val/validation_state.h" |
25 | | |
26 | | namespace spvtools { |
27 | | namespace val { |
28 | | |
29 | | namespace { |
30 | | |
31 | 0 | bool IsNumericScalarOrVectorType(ValidationState_t& _, uint32_t type_id) { |
32 | 0 | return _.IsFloatScalarType(type_id) || _.IsFloatVectorType(type_id) || |
33 | 0 | _.IsIntScalarType(type_id) || _.IsIntVectorType(type_id); |
34 | 0 | } |
35 | | |
36 | | bool IsIEEEOrAlternativeFloatTypeAllowedForOCPMicroscalingFConvert( |
37 | 0 | ValidationState_t& _, uint32_t type_id) { |
38 | 0 | const uint32_t component_type = _.GetComponentType(type_id); |
39 | 0 | const Instruction* inst = _.FindDef(component_type); |
40 | 0 | if (!inst || inst->opcode() != spv::Op::OpTypeFloat) return false; |
41 | | |
42 | 0 | if (inst->words().size() <= 3) return true; |
43 | | |
44 | 0 | const auto encoding = inst->GetOperandAs<spv::FPEncoding>(2); |
45 | 0 | return encoding == spv::FPEncoding::Float8E4M3EXT || |
46 | 0 | encoding == spv::FPEncoding::Float8E5M2EXT || |
47 | 0 | encoding == spv::FPEncoding::BFloat16KHR; |
48 | 0 | } |
49 | | |
50 | | spv_result_t ValidateVulkanOCPMicroscalingFloatIntConversion( |
51 | 34.5k | ValidationState_t& _, const Instruction* inst, uint32_t float_type) { |
52 | 34.5k | if (spvIsVulkanEnv(_.context()->target_env) && |
53 | 0 | _.ContainsOCPMicroscalingType(float_type)) { |
54 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
55 | 0 | << _.VkErrorID(12465) << spvOpcodeString(inst->opcode()) |
56 | 0 | << " must not consume or produce OCP microscaling types in the " |
57 | 0 | "Vulkan environment."; |
58 | 0 | } |
59 | 34.5k | return SPV_SUCCESS; |
60 | 34.5k | } |
61 | | |
62 | 0 | bool HasCoopMatTranspose(ValidationState_t& _, uint32_t id) { |
63 | 0 | return _.HasDecoration(id, spv::Decoration::CooperativeMatrixTransposeEXT); |
64 | 0 | } |
65 | | |
66 | | } // namespace |
67 | | |
68 | | spv_result_t ValidateShaderBitWidth(ValidationState_t& _, |
69 | 36.1k | const Instruction* inst) { |
70 | 36.1k | if (_.HasCapability(spv::Capability::Shader)) { |
71 | 36.0k | if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) || |
72 | 36.0k | _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) { |
73 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
74 | 0 | << "8- or 16-bit types can only be used with width-only " |
75 | 0 | "conversions"; |
76 | 0 | } |
77 | 36.0k | } |
78 | 36.1k | return SPV_SUCCESS; |
79 | 36.1k | } |
80 | | |
81 | | spv_result_t ValidateConvertFToU(ValidationState_t& _, const Instruction* inst, |
82 | 210 | uint32_t operand_index = 2) { |
83 | 210 | const spv::Op opcode = inst->opcode(); |
84 | 210 | const uint32_t result_type = inst->type_id(); |
85 | 210 | if (!_.IsUnsignedIntScalarType(result_type) && |
86 | 204 | !_.IsUnsignedIntVectorType(result_type) && |
87 | 35 | !_.IsUnsignedIntCooperativeMatrixType(result_type) && |
88 | 35 | !_.IsUnsignedIntCooperativeVectorNVType(result_type)) |
89 | 35 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
90 | 35 | << "Expected unsigned int scalar or vector type as Result Type: " |
91 | 35 | << spvOpcodeString(opcode); |
92 | | |
93 | 175 | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
94 | 175 | if (!input_type || |
95 | 172 | (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type) && |
96 | 4 | !_.IsFloatCooperativeMatrixType(input_type) && |
97 | 4 | !_.IsFloatCooperativeVectorNVType(input_type))) |
98 | 7 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
99 | 7 | << "Expected input to be float scalar or vector: " |
100 | 7 | << spvOpcodeString(opcode); |
101 | | |
102 | 168 | if (_.IsCooperativeVectorNVType(result_type) || |
103 | 168 | _.IsCooperativeVectorNVType(input_type)) { |
104 | 0 | spv_result_t ret = |
105 | 0 | _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); |
106 | 0 | if (ret != SPV_SUCCESS) return ret; |
107 | 168 | } else if (_.IsCooperativeMatrixType(result_type) || |
108 | 168 | _.IsCooperativeMatrixType(input_type)) { |
109 | 0 | spv_result_t ret = |
110 | 0 | _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true, |
111 | 0 | HasCoopMatTranspose(_, inst->id())); |
112 | 0 | if (ret != SPV_SUCCESS) return ret; |
113 | 168 | } else { |
114 | 168 | if (_.GetDimension(result_type) != _.GetDimension(input_type)) |
115 | 6 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
116 | 6 | << "Expected input to have the same dimension as Result Type: " |
117 | 6 | << spvOpcodeString(opcode); |
118 | 168 | } |
119 | | |
120 | 162 | if (auto error = |
121 | 162 | ValidateVulkanOCPMicroscalingFloatIntConversion(_, inst, input_type)) |
122 | 0 | return error; |
123 | | |
124 | 162 | if (auto error = ValidateShaderBitWidth(_, inst)) return error; |
125 | | |
126 | 162 | return SPV_SUCCESS; |
127 | 162 | } |
128 | | |
129 | | spv_result_t ValidateConvertFToS(ValidationState_t& _, const Instruction* inst, |
130 | 11.1k | uint32_t operand_index = 2) { |
131 | 11.1k | const spv::Op opcode = inst->opcode(); |
132 | 11.1k | const uint32_t result_type = inst->type_id(); |
133 | 11.1k | if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && |
134 | 12 | !_.IsIntCooperativeMatrixType(result_type) && |
135 | 12 | !_.IsIntCooperativeVectorNVType(result_type)) |
136 | 12 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
137 | 12 | << "Expected int scalar or vector type as Result Type: " |
138 | 12 | << spvOpcodeString(opcode); |
139 | | |
140 | 11.1k | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
141 | 11.1k | if (!input_type || |
142 | 11.1k | (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type) && |
143 | 12 | !_.IsFloatCooperativeMatrixType(input_type) && |
144 | 12 | !_.IsFloatCooperativeVectorNVType(input_type))) |
145 | 16 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
146 | 16 | << "Expected input to be float scalar or vector: " |
147 | 16 | << spvOpcodeString(opcode); |
148 | | |
149 | 11.1k | if (_.IsCooperativeVectorNVType(result_type) || |
150 | 11.1k | _.IsCooperativeVectorNVType(input_type)) { |
151 | 0 | spv_result_t ret = |
152 | 0 | _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); |
153 | 0 | if (ret != SPV_SUCCESS) return ret; |
154 | 11.1k | } else if (_.IsCooperativeMatrixType(result_type) || |
155 | 11.1k | _.IsCooperativeMatrixType(input_type)) { |
156 | 0 | spv_result_t ret = |
157 | 0 | _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true, |
158 | 0 | HasCoopMatTranspose(_, inst->id())); |
159 | 0 | if (ret != SPV_SUCCESS) return ret; |
160 | 11.1k | } else { |
161 | 11.1k | if (_.GetDimension(result_type) != _.GetDimension(input_type)) |
162 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
163 | 4 | << "Expected input to have the same dimension as Result Type: " |
164 | 4 | << spvOpcodeString(opcode); |
165 | 11.1k | } |
166 | | |
167 | 11.1k | if (auto error = |
168 | 11.1k | ValidateVulkanOCPMicroscalingFloatIntConversion(_, inst, input_type)) |
169 | 0 | return error; |
170 | | |
171 | 11.1k | if (auto error = ValidateShaderBitWidth(_, inst)) return error; |
172 | | |
173 | 11.1k | return SPV_SUCCESS; |
174 | 11.1k | } |
175 | | |
176 | | spv_result_t ValidateConvertIntToF(ValidationState_t& _, |
177 | | const Instruction* inst, |
178 | 23.3k | uint32_t operand_index = 2) { |
179 | 23.3k | const spv::Op opcode = inst->opcode(); |
180 | 23.3k | const uint32_t result_type = inst->type_id(); |
181 | 23.3k | if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) && |
182 | 28 | !_.IsFloatCooperativeMatrixType(result_type) && |
183 | 28 | !_.IsFloatCooperativeVectorNVType(result_type)) |
184 | 28 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
185 | 28 | << "Expected float scalar or vector type as Result Type: " |
186 | 28 | << spvOpcodeString(opcode); |
187 | | |
188 | 23.3k | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
189 | 23.3k | if (!input_type || |
190 | 23.3k | (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && |
191 | 36 | !_.IsIntCooperativeMatrixType(input_type) && |
192 | 36 | !_.IsIntCooperativeVectorNVType(input_type))) |
193 | 41 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
194 | 41 | << "Expected input to be int scalar or vector: " |
195 | 41 | << spvOpcodeString(opcode); |
196 | | |
197 | 23.2k | if (_.IsCooperativeVectorNVType(result_type) || |
198 | 23.2k | _.IsCooperativeVectorNVType(input_type)) { |
199 | 0 | spv_result_t ret = |
200 | 0 | _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); |
201 | 0 | if (ret != SPV_SUCCESS) return ret; |
202 | 23.2k | } else if (_.IsCooperativeMatrixType(result_type) || |
203 | 23.2k | _.IsCooperativeMatrixType(input_type)) { |
204 | 0 | spv_result_t ret = |
205 | 0 | _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true, |
206 | 0 | HasCoopMatTranspose(_, inst->id())); |
207 | 0 | if (ret != SPV_SUCCESS) return ret; |
208 | 23.2k | } else { |
209 | 23.2k | if (_.GetDimension(result_type) != _.GetDimension(input_type)) |
210 | 6 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
211 | 6 | << "Expected input to have the same dimension as Result Type: " |
212 | 6 | << spvOpcodeString(opcode); |
213 | 23.2k | } |
214 | | |
215 | 23.2k | if (auto error = |
216 | 23.2k | ValidateVulkanOCPMicroscalingFloatIntConversion(_, inst, result_type)) |
217 | 0 | return error; |
218 | | |
219 | 23.2k | if (auto error = ValidateShaderBitWidth(_, inst)) return error; |
220 | | |
221 | 23.2k | return SPV_SUCCESS; |
222 | 23.2k | } |
223 | | |
224 | | spv_result_t ValidateUConvert(ValidationState_t& _, const Instruction* inst, |
225 | 47 | uint32_t operand_index = 2) { |
226 | 47 | const spv::Op opcode = inst->opcode(); |
227 | 47 | const uint32_t result_type = inst->type_id(); |
228 | 47 | if (!_.IsUnsignedIntScalarType(result_type) && |
229 | 43 | !_.IsUnsignedIntVectorType(result_type) && |
230 | 35 | !_.IsUnsignedIntCooperativeMatrixType(result_type) && |
231 | 35 | !_.IsUnsignedIntCooperativeVectorNVType(result_type)) |
232 | 35 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
233 | 35 | << "Expected unsigned int scalar or vector type as Result Type: " |
234 | 35 | << spvOpcodeString(opcode); |
235 | | |
236 | 12 | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
237 | 12 | if (!input_type || |
238 | 11 | (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && |
239 | 3 | !_.IsIntCooperativeMatrixType(input_type) && |
240 | 3 | !_.IsIntCooperativeVectorNVType(input_type))) |
241 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
242 | 4 | << "Expected input to be int scalar or vector: " |
243 | 4 | << spvOpcodeString(opcode); |
244 | | |
245 | 8 | if (_.IsCooperativeVectorNVType(result_type) || |
246 | 8 | _.IsCooperativeVectorNVType(input_type)) { |
247 | 0 | spv_result_t ret = |
248 | 0 | _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); |
249 | 0 | if (ret != SPV_SUCCESS) return ret; |
250 | 8 | } else if (_.IsCooperativeMatrixType(result_type) || |
251 | 8 | _.IsCooperativeMatrixType(input_type)) { |
252 | 0 | spv_result_t ret = |
253 | 0 | _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true, |
254 | 0 | HasCoopMatTranspose(_, inst->id())); |
255 | 0 | if (ret != SPV_SUCCESS) return ret; |
256 | 8 | } else { |
257 | 8 | if (_.GetDimension(result_type) != _.GetDimension(input_type)) |
258 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
259 | 4 | << "Expected input to have the same dimension as Result Type: " |
260 | 4 | << spvOpcodeString(opcode); |
261 | 8 | } |
262 | | |
263 | 4 | if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) |
264 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
265 | 4 | << "Expected input to have different bit width from Result " |
266 | 4 | "Type: " |
267 | 4 | << spvOpcodeString(opcode); |
268 | 0 | return SPV_SUCCESS; |
269 | 4 | } |
270 | | |
271 | | spv_result_t ValidateSConvert(ValidationState_t& _, const Instruction* inst, |
272 | 33 | uint32_t operand_index = 2) { |
273 | 33 | const spv::Op opcode = inst->opcode(); |
274 | 33 | const uint32_t result_type = inst->type_id(); |
275 | 33 | if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && |
276 | 21 | !_.IsIntCooperativeMatrixType(result_type) && |
277 | 21 | !_.IsIntCooperativeVectorNVType(result_type)) |
278 | 21 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
279 | 21 | << "Expected int scalar or vector type as Result Type: " |
280 | 21 | << spvOpcodeString(opcode); |
281 | | |
282 | 12 | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
283 | 12 | if (!input_type || |
284 | 11 | (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && |
285 | 3 | !_.IsIntCooperativeMatrixType(input_type) && |
286 | 3 | !_.IsIntCooperativeVectorNVType(input_type))) |
287 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
288 | 4 | << "Expected input to be int scalar or vector: " |
289 | 4 | << spvOpcodeString(opcode); |
290 | | |
291 | 8 | if (_.IsCooperativeVectorNVType(result_type) || |
292 | 8 | _.IsCooperativeVectorNVType(input_type)) { |
293 | 0 | spv_result_t ret = |
294 | 0 | _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); |
295 | 0 | if (ret != SPV_SUCCESS) return ret; |
296 | 8 | } else if (_.IsCooperativeMatrixType(result_type) || |
297 | 8 | _.IsCooperativeMatrixType(input_type)) { |
298 | 0 | spv_result_t ret = |
299 | 0 | _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true, |
300 | 0 | HasCoopMatTranspose(_, inst->id())); |
301 | 0 | if (ret != SPV_SUCCESS) return ret; |
302 | 8 | } else { |
303 | 8 | if (_.GetDimension(result_type) != _.GetDimension(input_type)) |
304 | 3 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
305 | 3 | << "Expected input to have the same dimension as Result Type: " |
306 | 3 | << spvOpcodeString(opcode); |
307 | 8 | } |
308 | | |
309 | 5 | if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) |
310 | 5 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
311 | 5 | << "Expected input to have different bit width from Result " |
312 | 5 | "Type: " |
313 | 5 | << spvOpcodeString(opcode); |
314 | 0 | return SPV_SUCCESS; |
315 | 5 | } |
316 | | |
317 | | spv_result_t ValidateFConvert(ValidationState_t& _, const Instruction* inst, |
318 | 54 | uint32_t operand_index = 2) { |
319 | 54 | const spv::Op opcode = inst->opcode(); |
320 | 54 | const uint32_t result_type = inst->type_id(); |
321 | 54 | if (!_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type) && |
322 | 7 | !_.IsFloatCooperativeMatrixType(result_type) && |
323 | 7 | !_.IsFloatCooperativeVectorNVType(result_type)) |
324 | 7 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
325 | 7 | << "Expected float scalar or vector type as Result Type: " |
326 | 7 | << spvOpcodeString(opcode); |
327 | | |
328 | 47 | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
329 | 47 | if (!input_type || |
330 | 45 | (!_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type) && |
331 | 4 | !_.IsFloatCooperativeMatrixType(input_type) && |
332 | 4 | !_.IsFloatCooperativeVectorNVType(input_type))) |
333 | 6 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
334 | 6 | << "Expected input to be float scalar or vector: " |
335 | 6 | << spvOpcodeString(opcode); |
336 | | |
337 | 41 | if (_.IsCooperativeVectorNVType(result_type) || |
338 | 41 | _.IsCooperativeVectorNVType(input_type)) { |
339 | 0 | spv_result_t ret = |
340 | 0 | _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); |
341 | 0 | if (ret != SPV_SUCCESS) return ret; |
342 | 41 | } else if (_.IsCooperativeMatrixType(result_type) || |
343 | 41 | _.IsCooperativeMatrixType(input_type)) { |
344 | 0 | spv_result_t ret = |
345 | 0 | _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true, |
346 | 0 | HasCoopMatTranspose(_, inst->id())); |
347 | 0 | if (ret != SPV_SUCCESS) return ret; |
348 | 41 | } else { |
349 | 41 | if (_.GetDimension(result_type) != _.GetDimension(input_type)) |
350 | 28 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
351 | 28 | << "Expected input to have the same dimension as Result Type: " |
352 | 28 | << spvOpcodeString(opcode); |
353 | 41 | } |
354 | | |
355 | | // Scalar type |
356 | 13 | const uint32_t resScalarType = _.GetComponentType(result_type); |
357 | 13 | const uint32_t inputScalartype = _.GetComponentType(input_type); |
358 | 13 | if (resScalarType == inputScalartype) { |
359 | 13 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
360 | 13 | << "Expected component type of Value to be different from " |
361 | 13 | "component type of Result Type: " |
362 | 13 | << spvOpcodeString(opcode); |
363 | 13 | } |
364 | | |
365 | 0 | if (spvIsVulkanEnv(_.context()->target_env)) { |
366 | 0 | if (_.ContainsOCPMicroscalingType(result_type)) { |
367 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
368 | 0 | << _.VkErrorID(12466) << spvOpcodeString(opcode) |
369 | 0 | << " must not produce OCP microscaling types in the Vulkan " |
370 | 0 | "environment."; |
371 | 0 | } |
372 | 0 | if (_.ContainsOCPMicroscalingType(input_type) && |
373 | 0 | !IsIEEEOrAlternativeFloatTypeAllowedForOCPMicroscalingFConvert( |
374 | 0 | _, result_type)) { |
375 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
376 | 0 | << _.VkErrorID(12467) << spvOpcodeString(opcode) |
377 | 0 | << " consuming an OCP microscaling type in the Vulkan " |
378 | 0 | "environment must produce IEEE 754, Float8E4M3EXT, " |
379 | 0 | "Float8E5M2EXT, or BFloat16KHR."; |
380 | 0 | } |
381 | 0 | } |
382 | | |
383 | 0 | return SPV_SUCCESS; |
384 | 0 | } |
385 | | |
386 | | spv_result_t ValidateQuantizeToF16(ValidationState_t& _, |
387 | | const Instruction* inst, |
388 | 595 | uint32_t operand_index = 2) { |
389 | 595 | const spv::Op opcode = inst->opcode(); |
390 | 595 | const uint32_t result_type = inst->type_id(); |
391 | 595 | if ((!_.IsFloatScalarType(result_type) && |
392 | 483 | !_.IsFloatVectorType(result_type)) || |
393 | 591 | _.GetBitWidth(result_type) != 32) |
394 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
395 | 4 | << "Expected 32-bit float scalar or vector type as Result Type: " |
396 | 4 | << spvOpcodeString(opcode); |
397 | | |
398 | 591 | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
399 | 591 | if (input_type != result_type) |
400 | 24 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
401 | 24 | << "Expected input type to be equal to Result Type: " |
402 | 24 | << spvOpcodeString(opcode); |
403 | 567 | return SPV_SUCCESS; |
404 | 591 | } |
405 | | |
406 | | spv_result_t ValidateConvertPtrToU(ValidationState_t& _, |
407 | | const Instruction* inst, |
408 | 9 | uint32_t operand_index = 2) { |
409 | 9 | const spv::Op opcode = inst->opcode(); |
410 | 9 | const uint32_t result_type = inst->type_id(); |
411 | 9 | const bool has_masked_gather_scatter = |
412 | 9 | _.HasCapability(spv::Capability::MaskedGatherScatterINTEL); |
413 | | |
414 | 9 | bool valid_result_type = _.IsUnsignedIntScalarType(result_type); |
415 | 9 | if (!valid_result_type && has_masked_gather_scatter) { |
416 | 0 | valid_result_type = _.IsUnsignedIntVectorType(result_type); |
417 | 0 | } |
418 | | |
419 | 9 | if (!valid_result_type) { |
420 | 5 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
421 | 5 | << "Expected unsigned int scalar type as Result Type" |
422 | 5 | << (has_masked_gather_scatter ? " (or vector of unsigned int with " |
423 | 0 | "MaskedGatherScatterINTEL)" |
424 | 5 | : "") |
425 | 5 | << ": " << spvOpcodeString(opcode); |
426 | 5 | } |
427 | | |
428 | 4 | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
429 | | |
430 | 4 | bool valid_input_type = _.IsPointerType(input_type); |
431 | 4 | if (!valid_input_type && has_masked_gather_scatter && input_type) { |
432 | 0 | if (_.IsVectorType(input_type)) { |
433 | 0 | const uint32_t component_type = _.GetComponentType(input_type); |
434 | 0 | valid_input_type = _.IsPointerType(component_type); |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | 4 | if (!valid_input_type) { |
439 | 2 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
440 | 2 | << "Expected input to be a pointer" |
441 | 2 | << (has_masked_gather_scatter |
442 | 2 | ? " (or vector of pointers with MaskedGatherScatterINTEL)" |
443 | 2 | : "") |
444 | 2 | << ": " << spvOpcodeString(opcode); |
445 | 2 | } |
446 | | |
447 | 2 | if (has_masked_gather_scatter && _.IsVectorType(result_type)) { |
448 | 0 | if (!_.IsVectorType(input_type)) { |
449 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
450 | 0 | << "Expected input to be a vector when Result Type is a vector: " |
451 | 0 | << spvOpcodeString(opcode); |
452 | 0 | } |
453 | 0 | if (_.GetDimension(result_type) != _.GetDimension(input_type)) { |
454 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
455 | 0 | << "Expected input to have the same dimension as Result Type: " |
456 | 0 | << spvOpcodeString(opcode); |
457 | 0 | } |
458 | 0 | } |
459 | | |
460 | 2 | if (_.addressing_model() == spv::AddressingModel::Logical) |
461 | 1 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
462 | 1 | << "Logical addressing not supported: " << spvOpcodeString(opcode); |
463 | | |
464 | 1 | if (_.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) { |
465 | 0 | uint32_t ptr_type = input_type; |
466 | 0 | if (_.IsVectorType(input_type)) { |
467 | 0 | ptr_type = _.GetComponentType(input_type); |
468 | 0 | } |
469 | 0 | spv::StorageClass input_storage_class; |
470 | 0 | uint32_t input_data_type = 0; |
471 | 0 | _.GetPointerTypeInfo(ptr_type, &input_data_type, &input_storage_class); |
472 | 0 | if (input_storage_class != spv::StorageClass::PhysicalStorageBuffer) |
473 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
474 | 0 | << "Pointer storage class must be PhysicalStorageBuffer: " |
475 | 0 | << spvOpcodeString(opcode); |
476 | | |
477 | 0 | if (spvIsVulkanEnv(_.context()->target_env)) { |
478 | 0 | if (_.GetBitWidth(result_type) != 64) { |
479 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
480 | 0 | << _.VkErrorID(4710) |
481 | 0 | << "PhysicalStorageBuffer64 addressing mode requires the " |
482 | 0 | "result integer type to have a 64-bit width for Vulkan " |
483 | 0 | "environment."; |
484 | 0 | } |
485 | 0 | } |
486 | 0 | } |
487 | 1 | return SPV_SUCCESS; |
488 | 1 | } |
489 | | |
490 | | spv_result_t ValidateSatConvertInt(ValidationState_t& _, |
491 | 18 | const Instruction* inst) { |
492 | 18 | const spv::Op opcode = inst->opcode(); |
493 | 18 | const uint32_t result_type = inst->type_id(); |
494 | 18 | if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) |
495 | 6 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
496 | 6 | << "Expected int scalar or vector type as Result Type: " |
497 | 6 | << spvOpcodeString(opcode); |
498 | | |
499 | 12 | const uint32_t input_type = _.GetOperandTypeId(inst, 2); |
500 | 12 | if (!input_type || |
501 | 12 | (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type))) |
502 | 1 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
503 | 1 | << "Expected int scalar or vector as input: " |
504 | 1 | << spvOpcodeString(opcode); |
505 | | |
506 | 11 | if (_.GetDimension(result_type) != _.GetDimension(input_type)) |
507 | 1 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
508 | 1 | << "Expected input to have the same dimension as Result Type: " |
509 | 1 | << spvOpcodeString(opcode); |
510 | 10 | return SPV_SUCCESS; |
511 | 11 | } |
512 | | |
513 | | spv_result_t ValidateConvertUToPtr(ValidationState_t& _, |
514 | | const Instruction* inst, |
515 | 9 | uint32_t operand_index = 2) { |
516 | 9 | const spv::Op opcode = inst->opcode(); |
517 | 9 | const uint32_t result_type = inst->type_id(); |
518 | 9 | const bool has_masked_gather_scatter = |
519 | 9 | _.HasCapability(spv::Capability::MaskedGatherScatterINTEL); |
520 | | |
521 | 9 | bool valid_result_type = _.IsPointerType(result_type); |
522 | 9 | if (!valid_result_type && has_masked_gather_scatter) { |
523 | 0 | if (_.IsVectorType(result_type)) { |
524 | 0 | const uint32_t component_type = _.GetComponentType(result_type); |
525 | 0 | valid_result_type = _.IsPointerType(component_type); |
526 | 0 | } |
527 | 0 | } |
528 | | |
529 | 9 | if (!valid_result_type) { |
530 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
531 | 4 | << "Expected Result Type to be a pointer" |
532 | 4 | << (has_masked_gather_scatter |
533 | 4 | ? " (or vector of pointers with MaskedGatherScatterINTEL)" |
534 | 4 | : "") |
535 | 4 | << ": " << spvOpcodeString(opcode); |
536 | 4 | } |
537 | | |
538 | 5 | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
539 | | |
540 | 5 | bool valid_input_type = input_type && _.IsIntScalarType(input_type); |
541 | 5 | if (!valid_input_type && has_masked_gather_scatter && input_type) { |
542 | 0 | valid_input_type = _.IsIntVectorType(input_type); |
543 | 0 | } |
544 | | |
545 | 5 | if (!valid_input_type) { |
546 | 3 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
547 | 3 | << "Expected int scalar as input" |
548 | 3 | << (has_masked_gather_scatter |
549 | 3 | ? " (or vector of int with MaskedGatherScatterINTEL)" |
550 | 3 | : "") |
551 | 3 | << ": " << spvOpcodeString(opcode); |
552 | 3 | } |
553 | | |
554 | 2 | if (has_masked_gather_scatter && _.IsVectorType(result_type)) { |
555 | 0 | if (!_.IsVectorType(input_type)) { |
556 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
557 | 0 | << "Expected input to be a vector when Result Type is a vector: " |
558 | 0 | << spvOpcodeString(opcode); |
559 | 0 | } |
560 | 0 | if (_.GetDimension(result_type) != _.GetDimension(input_type)) { |
561 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
562 | 0 | << "Expected input to have the same dimension as Result Type: " |
563 | 0 | << spvOpcodeString(opcode); |
564 | 0 | } |
565 | 0 | } |
566 | | |
567 | 2 | if (_.addressing_model() == spv::AddressingModel::Logical) |
568 | 1 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
569 | 1 | << "Logical addressing not supported: " << spvOpcodeString(opcode); |
570 | | |
571 | 1 | if (_.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) { |
572 | 0 | uint32_t ptr_type = result_type; |
573 | 0 | if (_.IsVectorType(result_type)) { |
574 | 0 | ptr_type = _.GetComponentType(result_type); |
575 | 0 | } |
576 | 0 | spv::StorageClass result_storage_class; |
577 | 0 | uint32_t result_data_type = 0; |
578 | 0 | _.GetPointerTypeInfo(ptr_type, &result_data_type, &result_storage_class); |
579 | 0 | if (result_storage_class != spv::StorageClass::PhysicalStorageBuffer) |
580 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
581 | 0 | << "Pointer storage class must be PhysicalStorageBuffer: " |
582 | 0 | << spvOpcodeString(opcode); |
583 | | |
584 | 0 | if (spvIsVulkanEnv(_.context()->target_env)) { |
585 | 0 | if (_.GetBitWidth(input_type) != 64) { |
586 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
587 | 0 | << _.VkErrorID(4710) |
588 | 0 | << "PhysicalStorageBuffer64 addressing mode requires the " |
589 | 0 | "input integer to have a 64-bit width for Vulkan " |
590 | 0 | "environment."; |
591 | 0 | } |
592 | 0 | } |
593 | 0 | } |
594 | 1 | return SPV_SUCCESS; |
595 | 1 | } |
596 | | |
597 | | spv_result_t ValidatePtrCastToGeneric(ValidationState_t& _, |
598 | | const Instruction* inst, |
599 | 14 | uint32_t operand_index = 2) { |
600 | 14 | const spv::Op opcode = inst->opcode(); |
601 | 14 | const uint32_t result_type = inst->type_id(); |
602 | 14 | spv::StorageClass result_storage_class; |
603 | 14 | uint32_t result_data_type = 0; |
604 | 14 | if (!_.GetPointerTypeInfo(result_type, &result_data_type, |
605 | 14 | &result_storage_class)) |
606 | 14 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
607 | 14 | << "Expected Result Type to be a pointer: " |
608 | 14 | << spvOpcodeString(opcode); |
609 | | |
610 | 0 | if (result_storage_class != spv::StorageClass::Generic) |
611 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
612 | 0 | << "Expected Result Type to have storage class Generic: " |
613 | 0 | << spvOpcodeString(opcode); |
614 | | |
615 | 0 | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
616 | 0 | spv::StorageClass input_storage_class; |
617 | 0 | uint32_t input_data_type = 0; |
618 | 0 | if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class)) |
619 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
620 | 0 | << "Expected input to be a pointer: " << spvOpcodeString(opcode); |
621 | | |
622 | 0 | if (input_storage_class != spv::StorageClass::Workgroup && |
623 | 0 | input_storage_class != spv::StorageClass::CrossWorkgroup && |
624 | 0 | input_storage_class != spv::StorageClass::Function && |
625 | 0 | input_storage_class != spv::StorageClass::CodeSectionINTEL) |
626 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
627 | 0 | << "Expected input to have storage class Workgroup, " |
628 | 0 | << "CrossWorkgroup, Function or CodeSectionINTEL: " |
629 | 0 | << spvOpcodeString(opcode); |
630 | | |
631 | 0 | if (result_data_type != input_data_type) |
632 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
633 | 0 | << "Expected input and Result Type to point to the same type: " |
634 | 0 | << spvOpcodeString(opcode); |
635 | 0 | return SPV_SUCCESS; |
636 | 0 | } |
637 | | |
638 | | spv_result_t ValidateGenericCastToPtr(ValidationState_t& _, |
639 | | const Instruction* inst, |
640 | 15 | uint32_t operand_index = 2) { |
641 | 15 | const spv::Op opcode = inst->opcode(); |
642 | 15 | const uint32_t result_type = inst->type_id(); |
643 | 15 | spv::StorageClass result_storage_class; |
644 | 15 | uint32_t result_data_type = 0; |
645 | 15 | if (!_.GetPointerTypeInfo(result_type, &result_data_type, |
646 | 15 | &result_storage_class)) |
647 | 15 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
648 | 15 | << "Expected Result Type to be a pointer: " |
649 | 15 | << spvOpcodeString(opcode); |
650 | | |
651 | 0 | if (result_storage_class != spv::StorageClass::Workgroup && |
652 | 0 | result_storage_class != spv::StorageClass::CrossWorkgroup && |
653 | 0 | result_storage_class != spv::StorageClass::Function && |
654 | 0 | result_storage_class != spv::StorageClass::CodeSectionINTEL) |
655 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
656 | 0 | << "Expected Result Type to have storage class Workgroup, " |
657 | 0 | << "CrossWorkgroup, Function or CodeSectionINTEL: " |
658 | 0 | << spvOpcodeString(opcode); |
659 | | |
660 | 0 | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
661 | 0 | spv::StorageClass input_storage_class; |
662 | 0 | uint32_t input_data_type = 0; |
663 | 0 | if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class)) |
664 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
665 | 0 | << "Expected input to be a pointer: " << spvOpcodeString(opcode); |
666 | | |
667 | 0 | if (input_storage_class != spv::StorageClass::Generic) |
668 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
669 | 0 | << "Expected input to have storage class Generic: " |
670 | 0 | << spvOpcodeString(opcode); |
671 | | |
672 | 0 | if (result_data_type != input_data_type) |
673 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
674 | 0 | << "Expected input and Result Type to point to the same type: " |
675 | 0 | << spvOpcodeString(opcode); |
676 | 0 | return SPV_SUCCESS; |
677 | 0 | } |
678 | | |
679 | | spv_result_t ValidateGenericCastToPtrExplicit(ValidationState_t& _, |
680 | 0 | const Instruction* inst) { |
681 | 0 | const spv::Op opcode = inst->opcode(); |
682 | 0 | const uint32_t result_type = inst->type_id(); |
683 | 0 | spv::StorageClass result_storage_class; |
684 | 0 | uint32_t result_data_type = 0; |
685 | 0 | if (!_.GetPointerTypeInfo(result_type, &result_data_type, |
686 | 0 | &result_storage_class)) |
687 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
688 | 0 | << "Expected Result Type to be a pointer: " |
689 | 0 | << spvOpcodeString(opcode); |
690 | | |
691 | 0 | const auto target_storage_class = inst->GetOperandAs<spv::StorageClass>(3); |
692 | 0 | if (result_storage_class != target_storage_class) |
693 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
694 | 0 | << "Expected Result Type to be of target storage class: " |
695 | 0 | << spvOpcodeString(opcode); |
696 | | |
697 | 0 | const uint32_t input_type = _.GetOperandTypeId(inst, 2); |
698 | 0 | spv::StorageClass input_storage_class; |
699 | 0 | uint32_t input_data_type = 0; |
700 | 0 | if (!_.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class)) |
701 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
702 | 0 | << "Expected input to be a pointer: " << spvOpcodeString(opcode); |
703 | | |
704 | 0 | if (input_storage_class != spv::StorageClass::Generic) |
705 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
706 | 0 | << "Expected input to have storage class Generic: " |
707 | 0 | << spvOpcodeString(opcode); |
708 | | |
709 | 0 | if (result_data_type != input_data_type) |
710 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
711 | 0 | << "Expected input and Result Type to point to the same type: " |
712 | 0 | << spvOpcodeString(opcode); |
713 | | |
714 | 0 | if (target_storage_class != spv::StorageClass::Workgroup && |
715 | 0 | target_storage_class != spv::StorageClass::CrossWorkgroup && |
716 | 0 | target_storage_class != spv::StorageClass::Function && |
717 | 0 | target_storage_class != spv::StorageClass::CodeSectionINTEL) |
718 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
719 | 0 | << "Expected target storage class to be Workgroup, " |
720 | 0 | << "CrossWorkgroup, Function or CodeSectionINTEL: " |
721 | 0 | << spvOpcodeString(opcode); |
722 | 0 | return SPV_SUCCESS; |
723 | 0 | } |
724 | | |
725 | | spv_result_t ValidateBitcast(ValidationState_t& _, const Instruction* inst, |
726 | 1.61k | uint32_t operand_index = 2) { |
727 | 1.61k | const spv::Op opcode = inst->opcode(); |
728 | 1.61k | const uint32_t result_type = inst->type_id(); |
729 | 1.61k | const uint32_t input_type = _.GetOperandTypeId(inst, operand_index); |
730 | 1.61k | if (!input_type) |
731 | 3 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
732 | 3 | << "Expected input to have a type: " << spvOpcodeString(opcode); |
733 | | |
734 | 1.60k | const bool result_is_pointer = _.IsPointerType(result_type); |
735 | 1.60k | const bool result_is_int_scalar = _.IsIntScalarType(result_type); |
736 | 1.60k | const bool input_is_pointer = _.IsPointerType(input_type); |
737 | 1.60k | const bool input_is_int_scalar = _.IsIntScalarType(input_type); |
738 | | |
739 | 1.60k | const bool result_is_coopmat = _.IsCooperativeMatrixType(result_type); |
740 | 1.60k | const bool input_is_coopmat = _.IsCooperativeMatrixType(input_type); |
741 | 1.60k | const bool result_is_coopvec = _.IsCooperativeVectorNVType(result_type); |
742 | 1.60k | const bool input_is_coopvec = _.IsCooperativeVectorNVType(input_type); |
743 | | |
744 | 1.60k | if (!result_is_pointer && !result_is_int_scalar && !result_is_coopmat && |
745 | 947 | !result_is_coopvec && !_.IsIntVectorType(result_type) && |
746 | 696 | !_.IsFloatScalarType(result_type) && !_.IsFloatVectorType(result_type)) |
747 | 5 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
748 | 5 | << "Expected Result Type to be a pointer or int or float vector " |
749 | 5 | << "or scalar type: " << spvOpcodeString(opcode); |
750 | | |
751 | 1.60k | if (!input_is_pointer && !input_is_int_scalar && !input_is_coopmat && |
752 | 757 | !input_is_coopvec && !_.IsIntVectorType(input_type) && |
753 | 636 | !_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type)) |
754 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
755 | 4 | << "Expected input to be a pointer or int or float vector " |
756 | 4 | << "or scalar: " << spvOpcodeString(opcode); |
757 | | |
758 | | // NV_cooperative_vector doesn't allow bitcasting between vec<->coopvec, |
759 | | // but long_vector does. |
760 | 1.59k | if (result_is_coopvec != input_is_coopvec && |
761 | 0 | !_.HasCapability(spv::Capability::LongVectorEXT)) |
762 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
763 | 0 | << "Cooperative vector can only be cast to another cooperative " |
764 | 0 | << "vector: " << spvOpcodeString(opcode); |
765 | | |
766 | 1.59k | if (result_is_coopmat != input_is_coopmat) |
767 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
768 | 0 | << "Cooperative matrix can only be cast to another cooperative " |
769 | 0 | << "matrix: " << spvOpcodeString(opcode); |
770 | | |
771 | 1.59k | if (result_is_coopvec && input_is_coopvec && |
772 | 0 | !_.HasCapability(spv::Capability::LongVectorEXT)) { |
773 | 0 | spv_result_t ret = |
774 | 0 | _.CooperativeVectorDimensionsMatch(inst, result_type, input_type); |
775 | 0 | if (ret != SPV_SUCCESS) return ret; |
776 | 0 | } |
777 | | |
778 | 1.59k | if (result_is_coopmat) { |
779 | 0 | spv_result_t ret = _.CooperativeMatrixShapesMatch(inst, result_type, |
780 | 0 | input_type, false, false); |
781 | 0 | if (ret != SPV_SUCCESS) return ret; |
782 | 0 | } |
783 | | |
784 | 1.59k | if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) || |
785 | 1.59k | _.HasExtension(kSPV_KHR_physical_storage_buffer)) { |
786 | 0 | const bool result_is_int_vector = _.IsIntVectorType(result_type); |
787 | 0 | const bool result_has_int32 = |
788 | 0 | _.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeInt, 32); |
789 | 0 | const bool input_is_int_vector = _.IsIntVectorType(input_type); |
790 | 0 | const bool input_has_int32 = |
791 | 0 | _.ContainsSizedIntOrFloatType(input_type, spv::Op::OpTypeInt, 32); |
792 | 0 | if (result_is_pointer && !input_is_pointer && !input_is_int_scalar && |
793 | 0 | !(input_is_int_vector && input_has_int32)) |
794 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
795 | 0 | << "In SPIR-V 1.5 or later (or with " |
796 | 0 | "SPV_KHR_physical_storage_buffer), expected input to be a " |
797 | 0 | "pointer, " |
798 | 0 | "int scalar or 32-bit int " |
799 | 0 | "vector if Result Type is pointer: " |
800 | 0 | << spvOpcodeString(opcode); |
801 | | |
802 | 0 | if (input_is_pointer && !result_is_pointer && !result_is_int_scalar && |
803 | 0 | !(result_is_int_vector && result_has_int32)) |
804 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
805 | 0 | << "In SPIR-V 1.5 or later (or with " |
806 | 0 | "SPV_KHR_physical_storage_buffer), pointer can only be " |
807 | 0 | "converted to " |
808 | 0 | "another pointer, int " |
809 | 0 | "scalar or 32-bit int vector: " |
810 | 0 | << spvOpcodeString(opcode); |
811 | 1.59k | } else { |
812 | 1.59k | if (result_is_pointer && !input_is_pointer && !input_is_int_scalar) |
813 | 3 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
814 | 3 | << "In SPIR-V 1.4 or earlier (and without " |
815 | 3 | "SPV_KHR_physical_storage_buffer), expected input to be a " |
816 | 3 | "pointer " |
817 | 3 | "or int scalar if Result " |
818 | 3 | "Type is pointer: " |
819 | 3 | << spvOpcodeString(opcode); |
820 | | |
821 | 1.59k | if (input_is_pointer && !result_is_pointer && !result_is_int_scalar) |
822 | 4 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
823 | 4 | << "In SPIR-V 1.4 or earlier (and without " |
824 | 4 | "SPV_KHR_physical_storage_buffer), pointer can only be " |
825 | 4 | "converted " |
826 | 4 | "to another pointer or int " |
827 | 4 | "scalar: " |
828 | 4 | << spvOpcodeString(opcode); |
829 | 1.59k | } |
830 | | |
831 | 1.59k | if (!result_is_pointer && !input_is_pointer) { |
832 | 1.49k | const uint32_t result_size = |
833 | 1.49k | _.GetBitWidth(result_type) * _.GetDimension(result_type); |
834 | 1.49k | const uint32_t input_size = |
835 | 1.49k | _.GetBitWidth(input_type) * _.GetDimension(input_type); |
836 | 1.49k | if (result_size != input_size) |
837 | 11 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
838 | 11 | << "Expected input to have the same total bit width as " |
839 | 11 | << "Result Type: " << spvOpcodeString(opcode); |
840 | 1.49k | } |
841 | | |
842 | 1.58k | if (auto error = ValidateShaderBitWidth(_, inst)) return error; |
843 | | |
844 | 1.58k | return SPV_SUCCESS; |
845 | 1.58k | } |
846 | | |
847 | | spv_result_t ValidateConvertUToAccelerationStructure(ValidationState_t& _, |
848 | 0 | const Instruction* inst) { |
849 | 0 | const spv::Op opcode = inst->opcode(); |
850 | 0 | const uint32_t result_type = inst->type_id(); |
851 | 0 | if (!_.IsAccelerationStructureType(result_type)) { |
852 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
853 | 0 | << "Expected Result Type to be a Acceleration Structure: " |
854 | 0 | << spvOpcodeString(opcode); |
855 | 0 | } |
856 | | |
857 | 0 | const uint32_t input_type = _.GetOperandTypeId(inst, 2); |
858 | 0 | if (!input_type || !_.IsUnsigned64BitHandle(input_type)) { |
859 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
860 | 0 | << "Expected 64-bit uint scalar or 2-component 32-bit uint " |
861 | 0 | "vector as input: " |
862 | 0 | << spvOpcodeString(opcode); |
863 | 0 | } |
864 | 0 | return SPV_SUCCESS; |
865 | 0 | } |
866 | | |
867 | | spv_result_t ValidateBitcastExtract(ValidationState_t& _, |
868 | 0 | const Instruction* inst) { |
869 | 0 | const spv::Op opcode = inst->opcode(); |
870 | 0 | const uint32_t result_type = inst->type_id(); |
871 | 0 | const uint32_t base_type = _.GetOperandTypeId(inst, 2); |
872 | 0 | const uint32_t offset_type = _.GetOperandTypeId(inst, 3); |
873 | |
|
874 | 0 | if (!IsNumericScalarOrVectorType(_, result_type)) { |
875 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
876 | 0 | << _.VkErrorID(12468) |
877 | 0 | << "Expected Result Type to be a numerical scalar or vector type: " |
878 | 0 | << spvOpcodeString(opcode); |
879 | 0 | } |
880 | | |
881 | 0 | if (spvIsVulkanEnv(_.context()->target_env) && |
882 | 0 | !_.ContainsOCPMicroscalingNonByteType(result_type)) { |
883 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
884 | 0 | << _.VkErrorID(12468) |
885 | 0 | << "Expected Result Type to be a Float4EXT or Float6EXT type in " |
886 | 0 | "the Vulkan environment: " |
887 | 0 | << spvOpcodeString(opcode); |
888 | 0 | } |
889 | | |
890 | 0 | if (!base_type || !IsNumericScalarOrVectorType(_, base_type)) { |
891 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
892 | 0 | << _.VkErrorID(12469) |
893 | 0 | << "Expected Base to be a numerical scalar or vector type: " |
894 | 0 | << spvOpcodeString(opcode); |
895 | 0 | } |
896 | | |
897 | 0 | if (spvIsVulkanEnv(_.context()->target_env) && |
898 | 0 | !_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) { |
899 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
900 | 0 | << _.VkErrorID(12469) |
901 | 0 | << "Expected Base to be an integer scalar or vector type in the " |
902 | 0 | "Vulkan environment: " |
903 | 0 | << spvOpcodeString(opcode); |
904 | 0 | } |
905 | | |
906 | 0 | if (_.GetDimension(result_type) != _.GetDimension(base_type)) { |
907 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
908 | 0 | << "Expected Base to have the same dimension as Result Type: " |
909 | 0 | << spvOpcodeString(opcode); |
910 | 0 | } |
911 | | |
912 | 0 | if (_.GetBitWidth(base_type) <= _.GetBitWidth(result_type)) { |
913 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
914 | 0 | << "Expected Base component bit width to be greater than Result " |
915 | 0 | "Type component bit width: " |
916 | 0 | << spvOpcodeString(opcode); |
917 | 0 | } |
918 | | |
919 | 0 | if (!offset_type || !_.IsIntScalarType(offset_type)) { |
920 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
921 | 0 | << "Expected Offset to be a scalar integer type: " |
922 | 0 | << spvOpcodeString(opcode); |
923 | 0 | } |
924 | | |
925 | 0 | return SPV_SUCCESS; |
926 | 0 | } |
927 | | |
928 | | spv_result_t ValidateCooperativeMatrix(ValidationState_t& _, |
929 | 0 | const Instruction* inst) { |
930 | 0 | const spv::Op opcode = inst->opcode(); |
931 | 0 | const uint32_t result_type = inst->type_id(); |
932 | 0 | if (!_.IsCooperativeMatrixKHRType(result_type)) { |
933 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
934 | 0 | << "Expected OpTypeCooperativeMatrixKHR Result Type: " |
935 | 0 | << spvOpcodeString(opcode); |
936 | 0 | } |
937 | 0 | const uint32_t input_type = _.GetOperandTypeId(inst, 2); |
938 | 0 | if (!_.IsCooperativeMatrixKHRType(input_type)) { |
939 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
940 | 0 | << "Expected OpTypeCooperativeMatrixKHR type for Matrix input: " |
941 | 0 | << spvOpcodeString(opcode); |
942 | 0 | } |
943 | | |
944 | 0 | const bool has_transpose_decoration = HasCoopMatTranspose(_, inst->id()); |
945 | 0 | const bool swap_row_col = opcode == spv::Op::OpCooperativeMatrixTransposeNV || |
946 | 0 | has_transpose_decoration; |
947 | 0 | if (auto error = _.CooperativeMatrixShapesMatch(inst, result_type, input_type, |
948 | 0 | true, swap_row_col)) |
949 | 0 | return error; |
950 | | |
951 | 0 | if (opcode == spv::Op::OpCooperativeMatrixConvertUseEXT) { |
952 | 0 | auto result_comp_type_id = |
953 | 0 | _.FindDef(result_type)->GetOperandAs<uint32_t>(1); |
954 | 0 | auto input_comp_type_id = _.FindDef(input_type)->GetOperandAs<uint32_t>(1); |
955 | 0 | auto result_comp_type = _.FindDef(result_comp_type_id); |
956 | 0 | auto input_comp_type = _.FindDef(input_comp_type_id); |
957 | |
|
958 | 0 | const bool same_component_type = result_comp_type_id == input_comp_type_id; |
959 | 0 | const bool signedness_only_difference = |
960 | 0 | result_comp_type->opcode() == spv::Op::OpTypeInt && |
961 | 0 | input_comp_type->opcode() == spv::Op::OpTypeInt && |
962 | 0 | result_comp_type->word(2) == input_comp_type->word(2) && |
963 | 0 | result_comp_type->word(3) != input_comp_type->word(3); |
964 | 0 | if (!same_component_type && !signedness_only_difference) { |
965 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
966 | 0 | << "Result Type and Matrix component types mismatch: " |
967 | 0 | << spvOpcodeString(opcode); |
968 | 0 | } |
969 | | |
970 | 0 | const auto result_use_id = |
971 | 0 | _.FindDef(result_type)->GetOperandAs<uint32_t>(5); |
972 | 0 | const auto input_use_id = _.FindDef(input_type)->GetOperandAs<uint32_t>(5); |
973 | 0 | const auto result_use_eval = _.EvalInt32IfConst(result_use_id); |
974 | 0 | const auto input_use_eval = _.EvalInt32IfConst(input_use_id); |
975 | 0 | const bool result_is_const_int32 = std::get<1>(result_use_eval); |
976 | 0 | const bool input_is_const_int32 = std::get<1>(input_use_eval); |
977 | 0 | const uint32_t result_use = std::get<2>(result_use_eval); |
978 | 0 | const uint32_t input_use = std::get<2>(input_use_eval); |
979 | |
|
980 | 0 | const auto is_accumulator = [](uint32_t use) { |
981 | 0 | return use == uint32_t(spv::CooperativeMatrixUse::MatrixAccumulatorKHR); |
982 | 0 | }; |
983 | 0 | const auto is_a_or_b = [](uint32_t use) { |
984 | 0 | return use == uint32_t(spv::CooperativeMatrixUse::MatrixAKHR) || |
985 | 0 | use == uint32_t(spv::CooperativeMatrixUse::MatrixBKHR); |
986 | 0 | }; |
987 | |
|
988 | 0 | bool invalid_use_change = result_use_id == input_use_id; |
989 | 0 | if (_.HasCapability(spv::Capability::CooperativeMatrixConversionsEXT)) { |
990 | 0 | if (result_is_const_int32 && input_is_const_int32) { |
991 | 0 | invalid_use_change |= |
992 | 0 | !((is_accumulator(input_use) && is_a_or_b(result_use)) || |
993 | 0 | (is_a_or_b(input_use) && is_accumulator(result_use))); |
994 | 0 | } else { |
995 | 0 | invalid_use_change |= |
996 | 0 | (result_is_const_int32 && !is_accumulator(result_use) && |
997 | 0 | !is_a_or_b(result_use)) || |
998 | 0 | (input_is_const_int32 && !is_accumulator(input_use) && |
999 | 0 | !is_a_or_b(input_use)); |
1000 | 0 | } |
1001 | 0 | } else { |
1002 | 0 | invalid_use_change |= |
1003 | 0 | (result_is_const_int32 && !is_a_or_b(result_use)) || |
1004 | 0 | (input_is_const_int32 && !is_accumulator(input_use)); |
1005 | 0 | } |
1006 | |
|
1007 | 0 | if (invalid_use_change) { |
1008 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
1009 | 0 | << "Matrix and Result Type must convert between " |
1010 | 0 | "MatrixAccumulatorKHR and MatrixAKHR or MatrixBKHR: " |
1011 | 0 | << spvOpcodeString(opcode); |
1012 | 0 | } |
1013 | 0 | } |
1014 | | |
1015 | 0 | if (opcode == spv::Op::OpCooperativeMatrixTransposeNV) { |
1016 | 0 | const auto result_use_id = |
1017 | 0 | _.FindDef(result_type)->GetOperandAs<uint32_t>(5); |
1018 | 0 | const auto input_use_id = _.FindDef(input_type)->GetOperandAs<uint32_t>(5); |
1019 | 0 | const auto result_use_eval = _.EvalInt32IfConst(result_use_id); |
1020 | 0 | const auto input_use_eval = _.EvalInt32IfConst(input_use_id); |
1021 | 0 | const bool result_is_const_int32 = std::get<1>(result_use_eval); |
1022 | 0 | const bool input_is_const_int32 = std::get<1>(input_use_eval); |
1023 | 0 | const uint32_t result_use = std::get<2>(result_use_eval); |
1024 | 0 | const uint32_t input_use = std::get<2>(input_use_eval); |
1025 | 0 | if (result_use_id == input_use_id || |
1026 | 0 | (result_is_const_int32 && |
1027 | 0 | result_use != uint32_t(spv::CooperativeMatrixUse::MatrixBKHR)) || |
1028 | 0 | (input_is_const_int32 && |
1029 | 0 | input_use != |
1030 | 0 | uint32_t(spv::CooperativeMatrixUse::MatrixAccumulatorKHR))) { |
1031 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
1032 | 0 | << "Result Type must have UseB and Matrix must have " |
1033 | 0 | "UseAccumulator: " |
1034 | 0 | << spvOpcodeString(opcode); |
1035 | 0 | } |
1036 | 0 | } |
1037 | 0 | return SPV_SUCCESS; |
1038 | 0 | } |
1039 | | |
1040 | | spv_result_t ValidateBitCastArray(ValidationState_t& _, |
1041 | 0 | const Instruction* inst) { |
1042 | 0 | const spv::Op opcode = inst->opcode(); |
1043 | 0 | const uint32_t result_type = inst->type_id(); |
1044 | 0 | const auto result_type_inst = _.FindDef(result_type); |
1045 | 0 | const auto source = _.FindDef(inst->GetOperandAs<uint32_t>(2u)); |
1046 | 0 | const auto source_type_inst = _.FindDef(source->type_id()); |
1047 | | |
1048 | | // Are the input and the result arrays? |
1049 | 0 | if (result_type_inst->opcode() != spv::Op::OpTypeArray || |
1050 | 0 | source_type_inst->opcode() != spv::Op::OpTypeArray) { |
1051 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
1052 | 0 | << "Opcode " << spvOpcodeString(opcode) |
1053 | 0 | << " requires OpTypeArray operands for the input and the " |
1054 | 0 | "result."; |
1055 | 0 | } |
1056 | | |
1057 | 0 | const auto source_elt_type = _.GetComponentType(source_type_inst->id()); |
1058 | 0 | const auto result_elt_type = _.GetComponentType(result_type_inst->id()); |
1059 | |
|
1060 | 0 | if (!_.IsIntNOrFP32OrFP16<32>(source_elt_type)) { |
1061 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
1062 | 0 | << "Opcode " << spvOpcodeString(opcode) |
1063 | 0 | << " requires the source element type be one of 32-bit " |
1064 | 0 | "OpTypeInt " |
1065 | 0 | "(signed/unsigned), 32-bit OpTypeFloat and 16-bit " |
1066 | 0 | "OpTypeFloat"; |
1067 | 0 | } |
1068 | | |
1069 | 0 | if (!_.IsIntNOrFP32OrFP16<32>(result_elt_type)) { |
1070 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
1071 | 0 | << "Opcode " << spvOpcodeString(opcode) |
1072 | 0 | << " requires the result element type be one of 32-bit " |
1073 | 0 | "OpTypeInt " |
1074 | 0 | "(signed/unsigned), 32-bit OpTypeFloat and 16-bit " |
1075 | 0 | "OpTypeFloat"; |
1076 | 0 | } |
1077 | | |
1078 | 0 | unsigned src_arr_len_id = source_type_inst->GetOperandAs<unsigned>(2u); |
1079 | 0 | unsigned res_arr_len_id = result_type_inst->GetOperandAs<unsigned>(2u); |
1080 | | |
1081 | | // Are the input and result element types compatible? |
1082 | 0 | unsigned src_arr_len = UINT_MAX, res_arr_len = UINT_MAX; |
1083 | 0 | bool src_arr_len_status = |
1084 | 0 | _.GetConstantValueAs<unsigned>(src_arr_len_id, src_arr_len); |
1085 | 0 | bool res_arr_len_status = |
1086 | 0 | _.GetConstantValueAs<unsigned>(res_arr_len_id, res_arr_len); |
1087 | |
|
1088 | 0 | bool is_src_arr_len_spec_const = |
1089 | 0 | spvOpcodeIsSpecConstant(_.FindDef(src_arr_len_id)->opcode()); |
1090 | 0 | bool is_res_arr_len_spec_const = |
1091 | 0 | spvOpcodeIsSpecConstant(_.FindDef(res_arr_len_id)->opcode()); |
1092 | |
|
1093 | 0 | unsigned source_bitlen = _.GetBitWidth(source_elt_type) * src_arr_len; |
1094 | 0 | unsigned result_bitlen = _.GetBitWidth(result_elt_type) * res_arr_len; |
1095 | 0 | if (!is_src_arr_len_spec_const && !is_res_arr_len_spec_const && |
1096 | 0 | (!src_arr_len_status || !res_arr_len_status || |
1097 | 0 | source_bitlen != result_bitlen)) { |
1098 | 0 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
1099 | 0 | << "Opcode " << spvOpcodeString(opcode) |
1100 | 0 | << " requires source and result types be compatible for " |
1101 | 0 | "conversion."; |
1102 | 0 | } |
1103 | 0 | return SPV_SUCCESS; |
1104 | 0 | } |
1105 | | |
1106 | | // Validates correctness of conversion instructions. |
1107 | 14.3M | spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { |
1108 | 14.3M | switch (inst->opcode()) { |
1109 | 196 | case spv::Op::OpConvertFToU: |
1110 | 196 | return ValidateConvertFToU(_, inst); |
1111 | 11.1k | case spv::Op::OpConvertFToS: |
1112 | 11.1k | return ValidateConvertFToS(_, inst); |
1113 | 22.3k | case spv::Op::OpConvertSToF: |
1114 | 23.3k | case spv::Op::OpConvertUToF: |
1115 | 23.3k | return ValidateConvertIntToF(_, inst); |
1116 | 37 | case spv::Op::OpUConvert: |
1117 | 37 | return ValidateUConvert(_, inst); |
1118 | 18 | case spv::Op::OpSConvert: |
1119 | 18 | return ValidateSConvert(_, inst); |
1120 | 22 | case spv::Op::OpFConvert: |
1121 | 22 | return ValidateFConvert(_, inst); |
1122 | 555 | case spv::Op::OpQuantizeToF16: |
1123 | 555 | return ValidateQuantizeToF16(_, inst); |
1124 | 5 | case spv::Op::OpConvertPtrToU: |
1125 | 5 | return ValidateConvertPtrToU(_, inst); |
1126 | 8 | case spv::Op::OpSatConvertSToU: |
1127 | 18 | case spv::Op::OpSatConvertUToS: |
1128 | 18 | return ValidateSatConvertInt(_, inst); |
1129 | 6 | case spv::Op::OpConvertUToPtr: |
1130 | 6 | return ValidateConvertUToPtr(_, inst); |
1131 | 1 | case spv::Op::OpPtrCastToGeneric: |
1132 | 1 | return ValidatePtrCastToGeneric(_, inst); |
1133 | 1 | case spv::Op::OpGenericCastToPtr: |
1134 | 1 | return ValidateGenericCastToPtr(_, inst); |
1135 | 0 | case spv::Op::OpGenericCastToPtrExplicit: |
1136 | 0 | return ValidateGenericCastToPtrExplicit(_, inst); |
1137 | 1.49k | case spv::Op::OpBitcast: |
1138 | 1.49k | return ValidateBitcast(_, inst); |
1139 | 0 | case spv::Op::OpBitcastExtractEXT: |
1140 | 0 | return ValidateBitcastExtract(_, inst); |
1141 | 0 | case spv::Op::OpConvertUToAccelerationStructureKHR: |
1142 | 0 | return ValidateConvertUToAccelerationStructure(_, inst); |
1143 | 0 | case spv::Op::OpCooperativeMatrixConvertUseEXT: |
1144 | 0 | case spv::Op::OpCooperativeMatrixTransposeNV: |
1145 | 0 | return ValidateCooperativeMatrix(_, inst); |
1146 | 0 | case spv::Op::OpBitCastArrayQCOM: |
1147 | 0 | return ValidateBitCastArray(_, inst); |
1148 | | |
1149 | 527 | case spv::Op::OpSpecConstantOp: { |
1150 | 527 | switch (inst->GetOperandAs<spv::Op>(2u)) { |
1151 | 10 | case spv::Op::OpUConvert: |
1152 | 10 | return ValidateUConvert(_, inst, 3); |
1153 | 15 | case spv::Op::OpSConvert: |
1154 | 15 | return ValidateSConvert(_, inst, 3); |
1155 | 32 | case spv::Op::OpFConvert: |
1156 | 32 | return ValidateFConvert(_, inst, 3); |
1157 | 13 | case spv::Op::OpConvertSToF: |
1158 | 23 | case spv::Op::OpConvertUToF: |
1159 | 23 | return ValidateConvertIntToF(_, inst, 3); |
1160 | 6 | case spv::Op::OpConvertFToS: |
1161 | 6 | return ValidateConvertFToS(_, inst, 3); |
1162 | 14 | case spv::Op::OpConvertFToU: |
1163 | 14 | return ValidateConvertFToU(_, inst, 3); |
1164 | 40 | case spv::Op::OpQuantizeToF16: |
1165 | 40 | return ValidateQuantizeToF16(_, inst, 3); |
1166 | 4 | case spv::Op::OpConvertPtrToU: |
1167 | 4 | return ValidateConvertPtrToU(_, inst, 3); |
1168 | 3 | case spv::Op::OpConvertUToPtr: |
1169 | 3 | return ValidateConvertUToPtr(_, inst, 3); |
1170 | 14 | case spv::Op::OpGenericCastToPtr: |
1171 | 14 | return ValidateGenericCastToPtr(_, inst, 3); |
1172 | 13 | case spv::Op::OpPtrCastToGeneric: |
1173 | 13 | return ValidatePtrCastToGeneric(_, inst, 3); |
1174 | 119 | case spv::Op::OpBitcast: |
1175 | 119 | return ValidateBitcast(_, inst, 3); |
1176 | 234 | default: |
1177 | 234 | break; |
1178 | 527 | } |
1179 | 234 | break; |
1180 | 527 | } |
1181 | 14.3M | default: |
1182 | 14.3M | break; |
1183 | 14.3M | } |
1184 | | |
1185 | 14.3M | return SPV_SUCCESS; |
1186 | 14.3M | } |
1187 | | |
1188 | | } // namespace val |
1189 | | } // namespace spvtools |