• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsSupportGuaranteedVulkan_1_2(uint32_t capability)58 bool 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 
IsSupportOptionalVulkan_1_0(uint32_t capability)67 bool 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 
IsSupportOptionalVulkan_1_1(uint32_t capability)105 bool 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 
IsSupportOptionalVulkan_1_2(uint32_t capability)133 bool 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 
IsSupportGuaranteedOpenCL_1_2(uint32_t capability,bool embedded_profile)165 bool 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 
IsSupportGuaranteedOpenCL_2_0(uint32_t capability,bool embedded_profile)184 bool 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 
IsSupportGuaranteedOpenCL_2_2(uint32_t capability,bool embedded_profile)196 bool 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 
IsSupportOptionalOpenCL_1_2(uint32_t capability)207 bool 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.
IsEnabledByExtension(ValidationState_t & _,uint32_t capability)217 bool 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 
IsEnabledByCapabilityOpenCL_1_2(ValidationState_t & _,uint32_t capability)233 bool 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 
IsEnabledByCapabilityOpenCL_2_0(ValidationState_t & _,uint32_t capability)249 bool 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 
IsSupportGuaranteedWebGPU(uint32_t capability)266 bool 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.
CapabilityPass(ValidationState_t & _,const Instruction * inst)283 spv_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