Coverage Report

Created: 2025-12-31 06:15

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
696k
bool DecorationTakesIdParameters(spv::Decoration type) {
30
696k
  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
      return true;
41
696k
    default:
42
696k
      break;
43
696k
  }
44
696k
  return false;
45
696k
}
46
47
253k
bool IsMemberDecorationOnly(spv::Decoration dec) {
48
253k
  switch (dec) {
49
3
    case spv::Decoration::RowMajor:
50
7
    case spv::Decoration::ColMajor:
51
8
    case spv::Decoration::MatrixStride:
52
      // SPIR-V spec bug? Offset is generated on variables when dealing with
53
      // transform feedback.
54
      // case spv::Decoration::Offset:
55
8
      return true;
56
253k
    default:
57
253k
      break;
58
253k
  }
59
253k
  return false;
60
253k
}
61
62
7.21k
bool IsNotMemberDecoration(spv::Decoration dec) {
63
7.21k
  switch (dec) {
64
3
    case spv::Decoration::SpecId:
65
6
    case spv::Decoration::Block:
66
9
    case spv::Decoration::BufferBlock:
67
12
    case spv::Decoration::ArrayStride:
68
15
    case spv::Decoration::GLSLShared:
69
18
    case spv::Decoration::GLSLPacked:
70
18
    case spv::Decoration::CPacked:
71
    // TODO: https://github.com/KhronosGroup/glslang/issues/703:
72
    // glslang applies Restrict to structure members.
73
    // case spv::Decoration::Restrict:
74
21
    case spv::Decoration::Aliased:
75
21
    case spv::Decoration::Constant:
76
24
    case spv::Decoration::Uniform:
77
24
    case spv::Decoration::UniformId:
78
24
    case spv::Decoration::SaturatedConversion:
79
27
    case spv::Decoration::Index:
80
30
    case spv::Decoration::Binding:
81
33
    case spv::Decoration::DescriptorSet:
82
35
    case spv::Decoration::FuncParamAttr:
83
38
    case spv::Decoration::FPRoundingMode:
84
40
    case spv::Decoration::FPFastMathMode:
85
40
    case spv::Decoration::LinkageAttributes:
86
43
    case spv::Decoration::NoContraction:
87
44
    case spv::Decoration::InputAttachmentIndex:
88
46
    case spv::Decoration::Alignment:
89
46
    case spv::Decoration::MaxByteOffset:
90
46
    case spv::Decoration::AlignmentId:
91
46
    case spv::Decoration::MaxByteOffsetId:
92
46
    case spv::Decoration::NoSignedWrap:
93
46
    case spv::Decoration::NoUnsignedWrap:
94
46
    case spv::Decoration::NonUniform:
95
46
    case spv::Decoration::RestrictPointer:
96
46
    case spv::Decoration::AliasedPointer:
97
46
    case spv::Decoration::CounterBuffer:
98
46
      return true;
99
7.16k
    default:
100
7.16k
      break;
101
7.21k
  }
102
7.16k
  return false;
103
7.21k
}
104
105
spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
106
                                      const Instruction* inst,
107
253k
                                      const Instruction* target) {
108
253k
  auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream {
109
119
    DiagnosticStream ds = std::move(
110
119
        _.diag(SPV_ERROR_INVALID_ID, inst)
111
119
        << _.VkErrorID(vuid) << _.SpvDecorationString(dec)
112
119
        << " decoration on target <id> " << _.getIdName(target->id()) << " ");
113
119
    return ds;
114
119
  };
115
253k
  switch (dec) {
116
56
    case spv::Decoration::SpecId:
117
56
      if (!spvOpcodeIsScalarSpecConstant(target->opcode())) {
118
10
        return fail(0) << "must be a scalar specialization constant";
119
10
      }
120
46
      break;
121
3.56k
    case spv::Decoration::Block:
122
4.60k
    case spv::Decoration::BufferBlock:
123
4.88k
    case spv::Decoration::GLSLShared:
124
5.45k
    case spv::Decoration::GLSLPacked:
125
5.54k
    case spv::Decoration::CPacked:
126
5.54k
      if (target->opcode() != spv::Op::OpTypeStruct) {
127
4
        return fail(0) << "must be a structure type";
128
4
      }
129
5.54k
      break;
130
5.54k
    case spv::Decoration::ArrayStride:
131
1.26k
      if (target->opcode() != spv::Op::OpTypeArray &&
132
227
          target->opcode() != spv::Op::OpTypeRuntimeArray &&
133
99
          target->opcode() != spv::Op::OpTypePointer &&
134
11
          target->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
135
11
        return fail(0) << "must be an array or pointer type";
136
11
      }
137
1.25k
      break;
138
3.37k
    case spv::Decoration::BuiltIn:
139
3.37k
      if (target->opcode() != spv::Op::OpVariable &&
140
236
          target->opcode() != spv::Op::OpUntypedVariableKHR &&
141
236
          !spvOpcodeIsConstant(target->opcode())) {
142
5
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
143
5
               << "BuiltIns can only target variables, structure members or "
144
5
                  "constants";
145
5
      }
146
3.37k
      if (_.HasCapability(spv::Capability::Shader) &&
147
3.34k
          inst->GetOperandAs<spv::BuiltIn>(2) == spv::BuiltIn::WorkgroupSize) {
148
232
        if (!spvOpcodeIsConstant(target->opcode())) {
149
5
          return fail(0) << "must be a constant for WorkgroupSize";
150
5
        }
151
3.14k
      } else if (target->opcode() != spv::Op::OpVariable &&
152
4
                 target->opcode() != spv::Op::OpUntypedVariableKHR) {
153
4
        return fail(0) << "must be a variable";
154
4
      }
155
3.36k
      break;
156
3.36k
    case spv::Decoration::NoPerspective:
157
28.8k
    case spv::Decoration::Flat:
158
28.8k
    case spv::Decoration::Patch:
159
33.0k
    case spv::Decoration::Centroid:
160
33.2k
    case spv::Decoration::Sample:
161
46.1k
    case spv::Decoration::Restrict:
162
126k
    case spv::Decoration::Aliased:
163
131k
    case spv::Decoration::Volatile:
164
133k
    case spv::Decoration::Coherent:
165
133k
    case spv::Decoration::NonWritable:
166
133k
    case spv::Decoration::NonReadable:
167
133k
    case spv::Decoration::XfbBuffer:
168
133k
    case spv::Decoration::XfbStride:
169
134k
    case spv::Decoration::Component:
170
134k
    case spv::Decoration::Stream:
171
134k
    case spv::Decoration::RestrictPointer:
172
134k
    case spv::Decoration::AliasedPointer:
173
134k
    case spv::Decoration::PerPrimitiveEXT:
174
134k
      if (target->opcode() != spv::Op::OpVariable &&
175
85
          target->opcode() != spv::Op::OpUntypedVariableKHR &&
176
85
          target->opcode() != spv::Op::OpFunctionParameter &&
177
55
          target->opcode() != spv::Op::OpRawAccessChainNV) {
178
55
        return fail(0) << "must be a memory object declaration";
179
55
      }
180
134k
      if (!_.IsPointerType(target->type_id())) {
181
3
        return fail(0) << "must be a pointer type";
182
3
      }
183
134k
      break;
184
134k
    case spv::Decoration::Invariant:
185
1.19k
    case spv::Decoration::Constant:
186
7.06k
    case spv::Decoration::Location:
187
8.58k
    case spv::Decoration::Index:
188
13.1k
    case spv::Decoration::Binding:
189
16.9k
    case spv::Decoration::DescriptorSet:
190
16.9k
    case spv::Decoration::InputAttachmentIndex:
191
16.9k
      if (target->opcode() != spv::Op::OpVariable &&
192
27
          target->opcode() != spv::Op::OpUntypedVariableKHR) {
193
27
        return fail(0) << "must be a variable";
194
27
      }
195
16.9k
      break;
196
91.7k
    default:
197
91.7k
      break;
198
253k
  }
199
200
253k
  if (spvIsVulkanEnv(_.context()->target_env)) {
201
    // The following were all checked as pointer types above.
202
0
    spv::StorageClass sc = spv::StorageClass::Uniform;
203
0
    const auto type = _.FindDef(target->type_id());
204
0
    if (type && type->operands().size() > 2) {
205
0
      sc = type->GetOperandAs<spv::StorageClass>(1);
206
0
    }
207
0
    switch (dec) {
208
0
      case spv::Decoration::Location:
209
0
      case spv::Decoration::Component:
210
        // Location is used for input, output, tile image, and ray tracing
211
        // stages.
212
0
        if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output &&
213
0
            sc != spv::StorageClass::RayPayloadKHR &&
214
0
            sc != spv::StorageClass::IncomingRayPayloadKHR &&
215
0
            sc != spv::StorageClass::HitAttributeKHR &&
216
0
            sc != spv::StorageClass::CallableDataKHR &&
217
0
            sc != spv::StorageClass::IncomingCallableDataKHR &&
218
0
            sc != spv::StorageClass::ShaderRecordBufferKHR &&
219
0
            sc != spv::StorageClass::HitObjectAttributeNV &&
220
0
            sc != spv::StorageClass::HitObjectAttributeEXT &&
221
0
            sc != spv::StorageClass::TileImageEXT) {
222
0
          return _.diag(SPV_ERROR_INVALID_ID, target)
223
0
                 << _.VkErrorID(6672) << _.SpvDecorationString(dec)
224
0
                 << " decoration must not be applied to this storage class";
225
0
        }
226
0
        break;
227
0
      case spv::Decoration::Index:
228
        // Langauge from SPIR-V definition of Index
229
0
        if (sc != spv::StorageClass::Output) {
230
0
          return fail(0) << "must be in the Output storage class";
231
0
        }
232
0
        break;
233
0
      case spv::Decoration::Binding:
234
0
      case spv::Decoration::DescriptorSet:
235
0
        if (sc != spv::StorageClass::StorageBuffer &&
236
0
            sc != spv::StorageClass::Uniform &&
237
0
            sc != spv::StorageClass::UniformConstant &&
238
0
            sc != spv::StorageClass::TileAttachmentQCOM) {
239
0
          return fail(6491) << "must be in the StorageBuffer, Uniform, or "
240
0
                               "UniformConstant storage class";
241
0
        }
242
0
        break;
243
0
      case spv::Decoration::InputAttachmentIndex:
244
0
        if (sc != spv::StorageClass::UniformConstant) {
245
0
          return fail(6678) << "must be in the UniformConstant storage class";
246
0
        }
247
0
        break;
248
0
      case spv::Decoration::Flat:
249
0
      case spv::Decoration::NoPerspective:
250
0
      case spv::Decoration::Centroid:
251
0
      case spv::Decoration::Sample:
252
0
        if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output) {
253
0
          return fail(4670) << "storage class must be Input or Output";
254
0
        }
255
0
        break;
256
0
      case spv::Decoration::PerVertexKHR:
257
0
        if (sc != spv::StorageClass::Input) {
258
0
          return fail(6777) << "storage class must be Input";
259
0
        }
260
0
        break;
261
0
      default:
262
0
        break;
263
0
    }
264
0
  }
265
253k
  return SPV_SUCCESS;
266
253k
}
267
268
696k
spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
269
696k
  const auto decoration = inst->GetOperandAs<spv::Decoration>(1);
270
696k
  const auto target_id = inst->GetOperandAs<uint32_t>(0);
271
696k
  const auto target = _.FindDef(target_id);
272
696k
  if (!target) {
273
0
    return _.diag(SPV_ERROR_INVALID_ID, inst) << "target is not defined";
274
0
  }
275
276
696k
  if (spvIsVulkanEnv(_.context()->target_env)) {
277
0
    if ((decoration == spv::Decoration::GLSLShared) ||
278
0
        (decoration == spv::Decoration::GLSLPacked)) {
279
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
280
0
             << _.VkErrorID(4669) << "OpDecorate decoration '"
281
0
             << _.SpvDecorationString(decoration)
282
0
             << "' is not valid for the Vulkan execution environment.";
283
0
    }
284
0
  }
285
286
696k
  if (decoration == spv::Decoration::FPFastMathMode) {
287
3.05k
    if (_.HasDecoration(target_id, spv::Decoration::NoContraction)) {
288
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
289
0
             << "FPFastMathMode and NoContraction cannot decorate the same "
290
0
                "target";
291
0
    }
292
3.05k
    auto mask = inst->GetOperandAs<spv::FPFastMathModeMask>(2);
293
3.05k
    if ((mask & spv::FPFastMathModeMask::AllowTransform) !=
294
3.05k
            spv::FPFastMathModeMask::MaskNone &&
295
0
        ((mask & (spv::FPFastMathModeMask::AllowContract |
296
0
                  spv::FPFastMathModeMask::AllowReassoc)) !=
297
0
         (spv::FPFastMathModeMask::AllowContract |
298
0
          spv::FPFastMathModeMask::AllowReassoc))) {
299
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
300
0
             << "AllowReassoc and AllowContract must be specified when "
301
0
                "AllowTransform is specified";
302
0
    }
303
3.05k
  }
304
305
  // This is checked from both sides since we register decorations as we go.
306
696k
  if (decoration == spv::Decoration::NoContraction) {
307
41.0k
    if (_.HasDecoration(target_id, spv::Decoration::FPFastMathMode)) {
308
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
309
0
             << "FPFastMathMode and NoContraction cannot decorate the same "
310
0
                "target";
311
0
    }
312
41.0k
  }
313
314
696k
  if (DecorationTakesIdParameters(decoration)) {
315
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
316
0
           << "Decorations taking ID parameters may not be used with "
317
0
              "OpDecorateId";
318
0
  }
319
320
696k
  if (target->opcode() != spv::Op::OpDecorationGroup) {
321
253k
    if (IsMemberDecorationOnly(decoration)) {
322
8
      return _.diag(SPV_ERROR_INVALID_ID, inst)
323
8
             << _.SpvDecorationString(decoration)
324
8
             << " can only be applied to structure members";
325
8
    }
326
327
253k
    if (auto error = ValidateDecorationTarget(_, decoration, inst, target)) {
328
124
      return error;
329
124
    }
330
253k
  }
331
332
  // TODO: Add validations for all decorations.
333
696k
  return SPV_SUCCESS;
334
696k
}
335
336
0
spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
337
0
  const auto target_id = inst->GetOperandAs<uint32_t>(0);
338
0
  const auto target = _.FindDef(target_id);
339
0
  if (target && spv::Op::OpDecorationGroup == target->opcode()) {
340
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
341
0
           << "OpMemberDecorate Target <id> " << _.getIdName(target_id)
342
0
           << " must not be an OpDecorationGroup instruction.";
343
0
  }
344
345
0
  const auto decoration = inst->GetOperandAs<spv::Decoration>(1);
346
0
  if (!DecorationTakesIdParameters(decoration)) {
347
0
    return _.diag(SPV_ERROR_INVALID_ID, inst)
348
0
           << "Decorations that don't take ID parameters may not be used with "
349
0
              "OpDecorateId";
350
0
  }
351
352
0
  for (uint32_t i = 2; i < inst->operands().size(); ++i) {
353
0
    const auto param_id = inst->GetOperandAs<uint32_t>(i);
354
0
    const auto param = _.FindDef(param_id);
355
356
    // Both target and param are elements of ordered_instructions we can
357
    // determine their relative positions in the SPIR-V module by comparing
358
    // pointers.
359
0
    if (target <= param) {
360
0
      return _.diag(SPV_ERROR_INVALID_ID, inst)
361
0
             << "Parameter <ID> " << _.getIdName(param_id)
362
0
             << " must appear earlier in the binary than the target";
363
0
    }
364
0
  }
365
366
  // No member decorations take id parameters, so we don't bother checking if
367
  // we are using a member only decoration here.
368
369
  // TODO: Add validations for these decorations.
370
  // UniformId is covered elsewhere.
371
0
  return SPV_SUCCESS;
372
0
}
373
374
spv_result_t ValidateMemberDecorate(ValidationState_t& _,
375
7.28k
                                    const Instruction* inst) {
376
7.28k
  const auto struct_type_id = inst->GetOperandAs<uint32_t>(0);
377
7.28k
  const auto struct_type = _.FindDef(struct_type_id);
378
7.28k
  if (!struct_type || spv::Op::OpTypeStruct != struct_type->opcode()) {
379
8
    return _.diag(SPV_ERROR_INVALID_ID, inst)
380
8
           << "OpMemberDecorate Structure type <id> "
381
8
           << _.getIdName(struct_type_id) << " is not a struct type.";
382
8
  }
383
7.27k
  const auto member = inst->GetOperandAs<uint32_t>(1);
384
7.27k
  const auto member_count =
385
7.27k
      static_cast<uint32_t>(struct_type->words().size() - 2);
386
7.27k
  if (member_count <= member) {
387
60
    return _.diag(SPV_ERROR_INVALID_ID, inst)
388
60
           << "Index " << member
389
60
           << " provided in OpMemberDecorate for struct <id> "
390
60
           << _.getIdName(struct_type_id)
391
60
           << " is out of bounds. The structure has " << member_count
392
60
           << " members. Largest valid index is " << member_count - 1 << ".";
393
60
  }
394
395
7.21k
  const auto decoration = inst->GetOperandAs<spv::Decoration>(2);
396
7.21k
  if (IsNotMemberDecoration(decoration)) {
397
46
    return _.diag(SPV_ERROR_INVALID_ID, inst)
398
46
           << _.SpvDecorationString(decoration)
399
46
           << " cannot be applied to structure members";
400
46
  }
401
402
7.16k
  return SPV_SUCCESS;
403
7.21k
}
404
405
spv_result_t ValidateDecorationGroup(ValidationState_t& _,
406
6.00k
                                     const Instruction* inst) {
407
6.00k
  const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
408
6.00k
  const auto decoration_group = _.FindDef(decoration_group_id);
409
9.68M
  for (auto pair : decoration_group->uses()) {
410
9.68M
    auto use = pair.first;
411
9.68M
    if (use->opcode() != spv::Op::OpDecorate &&
412
9.24M
        use->opcode() != spv::Op::OpGroupDecorate &&
413
1.50M
        use->opcode() != spv::Op::OpGroupMemberDecorate &&
414
349
        use->opcode() != spv::Op::OpName && !use->IsNonSemantic()) {
415
8
      return _.diag(SPV_ERROR_INVALID_ID, inst)
416
8
             << "Result id of OpDecorationGroup can only "
417
8
             << "be targeted by OpName, OpGroupDecorate, "
418
8
             << "OpDecorate, OpDecorateId, and OpGroupMemberDecorate";
419
8
    }
420
9.68M
  }
421
5.99k
  return SPV_SUCCESS;
422
6.00k
}
423
424
spv_result_t ValidateGroupDecorate(ValidationState_t& _,
425
7.73M
                                   const Instruction* inst) {
426
7.73M
  const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
427
7.73M
  auto decoration_group = _.FindDef(decoration_group_id);
428
7.73M
  if (!decoration_group ||
429
7.73M
      spv::Op::OpDecorationGroup != decoration_group->opcode()) {
430
3
    return _.diag(SPV_ERROR_INVALID_ID, inst)
431
3
           << "OpGroupDecorate Decoration group <id> "
432
3
           << _.getIdName(decoration_group_id) << " is not a decoration group.";
433
3
  }
434
7.75M
  for (unsigned i = 1; i < inst->operands().size(); ++i) {
435
16.7k
    auto target_id = inst->GetOperandAs<uint32_t>(i);
436
16.7k
    auto target = _.FindDef(target_id);
437
16.7k
    if (!target || target->opcode() == spv::Op::OpDecorationGroup) {
438
18
      return _.diag(SPV_ERROR_INVALID_ID, inst)
439
18
             << "OpGroupDecorate may not target OpDecorationGroup <id> "
440
18
             << _.getIdName(target_id);
441
18
    }
442
16.7k
  }
443
7.73M
  return SPV_SUCCESS;
444
7.73M
}
445
446
spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _,
447
1.50M
                                         const Instruction* inst) {
448
1.50M
  const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
449
1.50M
  const auto decoration_group = _.FindDef(decoration_group_id);
450
1.50M
  if (!decoration_group ||
451
1.50M
      spv::Op::OpDecorationGroup != decoration_group->opcode()) {
452
3
    return _.diag(SPV_ERROR_INVALID_ID, inst)
453
3
           << "OpGroupMemberDecorate Decoration group <id> "
454
3
           << _.getIdName(decoration_group_id) << " is not a decoration group.";
455
3
  }
456
  // Grammar checks ensures that the number of arguments to this instruction
457
  // is an odd number: 1 decoration group + (id,literal) pairs.
458
1.51M
  for (size_t i = 1; i + 1 < inst->operands().size(); i += 2) {
459
3.67k
    const uint32_t struct_id = inst->GetOperandAs<uint32_t>(i);
460
3.67k
    const uint32_t index = inst->GetOperandAs<uint32_t>(i + 1);
461
3.67k
    auto struct_instr = _.FindDef(struct_id);
462
3.67k
    if (!struct_instr || spv::Op::OpTypeStruct != struct_instr->opcode()) {
463
7
      return _.diag(SPV_ERROR_INVALID_ID, inst)
464
7
             << "OpGroupMemberDecorate Structure type <id> "
465
7
             << _.getIdName(struct_id) << " is not a struct type.";
466
7
    }
467
3.67k
    const uint32_t num_struct_members =
468
3.67k
        static_cast<uint32_t>(struct_instr->words().size() - 2);
469
3.67k
    if (index >= num_struct_members) {
470
47
      return _.diag(SPV_ERROR_INVALID_ID, inst)
471
47
             << "Index " << index
472
47
             << " provided in OpGroupMemberDecorate for struct <id> "
473
47
             << _.getIdName(struct_id)
474
47
             << " is out of bounds. The structure has " << num_struct_members
475
47
             << " members. Largest valid index is " << num_struct_members - 1
476
47
             << ".";
477
47
    }
478
3.67k
  }
479
1.50M
  return SPV_SUCCESS;
480
1.50M
}
481
482
// Registers necessary decoration(s) for the appropriate IDs based on the
483
// instruction.
484
spv_result_t RegisterDecorations(ValidationState_t& _,
485
13.1M
                                 const Instruction* inst) {
486
13.1M
  switch (inst->opcode()) {
487
696k
    case spv::Op::OpDecorate:
488
696k
    case spv::Op::OpDecorateId: {
489
696k
      const uint32_t target_id = inst->word(1);
490
696k
      const spv::Decoration dec_type =
491
696k
          static_cast<spv::Decoration>(inst->word(2));
492
696k
      std::vector<uint32_t> dec_params;
493
696k
      if (inst->words().size() > 3) {
494
367k
        dec_params.insert(dec_params.end(), inst->words().begin() + 3,
495
367k
                          inst->words().end());
496
367k
      }
497
696k
      _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params));
498
696k
      break;
499
696k
    }
500
7.16k
    case spv::Op::OpMemberDecorate: {
501
7.16k
      const uint32_t struct_id = inst->word(1);
502
7.16k
      const uint32_t index = inst->word(2);
503
7.16k
      const spv::Decoration dec_type =
504
7.16k
          static_cast<spv::Decoration>(inst->word(3));
505
7.16k
      std::vector<uint32_t> dec_params;
506
7.16k
      if (inst->words().size() > 4) {
507
4.99k
        dec_params.insert(dec_params.end(), inst->words().begin() + 4,
508
4.99k
                          inst->words().end());
509
4.99k
      }
510
7.16k
      _.RegisterDecorationForId(struct_id,
511
7.16k
                                Decoration(dec_type, dec_params, index));
512
7.16k
      break;
513
696k
    }
514
5.99k
    case spv::Op::OpDecorationGroup: {
515
      // We don't need to do anything right now. Assigning decorations to groups
516
      // will be taken care of via OpGroupDecorate.
517
5.99k
      break;
518
696k
    }
519
7.73M
    case spv::Op::OpGroupDecorate: {
520
      // Word 1 is the group <id>. All subsequent words are target <id>s that
521
      // are going to be decorated with the decorations.
522
7.73M
      const uint32_t decoration_group_id = inst->word(1);
523
7.73M
      std::set<Decoration>& group_decorations =
524
7.73M
          _.id_decorations(decoration_group_id);
525
7.75M
      for (size_t i = 2; i < inst->words().size(); ++i) {
526
16.7k
        const uint32_t target_id = inst->word(i);
527
16.7k
        _.RegisterDecorationsForId(target_id, group_decorations.begin(),
528
16.7k
                                   group_decorations.end());
529
16.7k
      }
530
7.73M
      break;
531
696k
    }
532
1.50M
    case spv::Op::OpGroupMemberDecorate: {
533
      // Word 1 is the Decoration Group <id> followed by (struct<id>,literal)
534
      // pairs. All decorations of the group should be applied to all the struct
535
      // members that are specified in the instructions.
536
1.50M
      const uint32_t decoration_group_id = inst->word(1);
537
1.50M
      std::set<Decoration>& group_decorations =
538
1.50M
          _.id_decorations(decoration_group_id);
539
      // Grammar checks ensures that the number of arguments to this instruction
540
      // is an odd number: 1 decoration group + (id,literal) pairs.
541
1.51M
      for (size_t i = 2; i + 1 < inst->words().size(); i = i + 2) {
542
3.61k
        const uint32_t struct_id = inst->word(i);
543
3.61k
        const uint32_t index = inst->word(i + 1);
544
        // ID validation phase ensures this is in fact a struct instruction and
545
        // that the index is not out of bound.
546
3.61k
        _.RegisterDecorationsForStructMember(struct_id, index,
547
3.61k
                                             group_decorations.begin(),
548
3.61k
                                             group_decorations.end());
549
3.61k
      }
550
1.50M
      break;
551
696k
    }
552
3.17M
    default:
553
3.17M
      break;
554
13.1M
  }
555
13.1M
  return SPV_SUCCESS;
556
13.1M
}
557
558
}  // namespace
559
560
13.1M
spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) {
561
13.1M
  switch (inst->opcode()) {
562
696k
    case spv::Op::OpDecorate:
563
696k
      if (auto error = ValidateDecorate(_, inst)) return error;
564
696k
      break;
565
696k
    case spv::Op::OpDecorateId:
566
0
      if (auto error = ValidateDecorateId(_, inst)) return error;
567
0
      break;
568
    // TODO(dneto): spv::Op::OpDecorateStringGOOGLE
569
    // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253
570
7.28k
    case spv::Op::OpMemberDecorate:
571
7.28k
      if (auto error = ValidateMemberDecorate(_, inst)) return error;
572
7.16k
      break;
573
7.16k
    case spv::Op::OpDecorationGroup:
574
6.00k
      if (auto error = ValidateDecorationGroup(_, inst)) return error;
575
5.99k
      break;
576
7.73M
    case spv::Op::OpGroupDecorate:
577
7.73M
      if (auto error = ValidateGroupDecorate(_, inst)) return error;
578
7.73M
      break;
579
7.73M
    case spv::Op::OpGroupMemberDecorate:
580
1.50M
      if (auto error = ValidateGroupMemberDecorate(_, inst)) return error;
581
1.50M
      break;
582
3.17M
    default:
583
3.17M
      break;
584
13.1M
  }
585
586
  // In order to validate decoration rules, we need to know all the decorations
587
  // that are applied to any given <id>.
588
13.1M
  RegisterDecorations(_, inst);
589
590
13.1M
  return SPV_SUCCESS;
591
13.1M
}
592
593
}  // namespace val
594
}  // namespace spvtools