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