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/diagnostic.h"
18 #include "source/opcode.h"
19 #include "source/spirv_constant.h"
20 #include "source/spirv_target_env.h"
21 #include "source/val/instruction.h"
22 #include "source/val/validate.h"
23 #include "source/val/validation_state.h"
24 
25 namespace spvtools {
26 namespace val {
27 
28 // Validates correctness of conversion instructions.
ConversionPass(ValidationState_t & _,const Instruction * inst)29 spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) {
30   const SpvOp opcode = inst->opcode();
31   const uint32_t result_type = inst->type_id();
32 
33   switch (opcode) {
34     case SpvOpConvertFToU: {
35       if (!_.IsUnsignedIntScalarType(result_type) &&
36           !_.IsUnsignedIntVectorType(result_type) &&
37           !_.IsUnsignedIntCooperativeMatrixType(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         return _.diag(SPV_ERROR_INVALID_DATA, inst)
47                << "Expected input to be float scalar or vector: "
48                << spvOpcodeString(opcode);
49 
50       if (_.IsCooperativeMatrixType(result_type) ||
51           _.IsCooperativeMatrixType(input_type)) {
52         spv_result_t ret =
53             _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
54         if (ret != SPV_SUCCESS) return ret;
55       } else {
56         if (_.GetDimension(result_type) != _.GetDimension(input_type))
57           return _.diag(SPV_ERROR_INVALID_DATA, inst)
58                  << "Expected input to have the same dimension as Result Type: "
59                  << spvOpcodeString(opcode);
60       }
61 
62       break;
63     }
64 
65     case SpvOpConvertFToS: {
66       if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
67           !_.IsIntCooperativeMatrixType(result_type))
68         return _.diag(SPV_ERROR_INVALID_DATA, inst)
69                << "Expected int scalar or vector type as Result Type: "
70                << spvOpcodeString(opcode);
71 
72       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
73       if (!input_type || (!_.IsFloatScalarType(input_type) &&
74                           !_.IsFloatVectorType(input_type) &&
75                           !_.IsFloatCooperativeMatrixType(input_type)))
76         return _.diag(SPV_ERROR_INVALID_DATA, inst)
77                << "Expected input to be float scalar or vector: "
78                << spvOpcodeString(opcode);
79 
80       if (_.IsCooperativeMatrixType(result_type) ||
81           _.IsCooperativeMatrixType(input_type)) {
82         spv_result_t ret =
83             _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
84         if (ret != SPV_SUCCESS) return ret;
85       } else {
86         if (_.GetDimension(result_type) != _.GetDimension(input_type))
87           return _.diag(SPV_ERROR_INVALID_DATA, inst)
88                  << "Expected input to have the same dimension as Result Type: "
89                  << spvOpcodeString(opcode);
90       }
91 
92       break;
93     }
94 
95     case SpvOpConvertSToF:
96     case SpvOpConvertUToF: {
97       if (!_.IsFloatScalarType(result_type) &&
98           !_.IsFloatVectorType(result_type) &&
99           !_.IsFloatCooperativeMatrixType(result_type))
100         return _.diag(SPV_ERROR_INVALID_DATA, inst)
101                << "Expected float scalar or vector type as Result Type: "
102                << spvOpcodeString(opcode);
103 
104       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
105       if (!input_type ||
106           (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
107            !_.IsIntCooperativeMatrixType(input_type)))
108         return _.diag(SPV_ERROR_INVALID_DATA, inst)
109                << "Expected input to be int scalar or vector: "
110                << spvOpcodeString(opcode);
111 
112       if (_.IsCooperativeMatrixType(result_type) ||
113           _.IsCooperativeMatrixType(input_type)) {
114         spv_result_t ret =
115             _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
116         if (ret != SPV_SUCCESS) return ret;
117       } else {
118         if (_.GetDimension(result_type) != _.GetDimension(input_type))
119           return _.diag(SPV_ERROR_INVALID_DATA, inst)
120                  << "Expected input to have the same dimension as Result Type: "
121                  << spvOpcodeString(opcode);
122       }
123 
124       break;
125     }
126 
127     case SpvOpUConvert: {
128       if (!_.IsUnsignedIntScalarType(result_type) &&
129           !_.IsUnsignedIntVectorType(result_type) &&
130           !_.IsUnsignedIntCooperativeMatrixType(result_type))
131         return _.diag(SPV_ERROR_INVALID_DATA, inst)
132                << "Expected unsigned int scalar or vector type as Result Type: "
133                << spvOpcodeString(opcode);
134 
135       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
136       if (!input_type ||
137           (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
138            !_.IsIntCooperativeMatrixType(input_type)))
139         return _.diag(SPV_ERROR_INVALID_DATA, inst)
140                << "Expected input to be int scalar or vector: "
141                << spvOpcodeString(opcode);
142 
143       if (_.IsCooperativeMatrixType(result_type) ||
144           _.IsCooperativeMatrixType(input_type)) {
145         spv_result_t ret =
146             _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
147         if (ret != SPV_SUCCESS) return ret;
148       } else {
149         if (_.GetDimension(result_type) != _.GetDimension(input_type))
150           return _.diag(SPV_ERROR_INVALID_DATA, inst)
151                  << "Expected input to have the same dimension as Result Type: "
152                  << spvOpcodeString(opcode);
153       }
154 
155       if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
156         return _.diag(SPV_ERROR_INVALID_DATA, inst)
157                << "Expected input to have different bit width from Result "
158                   "Type: "
159                << spvOpcodeString(opcode);
160       break;
161     }
162 
163     case SpvOpSConvert: {
164       if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) &&
165           !_.IsIntCooperativeMatrixType(result_type))
166         return _.diag(SPV_ERROR_INVALID_DATA, inst)
167                << "Expected int scalar or vector type as Result Type: "
168                << spvOpcodeString(opcode);
169 
170       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
171       if (!input_type ||
172           (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) &&
173            !_.IsIntCooperativeMatrixType(input_type)))
174         return _.diag(SPV_ERROR_INVALID_DATA, inst)
175                << "Expected input to be int scalar or vector: "
176                << spvOpcodeString(opcode);
177 
178       if (_.IsCooperativeMatrixType(result_type) ||
179           _.IsCooperativeMatrixType(input_type)) {
180         spv_result_t ret =
181             _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
182         if (ret != SPV_SUCCESS) return ret;
183       } else {
184         if (_.GetDimension(result_type) != _.GetDimension(input_type))
185           return _.diag(SPV_ERROR_INVALID_DATA, inst)
186                  << "Expected input to have the same dimension as Result Type: "
187                  << spvOpcodeString(opcode);
188       }
189 
190       if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
191         return _.diag(SPV_ERROR_INVALID_DATA, inst)
192                << "Expected input to have different bit width from Result "
193                   "Type: "
194                << spvOpcodeString(opcode);
195       break;
196     }
197 
198     case SpvOpFConvert: {
199       if (!_.IsFloatScalarType(result_type) &&
200           !_.IsFloatVectorType(result_type) &&
201           !_.IsFloatCooperativeMatrixType(result_type))
202         return _.diag(SPV_ERROR_INVALID_DATA, inst)
203                << "Expected float scalar or vector type as Result Type: "
204                << spvOpcodeString(opcode);
205 
206       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
207       if (!input_type || (!_.IsFloatScalarType(input_type) &&
208                           !_.IsFloatVectorType(input_type) &&
209                           !_.IsFloatCooperativeMatrixType(input_type)))
210         return _.diag(SPV_ERROR_INVALID_DATA, inst)
211                << "Expected input to be float scalar or vector: "
212                << spvOpcodeString(opcode);
213 
214       if (_.IsCooperativeMatrixType(result_type) ||
215           _.IsCooperativeMatrixType(input_type)) {
216         spv_result_t ret =
217             _.CooperativeMatrixShapesMatch(inst, result_type, input_type);
218         if (ret != SPV_SUCCESS) return ret;
219       } else {
220         if (_.GetDimension(result_type) != _.GetDimension(input_type))
221           return _.diag(SPV_ERROR_INVALID_DATA, inst)
222                  << "Expected input to have the same dimension as Result Type: "
223                  << spvOpcodeString(opcode);
224       }
225 
226       if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type))
227         return _.diag(SPV_ERROR_INVALID_DATA, inst)
228                << "Expected input to have different bit width from Result "
229                   "Type: "
230                << spvOpcodeString(opcode);
231       break;
232     }
233 
234     case SpvOpQuantizeToF16: {
235       if ((!_.IsFloatScalarType(result_type) &&
236            !_.IsFloatVectorType(result_type)) ||
237           _.GetBitWidth(result_type) != 32)
238         return _.diag(SPV_ERROR_INVALID_DATA, inst)
239                << "Expected 32-bit float scalar or vector type as Result Type: "
240                << spvOpcodeString(opcode);
241 
242       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
243       if (input_type != result_type)
244         return _.diag(SPV_ERROR_INVALID_DATA, inst)
245                << "Expected input type to be equal to Result Type: "
246                << spvOpcodeString(opcode);
247       break;
248     }
249 
250     case SpvOpConvertPtrToU: {
251       if (!_.IsUnsignedIntScalarType(result_type))
252         return _.diag(SPV_ERROR_INVALID_DATA, inst)
253                << "Expected unsigned int scalar type as Result Type: "
254                << spvOpcodeString(opcode);
255 
256       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
257       if (!_.IsPointerType(input_type))
258         return _.diag(SPV_ERROR_INVALID_DATA, inst)
259                << "Expected input to be a pointer: " << spvOpcodeString(opcode);
260 
261       if (_.addressing_model() == SpvAddressingModelLogical)
262         return _.diag(SPV_ERROR_INVALID_DATA, inst)
263                << "Logical addressing not supported: "
264                << spvOpcodeString(opcode);
265 
266       if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) {
267         uint32_t input_storage_class = 0;
268         uint32_t input_data_type = 0;
269         _.GetPointerTypeInfo(input_type, &input_data_type,
270                              &input_storage_class);
271         if (input_storage_class != SpvStorageClassPhysicalStorageBuffer)
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 SpvOpSatConvertSToU:
290     case SpvOpSatConvertUToS: {
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 SpvOpConvertUToPtr: {
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() == SpvAddressingModelLogical)
322         return _.diag(SPV_ERROR_INVALID_DATA, inst)
323                << "Logical addressing not supported: "
324                << spvOpcodeString(opcode);
325 
326       if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) {
327         uint32_t result_storage_class = 0;
328         uint32_t result_data_type = 0;
329         _.GetPointerTypeInfo(result_type, &result_data_type,
330                              &result_storage_class);
331         if (result_storage_class != SpvStorageClassPhysicalStorageBuffer)
332           return _.diag(SPV_ERROR_INVALID_DATA, inst)
333                  << "Pointer storage class must be PhysicalStorageBuffer: "
334                  << spvOpcodeString(opcode);
335 
336         if (spvIsVulkanEnv(_.context()->target_env)) {
337           if (_.GetBitWidth(input_type) != 64) {
338             return _.diag(SPV_ERROR_INVALID_DATA, inst)
339                    << _.VkErrorID(4710)
340                    << "PhysicalStorageBuffer64 addressing mode requires the "
341                       "input integer to have a 64-bit width for Vulkan "
342                       "environment.";
343           }
344         }
345       }
346       break;
347     }
348 
349     case SpvOpPtrCastToGeneric: {
350       uint32_t result_storage_class = 0;
351       uint32_t result_data_type = 0;
352       if (!_.GetPointerTypeInfo(result_type, &result_data_type,
353                                 &result_storage_class))
354         return _.diag(SPV_ERROR_INVALID_DATA, inst)
355                << "Expected Result Type to be a pointer: "
356                << spvOpcodeString(opcode);
357 
358       if (result_storage_class != SpvStorageClassGeneric)
359         return _.diag(SPV_ERROR_INVALID_DATA, inst)
360                << "Expected Result Type to have storage class Generic: "
361                << spvOpcodeString(opcode);
362 
363       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
364       uint32_t input_storage_class = 0;
365       uint32_t input_data_type = 0;
366       if (!_.GetPointerTypeInfo(input_type, &input_data_type,
367                                 &input_storage_class))
368         return _.diag(SPV_ERROR_INVALID_DATA, inst)
369                << "Expected input to be a pointer: " << spvOpcodeString(opcode);
370 
371       if (input_storage_class != SpvStorageClassWorkgroup &&
372           input_storage_class != SpvStorageClassCrossWorkgroup &&
373           input_storage_class != SpvStorageClassFunction)
374         return _.diag(SPV_ERROR_INVALID_DATA, inst)
375                << "Expected input to have storage class Workgroup, "
376                << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
377 
378       if (result_data_type != input_data_type)
379         return _.diag(SPV_ERROR_INVALID_DATA, inst)
380                << "Expected input and Result Type to point to the same type: "
381                << spvOpcodeString(opcode);
382       break;
383     }
384 
385     case SpvOpGenericCastToPtr: {
386       uint32_t result_storage_class = 0;
387       uint32_t result_data_type = 0;
388       if (!_.GetPointerTypeInfo(result_type, &result_data_type,
389                                 &result_storage_class))
390         return _.diag(SPV_ERROR_INVALID_DATA, inst)
391                << "Expected Result Type to be a pointer: "
392                << spvOpcodeString(opcode);
393 
394       if (result_storage_class != SpvStorageClassWorkgroup &&
395           result_storage_class != SpvStorageClassCrossWorkgroup &&
396           result_storage_class != SpvStorageClassFunction)
397         return _.diag(SPV_ERROR_INVALID_DATA, inst)
398                << "Expected Result Type to have storage class Workgroup, "
399                << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
400 
401       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
402       uint32_t input_storage_class = 0;
403       uint32_t input_data_type = 0;
404       if (!_.GetPointerTypeInfo(input_type, &input_data_type,
405                                 &input_storage_class))
406         return _.diag(SPV_ERROR_INVALID_DATA, inst)
407                << "Expected input to be a pointer: " << spvOpcodeString(opcode);
408 
409       if (input_storage_class != SpvStorageClassGeneric)
410         return _.diag(SPV_ERROR_INVALID_DATA, inst)
411                << "Expected input to have storage class Generic: "
412                << spvOpcodeString(opcode);
413 
414       if (result_data_type != input_data_type)
415         return _.diag(SPV_ERROR_INVALID_DATA, inst)
416                << "Expected input and Result Type to point to the same type: "
417                << spvOpcodeString(opcode);
418       break;
419     }
420 
421     case SpvOpGenericCastToPtrExplicit: {
422       uint32_t result_storage_class = 0;
423       uint32_t result_data_type = 0;
424       if (!_.GetPointerTypeInfo(result_type, &result_data_type,
425                                 &result_storage_class))
426         return _.diag(SPV_ERROR_INVALID_DATA, inst)
427                << "Expected Result Type to be a pointer: "
428                << spvOpcodeString(opcode);
429 
430       const uint32_t target_storage_class = inst->word(4);
431       if (result_storage_class != target_storage_class)
432         return _.diag(SPV_ERROR_INVALID_DATA, inst)
433                << "Expected Result Type to be of target storage class: "
434                << spvOpcodeString(opcode);
435 
436       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
437       uint32_t input_storage_class = 0;
438       uint32_t input_data_type = 0;
439       if (!_.GetPointerTypeInfo(input_type, &input_data_type,
440                                 &input_storage_class))
441         return _.diag(SPV_ERROR_INVALID_DATA, inst)
442                << "Expected input to be a pointer: " << spvOpcodeString(opcode);
443 
444       if (input_storage_class != SpvStorageClassGeneric)
445         return _.diag(SPV_ERROR_INVALID_DATA, inst)
446                << "Expected input to have storage class Generic: "
447                << spvOpcodeString(opcode);
448 
449       if (result_data_type != input_data_type)
450         return _.diag(SPV_ERROR_INVALID_DATA, inst)
451                << "Expected input and Result Type to point to the same type: "
452                << spvOpcodeString(opcode);
453 
454       if (target_storage_class != SpvStorageClassWorkgroup &&
455           target_storage_class != SpvStorageClassCrossWorkgroup &&
456           target_storage_class != SpvStorageClassFunction)
457         return _.diag(SPV_ERROR_INVALID_DATA, inst)
458                << "Expected target storage class to be Workgroup, "
459                << "CrossWorkgroup or Function: " << spvOpcodeString(opcode);
460       break;
461     }
462 
463     case SpvOpBitcast: {
464       const uint32_t input_type = _.GetOperandTypeId(inst, 2);
465       if (!input_type)
466         return _.diag(SPV_ERROR_INVALID_DATA, inst)
467                << "Expected input to have a type: " << spvOpcodeString(opcode);
468 
469       const bool result_is_pointer = _.IsPointerType(result_type);
470       const bool result_is_int_scalar = _.IsIntScalarType(result_type);
471       const bool input_is_pointer = _.IsPointerType(input_type);
472       const bool input_is_int_scalar = _.IsIntScalarType(input_type);
473 
474       if (!result_is_pointer && !result_is_int_scalar &&
475           !_.IsIntVectorType(result_type) &&
476           !_.IsFloatScalarType(result_type) &&
477           !_.IsFloatVectorType(result_type))
478         return _.diag(SPV_ERROR_INVALID_DATA, inst)
479                << "Expected Result Type to be a pointer or int or float vector "
480                << "or scalar type: " << spvOpcodeString(opcode);
481 
482       if (!input_is_pointer && !input_is_int_scalar &&
483           !_.IsIntVectorType(input_type) && !_.IsFloatScalarType(input_type) &&
484           !_.IsFloatVectorType(input_type))
485         return _.diag(SPV_ERROR_INVALID_DATA, inst)
486                << "Expected input to be a pointer or int or float vector "
487                << "or scalar: " << spvOpcodeString(opcode);
488 
489       if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) ||
490           _.HasExtension(kSPV_KHR_physical_storage_buffer)) {
491         const bool result_is_int_vector = _.IsIntVectorType(result_type);
492         const bool result_has_int32 =
493             _.ContainsSizedIntOrFloatType(result_type, SpvOpTypeInt, 32);
494         const bool input_is_int_vector = _.IsIntVectorType(input_type);
495         const bool input_has_int32 =
496             _.ContainsSizedIntOrFloatType(input_type, SpvOpTypeInt, 32);
497         if (result_is_pointer && !input_is_pointer && !input_is_int_scalar &&
498             !(input_is_int_vector && input_has_int32))
499           return _.diag(SPV_ERROR_INVALID_DATA, inst)
500                  << "Expected input to be a pointer, int scalar or 32-bit int "
501                     "vector if Result Type is pointer: "
502                  << spvOpcodeString(opcode);
503 
504         if (input_is_pointer && !result_is_pointer && !result_is_int_scalar &&
505             !(result_is_int_vector && result_has_int32))
506           return _.diag(SPV_ERROR_INVALID_DATA, inst)
507                  << "Pointer can only be converted to another pointer, int "
508                     "scalar or 32-bit int vector: "
509                  << spvOpcodeString(opcode);
510       } else {
511         if (result_is_pointer && !input_is_pointer && !input_is_int_scalar)
512           return _.diag(SPV_ERROR_INVALID_DATA, inst)
513                  << "Expected input to be a pointer or int scalar if Result "
514                     "Type is pointer: "
515                  << spvOpcodeString(opcode);
516 
517         if (input_is_pointer && !result_is_pointer && !result_is_int_scalar)
518           return _.diag(SPV_ERROR_INVALID_DATA, inst)
519                  << "Pointer can only be converted to another pointer or int "
520                     "scalar: "
521                  << spvOpcodeString(opcode);
522       }
523 
524       if (!result_is_pointer && !input_is_pointer) {
525         const uint32_t result_size =
526             _.GetBitWidth(result_type) * _.GetDimension(result_type);
527         const uint32_t input_size =
528             _.GetBitWidth(input_type) * _.GetDimension(input_type);
529         if (result_size != input_size)
530           return _.diag(SPV_ERROR_INVALID_DATA, inst)
531                  << "Expected input to have the same total bit width as "
532                  << "Result Type: " << spvOpcodeString(opcode);
533       }
534       break;
535     }
536 
537     default:
538       break;
539   }
540 
541   if (_.HasCapability(SpvCapabilityShader)) {
542     switch (inst->opcode()) {
543       case SpvOpConvertFToU:
544       case SpvOpConvertFToS:
545       case SpvOpConvertSToF:
546       case SpvOpConvertUToF:
547       case SpvOpBitcast:
548         if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) ||
549             _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) {
550           return _.diag(SPV_ERROR_INVALID_DATA, inst)
551                  << "8- or 16-bit types can only be used with width-only "
552                     "conversions";
553         }
554         break;
555       default:
556         break;
557     }
558   }
559 
560   return SPV_SUCCESS;
561 }
562 
563 }  // namespace val
564 }  // namespace spvtools
565