Coverage Report

Created: 2024-09-11 07:09

/src/spirv-tools/source/val/validate_instruction.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2015-2016 The Khronos Group Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
// Performs validation on instructions that appear inside of a SPIR-V block.
16
17
#include <cassert>
18
#include <sstream>
19
#include <string>
20
#include <vector>
21
22
#include "source/enum_set.h"
23
#include "source/enum_string_mapping.h"
24
#include "source/extensions.h"
25
#include "source/opcode.h"
26
#include "source/operand.h"
27
#include "source/spirv_constant.h"
28
#include "source/spirv_target_env.h"
29
#include "source/spirv_validator_options.h"
30
#include "source/util/string_utils.h"
31
#include "source/val/validate.h"
32
#include "source/val/validation_state.h"
33
34
namespace spvtools {
35
namespace val {
36
namespace {
37
38
std::string ToString(const CapabilitySet& capabilities,
39
334
                     const AssemblyGrammar& grammar) {
40
334
  std::stringstream ss;
41
430
  for (auto capability : capabilities) {
42
430
    spv_operand_desc desc;
43
430
    if (SPV_SUCCESS == grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
44
430
                                             uint32_t(capability), &desc))
45
430
      ss << desc->name << " ";
46
0
    else
47
0
      ss << uint32_t(capability) << " ";
48
430
  }
49
334
  return ss.str();
50
334
}
51
52
// Returns capabilities that enable an opcode.  An empty result is interpreted
53
// as no prohibition of use of the opcode.  If the result is non-empty, then
54
// the opcode may only be used if at least one of the capabilities is specified
55
// by the module.
56
CapabilitySet EnablingCapabilitiesForOp(const ValidationState_t& state,
57
10.6M
                                        spv::Op opcode) {
58
  // Exceptions for SPV_AMD_shader_ballot
59
10.6M
  switch (opcode) {
60
    // Normally these would require Group capability
61
0
    case spv::Op::OpGroupIAddNonUniformAMD:
62
0
    case spv::Op::OpGroupFAddNonUniformAMD:
63
0
    case spv::Op::OpGroupFMinNonUniformAMD:
64
0
    case spv::Op::OpGroupUMinNonUniformAMD:
65
0
    case spv::Op::OpGroupSMinNonUniformAMD:
66
0
    case spv::Op::OpGroupFMaxNonUniformAMD:
67
0
    case spv::Op::OpGroupUMaxNonUniformAMD:
68
0
    case spv::Op::OpGroupSMaxNonUniformAMD:
69
0
      if (state.HasExtension(kSPV_AMD_shader_ballot)) return CapabilitySet();
70
0
      break;
71
10.6M
    default:
72
10.6M
      break;
73
10.6M
  }
74
  // Look it up in the grammar
75
10.6M
  spv_opcode_desc opcode_desc = {};
76
10.6M
  if (SPV_SUCCESS == state.grammar().lookupOpcode(opcode, &opcode_desc)) {
77
10.6M
    return state.grammar().filterCapsAgainstTargetEnv(
78
10.6M
        opcode_desc->capabilities, opcode_desc->numCapabilities);
79
10.6M
  }
80
0
  return CapabilitySet();
81
10.6M
}
82
83
// Returns SPV_SUCCESS if, for the given operand, the target environment
84
// satsifies minimum version requirements, or if the module declares an
85
// enabling extension for the operand.  Otherwise emit a diagnostic and
86
// return an error code.
87
spv_result_t OperandVersionExtensionCheck(
88
    ValidationState_t& _, const Instruction* inst, size_t which_operand,
89
1.46M
    const spv_operand_desc_t& operand_desc, uint32_t word) {
90
1.46M
  const uint32_t module_version = _.version();
91
1.46M
  const uint32_t operand_min_version = operand_desc.minVersion;
92
1.46M
  const uint32_t operand_last_version = operand_desc.lastVersion;
93
1.46M
  const bool reserved = operand_min_version == 0xffffffffu;
94
1.46M
  const bool version_satisfied = !reserved &&
95
1.46M
                                 (operand_min_version <= module_version) &&
96
1.46M
                                 (module_version <= operand_last_version);
97
98
1.46M
  if (version_satisfied) {
99
1.46M
    return SPV_SUCCESS;
100
1.46M
  }
101
102
4.80k
  if (operand_last_version < module_version) {
103
6
    return _.diag(SPV_ERROR_WRONG_VERSION, inst)
104
6
           << spvtools::utils::CardinalToOrdinal(which_operand)
105
6
           << " operand of " << spvOpcodeString(inst->opcode()) << ": operand "
106
6
           << operand_desc.name << "(" << word << ") requires SPIR-V version "
107
6
           << SPV_SPIRV_VERSION_MAJOR_PART(operand_last_version) << "."
108
6
           << SPV_SPIRV_VERSION_MINOR_PART(operand_last_version)
109
6
           << " or earlier";
110
6
  }
111
112
4.80k
  if (!reserved && operand_desc.numExtensions == 0) {
113
43
    return _.diag(SPV_ERROR_WRONG_VERSION, inst)
114
43
           << spvtools::utils::CardinalToOrdinal(which_operand)
115
43
           << " operand of " << spvOpcodeString(inst->opcode()) << ": operand "
116
43
           << operand_desc.name << "(" << word << ") requires SPIR-V version "
117
43
           << SPV_SPIRV_VERSION_MAJOR_PART(operand_min_version) << "."
118
43
           << SPV_SPIRV_VERSION_MINOR_PART(operand_min_version) << " or later";
119
4.76k
  } else {
120
4.76k
    ExtensionSet required_extensions(operand_desc.numExtensions,
121
4.76k
                                     operand_desc.extensions);
122
4.76k
    if (!_.HasAnyOfExtensions(required_extensions)) {
123
275
      return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
124
275
             << spvtools::utils::CardinalToOrdinal(which_operand)
125
275
             << " operand of " << spvOpcodeString(inst->opcode())
126
275
             << ": operand " << operand_desc.name << "(" << word
127
275
             << ") requires one of these extensions: "
128
275
             << ExtensionSetToString(required_extensions);
129
275
    }
130
4.76k
  }
131
4.48k
  return SPV_SUCCESS;
132
4.80k
}
133
134
// Returns SPV_SUCCESS if the given operand is enabled by capabilities declared
135
// in the module.  Otherwise issues an error message and returns
136
// SPV_ERROR_INVALID_CAPABILITY.
137
spv_result_t CheckRequiredCapabilities(ValidationState_t& state,
138
                                       const Instruction* inst,
139
                                       size_t which_operand,
140
                                       const spv_parsed_operand_t& operand,
141
2.74M
                                       uint32_t word) {
142
  // Mere mention of PointSize, ClipDistance, or CullDistance in a Builtin
143
  // decoration does not require the associated capability.  The use of such
144
  // a variable value should trigger the capability requirement, but that's
145
  // not implemented yet.  This rule is independent of target environment.
146
  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/365
147
2.74M
  if (operand.type == SPV_OPERAND_TYPE_BUILT_IN) {
148
3.78k
    switch (spv::BuiltIn(word)) {
149
111
      case spv::BuiltIn::PointSize:
150
450
      case spv::BuiltIn::ClipDistance:
151
499
      case spv::BuiltIn::CullDistance:
152
499
        return SPV_SUCCESS;
153
3.28k
      default:
154
3.28k
        break;
155
3.78k
    }
156
2.74M
  } else if (operand.type == SPV_OPERAND_TYPE_FP_ROUNDING_MODE) {
157
    // Allow all FP rounding modes if requested
158
69
    if (state.features().free_fp_rounding_mode) {
159
0
      return SPV_SUCCESS;
160
0
    }
161
2.74M
  } else if (operand.type == SPV_OPERAND_TYPE_GROUP_OPERATION &&
162
2.74M
             state.features().group_ops_reduce_and_scans &&
163
2.74M
             (word <= uint32_t(spv::GroupOperation::ExclusiveScan))) {
164
    // Allow certain group operations if requested.
165
0
    return SPV_SUCCESS;
166
0
  }
167
168
2.74M
  CapabilitySet enabling_capabilities;
169
2.74M
  spv_operand_desc operand_desc = nullptr;
170
2.74M
  const auto lookup_result =
171
2.74M
      state.grammar().lookupOperand(operand.type, word, &operand_desc);
172
2.74M
  if (lookup_result == SPV_SUCCESS) {
173
    // Allow FPRoundingMode decoration if requested.
174
1.46M
    if (operand.type == SPV_OPERAND_TYPE_DECORATION &&
175
1.46M
        spv::Decoration(operand_desc->value) ==
176
1.10M
            spv::Decoration::FPRoundingMode) {
177
69
      if (state.features().free_fp_rounding_mode) return SPV_SUCCESS;
178
179
      // Vulkan API requires more capabilities on rounding mode.
180
69
      if (spvIsVulkanEnv(state.context()->target_env)) {
181
0
        enabling_capabilities.insert(
182
0
            spv::Capability::StorageUniformBufferBlock16);
183
0
        enabling_capabilities.insert(spv::Capability::StorageUniform16);
184
0
        enabling_capabilities.insert(spv::Capability::StoragePushConstant16);
185
0
        enabling_capabilities.insert(spv::Capability::StorageInputOutput16);
186
0
      }
187
1.46M
    } else {
188
1.46M
      enabling_capabilities = state.grammar().filterCapsAgainstTargetEnv(
189
1.46M
          operand_desc->capabilities, operand_desc->numCapabilities);
190
1.46M
    }
191
192
    // When encountering an OpCapability instruction, the instruction pass
193
    // registers a capability with the module *before* checking capabilities.
194
    // So in the case of an OpCapability instruction, don't bother checking
195
    // enablement by another capability.
196
1.46M
    if (inst->opcode() != spv::Op::OpCapability) {
197
1.43M
      const bool enabled_by_cap =
198
1.43M
          state.HasAnyOfCapabilities(enabling_capabilities);
199
1.43M
      if (!enabling_capabilities.empty() && !enabled_by_cap) {
200
258
        return state.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
201
258
               << "Operand " << which_operand << " of "
202
258
               << spvOpcodeString(inst->opcode())
203
258
               << " requires one of these capabilities: "
204
258
               << ToString(enabling_capabilities, state.grammar());
205
258
      }
206
1.43M
    }
207
1.46M
    return OperandVersionExtensionCheck(state, inst, which_operand,
208
1.46M
                                        *operand_desc, word);
209
1.46M
  }
210
1.27M
  return SPV_SUCCESS;
211
2.74M
}
212
213
// Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction
214
// is explicitly reserved in the SPIR-V core spec.  Otherwise return
215
// SPV_SUCCESS.
216
10.6M
spv_result_t ReservedCheck(ValidationState_t& _, const Instruction* inst) {
217
10.6M
  const spv::Op opcode = inst->opcode();
218
10.6M
  switch (opcode) {
219
    // These instructions are enabled by a capability, but should never
220
    // be used anyway.
221
0
    case spv::Op::OpImageSparseSampleProjImplicitLod:
222
1
    case spv::Op::OpImageSparseSampleProjExplicitLod:
223
2
    case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
224
2
    case spv::Op::OpImageSparseSampleProjDrefExplicitLod: {
225
2
      spv_opcode_desc inst_desc;
226
2
      _.grammar().lookupOpcode(opcode, &inst_desc);
227
2
      return _.diag(SPV_ERROR_INVALID_BINARY, inst)
228
2
             << "Invalid Opcode name 'Op" << inst_desc->name << "'";
229
2
    }
230
10.6M
    default:
231
10.6M
      break;
232
10.6M
  }
233
10.6M
  return SPV_SUCCESS;
234
10.6M
}
235
236
// Returns SPV_ERROR_INVALID_CAPABILITY and emits a diagnostic if the
237
// instruction is invalid because the required capability isn't declared
238
// in the module.
239
10.6M
spv_result_t CapabilityCheck(ValidationState_t& _, const Instruction* inst) {
240
10.6M
  const spv::Op opcode = inst->opcode();
241
10.6M
  CapabilitySet opcode_caps = EnablingCapabilitiesForOp(_, opcode);
242
10.6M
  if (!_.HasAnyOfCapabilities(opcode_caps)) {
243
76
    return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
244
76
           << "Opcode " << spvOpcodeString(opcode)
245
76
           << " requires one of these capabilities: "
246
76
           << ToString(opcode_caps, _.grammar());
247
76
  }
248
27.4M
  for (size_t i = 0; i < inst->operands().size(); ++i) {
249
16.8M
    const auto& operand = inst->operand(i);
250
16.8M
    const auto word = inst->word(operand.offset);
251
16.8M
    if (spvOperandIsConcreteMask(operand.type)) {
252
      // Check for required capabilities for each bit position of the mask.
253
4.90M
      for (uint32_t mask_bit = 0x80000000; mask_bit; mask_bit >>= 1) {
254
4.75M
        if (word & mask_bit) {
255
54.8k
          spv_result_t status =
256
54.8k
              CheckRequiredCapabilities(_, inst, i + 1, operand, mask_bit);
257
54.8k
          if (status != SPV_SUCCESS) return status;
258
54.8k
        }
259
4.75M
      }
260
16.6M
    } else if (spvIsIdType(operand.type)) {
261
      // TODO(dneto): Check the value referenced by this Id, if we can compute
262
      // it.  For now, just punt, to fix issue 248:
263
      // https://github.com/KhronosGroup/SPIRV-Tools/issues/248
264
13.9M
    } else {
265
      // Check the operand word as a whole.
266
2.69M
      spv_result_t status =
267
2.69M
          CheckRequiredCapabilities(_, inst, i + 1, operand, word);
268
2.69M
      if (status != SPV_SUCCESS) return status;
269
2.69M
    }
270
16.8M
  }
271
10.6M
  return SPV_SUCCESS;
272
10.6M
}
273
274
// Checks that the instruction can be used in this target environment's base
275
// version. Assumes that CapabilityCheck has checked direct capability
276
// dependencies for the opcode.
277
10.6M
spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
278
10.6M
  const auto opcode = inst->opcode();
279
10.6M
  spv_opcode_desc inst_desc;
280
10.6M
  const spv_result_t r = _.grammar().lookupOpcode(opcode, &inst_desc);
281
10.6M
  assert(r == SPV_SUCCESS);
282
10.6M
  (void)r;
283
284
10.6M
  const auto min_version = inst_desc->minVersion;
285
10.6M
  const auto last_version = inst_desc->lastVersion;
286
10.6M
  const auto module_version = _.version();
287
288
10.6M
  if (last_version < module_version) {
289
0
    return _.diag(SPV_ERROR_WRONG_VERSION, inst)
290
0
           << spvOpcodeString(opcode) << " requires SPIR-V version "
291
0
           << SPV_SPIRV_VERSION_MAJOR_PART(last_version) << "."
292
0
           << SPV_SPIRV_VERSION_MINOR_PART(last_version) << " or earlier";
293
0
  }
294
295
  // OpTerminateInvocation is special because it is enabled by Shader
296
  // capability, but also requires an extension and/or version check.
297
10.6M
  const bool capability_check_is_sufficient =
298
10.6M
      inst->opcode() != spv::Op::OpTerminateInvocation;
299
300
10.6M
  if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) {
301
    // We already checked that the direct capability dependency has been
302
    // satisfied. We don't need to check any further.
303
21.8k
    return SPV_SUCCESS;
304
21.8k
  }
305
306
10.6M
  ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions);
307
10.6M
  if (exts.empty()) {
308
    // If no extensions can enable this instruction, then emit error
309
    // messages only concerning core SPIR-V versions if errors happen.
310
10.6M
    if (min_version == ~0u) {
311
0
      return _.diag(SPV_ERROR_WRONG_VERSION, inst)
312
0
             << spvOpcodeString(opcode) << " is reserved for future use.";
313
0
    }
314
315
10.6M
    if (module_version < min_version) {
316
0
      return _.diag(SPV_ERROR_WRONG_VERSION, inst)
317
0
             << spvOpcodeString(opcode) << " requires SPIR-V version "
318
0
             << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "."
319
0
             << SPV_SPIRV_VERSION_MINOR_PART(min_version) << " at minimum.";
320
0
    }
321
10.6M
  } else if (!_.HasAnyOfExtensions(exts)) {
322
    // Otherwise, we only error out when no enabling extensions are
323
    // registered.
324
12
    if (min_version == ~0u) {
325
0
      return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
326
0
             << spvOpcodeString(opcode)
327
0
             << " requires one of the following extensions: "
328
0
             << ExtensionSetToString(exts);
329
0
    }
330
331
12
    if (module_version < min_version) {
332
12
      return _.diag(SPV_ERROR_WRONG_VERSION, inst)
333
12
             << spvOpcodeString(opcode) << " requires SPIR-V version "
334
12
             << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "."
335
12
             << SPV_SPIRV_VERSION_MINOR_PART(min_version)
336
12
             << " at minimum or one of the following extensions: "
337
12
             << ExtensionSetToString(exts);
338
12
    }
339
12
  }
340
341
10.6M
  return SPV_SUCCESS;
342
10.6M
}
343
344
// Checks that the Resuld <id> is within the valid bound.
345
10.6M
spv_result_t LimitCheckIdBound(ValidationState_t& _, const Instruction* inst) {
346
10.6M
  if (inst->id() >= _.getIdBound()) {
347
671
    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
348
671
           << "Result <id> '" << inst->id()
349
671
           << "' must be less than the ID bound '" << _.getIdBound() << "'.";
350
671
  }
351
10.6M
  return SPV_SUCCESS;
352
10.6M
}
353
354
// Checks that the number of OpTypeStruct members is within the limit.
355
10.6M
spv_result_t LimitCheckStruct(ValidationState_t& _, const Instruction* inst) {
356
10.6M
  if (spv::Op::OpTypeStruct != inst->opcode()) {
357
10.6M
    return SPV_SUCCESS;
358
10.6M
  }
359
360
  // Number of members is the number of operands of the instruction minus 1.
361
  // One operand is the result ID.
362
17.8k
  const uint16_t limit =
363
17.8k
      static_cast<uint16_t>(_.options()->universal_limits_.max_struct_members);
364
17.8k
  if (inst->operands().size() - 1 > limit) {
365
0
    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
366
0
           << "Number of OpTypeStruct members (" << inst->operands().size() - 1
367
0
           << ") has exceeded the limit (" << limit << ").";
368
0
  }
369
370
  // Section 2.17 of SPIRV Spec specifies that the "Structure Nesting Depth"
371
  // must be less than or equal to 255.
372
  // This is interpreted as structures including other structures as
373
  // members. The code does not follow pointers or look into arrays to see
374
  // if we reach a structure downstream. The nesting depth of a struct is
375
  // 1+(largest depth of any member). Scalars are at depth 0.
376
17.8k
  uint32_t max_member_depth = 0;
377
  // Struct members start at word 2 of OpTypeStruct instruction.
378
34.2k
  for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) {
379
16.3k
    auto member = inst->word(word_i);
380
16.3k
    auto memberTypeInstr = _.FindDef(member);
381
16.3k
    if (memberTypeInstr && spv::Op::OpTypeStruct == memberTypeInstr->opcode()) {
382
1.32k
      max_member_depth = std::max(
383
1.32k
          max_member_depth, _.struct_nesting_depth(memberTypeInstr->id()));
384
1.32k
    }
385
16.3k
  }
386
387
17.8k
  const uint32_t depth_limit = _.options()->universal_limits_.max_struct_depth;
388
17.8k
  const uint32_t cur_depth = 1 + max_member_depth;
389
17.8k
  _.set_struct_nesting_depth(inst->id(), cur_depth);
390
17.8k
  if (cur_depth > depth_limit) {
391
0
    return _.diag(SPV_ERROR_INVALID_BINARY, inst)
392
0
           << "Structure Nesting Depth may not be larger than " << depth_limit
393
0
           << ". Found " << cur_depth << ".";
394
0
  }
395
17.8k
  return SPV_SUCCESS;
396
17.8k
}
397
398
// Checks that the number of (literal, label) pairs in OpSwitch is within
399
// the limit.
400
10.6M
spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) {
401
10.6M
  if (spv::Op::OpSwitch == inst->opcode()) {
402
    // The instruction syntax is as follows:
403
    // OpSwitch <selector ID> <Default ID> literal label literal label ...
404
    // literal,label pairs come after the first 2 operands.
405
    // It is guaranteed at this point that num_operands is an even number.
406
11.7k
    size_t num_pairs = (inst->operands().size() - 2) / 2;
407
11.7k
    const unsigned int num_pairs_limit =
408
11.7k
        _.options()->universal_limits_.max_switch_branches;
409
11.7k
    if (num_pairs > num_pairs_limit) {
410
0
      return _.diag(SPV_ERROR_INVALID_BINARY, inst)
411
0
             << "Number of (literal, label) pairs in OpSwitch (" << num_pairs
412
0
             << ") exceeds the limit (" << num_pairs_limit << ").";
413
0
    }
414
11.7k
  }
415
10.6M
  return SPV_SUCCESS;
416
10.6M
}
417
418
// Ensure the number of variables of the given class does not exceed the
419
// limit.
420
spv_result_t LimitCheckNumVars(ValidationState_t& _, const uint32_t var_id,
421
109k
                               const spv::StorageClass storage_class) {
422
109k
  if (spv::StorageClass::Function == storage_class) {
423
76.7k
    _.registerLocalVariable(var_id);
424
76.7k
    const uint32_t num_local_vars_limit =
425
76.7k
        _.options()->universal_limits_.max_local_variables;
426
76.7k
    if (_.num_local_vars() > num_local_vars_limit) {
427
0
      return _.diag(SPV_ERROR_INVALID_BINARY, nullptr)
428
0
             << "Number of local variables ('Function' Storage Class) "
429
0
                "exceeded the valid limit ("
430
0
             << num_local_vars_limit << ").";
431
0
    }
432
76.7k
  } else {
433
32.2k
    _.registerGlobalVariable(var_id);
434
32.2k
    const uint32_t num_global_vars_limit =
435
32.2k
        _.options()->universal_limits_.max_global_variables;
436
32.2k
    if (_.num_global_vars() > num_global_vars_limit) {
437
0
      return _.diag(SPV_ERROR_INVALID_BINARY, nullptr)
438
0
             << "Number of Global Variables (Storage Class other than "
439
0
                "'Function') exceeded the valid limit ("
440
0
             << num_global_vars_limit << ").";
441
0
    }
442
32.2k
  }
443
109k
  return SPV_SUCCESS;
444
109k
}
445
446
// Parses OpExtension instruction and logs warnings if unsuccessful.
447
spv_result_t CheckIfKnownExtension(ValidationState_t& _,
448
8.02k
                                   const Instruction* inst) {
449
8.02k
  const std::string extension_str = GetExtensionString(&(inst->c_inst()));
450
8.02k
  Extension extension;
451
8.02k
  if (!GetExtensionFromString(extension_str.c_str(), &extension)) {
452
6.64k
    return _.diag(SPV_WARNING, inst)
453
6.64k
           << "Found unrecognized extension " << extension_str;
454
6.64k
  }
455
1.37k
  return SPV_SUCCESS;
456
8.02k
}
457
458
}  // namespace
459
460
10.6M
spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) {
461
10.6M
  const spv::Op opcode = inst->opcode();
462
10.6M
  if (opcode == spv::Op::OpExtension) {
463
8.02k
    CheckIfKnownExtension(_, inst);
464
10.6M
  } else if (opcode == spv::Op::OpCapability) {
465
35.0k
    _.RegisterCapability(inst->GetOperandAs<spv::Capability>(0));
466
10.5M
  } else if (opcode == spv::Op::OpMemoryModel) {
467
24.5k
    if (_.has_memory_model_specified()) {
468
12
      return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
469
12
             << "OpMemoryModel should only be provided once.";
470
12
    }
471
24.5k
    _.set_addressing_model(inst->GetOperandAs<spv::AddressingModel>(0));
472
24.5k
    _.set_memory_model(inst->GetOperandAs<spv::MemoryModel>(1));
473
10.5M
  } else if (opcode == spv::Op::OpExecutionMode ||
474
10.5M
             opcode == spv::Op::OpExecutionModeId) {
475
26.1k
    const uint32_t entry_point = inst->word(1);
476
26.1k
    _.RegisterExecutionModeForEntryPoint(entry_point,
477
26.1k
                                         spv::ExecutionMode(inst->word(2)));
478
10.5M
  } else if (opcode == spv::Op::OpVariable) {
479
109k
    const auto storage_class = inst->GetOperandAs<spv::StorageClass>(2);
480
109k
    if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) {
481
0
      return error;
482
0
    }
483
10.4M
  } else if (opcode == spv::Op::OpSamplerImageAddressingModeNV) {
484
2
    if (!_.HasCapability(spv::Capability::BindlessTextureNV)) {
485
2
      return _.diag(SPV_ERROR_MISSING_EXTENSION, inst)
486
2
             << "OpSamplerImageAddressingModeNV supported only with extension "
487
2
                "SPV_NV_bindless_texture";
488
2
    }
489
0
    uint32_t bitwidth = inst->GetOperandAs<uint32_t>(0);
490
0
    if (_.samplerimage_variable_address_mode() != 0) {
491
0
      return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
492
0
             << "OpSamplerImageAddressingModeNV should only be provided once";
493
0
    }
494
0
    if (bitwidth != 32 && bitwidth != 64) {
495
0
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
496
0
             << "OpSamplerImageAddressingModeNV bitwidth should be 64 or 32";
497
0
    }
498
0
    _.set_samplerimage_variable_address_mode(bitwidth);
499
0
  }
500
501
10.6M
  if (auto error = ReservedCheck(_, inst)) return error;
502
10.6M
  if (auto error = CapabilityCheck(_, inst)) return error;
503
10.6M
  if (auto error = LimitCheckIdBound(_, inst)) return error;
504
10.6M
  if (auto error = LimitCheckStruct(_, inst)) return error;
505
10.6M
  if (auto error = LimitCheckSwitch(_, inst)) return error;
506
10.6M
  if (auto error = VersionCheck(_, inst)) return error;
507
508
  // All instruction checks have passed.
509
10.6M
  return SPV_SUCCESS;
510
10.6M
}
511
512
}  // namespace val
513
}  // namespace spvtools