Coverage Report

Created: 2026-03-31 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/val/validate_annotation.cpp
Line
Count
Source
1
// Copyright (c) 2018 Google LLC.
2
// Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
3
// reserved.
4
//
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
//     http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
17
#include "source/opcode.h"
18
#include "source/spirv_target_env.h"
19
#include "source/val/instruction.h"
20
#include "source/val/validate.h"
21
#include "source/val/validation_state.h"
22
23
namespace spvtools {
24
namespace val {
25
namespace {
26
27
// Returns true if the decoration takes ID parameters.
28
// TODO(dneto): This can be generated from the grammar.
29
474k
bool DecorationTakesIdParameters(spv::Decoration type) {
30
474k
  switch (type) {
31
0
    case spv::Decoration::UniformId:
32
0
    case spv::Decoration::AlignmentId:
33
0
    case spv::Decoration::MaxByteOffsetId:
34
0
    case spv::Decoration::HlslCounterBufferGOOGLE:
35
0
    case spv::Decoration::NodeMaxPayloadsAMDX:
36
0
    case spv::Decoration::NodeSharesPayloadLimitsWithAMDX:
37
0
    case spv::Decoration::PayloadNodeArraySizeAMDX:
38
0
    case spv::Decoration::PayloadNodeNameAMDX:
39
0
    case spv::Decoration::PayloadNodeBaseIndexAMDX:
40
0
    case spv::Decoration::ArrayStrideIdEXT:
41
0
    case spv::Decoration::OffsetIdEXT:
42
0
    case spv::Decoration::AliasScopeINTEL:
43
0
    case spv::Decoration::NoAliasINTEL:
44
0
      return true;
45
474k
    default:
46
474k
      break;
47
474k
  }
48
474k
  return false;
49
474k
}
50
51
206k
bool IsMemberDecorationOnly(spv::Decoration dec) {
52
206k
  switch (dec) {
53
4
    case spv::Decoration::RowMajor:
54
8
    case spv::Decoration::ColMajor:
55
9
    case spv::Decoration::MatrixStride:
56
      // SPIR-V spec bug? Offset is generated on variables when dealing with
57
      // transform feedback.
58
      // case spv::Decoration::Offset:
59
9
      return true;
60
206k
    default:
61
206k
      break;
62
206k
  }
63
206k
  return false;
64
206k
}
65
66
7.63k
bool IsNotMemberDecoration(spv::Decoration dec) {
67
7.63k
  switch (dec) {
68
3
    case spv::Decoration::SpecId:
69
6
    case spv::Decoration::Block:
70
9
    case spv::Decoration::BufferBlock:
71
12
    case spv::Decoration::ArrayStride:
72
12
    case spv::Decoration::ArrayStrideIdEXT:
73
15
    case spv::Decoration::GLSLShared:
74
18
    case spv::Decoration::GLSLPacked:
75
18
    case spv::Decoration::CPacked:
76
    // TODO: https://github.com/KhronosGroup/glslang/issues/703:
77
    // glslang applies Restrict to structure members.
78
    // case spv::Decoration::Restrict:
79
21
    case spv::Decoration::Aliased:
80
22
    case spv::Decoration::Constant:
81
25
    case spv::Decoration::Uniform:
82
25
    case spv::Decoration::UniformId:
83
26
    case spv::Decoration::SaturatedConversion:
84
29
    case spv::Decoration::Index:
85
32
    case spv::Decoration::Binding:
86
35
    case spv::Decoration::DescriptorSet:
87
37
    case spv::Decoration::FuncParamAttr:
88
40
    case spv::Decoration::FPRoundingMode:
89
42
    case spv::Decoration::FPFastMathMode:
90
42
    case spv::Decoration::LinkageAttributes:
91
45
    case spv::Decoration::NoContraction:
92
46
    case spv::Decoration::InputAttachmentIndex:
93
48
    case spv::Decoration::Alignment:
94
48
    case spv::Decoration::MaxByteOffset:
95
48
    case spv::Decoration::AlignmentId:
96
48
    case spv::Decoration::MaxByteOffsetId:
97
48
    case spv::Decoration::NoSignedWrap:
98
48
    case spv::Decoration::NoUnsignedWrap:
99
48
    case spv::Decoration::NonUniform:
100
48
    case spv::Decoration::RestrictPointer:
101
48
    case spv::Decoration::AliasedPointer:
102
48
    case spv::Decoration::CounterBuffer:
103
48
      return true;
104
7.58k
    default:
105
7.58k
      break;
106
7.63k
  }
107
7.58k
  return false;
108
7.63k
}
109
110
spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
111
                                      const Instruction* inst,
112
206k
                                      const Instruction* target) {
113
206k
  auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream {
114
117
    DiagnosticStream ds = std::move(
115
117
        _.diag(SPV_ERROR_INVALID_ID, inst)
116
117
        << _.VkErrorID(vuid) << _.SpvDecorationString(dec)
117
117
        << " decoration on target <id> " << _.getIdName(target->id()) << " ");
118
117
    return ds;
119
117
  };
120
206k
  switch (dec) {
121
96
    case spv::Decoration::SpecId:
122
96
      if (!spvOpcodeIsScalarSpecConstant(target->opcode())) {
123
12
        return fail(0) << "must be a scalar specialization constant";
124
12
      }
125
84
      break;
126
3.80k
    case spv::Decoration::Block:
127
7.80k
    case spv::Decoration::BufferBlock:
128
8.19k
    case spv::Decoration::GLSLShared:
129
8.44k
    case spv::Decoration::GLSLPacked:
130
8.55k
    case spv::Decoration::CPacked:
131
8.55k
      if (target->opcode() != spv::Op::OpTypeStruct) {
132
7
        return fail(0) << "must be a structure type";
133
7
      }
134
8.54k
      break;
135
8.54k
    case spv::Decoration::ArrayStride:
136
1.14k
      if (target->opcode() != spv::Op::OpTypeArray &&
137
180
          target->opcode() != spv::Op::OpTypeRuntimeArray &&
138
100
          target->opcode() != spv::Op::OpTypePointer &&
139
9
          target->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
140
9
        return fail(0) << "must be an array or pointer type";
141
9
      }
142
1.13k
      break;
143
3.55k
    case spv::Decoration::BuiltIn:
144
3.55k
      if (target->opcode() != spv::Op::OpVariable &&
145
225
          target->opcode() != spv::Op::OpUntypedVariableKHR &&
146
225
          !spvOpcodeIsConstant(target->opcode())) {
147
5
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
148
5
               << "BuiltIns can only target variables, structure members or "
149
5
                  "constants";
150
5
      }
151
3.55k
      if (_.HasCapability(spv::Capability::Shader) &&
152
3.50k
          inst->GetOperandAs<spv::BuiltIn>(2) == spv::BuiltIn::WorkgroupSize) {
153
219
        if (!spvOpcodeIsConstant(target->opcode())) {
154
5
          return fail(0) << "must be a constant for WorkgroupSize";
155
5
        }
156
3.33k
      } else if (target->opcode() != spv::Op::OpVariable &&
157
6
                 target->opcode() != spv::Op::OpUntypedVariableKHR) {
158
6
        return fail(0) << "must be a variable";
159
6
      }
160
3.53k
      break;
161
3.53k
    case spv::Decoration::NoPerspective:
162
16.9k
    case spv::Decoration::Flat:
163
16.9k
    case spv::Decoration::Patch:
164
19.2k
    case spv::Decoration::Centroid:
165
19.5k
    case spv::Decoration::Sample:
166
27.6k
    case spv::Decoration::Restrict:
167
76.5k
    case spv::Decoration::Aliased:
168
79.3k
    case spv::Decoration::Volatile:
169
80.3k
    case spv::Decoration::Coherent:
170
80.5k
    case spv::Decoration::NonWritable:
171
80.7k
    case spv::Decoration::NonReadable:
172
80.7k
    case spv::Decoration::XfbBuffer:
173
80.7k
    case spv::Decoration::XfbStride:
174
82.6k
    case spv::Decoration::Component:
175
82.6k
    case spv::Decoration::Stream:
176
82.6k
    case spv::Decoration::RestrictPointer:
177
82.6k
    case spv::Decoration::AliasedPointer:
178
82.6k
    case spv::Decoration::PerPrimitiveEXT:
179
82.6k
      if (target->opcode() != spv::Op::OpVariable &&
180
87
          target->opcode() != spv::Op::OpUntypedVariableKHR &&
181
87
          target->opcode() != spv::Op::OpFunctionParameter &&
182
48
          target->opcode() != spv::Op::OpRawAccessChainNV &&
183
48
          target->opcode() != spv::Op::OpBufferPointerEXT) {
184
48
        return fail(0) << "must be a memory object declaration";
185
48
      }
186
82.5k
      if (!_.IsPointerType(target->type_id())) {
187
3
        return fail(0) << "must be a pointer type";
188
3
      }
189
82.5k
      break;
190
82.5k
    case spv::Decoration::Invariant:
191
1.13k
    case spv::Decoration::Constant:
192
7.69k
    case spv::Decoration::Location:
193
8.27k
    case spv::Decoration::Index:
194
13.1k
    case spv::Decoration::Binding:
195
17.3k
    case spv::Decoration::DescriptorSet:
196
17.3k
    case spv::Decoration::InputAttachmentIndex:
197
17.3k
      if (target->opcode() != spv::Op::OpVariable &&
198
27
          target->opcode() != spv::Op::OpUntypedVariableKHR) {
199
27
        return fail(0) << "must be a variable";
200
27
      }
201
17.3k
      break;
202
93.5k
    default:
203
93.5k
      break;
204
206k
  }
205
206
206k
  if (spvIsVulkanEnv(_.context()->target_env)) {
207
    // The following were all checked as pointer types above.
208
0
    spv::StorageClass sc = spv::StorageClass::Uniform;
209
0
    const auto type = _.FindDef(target->type_id());
210
0
    if (type && type->operands().size() > 2) {
211
0
      sc = type->GetOperandAs<spv::StorageClass>(1);
212
0
    }
213
0
    switch (dec) {
214
0
      case spv::Decoration::Location:
215
0
      case spv::Decoration::Component:
216
        // Location is used for input, output, tile image, and ray tracing
217
        // stages.
218
0
        if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output &&
219
0
            sc != spv::StorageClass::RayPayloadKHR &&
220
0
            sc != spv::StorageClass::IncomingRayPayloadKHR &&
221
0
            sc != spv::StorageClass::HitAttributeKHR &&
222
0
            sc != spv::StorageClass::CallableDataKHR &&
223
0
            sc != spv::StorageClass::IncomingCallableDataKHR &&
224
0
            sc != spv::StorageClass::ShaderRecordBufferKHR &&
225
0
            sc != spv::StorageClass::HitObjectAttributeNV &&
226
0
            sc != spv::StorageClass::HitObjectAttributeEXT &&
227
0
            sc != spv::StorageClass::TileImageEXT) {
228
0
          return _.diag(SPV_ERROR_INVALID_ID, target)
229
0
                 << _.VkErrorID(6672) << _.SpvDecorationString(dec)
230
0
                 << " decoration must not be applied to this storage class";
231
0
        }
232
0
        break;
233
0
      case spv::Decoration::Index:
234
        // Langauge from SPIR-V definition of Index
235
0
        if (sc != spv::StorageClass::Output) {
236
0
          return fail(0) << "must be in the Output storage class";
237
0
        }
238
0
        break;
239
0
      case spv::Decoration::Binding:
240
0
      case spv::Decoration::DescriptorSet:
241
0
        if (sc != spv::StorageClass::StorageBuffer &&
242
0
            sc != spv::StorageClass::Uniform &&
243
0
            sc != spv::StorageClass::UniformConstant &&
244
0
            sc != spv::StorageClass::TileAttachmentQCOM) {
245
0
          return fail(6491) << "must be in the StorageBuffer, Uniform, or "
246
0
                               "UniformConstant storage class";
247
0
        }
248
0
        break;
249
0
      case spv::Decoration::InputAttachmentIndex:
250
0
        if (sc != spv::StorageClass::UniformConstant) {
251
0
          return fail(6678) << "must be in the UniformConstant storage class";
252
0
        }
253
0
        break;
254
0
      case spv::Decoration::Flat:
255
0
      case spv::Decoration::NoPerspective:
256
0
      case spv::Decoration::Centroid:
257
0
      case spv::Decoration::Sample:
258
0
        if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output) {
259
0
          return fail(4670) << "storage class must be Input or Output";
260
0
        }
261
0
        break;
262
0
      case spv::Decoration::PerVertexKHR:
263
0
        if (sc != spv::StorageClass::Input) {
264
0
          return fail(6777) << "storage class must be Input";
265
0
        }
266
0
        break;
267
0
      default:
268
0
        break;
269
0
    }
270
0
  }
271
206k
  return SPV_SUCCESS;
272
206k
}
273
274
474k
spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
275
474k
  const auto decoration = inst->GetOperandAs<spv::Decoration>(1);
276
474k
  const auto target_id = inst->GetOperandAs<uint32_t>(0);
277
474k
  const auto target = _.FindDef(target_id);
278
474k
  if (!target) {
279
0
    return _.diag(SPV_ERROR_INVALID_ID, inst) << "target is not defined";
280
0
  }
281
282
474k
  if (spvIsVulkanEnv(_.context()->target_env)) {
283
0
    if ((decoration == spv::Decoration::GLSLShared) ||
284
0
        (decoration == spv::Decoration::GLSLPacked)) {
285
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
286
0
             << _.VkErrorID(4669) << "OpDecorate decoration '"
287
0
             << _.SpvDecorationString(decoration)
288
0
             << "' is not valid for the Vulkan execution environment.";
289
0
    }
290
0
  }
291
292
474k
  if (decoration == spv::Decoration::FPFastMathMode) {
293
3.87k
    if (_.HasDecoration(target_id, spv::Decoration::NoContraction)) {
294
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
295
0
             << "FPFastMathMode and NoContraction cannot decorate the same "
296
0
                "target";
297
0
    }
298
3.87k
    auto mask = inst->GetOperandAs<spv::FPFastMathModeMask>(2);
299
3.87k
    if ((mask & spv::FPFastMathModeMask::AllowTransform) !=
300
3.87k
            spv::FPFastMathModeMask::MaskNone &&
301
0
        ((mask & (spv::FPFastMathModeMask::AllowContract |
302
0
                  spv::FPFastMathModeMask::AllowReassoc)) !=
303
0
         (spv::FPFastMathModeMask::AllowContract |
304
0
          spv::FPFastMathModeMask::AllowReassoc))) {
305
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
306
0
             << "AllowReassoc and AllowContract must be specified when "
307
0
                "AllowTransform is specified";
308
0
    }
309
3.87k
  }
310
311
  // This is checked from both sides since we register decorations as we go.
312
474k
  if (decoration == spv::Decoration::NoContraction) {
313
20.8k
    if (_.HasDecoration(target_id, spv::Decoration::FPFastMathMode)) {
314
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
315
0
             << "FPFastMathMode and NoContraction cannot decorate the same "
316
0
                "target";
317
0
    }
318
20.8k
  }
319
320
474k
  if (DecorationTakesIdParameters(decoration)) {
321
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
322
0
           << "Decorations taking ID parameters may not be used with "
323
0
              "OpDecorateId";
324
0
  }
325
326
474k
  if (target->opcode() != spv::Op::OpDecorationGroup) {
327
206k
    if (IsMemberDecorationOnly(decoration)) {
328
9
      return _.diag(SPV_ERROR_INVALID_ID, inst)
329
9
             << _.SpvDecorationString(decoration)
330
9
             << " can only be applied to structure members";
331
9
    }
332
333
206k
    if (auto error = ValidateDecorationTarget(_, decoration, inst, target)) {
334
122
      return error;
335
122
    }
336
206k
  }
337
338
  // TODO: Add validations for all decorations.
339
474k
  return SPV_SUCCESS;
340
474k
}
341
342
0
spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
343
0
  const auto target_id = inst->GetOperandAs<uint32_t>(0);
344
0
  const auto target = _.FindDef(target_id);
345
0
  if (target && spv::Op::OpDecorationGroup == target->opcode()) {
346
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
347
0
           << "OpMemberDecorate Target <id> " << _.getIdName(target_id)
348
0
           << " must not be an OpDecorationGroup instruction.";
349
0
  }
350
351
0
  const auto decoration = inst->GetOperandAs<spv::Decoration>(1);
352
0
  if (!DecorationTakesIdParameters(decoration)) {
353
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
354
0
           << "Decorations that don't take ID parameters may not be used with "
355
0
              "OpDecorateId";
356
0
  }
357
358
0
  if (decoration == spv::Decoration::ArrayStrideIdEXT) {
359
0
    if (target->opcode() != spv::Op::OpTypeArray &&
360
0
        target->opcode() != spv::Op::OpTypeRuntimeArray) {
361
      // ArrayStrideIdEXT is suppose to identical to ArrayStride, which would
362
      // allow it to be a OpTypePointer/OpTypeUntypedPointerKHR
363
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
364
0
             << "ArrayStrideIdEXT decoration must only be applied to array "
365
0
                "types.";
366
0
    } else {
367
0
      const uint32_t operand_id = inst->GetOperandAs<uint32_t>(2);
368
0
      if (!_.IsIntScalarType(_.GetTypeId(operand_id), 32)) {
369
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
370
0
               << "ArrayStrideIdEXT extra operand must be a 32-bit int "
371
0
                  "scalar type.";
372
0
      }
373
374
      // Even if spec constant, validation layers will test when frozen
375
0
      uint64_t stride_value = 0;
376
0
      if (_.EvalConstantValUint64(operand_id, &stride_value)) {
377
0
        if (stride_value == 0) {
378
0
          return _.diag(SPV_ERROR_INVALID_ID, inst)
379
0
                 << "ArrayStrideIdEXT contains a stride of zero.";
380
0
        }
381
0
      }
382
383
      // Strip array and should be the descriptor type
384
0
      const uint32_t element_type =
385
0
          _.FindDef(target_id)->GetOperandAs<uint32_t>(1);
386
0
      if (!_.IsDescriptorType(element_type)) {
387
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
388
0
               << "ArrayStrideIdEXT decoration must only be applied to"
389
0
               << " array type containing a Descriptor type.";
390
0
      }
391
0
    }
392
0
  }
393
394
0
  for (uint32_t i = 2; i < inst->operands().size(); ++i) {
395
0
    const auto param_id = inst->GetOperandAs<uint32_t>(i);
396
0
    const auto param = _.FindDef(param_id);
397
398
    // Both target and param are elements of ordered_instructions we can
399
    // determine their relative positions in the SPIR-V module by comparing
400
    // pointers.
401
0
    if (target <= param) {
402
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
403
0
             << "Parameter <ID> " << _.getIdName(param_id)
404
0
             << " must appear earlier in the binary than the target";
405
0
    }
406
0
  }
407
408
  // No member decorations take id parameters, so we don't bother checking if
409
  // we are using a member only decoration here.
410
411
  // TODO: Add validations for these decorations.
412
  // UniformId is covered elsewhere.
413
0
  return SPV_SUCCESS;
414
0
}
415
416
spv_result_t ValidateMemberDecorate(ValidationState_t& _,
417
7.66k
                                    const Instruction* inst) {
418
7.66k
  const auto struct_type_id = inst->GetOperandAs<uint32_t>(0);
419
7.66k
  const auto struct_type = _.FindDef(struct_type_id);
420
7.66k
  const bool is_mem_dec_id_inst =
421
7.66k
      (inst->opcode() == spv::Op::OpMemberDecorateIdEXT);
422
7.66k
  if (!struct_type || spv::Op::OpTypeStruct != struct_type->opcode()) {
423
12
    return _.diag(SPV_ERROR_INVALID_ID, inst)
424
12
           << (is_mem_dec_id_inst ? "OpMemberDecorateIdEXT"
425
12
                                  : "OpMemberDecorate")
426
12
           << " Structure type <id> " << _.getIdName(struct_type_id)
427
12
           << " is not a struct type.";
428
12
  }
429
7.64k
  const auto member = inst->GetOperandAs<uint32_t>(1);
430
7.64k
  const auto member_count =
431
7.64k
      static_cast<uint32_t>(struct_type->words().size() - 2);
432
7.64k
  if (member_count <= member) {
433
17
    return _.diag(SPV_ERROR_INVALID_ID, inst)
434
17
           << "Index " << member << " provided in "
435
17
           << (is_mem_dec_id_inst ? "OpMemberDecorateIdEXT"
436
17
                                  : "OpMemberDecorate")
437
17
           << " for struct <id> " << _.getIdName(struct_type_id)
438
17
           << " is out of bounds. The structure has " << member_count
439
17
           << " members. Largest valid index is " << member_count - 1 << ".";
440
17
  }
441
442
7.63k
  const auto decoration = inst->GetOperandAs<spv::Decoration>(2);
443
7.63k
  if (is_mem_dec_id_inst) {
444
0
    if (decoration != spv::Decoration::OffsetIdEXT) {
445
0
      if (decoration == spv::Decoration::ArrayStrideIdEXT) {
446
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
447
0
               << "ArrayStrideIdEXT could only be directly applied"
448
0
               << " to array type using OpDecorateId.";
449
0
      } else {
450
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
451
0
               << "Decoration operand could only be OffsetIdEXT.";
452
0
      }
453
0
    }
454
455
0
    const auto is_descriptor_type = [&_](const Instruction* type_inst) {
456
0
      return _.IsDescriptorType(type_inst->opcode());
457
0
    };
458
459
    // recursively scans the struct to find if anything has a descriptor type,
460
    // must be at least 1
461
0
    if (decoration == spv::Decoration::OffsetIdEXT) {
462
0
      const uint32_t operand_id = inst->GetOperandAs<uint32_t>(3);
463
0
      if (!_.IsIntScalarType(_.GetTypeId(operand_id), 32)) {
464
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
465
0
               << "OffsetIdEXT extra operand must be a 32-bit int scalar type.";
466
0
      }
467
0
      if (!_.ContainsType(struct_type_id, is_descriptor_type, true)) {
468
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
469
0
               << "OffsetIdEXT decoration in MemberDecorateIdEXT must only be "
470
0
                  "applied to members of structs where the struct contains "
471
0
                  "descriptor types.";
472
0
      }
473
0
    }
474
475
0
    for (uint32_t elem_idx = 3; elem_idx < inst->operands().size();
476
0
         elem_idx++) {
477
0
      if (_.FindDef(inst->GetOperandAs<uint32_t>(elem_idx)) > struct_type) {
478
0
        return _.diag(SPV_ERROR_INVALID_ID, inst)
479
0
               << "All <id> Extra Operands must appear before Structure Type.";
480
0
      }
481
0
    }
482
0
  }
483
484
7.63k
  if (IsNotMemberDecoration(decoration)) {
485
48
    return _.diag(SPV_ERROR_INVALID_ID, inst)
486
48
           << _.SpvDecorationString(decoration)
487
48
           << " cannot be applied to structure members";
488
48
  }
489
490
7.58k
  return SPV_SUCCESS;
491
7.63k
}
492
493
spv_result_t ValidateDecorationGroup(ValidationState_t& _,
494
6.32k
                                     const Instruction* inst) {
495
6.32k
  const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
496
6.32k
  const auto decoration_group = _.FindDef(decoration_group_id);
497
10.8M
  for (auto pair : decoration_group->uses()) {
498
10.8M
    auto use = pair.first;
499
10.8M
    if (use->opcode() != spv::Op::OpDecorate &&
500
10.6M
        use->opcode() != spv::Op::OpGroupDecorate &&
501
1.64M
        use->opcode() != spv::Op::OpGroupMemberDecorate &&
502
400
        use->opcode() != spv::Op::OpName && !use->IsNonSemantic()) {
503
9
      return _.diag(SPV_ERROR_INVALID_ID, inst)
504
9
             << "Result id of OpDecorationGroup can only "
505
9
             << "be targeted by OpName, OpGroupDecorate, "
506
9
             << "OpDecorate, OpDecorateId, and OpGroupMemberDecorate";
507
9
    }
508
10.8M
  }
509
6.31k
  return SPV_SUCCESS;
510
6.32k
}
511
512
spv_result_t ValidateGroupDecorate(ValidationState_t& _,
513
8.97M
                                   const Instruction* inst) {
514
8.97M
  const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
515
8.97M
  auto decoration_group = _.FindDef(decoration_group_id);
516
8.97M
  if (!decoration_group ||
517
8.97M
      spv::Op::OpDecorationGroup != decoration_group->opcode()) {
518
4
    return _.diag(SPV_ERROR_INVALID_ID, inst)
519
4
           << "OpGroupDecorate Decoration group <id> "
520
4
           << _.getIdName(decoration_group_id) << " is not a decoration group.";
521
4
  }
522
8.98M
  for (unsigned i = 1; i < inst->operands().size(); ++i) {
523
16.7k
    auto target_id = inst->GetOperandAs<uint32_t>(i);
524
16.7k
    auto target = _.FindDef(target_id);
525
16.7k
    if (!target || target->opcode() == spv::Op::OpDecorationGroup) {
526
17
      return _.diag(SPV_ERROR_INVALID_ID, inst)
527
17
             << "OpGroupDecorate may not target OpDecorationGroup <id> "
528
17
             << _.getIdName(target_id);
529
17
    }
530
16.7k
  }
531
8.97M
  return SPV_SUCCESS;
532
8.97M
}
533
534
spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _,
535
1.64M
                                         const Instruction* inst) {
536
1.64M
  const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
537
1.64M
  const auto decoration_group = _.FindDef(decoration_group_id);
538
1.64M
  if (!decoration_group ||
539
1.64M
      spv::Op::OpDecorationGroup != decoration_group->opcode()) {
540
4
    return _.diag(SPV_ERROR_INVALID_ID, inst)
541
4
           << "OpGroupMemberDecorate Decoration group <id> "
542
4
           << _.getIdName(decoration_group_id) << " is not a decoration group.";
543
4
  }
544
  // Grammar checks ensures that the number of arguments to this instruction
545
  // is an odd number: 1 decoration group + (id,literal) pairs.
546
1.65M
  for (size_t i = 1; i + 1 < inst->operands().size(); i += 2) {
547
8.78k
    const uint32_t struct_id = inst->GetOperandAs<uint32_t>(i);
548
8.78k
    const uint32_t index = inst->GetOperandAs<uint32_t>(i + 1);
549
8.78k
    auto struct_instr = _.FindDef(struct_id);
550
8.78k
    if (!struct_instr || spv::Op::OpTypeStruct != struct_instr->opcode()) {
551
7
      return _.diag(SPV_ERROR_INVALID_ID, inst)
552
7
             << "OpGroupMemberDecorate Structure type <id> "
553
7
             << _.getIdName(struct_id) << " is not a struct type.";
554
7
    }
555
8.77k
    const uint32_t num_struct_members =
556
8.77k
        static_cast<uint32_t>(struct_instr->words().size() - 2);
557
8.77k
    if (index >= num_struct_members) {
558
82
      return _.diag(SPV_ERROR_INVALID_ID, inst)
559
82
             << "Index " << index
560
82
             << " provided in OpGroupMemberDecorate for struct <id> "
561
82
             << _.getIdName(struct_id)
562
82
             << " is out of bounds. The structure has " << num_struct_members
563
82
             << " members. Largest valid index is " << num_struct_members - 1
564
82
             << ".";
565
82
    }
566
8.77k
  }
567
1.64M
  return SPV_SUCCESS;
568
1.64M
}
569
570
// Registers necessary decoration(s) for the appropriate IDs based on the
571
// instruction.
572
spv_result_t RegisterDecorations(ValidationState_t& _,
573
14.5M
                                 const Instruction* inst) {
574
14.5M
  switch (inst->opcode()) {
575
474k
    case spv::Op::OpDecorate:
576
474k
    case spv::Op::OpDecorateId: {
577
474k
      const uint32_t target_id = inst->word(1);
578
474k
      const spv::Decoration dec_type =
579
474k
          static_cast<spv::Decoration>(inst->word(2));
580
474k
      std::vector<uint32_t> dec_params;
581
474k
      if (inst->words().size() > 3) {
582
246k
        dec_params.insert(dec_params.end(), inst->words().begin() + 3,
583
246k
                          inst->words().end());
584
246k
      }
585
474k
      _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params));
586
474k
      break;
587
474k
    }
588
7.58k
    case spv::Op::OpMemberDecorate:
589
7.58k
    case spv::Op::OpMemberDecorateIdEXT: {
590
7.58k
      const uint32_t struct_id = inst->word(1);
591
7.58k
      const uint32_t index = inst->word(2);
592
7.58k
      const spv::Decoration dec_type =
593
7.58k
          static_cast<spv::Decoration>(inst->word(3));
594
7.58k
      std::vector<uint32_t> dec_params;
595
7.58k
      if (inst->words().size() > 4) {
596
5.30k
        dec_params.insert(dec_params.end(), inst->words().begin() + 4,
597
5.30k
                          inst->words().end());
598
5.30k
      }
599
7.58k
      _.RegisterDecorationForId(struct_id,
600
7.58k
                                Decoration(dec_type, dec_params, index));
601
7.58k
      break;
602
7.58k
    }
603
6.31k
    case spv::Op::OpDecorationGroup: {
604
      // We don't need to do anything right now. Assigning decorations to groups
605
      // will be taken care of via OpGroupDecorate.
606
6.31k
      break;
607
7.58k
    }
608
8.97M
    case spv::Op::OpGroupDecorate: {
609
      // Word 1 is the group <id>. All subsequent words are target <id>s that
610
      // are going to be decorated with the decorations.
611
8.97M
      const uint32_t decoration_group_id = inst->word(1);
612
8.97M
      std::set<Decoration>& group_decorations =
613
8.97M
          _.id_decorations(decoration_group_id);
614
8.98M
      for (size_t i = 2; i < inst->words().size(); ++i) {
615
16.7k
        const uint32_t target_id = inst->word(i);
616
16.7k
        _.RegisterDecorationsForId(target_id, group_decorations.begin(),
617
16.7k
                                   group_decorations.end());
618
16.7k
      }
619
8.97M
      break;
620
7.58k
    }
621
1.64M
    case spv::Op::OpGroupMemberDecorate: {
622
      // Word 1 is the Decoration Group <id> followed by (struct<id>,literal)
623
      // pairs. All decorations of the group should be applied to all the struct
624
      // members that are specified in the instructions.
625
1.64M
      const uint32_t decoration_group_id = inst->word(1);
626
1.64M
      std::set<Decoration>& group_decorations =
627
1.64M
          _.id_decorations(decoration_group_id);
628
      // Grammar checks ensures that the number of arguments to this instruction
629
      // is an odd number: 1 decoration group + (id,literal) pairs.
630
1.65M
      for (size_t i = 2; i + 1 < inst->words().size(); i = i + 2) {
631
8.68k
        const uint32_t struct_id = inst->word(i);
632
8.68k
        const uint32_t index = inst->word(i + 1);
633
        // ID validation phase ensures this is in fact a struct instruction and
634
        // that the index is not out of bound.
635
8.68k
        _.RegisterDecorationsForStructMember(struct_id, index,
636
8.68k
                                             group_decorations.begin(),
637
8.68k
                                             group_decorations.end());
638
8.68k
      }
639
1.64M
      break;
640
7.58k
    }
641
3.44M
    default:
642
3.44M
      break;
643
14.5M
  }
644
14.5M
  return SPV_SUCCESS;
645
14.5M
}
646
647
}  // namespace
648
649
14.5M
spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) {
650
14.5M
  switch (inst->opcode()) {
651
474k
    case spv::Op::OpDecorate:
652
474k
      if (auto error = ValidateDecorate(_, inst)) return error;
653
474k
      break;
654
474k
    case spv::Op::OpDecorateId:
655
0
      if (auto error = ValidateDecorateId(_, inst)) return error;
656
0
      break;
657
    // TODO(dneto): spv::Op::OpDecorateStringGOOGLE
658
    // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253
659
7.66k
    case spv::Op::OpMemberDecorate:
660
7.66k
    case spv::Op::OpMemberDecorateIdEXT:
661
7.66k
      if (auto error = ValidateMemberDecorate(_, inst)) return error;
662
7.58k
      break;
663
7.58k
    case spv::Op::OpDecorationGroup:
664
6.32k
      if (auto error = ValidateDecorationGroup(_, inst)) return error;
665
6.31k
      break;
666
8.97M
    case spv::Op::OpGroupDecorate:
667
8.97M
      if (auto error = ValidateGroupDecorate(_, inst)) return error;
668
8.97M
      break;
669
8.97M
    case spv::Op::OpGroupMemberDecorate:
670
1.64M
      if (auto error = ValidateGroupMemberDecorate(_, inst)) return error;
671
1.64M
      break;
672
3.44M
    default:
673
3.44M
      break;
674
14.5M
  }
675
676
  // In order to validate decoration rules, we need to know all the decorations
677
  // that are applied to any given <id>.
678
14.5M
  RegisterDecorations(_, inst);
679
680
14.5M
  return SPV_SUCCESS;
681
14.5M
}
682
683
}  // namespace val
684
}  // namespace spvtools