Coverage Report

Created: 2026-11-12 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/val/validate_constants.cpp
Line
Count
Source
1
// Copyright (c) 2018 Google LLC.
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
#include "source/opcode.h"
16
#include "source/val/instruction.h"
17
#include "source/val/validate.h"
18
#include "source/val/validation_state.h"
19
20
namespace spvtools {
21
namespace val {
22
namespace {
23
24
spv_result_t ValidateConstantBool(ValidationState_t& _,
25
3.16k
                                  const Instruction* inst) {
26
3.16k
  auto type = _.FindDef(inst->type_id());
27
3.16k
  if (!type || type->opcode() != spv::Op::OpTypeBool) {
28
5
    return _.diag(SPV_ERROR_INVALID_ID, inst)
29
5
           << "Op" << spvOpcodeString(inst->opcode()) << " Result Type <id> "
30
5
           << _.getIdName(inst->type_id()) << " is not a boolean type.";
31
5
  }
32
33
3.15k
  return SPV_SUCCESS;
34
3.16k
}
35
36
20.3k
bool isCompositeType(const Instruction* inst) {
37
20.3k
  bool is_tensor = inst->opcode() == spv::Op::OpTypeTensorARM;
38
20.3k
  bool tensor_is_shaped = inst->words().size() == 5;
39
20.3k
  return spvOpcodeIsComposite(inst->opcode()) ||
40
7
         (is_tensor && tensor_is_shaped);
41
20.3k
}
42
43
spv_result_t ValidateConstantOperand(ValidationState_t& _,
44
76.5k
                                     const Instruction* inst, size_t operand) {
45
76.5k
  std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode());
46
47
76.5k
  const auto operand_id = inst->GetOperandAs<uint32_t>(operand);
48
76.5k
  const bool inst_is_spec_constant = spvOpcodeIsSpecConstant(inst->opcode());
49
76.5k
  const auto operand_opcode = _.GetIdOpcode(operand_id);
50
76.5k
  const bool is_constant = spvOpcodeIsConstantOrUndef(operand_opcode);
51
76.5k
  const bool is_spec_constant = spvOpcodeIsSpecConstant(operand_opcode);
52
76.5k
  if (!is_constant) {
53
    // All operands must be constant, undef, or poison.
54
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
55
0
           << opcode_name
56
0
           << " must only have constant, undef, or poison operands: <id> "
57
0
           << _.getIdName(operand_id);
58
76.5k
  } else if (!inst_is_spec_constant && is_spec_constant) {
59
    // Spec constants are only allowed for spec constant opcodes.
60
9
    return _.diag(SPV_ERROR_INVALID_ID, inst)
61
9
           << opcode_name << " must not have spec constant operands: <id> "
62
9
           << _.getIdName(operand_id);
63
9
  }
64
65
76.5k
  return SPV_SUCCESS;
66
76.5k
}
67
68
spv_result_t ValidateConstantComposite(ValidationState_t& _,
69
20.3k
                                       const Instruction* inst) {
70
20.3k
  std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode());
71
72
20.3k
  const auto result_type = _.FindDef(inst->type_id());
73
20.3k
  if (!result_type || !isCompositeType(result_type)) {
74
7
    return _.diag(SPV_ERROR_INVALID_ID, inst)
75
7
           << opcode_name << " Result Type <id> "
76
7
           << _.getIdName(inst->type_id()) << " is not a composite type.";
77
7
  }
78
79
20.3k
  const auto constituent_count = inst->operands().size() - 2;
80
20.3k
  switch (result_type->opcode()) {
81
17.2k
    case spv::Op::OpTypeVector:
82
17.2k
    case spv::Op::OpTypeVectorIdEXT: {
83
17.2k
      uint32_t num_result_components = _.GetDimension(result_type->id());
84
17.2k
      bool comp_is_int32 = true, comp_is_const_int32 = true;
85
86
17.2k
      if (result_type->opcode() == spv::Op::OpTypeVectorIdEXT) {
87
0
        uint32_t comp_count_id = result_type->GetOperandAs<uint32_t>(2);
88
0
        std::tie(comp_is_int32, comp_is_const_int32, num_result_components) =
89
0
            _.EvalInt32IfConst(comp_count_id);
90
0
      }
91
92
17.2k
      if (comp_is_const_int32 && num_result_components != constituent_count) {
93
        // TODO: Output ID's on diagnostic
94
6
        return _.diag(SPV_ERROR_INVALID_ID, inst)
95
6
               << opcode_name
96
6
               << " Constituent <id> count does not match "
97
6
                  "Result Type <id> "
98
6
               << _.getIdName(result_type->id()) << "s vector component count.";
99
6
      }
100
17.2k
      const auto component_type =
101
17.2k
          _.FindDef(result_type->GetOperandAs<uint32_t>(1));
102
17.2k
      if (!component_type) {
103
0
        return _.diag(SPV_ERROR_INVALID_ID, result_type)
104
0
               << "Component type is not defined.";
105
0
      }
106
17.2k
      for (size_t constituent_index = 2;
107
76.1k
           constituent_index < inst->operands().size(); constituent_index++) {
108
58.9k
        const auto constituent_id =
109
58.9k
            inst->GetOperandAs<uint32_t>(constituent_index);
110
58.9k
        const auto constituent = _.FindDef(constituent_id);
111
58.9k
        const auto constituent_result_type = _.FindDef(constituent->type_id());
112
58.9k
        if (!constituent_result_type ||
113
58.9k
            component_type->id() != constituent_result_type->id()) {
114
10
          return _.diag(SPV_ERROR_INVALID_ID, inst)
115
10
                 << opcode_name << " Constituent <id> "
116
10
                 << _.getIdName(constituent_id)
117
10
                 << "s type does not match Result Type <id> "
118
10
                 << _.getIdName(result_type->id()) << "s vector element type.";
119
10
        }
120
58.9k
      }
121
17.2k
    } break;
122
17.1k
    case spv::Op::OpTypeMatrix: {
123
41
      const auto column_count = result_type->GetOperandAs<uint32_t>(2);
124
41
      if (column_count != constituent_count) {
125
        // TODO: Output ID's on diagnostic
126
4
        return _.diag(SPV_ERROR_INVALID_ID, inst)
127
4
               << opcode_name
128
4
               << " Constituent <id> count does not match "
129
4
                  "Result Type <id> "
130
4
               << _.getIdName(result_type->id()) << "s matrix column count.";
131
4
      }
132
133
37
      const auto column_type =
134
37
          _.FindDef(result_type->GetOperandAs<uint32_t>(1));
135
37
      if (!column_type) {
136
0
        return _.diag(SPV_ERROR_INVALID_ID, result_type)
137
0
               << "Column type is not defined.";
138
0
      }
139
37
      const auto component_count = column_type->GetOperandAs<uint32_t>(2);
140
37
      const auto component_type =
141
37
          _.FindDef(column_type->GetOperandAs<uint32_t>(1));
142
37
      if (!component_type) {
143
0
        return _.diag(SPV_ERROR_INVALID_ID, column_type)
144
0
               << "Component type is not defined.";
145
0
      }
146
147
37
      for (size_t constituent_index = 2;
148
111
           constituent_index < inst->operands().size(); constituent_index++) {
149
84
        const auto constituent_id =
150
84
            inst->GetOperandAs<uint32_t>(constituent_index);
151
84
        const auto constituent = _.FindDef(constituent_id);
152
84
        const auto vector = _.FindDef(constituent->type_id());
153
84
        if (!vector) {
154
2
          return _.diag(SPV_ERROR_INVALID_ID, constituent)
155
2
                 << "Result type is not defined.";
156
2
        }
157
82
        if (column_type->opcode() != vector->opcode()) {
158
6
          return _.diag(SPV_ERROR_INVALID_ID, inst)
159
6
                 << opcode_name << " Constituent <id> "
160
6
                 << _.getIdName(constituent_id)
161
6
                 << " type does not match Result Type <id> "
162
6
                 << _.getIdName(result_type->id()) << "s matrix column type.";
163
6
        }
164
76
        const auto vector_component_type =
165
76
            _.FindDef(vector->GetOperandAs<uint32_t>(1));
166
76
        if (component_type->id() != vector_component_type->id()) {
167
0
          return _.diag(SPV_ERROR_INVALID_ID, inst)
168
0
                 << opcode_name << " Constituent <id> "
169
0
                 << _.getIdName(constituent_id)
170
0
                 << " component type does not match Result Type <id> "
171
0
                 << _.getIdName(result_type->id())
172
0
                 << "s matrix column component type.";
173
0
        }
174
76
        if (component_count != vector->GetOperandAs<uint32_t>(2)) {
175
2
          return _.diag(SPV_ERROR_INVALID_ID, inst)
176
2
                 << opcode_name << " Constituent <id> "
177
2
                 << _.getIdName(constituent_id)
178
2
                 << " vector component count does not match Result Type <id> "
179
2
                 << _.getIdName(result_type->id())
180
2
                 << "s vector component count.";
181
2
        }
182
76
      }
183
37
    } break;
184
555
    case spv::Op::OpTypeArray: {
185
555
      auto element_type = _.FindDef(result_type->GetOperandAs<uint32_t>(1));
186
555
      if (!element_type) {
187
0
        return _.diag(SPV_ERROR_INVALID_ID, result_type)
188
0
               << "Element type is not defined.";
189
0
      }
190
555
      const auto length = _.FindDef(result_type->GetOperandAs<uint32_t>(2));
191
555
      if (!length) {
192
0
        return _.diag(SPV_ERROR_INVALID_ID, result_type)
193
0
               << "Length is not defined.";
194
0
      }
195
555
      bool is_int32;
196
555
      bool is_const;
197
555
      uint32_t value;
198
555
      std::tie(is_int32, is_const, value) = _.EvalInt32IfConst(length->id());
199
555
      if (is_int32 && is_const && value != constituent_count) {
200
25
        return _.diag(SPV_ERROR_INVALID_ID, inst)
201
25
               << opcode_name
202
25
               << " Constituent count does not match "
203
25
                  "Result Type <id> "
204
25
               << _.getIdName(result_type->id()) << "s array length.";
205
25
      }
206
530
      for (size_t constituent_index = 2;
207
12.0k
           constituent_index < inst->operands().size(); constituent_index++) {
208
11.5k
        const auto constituent_id =
209
11.5k
            inst->GetOperandAs<uint32_t>(constituent_index);
210
11.5k
        const auto constituent = _.FindDef(constituent_id);
211
11.5k
        const auto constituent_type = _.FindDef(constituent->type_id());
212
11.5k
        if (!constituent_type) {
213
3
          return _.diag(SPV_ERROR_INVALID_ID, constituent)
214
3
                 << "Result type is not defined.";
215
3
        }
216
11.5k
        if (element_type->id() != constituent_type->id()) {
217
15
          return _.diag(SPV_ERROR_INVALID_ID, inst)
218
15
                 << opcode_name << " Constituent <id> "
219
15
                 << _.getIdName(constituent_id)
220
15
                 << "s type does not match Result Type <id> "
221
15
                 << _.getIdName(result_type->id()) << "s array element type.";
222
15
        }
223
11.5k
      }
224
530
    } break;
225
2.51k
    case spv::Op::OpTypeStruct: {
226
2.51k
      const auto member_count = result_type->operands().size() - 1;
227
2.51k
      if (member_count != constituent_count) {
228
2
        return _.diag(SPV_ERROR_INVALID_ID, inst)
229
2
               << opcode_name << " Constituent <id> "
230
2
               << _.getIdName(inst->type_id())
231
2
               << " count does not match Result Type <id> "
232
2
               << _.getIdName(result_type->id()) << "s struct member count.";
233
2
      }
234
2.51k
      for (uint32_t constituent_index = 2, member_index = 1;
235
9.80k
           constituent_index < inst->operands().size();
236
7.29k
           constituent_index++, member_index++) {
237
7.29k
        const auto constituent_id =
238
7.29k
            inst->GetOperandAs<uint32_t>(constituent_index);
239
7.29k
        const auto constituent = _.FindDef(constituent_id);
240
7.29k
        const auto constituent_type = _.FindDef(constituent->type_id());
241
7.29k
        if (!constituent_type) {
242
3
          return _.diag(SPV_ERROR_INVALID_ID, constituent)
243
3
                 << "Result type is not defined.";
244
3
        }
245
246
7.29k
        const auto member_type_id =
247
7.29k
            result_type->GetOperandAs<uint32_t>(member_index);
248
7.29k
        const auto member_type = _.FindDef(member_type_id);
249
7.29k
        if (!member_type || member_type->id() != constituent_type->id()) {
250
7
          return _.diag(SPV_ERROR_INVALID_ID, inst)
251
7
                 << opcode_name << " Constituent <id> "
252
7
                 << _.getIdName(constituent_id)
253
7
                 << " type does not match the Result Type <id> "
254
7
                 << _.getIdName(result_type->id()) << "s member type.";
255
7
        }
256
7.29k
      }
257
2.51k
    } break;
258
2.50k
    case spv::Op::OpTypeCooperativeMatrixKHR:
259
0
    case spv::Op::OpTypeCooperativeMatrixNV: {
260
0
      if (1 != constituent_count) {
261
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
262
0
               << opcode_name << " Constituent <id> "
263
0
               << _.getIdName(inst->type_id()) << " count must be one.";
264
0
      }
265
0
      const auto constituent_id = inst->GetOperandAs<uint32_t>(2);
266
0
      const auto constituent = _.FindDef(constituent_id);
267
0
      const auto constituent_type = _.FindDef(constituent->type_id());
268
0
      if (!constituent_type) {
269
0
        return _.diag(SPV_ERROR_INVALID_ID, constituent)
270
0
               << "Result type is not defined.";
271
0
      }
272
273
0
      const auto component_type_id = result_type->GetOperandAs<uint32_t>(1);
274
0
      const auto component_type = _.FindDef(component_type_id);
275
0
      if (!component_type || component_type->id() != constituent_type->id()) {
276
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
277
0
               << opcode_name << " Constituent <id> "
278
0
               << _.getIdName(constituent_id)
279
0
               << " type does not match the Result Type <id> "
280
0
               << _.getIdName(result_type->id()) << "s component type.";
281
0
      }
282
0
    } break;
283
0
    case spv::Op::OpTypeTensorARM: {
284
0
      auto inst_element_type =
285
0
          _.FindDef(result_type->GetOperandAs<uint32_t>(1));
286
0
      if (!inst_element_type) {
287
0
        return _.diag(SPV_ERROR_INVALID_ID, result_type)
288
0
               << "Element type is not defined.";
289
0
      }
290
0
      const auto inst_rank = _.FindDef(result_type->GetOperandAs<uint32_t>(2));
291
0
      if (!inst_rank) {
292
0
        return _.diag(SPV_ERROR_INVALID_ID, result_type)
293
0
               << "Rank is not defined.";
294
0
      }
295
0
      const auto inst_shape = _.FindDef(result_type->GetOperandAs<uint32_t>(3));
296
0
      if (!inst_shape) {
297
0
        return _.diag(SPV_ERROR_INVALID_ID, result_type)
298
0
               << "Shape is not defined.";
299
0
      }
300
301
0
      uint64_t rank = 0;
302
0
      _.EvalConstantValUint64(inst_rank->id(), &rank);
303
304
0
      uint64_t outermost_shape = 0;
305
0
      if (_.EvalConstantValUint64(inst_shape->GetOperandAs<uint32_t>(2),
306
0
                                  &outermost_shape) &&
307
0
          (outermost_shape != constituent_count)) {
308
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
309
0
               << opcode_name
310
0
               << " Constituent count does not match "
311
0
                  "the shape of Result Type <id> "
312
0
               << _.getIdName(result_type->id())
313
0
               << " along its outermost dimension, " << "expected "
314
0
               << outermost_shape << " but got " << constituent_count << ".";
315
0
      }
316
317
0
      for (size_t constituent_index = 2;
318
0
           constituent_index < inst->operands().size(); constituent_index++) {
319
0
        const auto constituent_id =
320
0
            inst->GetOperandAs<uint32_t>(constituent_index);
321
0
        const auto constituent = _.FindDef(constituent_id);
322
0
        const auto constituent_type = _.FindDef(constituent->type_id());
323
0
        if (!constituent_type) {
324
0
          return _.diag(SPV_ERROR_INVALID_ID, constituent)
325
0
                 << "Type of Constituent " << constituent_index - 2
326
0
                 << " is not defined.";
327
0
        }
328
329
0
        if (rank == 0) {
330
          // The rank of the returned tensor constant is not known.
331
          // Skip rank-dependent validation.
332
0
          continue;
333
0
        }
334
335
0
        if (rank == 1) {
336
0
          if (inst_element_type->id() != constituent_type->id()) {
337
0
            return _.diag(SPV_ERROR_INVALID_ID, inst)
338
0
                   << opcode_name << " Constituent <id> "
339
0
                   << _.getIdName(constituent_id)
340
0
                   << " type does not match the element type of the tensor ("
341
0
                   << _.getIdName(result_type->id()) << ").";
342
0
          }
343
0
        } else {
344
0
          if (constituent_type->opcode() != spv::Op::OpTypeTensorARM) {
345
0
            return _.diag(SPV_ERROR_INVALID_ID, inst)
346
0
                   << opcode_name << " Constituent <id> "
347
0
                   << _.getIdName(constituent_id)
348
0
                   << " must be an OpTypeTensorARM.";
349
0
          }
350
0
          auto inst_constituent_element_type =
351
0
              _.FindDef(constituent_type->GetOperandAs<uint32_t>(1));
352
0
          if (!inst_constituent_element_type ||
353
0
              inst_constituent_element_type->id() != inst_element_type->id()) {
354
0
            return _.diag(SPV_ERROR_INVALID_ID, inst)
355
0
                   << opcode_name << " Constituent <id> "
356
0
                   << _.getIdName(constituent_id)
357
0
                   << " must have the same Element Type as Result Type <id> "
358
0
                   << _.getIdName(result_type->id()) << ".";
359
0
          }
360
0
          auto inst_constituent_rank =
361
0
              _.FindDef(constituent_type->GetOperandAs<uint32_t>(2));
362
0
          uint64_t constituent_rank;
363
0
          if (inst_constituent_rank &&
364
0
              _.EvalConstantValUint64(inst_constituent_rank->id(),
365
0
                                      &constituent_rank) &&
366
0
              (constituent_rank != rank - 1)) {
367
0
            return _.diag(SPV_ERROR_INVALID_ID, inst)
368
0
                   << opcode_name << " Constituent <id> "
369
0
                   << _.getIdName(constituent_id)
370
0
                   << " must have a Rank that is 1 less than the Rank of "
371
0
                      "Result Type <id> "
372
0
                   << _.getIdName(result_type->id()) << ", expected "
373
0
                   << rank - 1 << " but got " << constituent_rank << ".";
374
0
          }
375
376
0
          auto inst_constituent_shape =
377
0
              _.FindDef(constituent_type->GetOperandAs<uint32_t>(3));
378
0
          if (!inst_constituent_shape) {
379
0
            return _.diag(SPV_ERROR_INVALID_ID, result_type)
380
0
                   << "Shape of Constituent " << constituent_index - 2
381
0
                   << " is not defined.";
382
0
          }
383
0
          for (size_t constituent_shape_index = 2;
384
0
               constituent_shape_index <
385
0
               inst_constituent_shape->operands().size();
386
0
               constituent_shape_index++) {
387
0
            size_t shape_index = constituent_shape_index + 1;
388
0
            uint64_t constituent_shape = 0, shape = 1;
389
0
            if (_.EvalConstantValUint64(
390
0
                    inst_constituent_shape->GetOperandAs<uint32_t>(
391
0
                        constituent_shape_index),
392
0
                    &constituent_shape) &&
393
0
                _.EvalConstantValUint64(
394
0
                    inst_shape->GetOperandAs<uint32_t>(shape_index), &shape) &&
395
0
                (constituent_shape != shape)) {
396
0
              return _.diag(SPV_ERROR_INVALID_ID, inst)
397
0
                     << opcode_name << " Constituent <id> "
398
0
                     << _.getIdName(constituent_id)
399
0
                     << " must have a Shape that matches that of Result Type "
400
0
                        "<id> "
401
0
                     << _.getIdName(result_type->id())
402
0
                     << " along all inner dimensions of Result Type, expected "
403
0
                     << shape << " for dimension "
404
0
                     << constituent_shape_index - 2
405
0
                     << " of Constituent but got " << constituent_shape << ".";
406
0
            }
407
0
          }
408
0
        }
409
0
      }
410
0
    } break;
411
9
    default:
412
9
      break;
413
20.3k
  }
414
415
96.7k
  for (size_t i = 2; i < inst->operands().size(); i++) {
416
76.5k
    if (auto error = ValidateConstantOperand(_, inst, i)) {
417
9
      return error;
418
9
    }
419
76.5k
  }
420
421
20.2k
  return SPV_SUCCESS;
422
20.2k
}
423
424
spv_result_t ValidateConstantCompositeReplicate(ValidationState_t& _,
425
0
                                                const Instruction* inst) {
426
0
  std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode());
427
428
0
  const auto result_type = _.FindDef(inst->type_id());
429
0
  if (!result_type || !isCompositeType(result_type)) {
430
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
431
0
           << opcode_name << " Result Type <id> "
432
0
           << _.getIdName(inst->type_id()) << " is not a composite type.";
433
0
  }
434
435
0
  const auto constituent_id = inst->GetOperandAs<uint32_t>(2);
436
0
  const auto constituent = _.FindDef(constituent_id);
437
0
  switch (result_type->opcode()) {
438
0
    case spv::Op::OpTypeVector:
439
0
    case spv::Op::OpTypeVectorIdEXT:
440
0
    case spv::Op::OpTypeMatrix:
441
0
    case spv::Op::OpTypeArray:
442
0
    case spv::Op::OpTypeCooperativeMatrixKHR:
443
0
    case spv::Op::OpTypeCooperativeMatrixNV:
444
0
    case spv::Op::OpTypeTensorARM: {
445
0
      const auto component_type = result_type->GetOperandAs<uint32_t>(1);
446
0
      if (component_type != constituent->type_id()) {
447
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
448
0
               << opcode_name << " Constituent <id> "
449
0
               << _.getIdName(constituent_id)
450
0
               << "s type does not match Result Type <id> "
451
0
               << _.getIdName(result_type->id()) << "s element type.";
452
0
      }
453
0
      break;
454
0
    }
455
0
    case spv::Op::OpTypeStruct: {
456
0
      const auto member_count = result_type->operands().size() - 1;
457
0
      for (uint32_t member_index = 1; member_index <= member_count;
458
0
           member_index++) {
459
0
        const auto member_type_id =
460
0
            result_type->GetOperandAs<uint32_t>(member_index);
461
0
        if (member_type_id != constituent->type_id()) {
462
0
          return _.diag(SPV_ERROR_INVALID_ID, inst)
463
0
                 << opcode_name << " Constituent <id> "
464
0
                 << _.getIdName(constituent_id)
465
0
                 << " type does not match the Result Type <id> "
466
0
                 << _.getIdName(result_type->id()) << "s member type.";
467
0
        }
468
0
      }
469
0
      break;
470
0
    }
471
0
    default:
472
0
      break;
473
0
  }
474
475
0
  return ValidateConstantOperand(_, inst, 2);
476
0
}
477
478
spv_result_t ValidateConstantSampler(ValidationState_t& _,
479
3
                                     const Instruction* inst) {
480
3
  const auto result_type = _.FindDef(inst->type_id());
481
3
  if (!result_type || result_type->opcode() != spv::Op::OpTypeSampler) {
482
2
    return _.diag(SPV_ERROR_INVALID_ID, result_type)
483
2
           << "OpConstantSampler Result Type <id> "
484
2
           << _.getIdName(inst->type_id()) << " is not a sampler type.";
485
2
  }
486
487
1
  return SPV_SUCCESS;
488
3
}
489
490
// True if instruction defines a type that can have a null value, as defined by
491
// the SPIR-V spec.  Tracks composite-type components through module to check
492
// nullability transitively.
493
bool IsTypeNullable(const std::vector<uint32_t>& instruction,
494
2.25k
                    const ValidationState_t& _) {
495
2.25k
  uint16_t opcode;
496
2.25k
  uint16_t word_count;
497
2.25k
  spvOpcodeSplit(instruction[0], &word_count, &opcode);
498
2.25k
  switch (static_cast<spv::Op>(opcode)) {
499
480
    case spv::Op::OpTypeBool:
500
1.35k
    case spv::Op::OpTypeInt:
501
1.60k
    case spv::Op::OpTypeFloat:
502
1.61k
    case spv::Op::OpTypeEvent:
503
1.62k
    case spv::Op::OpTypeDeviceEvent:
504
1.63k
    case spv::Op::OpTypeReserveId:
505
1.64k
    case spv::Op::OpTypeQueue:
506
1.64k
      return true;
507
14
    case spv::Op::OpTypeArray:
508
17
    case spv::Op::OpTypeMatrix:
509
17
    case spv::Op::OpTypeCooperativeMatrixNV:
510
17
    case spv::Op::OpTypeCooperativeMatrixKHR:
511
17
    case spv::Op::OpTypeVectorIdEXT:
512
95
    case spv::Op::OpTypeVector: {
513
95
      auto base_type = _.FindDef(instruction[2]);
514
95
      return base_type && IsTypeNullable(base_type->words(), _);
515
17
    }
516
327
    case spv::Op::OpTypeStruct: {
517
1.11k
      for (size_t elementIndex = 2; elementIndex < instruction.size();
518
795
           ++elementIndex) {
519
795
        auto element = _.FindDef(instruction[elementIndex]);
520
795
        if (!element || !IsTypeNullable(element->words(), _)) return false;
521
795
      }
522
320
      return true;
523
327
    }
524
0
    case spv::Op::OpTypeUntypedPointerKHR:
525
182
    case spv::Op::OpTypePointer:
526
182
      if (spv::StorageClass(instruction[2]) ==
527
182
          spv::StorageClass::PhysicalStorageBuffer) {
528
0
        return false;
529
0
      }
530
182
      return true;
531
0
    case spv::Op::OpTypeTensorARM: {
532
0
      auto elem_type = _.FindDef(instruction[2]);
533
0
      return (instruction.size() > 4) && elem_type &&
534
0
             IsTypeNullable(elem_type->words(), _);
535
182
    }
536
8
    default:
537
8
      return false;
538
2.25k
  }
539
2.25k
}
540
541
spv_result_t ValidateConstantNull(ValidationState_t& _,
542
1.36k
                                  const Instruction* inst) {
543
1.36k
  const auto result_type = _.FindDef(inst->type_id());
544
1.36k
  if (!result_type || !IsTypeNullable(result_type->words(), _)) {
545
8
    return _.diag(SPV_ERROR_INVALID_ID, inst)
546
8
           << "OpConstantNull Result Type <id> " << _.getIdName(inst->type_id())
547
8
           << " cannot have a null value.";
548
8
  }
549
550
1.35k
  return SPV_SUCCESS;
551
1.36k
}
552
553
spv_result_t ValidateConstantSizeOfEXT(ValidationState_t& _,
554
0
                                       const Instruction* inst) {
555
0
  const Instruction* result_type = _.FindDef(inst->type_id());
556
0
  const uint32_t bit_width = result_type->opcode() == spv::Op::OpTypeInt
557
0
                                 ? result_type->GetOperandAs<uint32_t>(1)
558
0
                                 : 0;
559
560
  // VVL will validate the SPV_EXT_shader_64bit_indexing interaction
561
0
  if (bit_width != 64 && bit_width != 32) {
562
0
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
563
0
           << "For OpConstantSizeOfEXT instruction, its result type "
564
0
           << "must be a 32-bit or 64-bit integer type scalar."
565
0
           << " (OpCapability Int64 is required for 64-bit)";
566
0
  }
567
568
0
  const uint32_t type_operand = inst->GetOperandAs<uint32_t>(2);
569
0
  if (!_.IsDescriptorType(type_operand)) {
570
0
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
571
0
           << "For OpConstantSizeOfEXT instruction, its Type operand <Id> "
572
0
           << _.getIdName(type_operand) << " must be a Descriptor type.";
573
0
  }
574
0
  return SPV_SUCCESS;
575
0
}
576
577
// Validates that OpSpecConstant specializes to either int or float type.
578
spv_result_t ValidateSpecConstant(ValidationState_t& _,
579
828
                                  const Instruction* inst) {
580
  // Operand 0 is the <id> of the type that we're specializing to.
581
828
  auto type_id = inst->GetOperandAs<const uint32_t>(0);
582
828
  auto type_instruction = _.FindDef(type_id);
583
828
  auto type_opcode = type_instruction->opcode();
584
828
  if (type_opcode != spv::Op::OpTypeInt &&
585
145
      type_opcode != spv::Op::OpTypeFloat) {
586
0
    return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Specialization constant "
587
0
                                                   "must be an integer or "
588
0
                                                   "floating-point number.";
589
0
  }
590
828
  return SPV_SUCCESS;
591
828
}
592
593
spv_result_t ValidateSpecConstantOp(ValidationState_t& _,
594
333
                                    const Instruction* inst) {
595
333
  const auto op = inst->GetOperandAs<spv::Op>(2);
596
597
  // The binary parser already ensures that the op is valid for *some*
598
  // environment.  Here we check restrictions.
599
333
  switch (op) {
600
9
    case spv::Op::OpQuantizeToF16:
601
9
      if (!_.HasCapability(spv::Capability::Shader)) {
602
1
        return _.diag(SPV_ERROR_INVALID_ID, inst)
603
1
               << "Specialization constant operation " << spvOpcodeString(op)
604
1
               << " requires Shader capability";
605
1
      }
606
8
      break;
607
608
8
    case spv::Op::OpUConvert:
609
6
      if (!_.features().uconvert_spec_constant_op &&
610
6
          !_.HasCapability(spv::Capability::Kernel)) {
611
1
        return _.diag(SPV_ERROR_INVALID_ID, inst)
612
1
               << "Prior to SPIR-V 1.4, specialization constant operation "
613
1
                  "UConvert requires Kernel capability or extension "
614
1
                  "SPV_AMD_gpu_shader_int16";
615
1
      }
616
5
      break;
617
618
5
    case spv::Op::OpConvertFToS:
619
3
    case spv::Op::OpConvertSToF:
620
21
    case spv::Op::OpConvertFToU:
621
24
    case spv::Op::OpConvertUToF:
622
26
    case spv::Op::OpConvertPtrToU:
623
27
    case spv::Op::OpConvertUToPtr:
624
30
    case spv::Op::OpGenericCastToPtr:
625
31
    case spv::Op::OpPtrCastToGeneric:
626
81
    case spv::Op::OpBitcast:
627
105
    case spv::Op::OpFNegate:
628
108
    case spv::Op::OpFAdd:
629
110
    case spv::Op::OpFSub:
630
113
    case spv::Op::OpFMul:
631
114
    case spv::Op::OpFDiv:
632
118
    case spv::Op::OpFRem:
633
120
    case spv::Op::OpFMod:
634
126
    case spv::Op::OpAccessChain:
635
129
    case spv::Op::OpInBoundsAccessChain:
636
130
    case spv::Op::OpPtrAccessChain:
637
131
    case spv::Op::OpInBoundsPtrAccessChain:
638
134
    case spv::Op::OpUntypedAccessChainKHR:
639
137
    case spv::Op::OpUntypedInBoundsAccessChainKHR:
640
138
    case spv::Op::OpUntypedPtrAccessChainKHR:
641
139
    case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
642
139
      if (!_.HasCapability(spv::Capability::Kernel)) {
643
11
        return _.diag(SPV_ERROR_INVALID_ID, inst)
644
11
               << "Specialization constant operation " << spvOpcodeString(op)
645
11
               << " requires Kernel capability";
646
11
      }
647
128
      break;
648
649
179
    default:
650
179
      break;
651
333
  }
652
653
  // TODO(dneto): Validate result type and arguments to the various operations.
654
320
  return SPV_SUCCESS;
655
333
}
656
657
spv_result_t ValidateConstantFunctionPointerINTEL(ValidationState_t& _,
658
0
                                                  const Instruction* inst) {
659
0
  const auto result_type = _.FindDef(inst->type_id());
660
  // Result Type must be a pointer type
661
0
  if (result_type->opcode() != spv::Op::OpTypePointer &&
662
0
      result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
663
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
664
0
           << "OpConstantFunctionPointerINTEL Result Type <id> "
665
0
           << _.getIdName(inst->type_id()) << " is not a pointer type";
666
0
  }
667
668
  // For typed pointers, check that pointee is a function type
669
0
  const Instruction* pointee_type = nullptr;
670
0
  if (result_type->opcode() == spv::Op::OpTypePointer) {
671
0
    pointee_type = _.FindDef(result_type->GetOperandAs<uint32_t>(2));
672
0
    if (pointee_type->opcode() != spv::Op::OpTypeFunction) {
673
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
674
0
             << "OpConstantFunctionPointerINTEL Result Type <id> "
675
0
             << _.getIdName(inst->type_id())
676
0
             << " must be a pointer to function type";
677
0
    }
678
0
  }
679
680
  // Validate that the function operand refers to an OpFunction
681
0
  const uint32_t function_id = inst->GetOperandAs<uint32_t>(2);
682
0
  const auto function_inst = _.FindDef(function_id);
683
0
  if (function_inst->opcode() != spv::Op::OpFunction) {
684
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
685
0
           << "OpConstantFunctionPointerINTEL Function operand <id> "
686
0
           << _.getIdName(function_id) << " is not an OpFunction";
687
0
  }
688
689
  // For typed pointers, validate that function type matches pointee type
690
0
  if (pointee_type) {
691
0
    const uint32_t function_type_id = function_inst->GetOperandAs<uint32_t>(3);
692
0
    if (function_type_id != pointee_type->id()) {
693
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
694
0
             << "OpConstantFunctionPointerINTEL Function operand <id> "
695
0
             << _.getIdName(function_id)
696
0
             << " type does not match the pointer's function type";
697
0
    }
698
0
  }
699
700
0
  return SPV_SUCCESS;
701
0
}
702
703
spv_result_t ValidateConstantData(ValidationState_t& _,
704
0
                                  const Instruction* inst) {
705
0
  const auto array_inst = _.FindDef(inst->type_id());
706
0
  if (array_inst->opcode() != spv::Op::OpTypeArray) {
707
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
708
0
           << "Result type must be an array.";
709
0
  }
710
711
0
  const auto element_type_inst =
712
0
      _.FindDef(array_inst->GetOperandAs<uint32_t>(1));
713
0
  if (!_.IsIntScalarType(element_type_inst->id())) {
714
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
715
0
           << "Result type must be an array of integer scalar type.";
716
0
  }
717
718
  // The Data literals are tightly packed, so an explicit layout stride on the
719
  // result type would contradict how the constant is encoded.
720
0
  if (_.HasDecoration(array_inst->id(), spv::Decoration::ArrayStride)) {
721
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
722
0
           << "Result type must not be decorated with ArrayStride.";
723
0
  }
724
725
0
  const uint32_t int_width = element_type_inst->word(2);
726
0
  const uint32_t data_words = static_cast<uint32_t>(inst->words().size() - 3);
727
728
0
  if (data_words == 0) {
729
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
730
0
           << "There must be at least 1 literal integer (because an array of "
731
0
              "zero is not allowed).";
732
0
  }
733
734
0
  uint64_t array_length = 0;
735
0
  if (!_.EvalConstantValUint64(array_inst->GetOperandAs<uint32_t>(2),
736
0
                               &array_length)) {
737
    // The length could be a SpecConstant, will need to be frozen to validate
738
0
    return SPV_SUCCESS;
739
0
  }
740
741
0
  const uint32_t words_needed =
742
0
      (((int_width / 8) * static_cast<uint32_t>(array_length) + 3) & ~3) / 4;
743
0
  if (data_words != words_needed) {
744
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
745
0
           << "contains " << data_words << " words of data, but needs to have "
746
0
           << words_needed << " words to match the array of " << array_length
747
0
           << " of " << int_width << "-bit ints.";
748
0
  }
749
750
0
  return SPV_SUCCESS;
751
0
}
752
753
}  // namespace
754
755
14.1M
spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst) {
756
14.1M
  switch (inst->opcode()) {
757
1.41k
    case spv::Op::OpConstantTrue:
758
2.85k
    case spv::Op::OpConstantFalse:
759
3.01k
    case spv::Op::OpSpecConstantTrue:
760
3.16k
    case spv::Op::OpSpecConstantFalse:
761
3.16k
      if (auto error = ValidateConstantBool(_, inst)) return error;
762
3.15k
      break;
763
20.1k
    case spv::Op::OpConstantComposite:
764
20.3k
    case spv::Op::OpSpecConstantComposite:
765
20.3k
      if (auto error = ValidateConstantComposite(_, inst)) return error;
766
20.2k
      break;
767
20.2k
    case spv::Op::OpConstantCompositeReplicateEXT:
768
0
    case spv::Op::OpSpecConstantCompositeReplicateEXT:
769
0
      if (auto error = ValidateConstantCompositeReplicate(_, inst))
770
0
        return error;
771
0
      break;
772
3
    case spv::Op::OpConstantSampler:
773
3
      if (auto error = ValidateConstantSampler(_, inst)) return error;
774
1
      break;
775
1.36k
    case spv::Op::OpConstantNull:
776
1.36k
      if (auto error = ValidateConstantNull(_, inst)) return error;
777
1.35k
      break;
778
1.35k
    case spv::Op::OpSpecConstant:
779
828
      if (auto error = ValidateSpecConstant(_, inst)) return error;
780
828
      break;
781
828
    case spv::Op::OpSpecConstantOp:
782
333
      if (auto error = ValidateSpecConstantOp(_, inst)) return error;
783
320
      break;
784
320
    case spv::Op::OpConstantSizeOfEXT:
785
0
      if (auto error = ValidateConstantSizeOfEXT(_, inst)) return error;
786
0
      break;
787
0
    case spv::Op::OpConstantFunctionPointerINTEL:
788
0
      if (auto error = ValidateConstantFunctionPointerINTEL(_, inst))
789
0
        return error;
790
0
      break;
791
0
    case spv::Op::OpConstantDataKHR:
792
0
    case spv::Op::OpSpecConstantDataKHR:
793
0
      if (auto error = ValidateConstantData(_, inst)) return error;
794
0
      break;
795
14.1M
    default:
796
14.1M
      break;
797
14.1M
  }
798
799
  // Generally disallow creating 8- or 16-bit constants unless the full
800
  // capabilities are present.
801
14.1M
  if (spvOpcodeIsConstant(inst->opcode()) &&
802
158k
      _.HasCapability(spv::Capability::Shader) &&
803
158k
      !_.IsPointerType(inst->type_id()) &&
804
158k
      _.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
805
1
    return _.diag(SPV_ERROR_INVALID_ID, inst)
806
1
           << "Cannot form constants of 8- or 16-bit types";
807
1
  }
808
809
14.1M
  return SPV_SUCCESS;
810
14.1M
}
811
812
}  // namespace val
813
}  // namespace spvtools