Coverage Report

Created: 2026-09-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/val/validate_capability.cpp
Line
Count
Source
1
// Copyright (c) 2017 Google Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
// Validates OpCapability instruction.
16
17
#include <cassert>
18
#include <string>
19
20
#include "source/opcode.h"
21
#include "source/table2.h"
22
#include "source/val/instruction.h"
23
#include "source/val/validate.h"
24
#include "source/val/validation_state.h"
25
26
namespace spvtools {
27
namespace val {
28
namespace {
29
30
0
bool IsSupportGuaranteedVulkan_1_0(uint32_t capability) {
31
0
  switch (spv::Capability(capability)) {
32
0
    case spv::Capability::Matrix:
33
0
    case spv::Capability::Shader:
34
0
    case spv::Capability::InputAttachment:
35
0
    case spv::Capability::Sampled1D:
36
0
    case spv::Capability::Image1D:
37
0
    case spv::Capability::SampledBuffer:
38
0
    case spv::Capability::ImageBuffer:
39
0
    case spv::Capability::ImageQuery:
40
0
    case spv::Capability::DerivativeControl:
41
0
      return true;
42
0
    default:
43
0
      break;
44
0
  }
45
0
  return false;
46
0
}
47
48
0
bool IsSupportGuaranteedVulkan_1_1(uint32_t capability) {
49
0
  if (IsSupportGuaranteedVulkan_1_0(capability)) return true;
50
0
  switch (spv::Capability(capability)) {
51
0
    case spv::Capability::DeviceGroup:
52
0
    case spv::Capability::MultiView:
53
0
      return true;
54
0
    default:
55
0
      break;
56
0
  }
57
0
  return false;
58
0
}
59
60
0
bool IsSupportGuaranteedVulkan_1_2(uint32_t capability) {
61
0
  if (IsSupportGuaranteedVulkan_1_1(capability)) return true;
62
0
  switch (spv::Capability(capability)) {
63
0
    case spv::Capability::ShaderNonUniform:
64
0
      return true;
65
0
    default:
66
0
      break;
67
0
  }
68
0
  return false;
69
0
}
70
71
0
bool IsSupportGuaranteedVulkan_1_3(uint32_t capability) {
72
0
  if (IsSupportGuaranteedVulkan_1_2(capability)) return true;
73
0
  switch (spv::Capability(capability)) {
74
0
    case spv::Capability::DotProduct:
75
0
    case spv::Capability::DotProductInputAll:
76
0
    case spv::Capability::DotProductInput4x8Bit:
77
0
    case spv::Capability::DotProductInput4x8BitPacked:
78
0
    case spv::Capability::VulkanMemoryModel:
79
0
    case spv::Capability::VulkanMemoryModelDeviceScope:
80
0
    case spv::Capability::PhysicalStorageBufferAddresses:
81
0
    case spv::Capability::DemoteToHelperInvocation:
82
0
      return true;
83
0
    default:
84
0
      break;
85
0
  }
86
0
  return false;
87
0
}
88
89
0
bool IsSupportGuaranteedVulkan_1_4(uint32_t capability) {
90
0
  if (IsSupportGuaranteedVulkan_1_3(capability)) return true;
91
0
  switch (spv::Capability(capability)) {
92
0
    case spv::Capability::UniformBufferArrayDynamicIndexing:
93
0
    case spv::Capability::SampledImageArrayDynamicIndexing:
94
0
    case spv::Capability::StorageBufferArrayDynamicIndexing:
95
0
    case spv::Capability::StorageImageArrayDynamicIndexing:
96
0
    case spv::Capability::Int16:
97
0
    case spv::Capability::StorageBuffer16BitAccess:
98
0
    case spv::Capability::VariablePointers:
99
0
    case spv::Capability::VariablePointersStorageBuffer:
100
0
    case spv::Capability::UniformTexelBufferArrayDynamicIndexing:
101
0
    case spv::Capability::StorageTexelBufferArrayDynamicIndexing:
102
0
    case spv::Capability::Int8:
103
0
    case spv::Capability::StorageBuffer8BitAccess:
104
0
    case spv::Capability::FloatControls2:
105
0
    case spv::Capability::SampleRateShading:
106
0
    case spv::Capability::StorageImageExtendedFormats:
107
0
    case spv::Capability::ImageGatherExtended:
108
0
      return true;
109
0
    default:
110
0
      break;
111
0
  }
112
0
  return false;
113
0
}
114
115
0
bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
116
0
  switch (spv::Capability(capability)) {
117
0
    case spv::Capability::Geometry:
118
0
    case spv::Capability::Tessellation:
119
0
    case spv::Capability::Float64:
120
0
    case spv::Capability::Int64:
121
0
    case spv::Capability::Int16:
122
0
    case spv::Capability::TessellationPointSize:
123
0
    case spv::Capability::GeometryPointSize:
124
0
    case spv::Capability::ImageGatherExtended:
125
0
    case spv::Capability::StorageImageMultisample:
126
0
    case spv::Capability::UniformBufferArrayDynamicIndexing:
127
0
    case spv::Capability::SampledImageArrayDynamicIndexing:
128
0
    case spv::Capability::StorageBufferArrayDynamicIndexing:
129
0
    case spv::Capability::StorageImageArrayDynamicIndexing:
130
0
    case spv::Capability::ClipDistance:
131
0
    case spv::Capability::CullDistance:
132
0
    case spv::Capability::ImageCubeArray:
133
0
    case spv::Capability::SampleRateShading:
134
0
    case spv::Capability::SparseResidency:
135
0
    case spv::Capability::MinLod:
136
0
    case spv::Capability::SampledCubeArray:
137
0
    case spv::Capability::ImageMSArray:
138
0
    case spv::Capability::StorageImageExtendedFormats:
139
0
    case spv::Capability::InterpolationFunction:
140
0
    case spv::Capability::StorageImageReadWithoutFormat:
141
0
    case spv::Capability::StorageImageWriteWithoutFormat:
142
0
    case spv::Capability::MultiViewport:
143
0
    case spv::Capability::Int64Atomics:
144
0
    case spv::Capability::TransformFeedback:
145
0
    case spv::Capability::GeometryStreams:
146
0
    case spv::Capability::Float16:
147
0
    case spv::Capability::Int8:
148
0
    case spv::Capability::BFloat16TypeKHR:
149
0
    case spv::Capability::Float8EXT:
150
0
    case spv::Capability::Float4EXT:
151
0
    case spv::Capability::Float6EXT:
152
0
    case spv::Capability::Float8UnsignedE8M0EXT:
153
0
    case spv::Capability::MXInt8EXT:
154
0
    case spv::Capability::BitcastExtractEXT:
155
0
    case spv::Capability::PushConstantBanksNV:
156
0
      return true;
157
0
    default:
158
0
      break;
159
0
  }
160
0
  return false;
161
0
}
162
163
0
bool IsSupportOptionalVulkan_1_1(uint32_t capability) {
164
0
  if (IsSupportOptionalVulkan_1_0(capability)) return true;
165
166
0
  switch (spv::Capability(capability)) {
167
0
    case spv::Capability::GroupNonUniform:
168
0
    case spv::Capability::GroupNonUniformVote:
169
0
    case spv::Capability::GroupNonUniformArithmetic:
170
0
    case spv::Capability::GroupNonUniformBallot:
171
0
    case spv::Capability::GroupNonUniformShuffle:
172
0
    case spv::Capability::GroupNonUniformShuffleRelative:
173
0
    case spv::Capability::GroupNonUniformClustered:
174
0
    case spv::Capability::GroupNonUniformQuad:
175
0
    case spv::Capability::DrawParameters:
176
    // Alias spv::Capability::StorageBuffer16BitAccess.
177
0
    case spv::Capability::StorageUniformBufferBlock16:
178
    // Alias spv::Capability::UniformAndStorageBuffer16BitAccess.
179
0
    case spv::Capability::StorageUniform16:
180
0
    case spv::Capability::StoragePushConstant16:
181
0
    case spv::Capability::StorageInputOutput16:
182
0
    case spv::Capability::DeviceGroup:
183
0
    case spv::Capability::MultiView:
184
0
    case spv::Capability::VariablePointersStorageBuffer:
185
0
    case spv::Capability::VariablePointers:
186
0
      return true;
187
0
    default:
188
0
      break;
189
0
  }
190
0
  return false;
191
0
}
192
193
0
bool IsSupportOptionalVulkan_1_2(uint32_t capability) {
194
0
  if (IsSupportOptionalVulkan_1_1(capability)) return true;
195
196
0
  switch (spv::Capability(capability)) {
197
0
    case spv::Capability::DenormPreserve:
198
0
    case spv::Capability::DenormFlushToZero:
199
0
    case spv::Capability::SignedZeroInfNanPreserve:
200
0
    case spv::Capability::RoundingModeRTE:
201
0
    case spv::Capability::RoundingModeRTZ:
202
0
    case spv::Capability::VulkanMemoryModel:
203
0
    case spv::Capability::VulkanMemoryModelDeviceScope:
204
0
    case spv::Capability::StorageBuffer8BitAccess:
205
0
    case spv::Capability::UniformAndStorageBuffer8BitAccess:
206
0
    case spv::Capability::StoragePushConstant8:
207
0
    case spv::Capability::ShaderViewportIndex:
208
0
    case spv::Capability::ShaderLayer:
209
0
    case spv::Capability::PhysicalStorageBufferAddresses:
210
0
    case spv::Capability::RuntimeDescriptorArray:
211
0
    case spv::Capability::UniformTexelBufferArrayDynamicIndexing:
212
0
    case spv::Capability::StorageTexelBufferArrayDynamicIndexing:
213
0
    case spv::Capability::UniformBufferArrayNonUniformIndexing:
214
0
    case spv::Capability::SampledImageArrayNonUniformIndexing:
215
0
    case spv::Capability::StorageBufferArrayNonUniformIndexing:
216
0
    case spv::Capability::StorageImageArrayNonUniformIndexing:
217
0
    case spv::Capability::InputAttachmentArrayNonUniformIndexing:
218
0
    case spv::Capability::UniformTexelBufferArrayNonUniformIndexing:
219
0
    case spv::Capability::StorageTexelBufferArrayNonUniformIndexing:
220
0
    case spv::Capability::InputAttachmentArrayDynamicIndexing:
221
0
    case spv::Capability::ShaderNonUniform:
222
0
      return true;
223
0
    default:
224
0
      break;
225
0
  }
226
0
  return false;
227
0
}
228
229
// Vulkan 1.3 only added required features.
230
0
bool IsSupportOptionalVulkan_1_3(uint32_t capability) {
231
0
  return IsSupportOptionalVulkan_1_2(capability);
232
0
}
233
234
// Vulkan 1.4 only added required features.
235
0
bool IsSupportOptionalVulkan_1_4(uint32_t capability) {
236
0
  return IsSupportOptionalVulkan_1_3(capability);
237
0
}
238
239
0
bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
240
0
  switch (spv::Capability(capability)) {
241
0
    case spv::Capability::Addresses:
242
0
    case spv::Capability::Float16Buffer:
243
0
    case spv::Capability::Int16:
244
0
    case spv::Capability::Int8:
245
0
    case spv::Capability::Kernel:
246
0
    case spv::Capability::Linkage:
247
0
    case spv::Capability::Vector16:
248
0
      return true;
249
0
    case spv::Capability::Int64:
250
0
      return !embedded_profile;
251
0
    default:
252
0
      break;
253
0
  }
254
0
  return false;
255
0
}
256
257
0
bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) {
258
0
  if (IsSupportGuaranteedOpenCL_1_2(capability, embedded_profile)) return true;
259
260
0
  switch (spv::Capability(capability)) {
261
0
    case spv::Capability::DeviceEnqueue:
262
0
    case spv::Capability::GenericPointer:
263
0
    case spv::Capability::Groups:
264
0
    case spv::Capability::Pipes:
265
0
      return true;
266
0
    default:
267
0
      break;
268
0
  }
269
0
  return false;
270
0
}
271
272
0
bool IsSupportGuaranteedOpenCL_2_2(uint32_t capability, bool embedded_profile) {
273
0
  if (IsSupportGuaranteedOpenCL_2_0(capability, embedded_profile)) return true;
274
275
0
  switch (spv::Capability(capability)) {
276
0
    case spv::Capability::SubgroupDispatch:
277
0
    case spv::Capability::PipeStorage:
278
0
      return true;
279
0
    default:
280
0
      break;
281
0
  }
282
0
  return false;
283
0
}
284
285
0
bool IsSupportOptionalOpenCL_1_2(uint32_t capability) {
286
0
  switch (spv::Capability(capability)) {
287
0
    case spv::Capability::ImageBasic:
288
0
    case spv::Capability::Float64:
289
0
    case spv::Capability::Float16:
290
0
      return true;
291
0
    default:
292
0
      break;
293
0
  }
294
0
  return false;
295
0
}
296
297
// Checks if |capability| was enabled by extension.
298
0
bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) {
299
0
  const spvtools::OperandDesc* operand_desc = nullptr;
300
0
  spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
301
0
                          &operand_desc);
302
303
  // operand_desc is expected to be not null, otherwise validator would have
304
  // failed at an earlier stage. This 'assert' is 'just in case'.
305
0
  assert(operand_desc);
306
307
0
  ExtensionSet operand_exts(operand_desc->extensions_range.count(),
308
0
                            operand_desc->extensions().data());
309
0
  if (operand_exts.empty()) return false;
310
311
0
  return _.HasAnyOfExtensions(operand_exts);
312
0
}
313
314
bool IsEnabledByCapabilityOpenCL_1_2(ValidationState_t& _,
315
0
                                     uint32_t capability) {
316
0
  if (_.HasCapability(spv::Capability::ImageBasic)) {
317
0
    switch (spv::Capability(capability)) {
318
0
      case spv::Capability::LiteralSampler:
319
0
      case spv::Capability::Sampled1D:
320
0
      case spv::Capability::Image1D:
321
0
      case spv::Capability::SampledBuffer:
322
0
      case spv::Capability::ImageBuffer:
323
0
        return true;
324
0
      default:
325
0
        break;
326
0
    }
327
0
    return false;
328
0
  }
329
0
  return false;
330
0
}
331
332
bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _,
333
0
                                     uint32_t capability) {
334
0
  if (_.HasCapability(spv::Capability::ImageBasic)) {
335
0
    switch (spv::Capability(capability)) {
336
0
      case spv::Capability::ImageReadWrite:
337
0
      case spv::Capability::LiteralSampler:
338
0
      case spv::Capability::Sampled1D:
339
0
      case spv::Capability::Image1D:
340
0
      case spv::Capability::SampledBuffer:
341
0
      case spv::Capability::ImageBuffer:
342
0
        return true;
343
0
      default:
344
0
        break;
345
0
    }
346
0
    return false;
347
0
  }
348
0
  return false;
349
0
}
350
351
}  // namespace
352
353
// Validates that capability declarations use operands allowed in the current
354
// context.
355
14.8M
spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) {
356
14.8M
  if (inst->opcode() != spv::Op::OpCapability &&
357
14.7M
      inst->opcode() != spv::Op::OpConditionalCapabilityINTEL)
358
14.7M
    return SPV_SUCCESS;
359
360
14.8M
  assert(!((inst->opcode() == spv::Op::OpCapability) ^
361
65.1k
           (inst->operands().size() == 1)));
362
65.1k
  assert(!((inst->opcode() == spv::Op::OpConditionalCapabilityINTEL) ^
363
65.1k
           (inst->operands().size() == 2)));
364
365
65.1k
  const uint32_t i_cap =
366
65.1k
      inst->opcode() == spv::Op::OpConditionalCapabilityINTEL ? 1 : 0;
367
65.1k
  const spv_parsed_operand_t& operand = inst->operand(i_cap);
368
369
65.1k
  assert(operand.num_words == 1);
370
65.1k
  assert(operand.offset < inst->words().size());
371
372
65.1k
  const uint32_t capability = inst->word(operand.offset);
373
65.1k
  const auto capability_str = [capability]() {
374
0
    const spvtools::OperandDesc* desc = nullptr;
375
0
    if (spvtools::LookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
376
0
                                &desc) != SPV_SUCCESS ||
377
0
        !desc) {
378
0
      return std::string("Unknown");
379
0
    }
380
0
    return std::string(desc->name().data());
381
0
  };
382
383
65.1k
  const auto env = _.context()->target_env;
384
65.1k
  const bool opencl_embedded = env == SPV_ENV_OPENCL_EMBEDDED_1_2 ||
385
65.1k
                               env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
386
65.1k
                               env == SPV_ENV_OPENCL_EMBEDDED_2_1 ||
387
65.1k
                               env == SPV_ENV_OPENCL_EMBEDDED_2_2;
388
65.1k
  const std::string opencl_profile = opencl_embedded ? "Embedded" : "Full";
389
65.1k
  if (env == SPV_ENV_VULKAN_1_0) {
390
0
    if (!IsSupportGuaranteedVulkan_1_0(capability) &&
391
0
        !IsSupportOptionalVulkan_1_0(capability) &&
392
0
        !IsEnabledByExtension(_, capability)) {
393
0
      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
394
0
             << "Capability " << capability_str()
395
0
             << " is not allowed by Vulkan 1.0 specification"
396
0
             << " (or requires extension)";
397
0
    }
398
65.1k
  } else if (env == SPV_ENV_VULKAN_1_1) {
399
0
    if (!IsSupportGuaranteedVulkan_1_1(capability) &&
400
0
        !IsSupportOptionalVulkan_1_1(capability) &&
401
0
        !IsEnabledByExtension(_, capability)) {
402
0
      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
403
0
             << "Capability " << capability_str()
404
0
             << " is not allowed by Vulkan 1.1 specification"
405
0
             << " (or requires extension)";
406
0
    }
407
65.1k
  } else if (env == SPV_ENV_VULKAN_1_2) {
408
0
    if (!IsSupportGuaranteedVulkan_1_2(capability) &&
409
0
        !IsSupportOptionalVulkan_1_2(capability) &&
410
0
        !IsEnabledByExtension(_, capability)) {
411
0
      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
412
0
             << "Capability " << capability_str()
413
0
             << " is not allowed by Vulkan 1.2 specification"
414
0
             << " (or requires extension)";
415
0
    }
416
65.1k
  } else if (env == SPV_ENV_VULKAN_1_3) {
417
0
    if (!IsSupportGuaranteedVulkan_1_3(capability) &&
418
0
        !IsSupportOptionalVulkan_1_3(capability) &&
419
0
        !IsEnabledByExtension(_, capability)) {
420
0
      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
421
0
             << "Capability " << capability_str()
422
0
             << " is not allowed by Vulkan 1.3 specification"
423
0
             << " (or requires extension)";
424
0
    }
425
65.1k
  } else if (env == SPV_ENV_VULKAN_1_4) {
426
0
    if (!IsSupportGuaranteedVulkan_1_4(capability) &&
427
0
        !IsSupportOptionalVulkan_1_4(capability) &&
428
0
        !IsEnabledByExtension(_, capability)) {
429
0
      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
430
0
             << "Capability " << capability_str()
431
0
             << " is not allowed by Vulkan 1.4 specification"
432
0
             << " (or requires extension)";
433
0
    }
434
65.1k
  } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) {
435
0
    if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) &&
436
0
        !IsSupportOptionalOpenCL_1_2(capability) &&
437
0
        !IsEnabledByExtension(_, capability) &&
438
0
        !IsEnabledByCapabilityOpenCL_1_2(_, capability)) {
439
0
      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
440
0
             << "Capability " << capability_str()
441
0
             << " is not allowed by OpenCL 1.2 " << opencl_profile
442
0
             << " Profile specification"
443
0
             << " (or requires extension or capability)";
444
0
    }
445
65.1k
  } else if (env == SPV_ENV_OPENCL_2_0 || env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
446
65.1k
             env == SPV_ENV_OPENCL_2_1 || env == SPV_ENV_OPENCL_EMBEDDED_2_1) {
447
0
    if (!IsSupportGuaranteedOpenCL_2_0(capability, opencl_embedded) &&
448
0
        !IsSupportOptionalOpenCL_1_2(capability) &&
449
0
        !IsEnabledByExtension(_, capability) &&
450
0
        !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
451
0
      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
452
0
             << "Capability " << capability_str()
453
0
             << " is not allowed by OpenCL 2.0/2.1 " << opencl_profile
454
0
             << " Profile specification"
455
0
             << " (or requires extension or capability)";
456
0
    }
457
65.1k
  } else if (env == SPV_ENV_OPENCL_2_2 || env == SPV_ENV_OPENCL_EMBEDDED_2_2) {
458
0
    if (!IsSupportGuaranteedOpenCL_2_2(capability, opencl_embedded) &&
459
0
        !IsSupportOptionalOpenCL_1_2(capability) &&
460
0
        !IsEnabledByExtension(_, capability) &&
461
0
        !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
462
0
      return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
463
0
             << "Capability " << capability_str()
464
0
             << " is not allowed by OpenCL 2.2 " << opencl_profile
465
0
             << " Profile specification"
466
0
             << " (or requires extension or capability)";
467
0
    }
468
0
  }
469
470
65.1k
  return SPV_SUCCESS;
471
65.1k
}
472
473
}  // namespace val
474
}  // namespace spvtools