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 "source/val/validate.h"
18
19#include <cassert>
20#include <string>
21#include <unordered_set>
22
23#include "source/diagnostic.h"
24#include "source/opcode.h"
25#include "source/val/instruction.h"
26#include "source/val/validation_state.h"
27
28namespace spvtools {
29namespace val {
30namespace {
31
32bool IsSupportGuaranteedVulkan_1_0(uint32_t capability) {
33 switch (capability) {
34 case SpvCapabilityMatrix:
35 case SpvCapabilityShader:
36 case SpvCapabilityInputAttachment:
37 case SpvCapabilitySampled1D:
38 case SpvCapabilityImage1D:
39 case SpvCapabilitySampledBuffer:
40 case SpvCapabilityImageBuffer:
41 case SpvCapabilityImageQuery:
42 case SpvCapabilityDerivativeControl:
43 return true;
44 }
45 return false;
46}
47
48bool IsSupportGuaranteedVulkan_1_1(uint32_t capability) {
49 if (IsSupportGuaranteedVulkan_1_0(capability)) return true;
50 switch (capability) {
51 case SpvCapabilityDeviceGroup:
52 case SpvCapabilityMultiView:
53 return true;
54 }
55 return false;
56}
57
58bool IsSupportGuaranteedVulkan_1_2(uint32_t capability) {
59 if (IsSupportGuaranteedVulkan_1_1(capability)) return true;
60 switch (capability) {
61 case SpvCapabilityShaderNonUniform:
62 return true;
63 }
64 return false;
65}
66
67bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
68 switch (capability) {
69 case SpvCapabilityGeometry:
70 case SpvCapabilityTessellation:
71 case SpvCapabilityFloat64:
72 case SpvCapabilityInt64:
73 case SpvCapabilityInt16:
74 case SpvCapabilityTessellationPointSize:
75 case SpvCapabilityGeometryPointSize:
76 case SpvCapabilityImageGatherExtended:
77 case SpvCapabilityStorageImageMultisample:
78 case SpvCapabilityUniformBufferArrayDynamicIndexing:
79 case SpvCapabilitySampledImageArrayDynamicIndexing:
80 case SpvCapabilityStorageBufferArrayDynamicIndexing:
81 case SpvCapabilityStorageImageArrayDynamicIndexing:
82 case SpvCapabilityClipDistance:
83 case SpvCapabilityCullDistance:
84 case SpvCapabilityImageCubeArray:
85 case SpvCapabilitySampleRateShading:
86 case SpvCapabilitySparseResidency:
87 case SpvCapabilityMinLod:
88 case SpvCapabilitySampledCubeArray:
89 case SpvCapabilityImageMSArray:
90 case SpvCapabilityStorageImageExtendedFormats:
91 case SpvCapabilityInterpolationFunction:
92 case SpvCapabilityStorageImageReadWithoutFormat:
93 case SpvCapabilityStorageImageWriteWithoutFormat:
94 case SpvCapabilityMultiViewport:
95 case SpvCapabilityInt64Atomics:
96 case SpvCapabilityTransformFeedback:
97 case SpvCapabilityGeometryStreams:
98 case SpvCapabilityFloat16:
99 case SpvCapabilityInt8:
100 return true;
101 }
102 return false;
103}
104
105bool IsSupportOptionalVulkan_1_1(uint32_t capability) {
106 if (IsSupportOptionalVulkan_1_0(capability)) return true;
107
108 switch (capability) {
109 case SpvCapabilityGroupNonUniform:
110 case SpvCapabilityGroupNonUniformVote:
111 case SpvCapabilityGroupNonUniformArithmetic:
112 case SpvCapabilityGroupNonUniformBallot:
113 case SpvCapabilityGroupNonUniformShuffle:
114 case SpvCapabilityGroupNonUniformShuffleRelative:
115 case SpvCapabilityGroupNonUniformClustered:
116 case SpvCapabilityGroupNonUniformQuad:
117 case SpvCapabilityDrawParameters:
118 // Alias SpvCapabilityStorageBuffer16BitAccess.
119 case SpvCapabilityStorageUniformBufferBlock16:
120 // Alias SpvCapabilityUniformAndStorageBuffer16BitAccess.
121 case SpvCapabilityStorageUniform16:
122 case SpvCapabilityStoragePushConstant16:
123 case SpvCapabilityStorageInputOutput16:
124 case SpvCapabilityDeviceGroup:
125 case SpvCapabilityMultiView:
126 case SpvCapabilityVariablePointersStorageBuffer:
127 case SpvCapabilityVariablePointers:
128 return true;
129 }
130 return false;
131}
132
133bool IsSupportOptionalVulkan_1_2(uint32_t capability) {
134 if (IsSupportOptionalVulkan_1_1(capability)) return true;
135
136 switch (capability) {
137 case SpvCapabilityDenormPreserve:
138 case SpvCapabilityDenormFlushToZero:
139 case SpvCapabilitySignedZeroInfNanPreserve:
140 case SpvCapabilityRoundingModeRTE:
141 case SpvCapabilityRoundingModeRTZ:
142 case SpvCapabilityVulkanMemoryModel:
143 case SpvCapabilityVulkanMemoryModelDeviceScope:
144 case SpvCapabilityStorageBuffer8BitAccess:
145 case SpvCapabilityUniformAndStorageBuffer8BitAccess:
146 case SpvCapabilityStoragePushConstant8:
147 case SpvCapabilityShaderViewportIndex:
148 case SpvCapabilityShaderLayer:
149 case SpvCapabilityPhysicalStorageBufferAddresses:
150 case SpvCapabilityRuntimeDescriptorArray:
151 case SpvCapabilityUniformTexelBufferArrayDynamicIndexing:
152 case SpvCapabilityStorageTexelBufferArrayDynamicIndexing:
153 case SpvCapabilityUniformBufferArrayNonUniformIndexing:
154 case SpvCapabilitySampledImageArrayNonUniformIndexing:
155 case SpvCapabilityStorageBufferArrayNonUniformIndexing:
156 case SpvCapabilityStorageImageArrayNonUniformIndexing:
157 case SpvCapabilityInputAttachmentArrayNonUniformIndexing:
158 case SpvCapabilityUniformTexelBufferArrayNonUniformIndexing:
159 case SpvCapabilityStorageTexelBufferArrayNonUniformIndexing:
160 return true;
161 }
162 return false;
163}
164
165bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
166 switch (capability) {
167 case SpvCapabilityAddresses:
168 case SpvCapabilityFloat16Buffer:
169 case SpvCapabilityGroups:
170 case SpvCapabilityInt16:
171 case SpvCapabilityInt8:
172 case SpvCapabilityKernel:
173 case SpvCapabilityLinkage:
174 case SpvCapabilityVector16:
175 return true;
176 case SpvCapabilityInt64:
177 return !embedded_profile;
178 case SpvCapabilityPipes:
179 return embedded_profile;
180 }
181 return false;
182}
183
184bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) {
185 if (IsSupportGuaranteedOpenCL_1_2(capability, embedded_profile)) return true;
186
187 switch (capability) {
188 case SpvCapabilityDeviceEnqueue:
189 case SpvCapabilityGenericPointer:
190 case SpvCapabilityPipes:
191 return true;
192 }
193 return false;
194}
195
196bool IsSupportGuaranteedOpenCL_2_2(uint32_t capability, bool embedded_profile) {
197 if (IsSupportGuaranteedOpenCL_2_0(capability, embedded_profile)) return true;
198
199 switch (capability) {
200 case SpvCapabilitySubgroupDispatch:
201 case SpvCapabilityPipeStorage:
202 return true;
203 }
204 return false;
205}
206
207bool IsSupportOptionalOpenCL_1_2(uint32_t capability) {
208 switch (capability) {
209 case SpvCapabilityImageBasic:
210 case SpvCapabilityFloat64:
211 return true;
212 }
213 return false;
214}
215
216// Checks if |capability| was enabled by extension.
217bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) {
218 spv_operand_desc operand_desc = nullptr;
219 _.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
220 &operand_desc);
221
222 // operand_desc is expected to be not null, otherwise validator would have
223 // failed at an earlier stage. This 'assert' is 'just in case'.
224 assert(operand_desc);
225
226 ExtensionSet operand_exts(operand_desc->numExtensions,
227 operand_desc->extensions);
228 if (operand_exts.IsEmpty()) return false;
229
230 return _.HasAnyOfExtensions(operand_exts);
231}
232
233bool IsEnabledByCapabilityOpenCL_1_2(ValidationState_t& _,
234 uint32_t capability) {
235 if (_.HasCapability(SpvCapabilityImageBasic)) {
236 switch (capability) {
237 case SpvCapabilityLiteralSampler:
238 case SpvCapabilitySampled1D:
239 case SpvCapabilityImage1D:
240 case SpvCapabilitySampledBuffer:
241 case SpvCapabilityImageBuffer:
242 return true;
243 }
244 return false;
245 }
246 return false;
247}
248
249bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _,
250 uint32_t capability) {
251 if (_.HasCapability(SpvCapabilityImageBasic)) {
252 switch (capability) {
253 case SpvCapabilityImageReadWrite:
254 case SpvCapabilityLiteralSampler:
255 case SpvCapabilitySampled1D:
256 case SpvCapabilityImage1D:
257 case SpvCapabilitySampledBuffer:
258 case SpvCapabilityImageBuffer:
259 return true;
260 }
261 return false;
262 }
263 return false;
264}
265
266bool IsSupportGuaranteedWebGPU(uint32_t capability) {
267 switch (capability) {
268 case SpvCapabilityMatrix:
269 case SpvCapabilityShader:
270 case SpvCapabilitySampled1D:
271 case SpvCapabilityImage1D:
272 case SpvCapabilityDerivativeControl:
273 case SpvCapabilityImageQuery:
274 return true;
275 }
276 return false;
277}
278
279} // namespace
280
281// Validates that capability declarations use operands allowed in the current
282// context.
283spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) {
284 if (inst->opcode() != SpvOpCapability) return SPV_SUCCESS;
285
286 assert(inst->operands().size() == 1);
287
288 const spv_parsed_operand_t& operand = inst->operand(0);
289
290 assert(operand.num_words == 1);
291 assert(operand.offset < inst->words().size());
292
293 const uint32_t capability = inst->word(operand.offset);
294 const auto capability_str = [&_, capability]() {
295 spv_operand_desc desc = nullptr;
296 if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
297 &desc) != SPV_SUCCESS ||
298 !desc) {
299 return std::string("Unknown");
300 }
301 return std::string(desc->name);
302 };
303
304 const auto env = _.context()->target_env;
305 const bool opencl_embedded = env == SPV_ENV_OPENCL_EMBEDDED_1_2 ||
306 env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
307 env == SPV_ENV_OPENCL_EMBEDDED_2_1 ||
308 env == SPV_ENV_OPENCL_EMBEDDED_2_2;
309 const std::string opencl_profile = opencl_embedded ? "Embedded" : "Full";
310 if (env == SPV_ENV_VULKAN_1_0) {
311 if (!IsSupportGuaranteedVulkan_1_0(capability) &&
312 !IsSupportOptionalVulkan_1_0(capability) &&
313 !IsEnabledByExtension(_, capability)) {
314 return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
315 << "Capability " << capability_str()
316 << " is not allowed by Vulkan 1.0 specification"
317 << " (or requires extension)";
318 }
319 } else if (env == SPV_ENV_VULKAN_1_1) {
320 if (!IsSupportGuaranteedVulkan_1_1(capability) &&
321 !IsSupportOptionalVulkan_1_1(capability) &&
322 !IsEnabledByExtension(_, capability)) {
323 return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
324 << "Capability " << capability_str()
325 << " is not allowed by Vulkan 1.1 specification"
326 << " (or requires extension)";
327 }
328 } else if (env == SPV_ENV_VULKAN_1_2) {
329 if (!IsSupportGuaranteedVulkan_1_2(capability) &&
330 !IsSupportOptionalVulkan_1_2(capability) &&
331 !IsEnabledByExtension(_, capability)) {
332 return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
333 << "Capability " << capability_str()
334 << " is not allowed by Vulkan 1.2 specification"
335 << " (or requires extension)";
336 }
337 } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) {
338 if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) &&
339 !IsSupportOptionalOpenCL_1_2(capability) &&
340 !IsEnabledByExtension(_, capability) &&
341 !IsEnabledByCapabilityOpenCL_1_2(_, capability)) {
342 return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
343 << "Capability " << capability_str()
344 << " is not allowed by OpenCL 1.2 " << opencl_profile
345 << " Profile specification"
346 << " (or requires extension or capability)";
347 }
348 } else if (env == SPV_ENV_OPENCL_2_0 || env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
349 env == SPV_ENV_OPENCL_2_1 || env == SPV_ENV_OPENCL_EMBEDDED_2_1) {
350 if (!IsSupportGuaranteedOpenCL_2_0(capability, opencl_embedded) &&
351 !IsSupportOptionalOpenCL_1_2(capability) &&
352 !IsEnabledByExtension(_, capability) &&
353 !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
354 return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
355 << "Capability " << capability_str()
356 << " is not allowed by OpenCL 2.0/2.1 " << opencl_profile
357 << " Profile specification"
358 << " (or requires extension or capability)";
359 }
360 } else if (env == SPV_ENV_OPENCL_2_2 || env == SPV_ENV_OPENCL_EMBEDDED_2_2) {
361 if (!IsSupportGuaranteedOpenCL_2_2(capability, opencl_embedded) &&
362 !IsSupportOptionalOpenCL_1_2(capability) &&
363 !IsEnabledByExtension(_, capability) &&
364 !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
365 return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
366 << "Capability " << capability_str()
367 << " is not allowed by OpenCL 2.2 " << opencl_profile
368 << " Profile specification"
369 << " (or requires extension or capability)";
370 }
371 } else if (env == SPV_ENV_WEBGPU_0) {
372 if (!IsSupportGuaranteedWebGPU(capability) &&
373 !IsEnabledByExtension(_, capability)) {
374 return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
375 << "Capability " << capability_str()
376 << " is not allowed by WebGPU specification"
377 << " (or requires extension)";
378 }
379 }
380
381 return SPV_SUCCESS;
382}
383
384} // namespace val
385} // namespace spvtools
386