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