Coverage Report

Created: 2026-09-28 07:06

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