Coverage Report

Created: 2025-11-16 06:14

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