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 "source/opcode.h"
18 #include "source/spirv_constant.h"
19 #include "source/spirv_target_env.h"
20 #include "source/val/instruction.h"
21 #include "source/val/validate.h"
22 #include "source/val/validation_state.h"
23
24 namespace spvtools {
25 namespace val {
26
27 // Validates correctness of conversion instructions.
ConversionPass(ValidationState_t & _,const Instruction * inst)28 spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
29 const spv::Op opcode = inst->opcode();
30 const uint32_t result_type = inst->type_id();
31
32 switch (opcode) {
33 case spv::Op::OpConvertFToU: {
34 if (!_.IsUnsignedIntScalarType(result_type) &&
35 !_.IsUnsignedIntVectorType(result_type) &&
36 !_.IsUnsignedIntCooperativeMatrixType(result_type) &&
37 !_.IsUnsignedIntCooperativeVectorNVType(result_type))
38 return _.diag(SPV_ERROR_INVALID_DATA, inst)
39 << "Expected unsigned int scalar or vector type as Result Type: "
40 << spvOpcodeString(opcode);
41
42 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
43 if (!input_type || (!_.IsFloatScalarType(input_type) &&
44 !_.IsFloatVectorType(input_type) &&
45 !_.IsFloatCooperativeMatrixType(input_type) &&
46 !_.IsFloatCooperativeVectorNVType(input_type)))
47 return _.diag(SPV_ERROR_INVALID_DATA, inst)
48 << "Expected input to be float scalar or vector: "
49 << spvOpcodeString(opcode);
50
51 if (_.IsCooperativeVectorNVType(result_type) ||
52 _.IsCooperativeVectorNVType(input_type)) {
53 spv_result_t ret =
54 _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
55 if (ret != SPV_SUCCESS) return ret;
56 } else if (_.IsCooperativeMatrixType(result_type) ||
57 _.IsCooperativeMatrixType(input_type)) {
58 spv_result_t ret =
59 _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
60 if (ret != SPV_SUCCESS) return ret;
61 } else {
62 if (_.GetDimension(result_type) != _.GetDimension(input_type))
63 return _.diag(SPV_ERROR_INVALID_DATA, inst)
64 << "Expected input to have the same dimension as Result Type: "
65 << spvOpcodeString(opcode);
66 }
67
68 break;
69 }
70
71 case spv::Op::OpConvertFToS: {
72 if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
73 !_.IsIntCooperativeMatrixType(result_type) &&
74 !_.IsIntCooperativeVectorNVType(result_type))
75 return _.diag(SPV_ERROR_INVALID_DATA, inst)
76 << "Expected int scalar or vector type as Result Type: "
77 << spvOpcodeString(opcode);
78
79 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
80 if (!input_type || (!_.IsFloatScalarType(input_type) &&
81 !_.IsFloatVectorType(input_type) &&
82 !_.IsFloatCooperativeMatrixType(input_type) &&
83 !_.IsFloatCooperativeVectorNVType(input_type)))
84 return _.diag(SPV_ERROR_INVALID_DATA, inst)
85 << "Expected input to be float scalar or vector: "
86 << spvOpcodeString(opcode);
87
88 if (_.IsCooperativeVectorNVType(result_type) ||
89 _.IsCooperativeVectorNVType(input_type)) {
90 spv_result_t ret =
91 _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
92 if (ret != SPV_SUCCESS) return ret;
93 } else if (_.IsCooperativeMatrixType(result_type) ||
94 _.IsCooperativeMatrixType(input_type)) {
95 spv_result_t ret =
96 _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
97 if (ret != SPV_SUCCESS) return ret;
98 } else {
99 if (_.GetDimension(result_type) != _.GetDimension(input_type))
100 return _.diag(SPV_ERROR_INVALID_DATA, inst)
101 << "Expected input to have the same dimension as Result Type: "
102 << spvOpcodeString(opcode);
103 }
104
105 break;
106 }
107
108 case spv::Op::OpConvertSToF:
109 case spv::Op::OpConvertUToF: {
110 if (!_.IsFloatScalarType(result_type) &&
111 !_.IsFloatVectorType(result_type) &&
112 !_.IsFloatCooperativeMatrixType(result_type) &&
113 !_.IsFloatCooperativeVectorNVType(result_type))
114 return _.diag(SPV_ERROR_INVALID_DATA, inst)
115 << "Expected float scalar or vector type as Result Type: "
116 << spvOpcodeString(opcode);
117
118 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
119 if (!input_type ||
120 (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
121 !_.IsIntCooperativeMatrixType(input_type) &&
122 !_.IsIntCooperativeVectorNVType(input_type)))
123 return _.diag(SPV_ERROR_INVALID_DATA, inst)
124 << "Expected input to be int scalar or vector: "
125 << spvOpcodeString(opcode);
126
127 if (_.IsCooperativeVectorNVType(result_type) ||
128 _.IsCooperativeVectorNVType(input_type)) {
129 spv_result_t ret =
130 _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
131 if (ret != SPV_SUCCESS) return ret;
132 } else if (_.IsCooperativeMatrixType(result_type) ||
133 _.IsCooperativeMatrixType(input_type)) {
134 spv_result_t ret =
135 _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
136 if (ret != SPV_SUCCESS) return ret;
137 } else {
138 if (_.GetDimension(result_type) != _.GetDimension(input_type))
139 return _.diag(SPV_ERROR_INVALID_DATA, inst)
140 << "Expected input to have the same dimension as Result Type: "
141 << spvOpcodeString(opcode);
142 }
143
144 break;
145 }
146
147 case spv::Op::OpUConvert: {
148 if (!_.IsUnsignedIntScalarType(result_type) &&
149 !_.IsUnsignedIntVectorType(result_type) &&
150 !_.IsUnsignedIntCooperativeMatrixType(result_type) &&
151 !_.IsUnsignedIntCooperativeVectorNVType(result_type))
152 return _.diag(SPV_ERROR_INVALID_DATA, inst)
153 << "Expected unsigned int scalar or vector type as Result Type: "
154 << spvOpcodeString(opcode);
155
156 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
157 if (!input_type ||
158 (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
159 !_.IsIntCooperativeMatrixType(input_type) &&
160 !_.IsIntCooperativeVectorNVType(input_type)))
161 return _.diag(SPV_ERROR_INVALID_DATA, inst)
162 << "Expected input to be int scalar or vector: "
163 << spvOpcodeString(opcode);
164
165 if (_.IsCooperativeVectorNVType(result_type) ||
166 _.IsCooperativeVectorNVType(input_type)) {
167 spv_result_t ret =
168 _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
169 if (ret != SPV_SUCCESS) return ret;
170 } else if (_.IsCooperativeMatrixType(result_type) ||
171 _.IsCooperativeMatrixType(input_type)) {
172 spv_result_t ret =
173 _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
174 if (ret != SPV_SUCCESS) return ret;
175 } else {
176 if (_.GetDimension(result_type) != _.GetDimension(input_type))
177 return _.diag(SPV_ERROR_INVALID_DATA, inst)
178 << "Expected input to have the same dimension as Result Type: "
179 << spvOpcodeString(opcode);
180 }
181
182 if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
183 return _.diag(SPV_ERROR_INVALID_DATA, inst)
184 << "Expected input to have different bit width from Result "
185 "Type: "
186 << spvOpcodeString(opcode);
187 break;
188 }
189
190 case spv::Op::OpSConvert: {
191 if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
192 !_.IsIntCooperativeMatrixType(result_type) &&
193 !_.IsIntCooperativeVectorNVType(result_type))
194 return _.diag(SPV_ERROR_INVALID_DATA, inst)
195 << "Expected int scalar or vector type as Result Type: "
196 << spvOpcodeString(opcode);
197
198 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
199 if (!input_type ||
200 (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
201 !_.IsIntCooperativeMatrixType(input_type) &&
202 !_.IsIntCooperativeVectorNVType(input_type)))
203 return _.diag(SPV_ERROR_INVALID_DATA, inst)
204 << "Expected input to be int scalar or vector: "
205 << spvOpcodeString(opcode);
206
207 if (_.IsCooperativeVectorNVType(result_type) ||
208 _.IsCooperativeVectorNVType(input_type)) {
209 spv_result_t ret =
210 _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
211 if (ret != SPV_SUCCESS) return ret;
212 } else if (_.IsCooperativeMatrixType(result_type) ||
213 _.IsCooperativeMatrixType(input_type)) {
214 spv_result_t ret =
215 _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
216 if (ret != SPV_SUCCESS) return ret;
217 } else {
218 if (_.GetDimension(result_type) != _.GetDimension(input_type))
219 return _.diag(SPV_ERROR_INVALID_DATA, inst)
220 << "Expected input to have the same dimension as Result Type: "
221 << spvOpcodeString(opcode);
222 }
223
224 if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
225 return _.diag(SPV_ERROR_INVALID_DATA, inst)
226 << "Expected input to have different bit width from Result "
227 "Type: "
228 << spvOpcodeString(opcode);
229 break;
230 }
231
232 case spv::Op::OpFConvert: {
233 if (!_.IsFloatScalarType(result_type) &&
234 !_.IsFloatVectorType(result_type) &&
235 !_.IsFloatCooperativeMatrixType(result_type) &&
236 !_.IsFloatCooperativeVectorNVType(result_type))
237 return _.diag(SPV_ERROR_INVALID_DATA, inst)
238 << "Expected float scalar or vector type as Result Type: "
239 << spvOpcodeString(opcode);
240
241 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
242 if (!input_type || (!_.IsFloatScalarType(input_type) &&
243 !_.IsFloatVectorType(input_type) &&
244 !_.IsFloatCooperativeMatrixType(input_type) &&
245 !_.IsFloatCooperativeVectorNVType(input_type)))
246 return _.diag(SPV_ERROR_INVALID_DATA, inst)
247 << "Expected input to be float scalar or vector: "
248 << spvOpcodeString(opcode);
249
250 if (_.IsCooperativeVectorNVType(result_type) ||
251 _.IsCooperativeVectorNVType(input_type)) {
252 spv_result_t ret =
253 _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
254 if (ret != SPV_SUCCESS) return ret;
255 } else if (_.IsCooperativeMatrixType(result_type) ||
256 _.IsCooperativeMatrixType(input_type)) {
257 spv_result_t ret =
258 _.CooperativeMatrixShapesMatch(inst, result_type, input_type, true);
259 if (ret != SPV_SUCCESS) return ret;
260 } else {
261 if (_.GetDimension(result_type) != _.GetDimension(input_type))
262 return _.diag(SPV_ERROR_INVALID_DATA, inst)
263 << "Expected input to have the same dimension as Result Type: "
264 << spvOpcodeString(opcode);
265 }
266
267 if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
268 return _.diag(SPV_ERROR_INVALID_DATA, inst)
269 << "Expected input to have different bit width from Result "
270 "Type: "
271 << spvOpcodeString(opcode);
272 break;
273 }
274
275 case spv::Op::OpQuantizeToF16: {
276 if ((!_.IsFloatScalarType(result_type) &&
277 !_.IsFloatVectorType(result_type)) ||
278 _.GetBitWidth(result_type) != 32)
279 return _.diag(SPV_ERROR_INVALID_DATA, inst)
280 << "Expected 32-bit float scalar or vector type as Result Type: "
281 << spvOpcodeString(opcode);
282
283 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
284 if (input_type != result_type)
285 return _.diag(SPV_ERROR_INVALID_DATA, inst)
286 << "Expected input type to be equal to Result Type: "
287 << spvOpcodeString(opcode);
288 break;
289 }
290
291 case spv::Op::OpConvertPtrToU: {
292 if (!_.IsUnsignedIntScalarType(result_type))
293 return _.diag(SPV_ERROR_INVALID_DATA, inst)
294 << "Expected unsigned int scalar type as Result Type: "
295 << spvOpcodeString(opcode);
296
297 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
298 if (!_.IsPointerType(input_type))
299 return _.diag(SPV_ERROR_INVALID_DATA, inst)
300 << "Expected input to be a pointer: " << spvOpcodeString(opcode);
301
302 if (_.addressing_model() == spv::AddressingModel::Logical)
303 return _.diag(SPV_ERROR_INVALID_DATA, inst)
304 << "Logical addressing not supported: "
305 << spvOpcodeString(opcode);
306
307 if (_.addressing_model() ==
308 spv::AddressingModel::PhysicalStorageBuffer64) {
309 spv::StorageClass input_storage_class;
310 uint32_t input_data_type = 0;
311 _.GetPointerTypeInfo(input_type, &input_data_type,
312 &input_storage_class);
313 if (input_storage_class != spv::StorageClass::PhysicalStorageBuffer)
314 return _.diag(SPV_ERROR_INVALID_DATA, inst)
315 << "Pointer storage class must be PhysicalStorageBuffer: "
316 << spvOpcodeString(opcode);
317
318 if (spvIsVulkanEnv(_.context()->target_env)) {
319 if (_.GetBitWidth(result_type) != 64) {
320 return _.diag(SPV_ERROR_INVALID_DATA, inst)
321 << _.VkErrorID(4710)
322 << "PhysicalStorageBuffer64 addressing mode requires the "
323 "result integer type to have a 64-bit width for Vulkan "
324 "environment.";
325 }
326 }
327 }
328 break;
329 }
330
331 case spv::Op::OpSatConvertSToU:
332 case spv::Op::OpSatConvertUToS: {
333 if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type))
334 return _.diag(SPV_ERROR_INVALID_DATA, inst)
335 << "Expected int scalar or vector type as Result Type: "
336 << spvOpcodeString(opcode);
337
338 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
339 if (!input_type ||
340 (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type)))
341 return _.diag(SPV_ERROR_INVALID_DATA, inst)
342 << "Expected int scalar or vector as input: "
343 << spvOpcodeString(opcode);
344
345 if (_.GetDimension(result_type) != _.GetDimension(input_type))
346 return _.diag(SPV_ERROR_INVALID_DATA, inst)
347 << "Expected input to have the same dimension as Result Type: "
348 << spvOpcodeString(opcode);
349 break;
350 }
351
352 case spv::Op::OpConvertUToPtr: {
353 if (!_.IsPointerType(result_type))
354 return _.diag(SPV_ERROR_INVALID_DATA, inst)
355 << "Expected Result Type to be a pointer: "
356 << spvOpcodeString(opcode);
357
358 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
359 if (!input_type || !_.IsIntScalarType(input_type))
360 return _.diag(SPV_ERROR_INVALID_DATA, inst)
361 << "Expected int scalar as input: " << spvOpcodeString(opcode);
362
363 if (_.addressing_model() == spv::AddressingModel::Logical)
364 return _.diag(SPV_ERROR_INVALID_DATA, inst)
365 << "Logical addressing not supported: "
366 << spvOpcodeString(opcode);
367
368 if (_.addressing_model() ==
369 spv::AddressingModel::PhysicalStorageBuffer64) {
370 spv::StorageClass result_storage_class;
371 uint32_t result_data_type = 0;
372 _.GetPointerTypeInfo(result_type, &result_data_type,
373 &result_storage_class);
374 if (result_storage_class != spv::StorageClass::PhysicalStorageBuffer)
375 return _.diag(SPV_ERROR_INVALID_DATA, inst)
376 << "Pointer storage class must be PhysicalStorageBuffer: "
377 << spvOpcodeString(opcode);
378
379 if (spvIsVulkanEnv(_.context()->target_env)) {
380 if (_.GetBitWidth(input_type) != 64) {
381 return _.diag(SPV_ERROR_INVALID_DATA, inst)
382 << _.VkErrorID(4710)
383 << "PhysicalStorageBuffer64 addressing mode requires the "
384 "input integer to have a 64-bit width for Vulkan "
385 "environment.";
386 }
387 }
388 }
389 break;
390 }
391
392 case spv::Op::OpPtrCastToGeneric: {
393 spv::StorageClass result_storage_class;
394 uint32_t result_data_type = 0;
395 if (!_.GetPointerTypeInfo(result_type, &result_data_type,
396 &result_storage_class))
397 return _.diag(SPV_ERROR_INVALID_DATA, inst)
398 << "Expected Result Type to be a pointer: "
399 << spvOpcodeString(opcode);
400
401 if (result_storage_class != spv::StorageClass::Generic)
402 return _.diag(SPV_ERROR_INVALID_DATA, inst)
403 << "Expected Result Type to have storage class Generic: "
404 << spvOpcodeString(opcode);
405
406 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
407 spv::StorageClass input_storage_class;
408 uint32_t input_data_type = 0;
409 if (!_.GetPointerTypeInfo(input_type, &input_data_type,
410 &input_storage_class))
411 return _.diag(SPV_ERROR_INVALID_DATA, inst)
412 << "Expected input to be a pointer: " << spvOpcodeString(opcode);
413
414 if (input_storage_class != spv::StorageClass::Workgroup &&
415 input_storage_class != spv::StorageClass::CrossWorkgroup &&
416 input_storage_class != spv::StorageClass::Function)
417 return _.diag(SPV_ERROR_INVALID_DATA, inst)
418 << "Expected input to have storage class Workgroup, "
419 << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
420
421 if (result_data_type != input_data_type)
422 return _.diag(SPV_ERROR_INVALID_DATA, inst)
423 << "Expected input and Result Type to point to the same type: "
424 << spvOpcodeString(opcode);
425 break;
426 }
427
428 case spv::Op::OpGenericCastToPtr: {
429 spv::StorageClass result_storage_class;
430 uint32_t result_data_type = 0;
431 if (!_.GetPointerTypeInfo(result_type, &result_data_type,
432 &result_storage_class))
433 return _.diag(SPV_ERROR_INVALID_DATA, inst)
434 << "Expected Result Type to be a pointer: "
435 << spvOpcodeString(opcode);
436
437 if (result_storage_class != spv::StorageClass::Workgroup &&
438 result_storage_class != spv::StorageClass::CrossWorkgroup &&
439 result_storage_class != spv::StorageClass::Function)
440 return _.diag(SPV_ERROR_INVALID_DATA, inst)
441 << "Expected Result Type to have storage class Workgroup, "
442 << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
443
444 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
445 spv::StorageClass input_storage_class;
446 uint32_t input_data_type = 0;
447 if (!_.GetPointerTypeInfo(input_type, &input_data_type,
448 &input_storage_class))
449 return _.diag(SPV_ERROR_INVALID_DATA, inst)
450 << "Expected input to be a pointer: " << spvOpcodeString(opcode);
451
452 if (input_storage_class != spv::StorageClass::Generic)
453 return _.diag(SPV_ERROR_INVALID_DATA, inst)
454 << "Expected input to have storage class Generic: "
455 << spvOpcodeString(opcode);
456
457 if (result_data_type != input_data_type)
458 return _.diag(SPV_ERROR_INVALID_DATA, inst)
459 << "Expected input and Result Type to point to the same type: "
460 << spvOpcodeString(opcode);
461 break;
462 }
463
464 case spv::Op::OpGenericCastToPtrExplicit: {
465 spv::StorageClass result_storage_class;
466 uint32_t result_data_type = 0;
467 if (!_.GetPointerTypeInfo(result_type, &result_data_type,
468 &result_storage_class))
469 return _.diag(SPV_ERROR_INVALID_DATA, inst)
470 << "Expected Result Type to be a pointer: "
471 << spvOpcodeString(opcode);
472
473 const auto target_storage_class =
474 inst->GetOperandAs<spv::StorageClass>(3);
475 if (result_storage_class != target_storage_class)
476 return _.diag(SPV_ERROR_INVALID_DATA, inst)
477 << "Expected Result Type to be of target storage class: "
478 << spvOpcodeString(opcode);
479
480 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
481 spv::StorageClass input_storage_class;
482 uint32_t input_data_type = 0;
483 if (!_.GetPointerTypeInfo(input_type, &input_data_type,
484 &input_storage_class))
485 return _.diag(SPV_ERROR_INVALID_DATA, inst)
486 << "Expected input to be a pointer: " << spvOpcodeString(opcode);
487
488 if (input_storage_class != spv::StorageClass::Generic)
489 return _.diag(SPV_ERROR_INVALID_DATA, inst)
490 << "Expected input to have storage class Generic: "
491 << spvOpcodeString(opcode);
492
493 if (result_data_type != input_data_type)
494 return _.diag(SPV_ERROR_INVALID_DATA, inst)
495 << "Expected input and Result Type to point to the same type: "
496 << spvOpcodeString(opcode);
497
498 if (target_storage_class != spv::StorageClass::Workgroup &&
499 target_storage_class != spv::StorageClass::CrossWorkgroup &&
500 target_storage_class != spv::StorageClass::Function)
501 return _.diag(SPV_ERROR_INVALID_DATA, inst)
502 << "Expected target storage class to be Workgroup, "
503 << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
504 break;
505 }
506
507 case spv::Op::OpBitcast: {
508 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
509 if (!input_type)
510 return _.diag(SPV_ERROR_INVALID_DATA, inst)
511 << "Expected input to have a type: " << spvOpcodeString(opcode);
512
513 const bool result_is_pointer = _.IsPointerType(result_type);
514 const bool result_is_int_scalar = _.IsIntScalarType(result_type);
515 const bool input_is_pointer = _.IsPointerType(input_type);
516 const bool input_is_int_scalar = _.IsIntScalarType(input_type);
517
518 const bool result_is_coopmat = _.IsCooperativeMatrixType(result_type);
519 const bool input_is_coopmat = _.IsCooperativeMatrixType(input_type);
520 const bool result_is_coopvec = _.IsCooperativeVectorNVType(result_type);
521 const bool input_is_coopvec = _.IsCooperativeVectorNVType(input_type);
522
523 if (!result_is_pointer && !result_is_int_scalar && !result_is_coopmat &&
524 !result_is_coopvec && !_.IsIntVectorType(result_type) &&
525 !_.IsFloatScalarType(result_type) &&
526 !_.IsFloatVectorType(result_type))
527 return _.diag(SPV_ERROR_INVALID_DATA, inst)
528 << "Expected Result Type to be a pointer or int or float vector "
529 << "or scalar type: " << spvOpcodeString(opcode);
530
531 if (!input_is_pointer && !input_is_int_scalar && !input_is_coopmat &&
532 !input_is_coopvec && !_.IsIntVectorType(input_type) &&
533 !_.IsFloatScalarType(input_type) && !_.IsFloatVectorType(input_type))
534 return _.diag(SPV_ERROR_INVALID_DATA, inst)
535 << "Expected input to be a pointer or int or float vector "
536 << "or scalar: " << spvOpcodeString(opcode);
537
538 if (result_is_coopvec != input_is_coopvec)
539 return _.diag(SPV_ERROR_INVALID_DATA, inst)
540 << "Cooperative vector can only be cast to another cooperative "
541 << "vector: " << spvOpcodeString(opcode);
542
543 if (result_is_coopmat != input_is_coopmat)
544 return _.diag(SPV_ERROR_INVALID_DATA, inst)
545 << "Cooperative matrix can only be cast to another cooperative "
546 << "matrix: " << spvOpcodeString(opcode);
547
548 if (result_is_coopvec) {
549 spv_result_t ret =
550 _.CooperativeVectorDimensionsMatch(inst, result_type, input_type);
551 if (ret != SPV_SUCCESS) return ret;
552 }
553
554 if (result_is_coopmat) {
555 spv_result_t ret = _.CooperativeMatrixShapesMatch(inst, result_type,
556 input_type, false);
557 if (ret != SPV_SUCCESS) return ret;
558 }
559
560 if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) ||
561 _.HasExtension(kSPV_KHR_physical_storage_buffer)) {
562 const bool result_is_int_vector = _.IsIntVectorType(result_type);
563 const bool result_has_int32 =
564 _.ContainsSizedIntOrFloatType(result_type, spv::Op::OpTypeInt, 32);
565 const bool input_is_int_vector = _.IsIntVectorType(input_type);
566 const bool input_has_int32 =
567 _.ContainsSizedIntOrFloatType(input_type, spv::Op::OpTypeInt, 32);
568 if (result_is_pointer && !input_is_pointer && !input_is_int_scalar &&
569 !(input_is_int_vector && input_has_int32))
570 return _.diag(SPV_ERROR_INVALID_DATA, inst)
571 << "Expected input to be a pointer, int scalar or 32-bit int "
572 "vector if Result Type is pointer: "
573 << spvOpcodeString(opcode);
574
575 if (input_is_pointer && !result_is_pointer && !result_is_int_scalar &&
576 !(result_is_int_vector && result_has_int32))
577 return _.diag(SPV_ERROR_INVALID_DATA, inst)
578 << "Pointer can only be converted to another pointer, int "
579 "scalar or 32-bit int vector: "
580 << spvOpcodeString(opcode);
581 } else {
582 if (result_is_pointer && !input_is_pointer && !input_is_int_scalar)
583 return _.diag(SPV_ERROR_INVALID_DATA, inst)
584 << "Expected input to be a pointer or int scalar if Result "
585 "Type is pointer: "
586 << spvOpcodeString(opcode);
587
588 if (input_is_pointer && !result_is_pointer && !result_is_int_scalar)
589 return _.diag(SPV_ERROR_INVALID_DATA, inst)
590 << "Pointer can only be converted to another pointer or int "
591 "scalar: "
592 << spvOpcodeString(opcode);
593 }
594
595 if (!result_is_pointer && !input_is_pointer) {
596 const uint32_t result_size =
597 _.GetBitWidth(result_type) * _.GetDimension(result_type);
598 const uint32_t input_size =
599 _.GetBitWidth(input_type) * _.GetDimension(input_type);
600 if (result_size != input_size)
601 return _.diag(SPV_ERROR_INVALID_DATA, inst)
602 << "Expected input to have the same total bit width as "
603 << "Result Type: " << spvOpcodeString(opcode);
604 }
605 break;
606 }
607
608 case spv::Op::OpConvertUToAccelerationStructureKHR: {
609 if (!_.IsAccelerationStructureType(result_type)) {
610 return _.diag(SPV_ERROR_INVALID_DATA, inst)
611 << "Expected Result Type to be a Acceleration Structure: "
612 << spvOpcodeString(opcode);
613 }
614
615 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
616 if (!input_type || !_.IsUnsigned64BitHandle(input_type)) {
617 return _.diag(SPV_ERROR_INVALID_DATA, inst)
618 << "Expected 64-bit uint scalar or 2-component 32-bit uint "
619 "vector as input: "
620 << spvOpcodeString(opcode);
621 }
622
623 break;
624 }
625
626 case spv::Op::OpCooperativeMatrixConvertNV:
627 case spv::Op::OpCooperativeMatrixTransposeNV: {
628 if (!_.IsCooperativeMatrixType(result_type)) {
629 return _.diag(SPV_ERROR_INVALID_DATA, inst)
630 << "Expected cooperative matrix Result Type: "
631 << spvOpcodeString(opcode);
632 }
633 const uint32_t input_type = _.GetOperandTypeId(inst, 2);
634 if (!_.IsCooperativeMatrixType(input_type)) {
635 return _.diag(SPV_ERROR_INVALID_DATA, inst)
636 << "Expected cooperative matrix type for Matrix input: "
637 << spvOpcodeString(opcode);
638 }
639
640 bool swap_row_col = (opcode == spv::Op::OpCooperativeMatrixTransposeNV);
641 if (auto error = _.CooperativeMatrixShapesMatch(
642 inst, result_type, input_type, true, swap_row_col))
643 return error;
644
645 if (opcode == spv::Op::OpCooperativeMatrixConvertNV) {
646 if (_.FindDef(result_type)->GetOperandAs<uint32_t>(1) !=
647 _.FindDef(input_type)->GetOperandAs<uint32_t>(1)) {
648 return _.diag(SPV_ERROR_INVALID_DATA, inst)
649 << "Result Type and Matrix component types mismatch: "
650 << spvOpcodeString(opcode);
651 }
652 }
653
654 if (opcode == spv::Op::OpCooperativeMatrixTransposeNV) {
655 if (!_.IsCooperativeMatrixBType(result_type)) {
656 return _.diag(SPV_ERROR_INVALID_DATA, inst)
657 << "Result Type must have UseB: " << spvOpcodeString(opcode);
658 }
659 }
660 break;
661 }
662
663 default:
664 break;
665 }
666
667 if (_.HasCapability(spv::Capability::Shader)) {
668 switch (inst->opcode()) {
669 case spv::Op::OpConvertFToU:
670 case spv::Op::OpConvertFToS:
671 case spv::Op::OpConvertSToF:
672 case spv::Op::OpConvertUToF:
673 case spv::Op::OpBitcast:
674 if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) ||
675 _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) {
676 return _.diag(SPV_ERROR_INVALID_DATA, inst)
677 << "8- or 16-bit types can only be used with width-only "
678 "conversions";
679 }
680 break;
681 default:
682 break;
683 }
684 }
685
686 return SPV_SUCCESS;
687 }
688
689 } // namespace val
690 } // namespace spvtools
691