xref: /aosp_15_r20/external/angle/third_party/spirv-tools/src/source/val/validate_capability.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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/val/instruction.h"
22 #include "source/val/validate.h"
23 #include "source/val/validation_state.h"
24 
25 namespace spvtools {
26 namespace val {
27 namespace {
28 
IsSupportGuaranteedVulkan_1_0(uint32_t capability)29 bool IsSupportGuaranteedVulkan_1_0(uint32_t capability) {
30   switch (spv::Capability(capability)) {
31     case spv::Capability::Matrix:
32     case spv::Capability::Shader:
33     case spv::Capability::InputAttachment:
34     case spv::Capability::Sampled1D:
35     case spv::Capability::Image1D:
36     case spv::Capability::SampledBuffer:
37     case spv::Capability::ImageBuffer:
38     case spv::Capability::ImageQuery:
39     case spv::Capability::DerivativeControl:
40       return true;
41     default:
42       break;
43   }
44   return false;
45 }
46 
IsSupportGuaranteedVulkan_1_1(uint32_t capability)47 bool IsSupportGuaranteedVulkan_1_1(uint32_t capability) {
48   if (IsSupportGuaranteedVulkan_1_0(capability)) return true;
49   switch (spv::Capability(capability)) {
50     case spv::Capability::DeviceGroup:
51     case spv::Capability::MultiView:
52       return true;
53     default:
54       break;
55   }
56   return false;
57 }
58 
IsSupportGuaranteedVulkan_1_2(uint32_t capability)59 bool IsSupportGuaranteedVulkan_1_2(uint32_t capability) {
60   if (IsSupportGuaranteedVulkan_1_1(capability)) return true;
61   switch (spv::Capability(capability)) {
62     case spv::Capability::ShaderNonUniform:
63       return true;
64     default:
65       break;
66   }
67   return false;
68 }
69 
IsSupportOptionalVulkan_1_0(uint32_t capability)70 bool IsSupportOptionalVulkan_1_0(uint32_t capability) {
71   switch (spv::Capability(capability)) {
72     case spv::Capability::Geometry:
73     case spv::Capability::Tessellation:
74     case spv::Capability::Float64:
75     case spv::Capability::Int64:
76     case spv::Capability::Int16:
77     case spv::Capability::TessellationPointSize:
78     case spv::Capability::GeometryPointSize:
79     case spv::Capability::ImageGatherExtended:
80     case spv::Capability::StorageImageMultisample:
81     case spv::Capability::UniformBufferArrayDynamicIndexing:
82     case spv::Capability::SampledImageArrayDynamicIndexing:
83     case spv::Capability::StorageBufferArrayDynamicIndexing:
84     case spv::Capability::StorageImageArrayDynamicIndexing:
85     case spv::Capability::ClipDistance:
86     case spv::Capability::CullDistance:
87     case spv::Capability::ImageCubeArray:
88     case spv::Capability::SampleRateShading:
89     case spv::Capability::SparseResidency:
90     case spv::Capability::MinLod:
91     case spv::Capability::SampledCubeArray:
92     case spv::Capability::ImageMSArray:
93     case spv::Capability::StorageImageExtendedFormats:
94     case spv::Capability::InterpolationFunction:
95     case spv::Capability::StorageImageReadWithoutFormat:
96     case spv::Capability::StorageImageWriteWithoutFormat:
97     case spv::Capability::MultiViewport:
98     case spv::Capability::Int64Atomics:
99     case spv::Capability::TransformFeedback:
100     case spv::Capability::GeometryStreams:
101     case spv::Capability::Float16:
102     case spv::Capability::Int8:
103       return true;
104     default:
105       break;
106   }
107   return false;
108 }
109 
IsSupportOptionalVulkan_1_1(uint32_t capability)110 bool IsSupportOptionalVulkan_1_1(uint32_t capability) {
111   if (IsSupportOptionalVulkan_1_0(capability)) return true;
112 
113   switch (spv::Capability(capability)) {
114     case spv::Capability::GroupNonUniform:
115     case spv::Capability::GroupNonUniformVote:
116     case spv::Capability::GroupNonUniformArithmetic:
117     case spv::Capability::GroupNonUniformBallot:
118     case spv::Capability::GroupNonUniformShuffle:
119     case spv::Capability::GroupNonUniformShuffleRelative:
120     case spv::Capability::GroupNonUniformClustered:
121     case spv::Capability::GroupNonUniformQuad:
122     case spv::Capability::DrawParameters:
123     // Alias spv::Capability::StorageBuffer16BitAccess.
124     case spv::Capability::StorageUniformBufferBlock16:
125     // Alias spv::Capability::UniformAndStorageBuffer16BitAccess.
126     case spv::Capability::StorageUniform16:
127     case spv::Capability::StoragePushConstant16:
128     case spv::Capability::StorageInputOutput16:
129     case spv::Capability::DeviceGroup:
130     case spv::Capability::MultiView:
131     case spv::Capability::VariablePointersStorageBuffer:
132     case spv::Capability::VariablePointers:
133       return true;
134     default:
135       break;
136   }
137   return false;
138 }
139 
IsSupportOptionalVulkan_1_2(uint32_t capability)140 bool IsSupportOptionalVulkan_1_2(uint32_t capability) {
141   if (IsSupportOptionalVulkan_1_1(capability)) return true;
142 
143   switch (spv::Capability(capability)) {
144     case spv::Capability::DenormPreserve:
145     case spv::Capability::DenormFlushToZero:
146     case spv::Capability::SignedZeroInfNanPreserve:
147     case spv::Capability::RoundingModeRTE:
148     case spv::Capability::RoundingModeRTZ:
149     case spv::Capability::VulkanMemoryModel:
150     case spv::Capability::VulkanMemoryModelDeviceScope:
151     case spv::Capability::StorageBuffer8BitAccess:
152     case spv::Capability::UniformAndStorageBuffer8BitAccess:
153     case spv::Capability::StoragePushConstant8:
154     case spv::Capability::ShaderViewportIndex:
155     case spv::Capability::ShaderLayer:
156     case spv::Capability::PhysicalStorageBufferAddresses:
157     case spv::Capability::RuntimeDescriptorArray:
158     case spv::Capability::UniformTexelBufferArrayDynamicIndexing:
159     case spv::Capability::StorageTexelBufferArrayDynamicIndexing:
160     case spv::Capability::UniformBufferArrayNonUniformIndexing:
161     case spv::Capability::SampledImageArrayNonUniformIndexing:
162     case spv::Capability::StorageBufferArrayNonUniformIndexing:
163     case spv::Capability::StorageImageArrayNonUniformIndexing:
164     case spv::Capability::InputAttachmentArrayNonUniformIndexing:
165     case spv::Capability::UniformTexelBufferArrayNonUniformIndexing:
166     case spv::Capability::StorageTexelBufferArrayNonUniformIndexing:
167       return true;
168     default:
169       break;
170   }
171   return false;
172 }
173 
IsSupportGuaranteedOpenCL_1_2(uint32_t capability,bool embedded_profile)174 bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
175   switch (spv::Capability(capability)) {
176     case spv::Capability::Addresses:
177     case spv::Capability::Float16Buffer:
178     case spv::Capability::Int16:
179     case spv::Capability::Int8:
180     case spv::Capability::Kernel:
181     case spv::Capability::Linkage:
182     case spv::Capability::Vector16:
183       return true;
184     case spv::Capability::Int64:
185       return !embedded_profile;
186     default:
187       break;
188   }
189   return false;
190 }
191 
IsSupportGuaranteedOpenCL_2_0(uint32_t capability,bool embedded_profile)192 bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) {
193   if (IsSupportGuaranteedOpenCL_1_2(capability, embedded_profile)) return true;
194 
195   switch (spv::Capability(capability)) {
196     case spv::Capability::DeviceEnqueue:
197     case spv::Capability::GenericPointer:
198     case spv::Capability::Groups:
199     case spv::Capability::Pipes:
200       return true;
201     default:
202       break;
203   }
204   return false;
205 }
206 
IsSupportGuaranteedOpenCL_2_2(uint32_t capability,bool embedded_profile)207 bool IsSupportGuaranteedOpenCL_2_2(uint32_t capability, bool embedded_profile) {
208   if (IsSupportGuaranteedOpenCL_2_0(capability, embedded_profile)) return true;
209 
210   switch (spv::Capability(capability)) {
211     case spv::Capability::SubgroupDispatch:
212     case spv::Capability::PipeStorage:
213       return true;
214     default:
215       break;
216   }
217   return false;
218 }
219 
IsSupportOptionalOpenCL_1_2(uint32_t capability)220 bool IsSupportOptionalOpenCL_1_2(uint32_t capability) {
221   switch (spv::Capability(capability)) {
222     case spv::Capability::ImageBasic:
223     case spv::Capability::Float64:
224       return true;
225     default:
226       break;
227   }
228   return false;
229 }
230 
231 // Checks if |capability| was enabled by extension.
IsEnabledByExtension(ValidationState_t & _,uint32_t capability)232 bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) {
233   spv_operand_desc operand_desc = nullptr;
234   _.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
235                             &operand_desc);
236 
237   // operand_desc is expected to be not null, otherwise validator would have
238   // failed at an earlier stage. This 'assert' is 'just in case'.
239   assert(operand_desc);
240 
241   ExtensionSet operand_exts(operand_desc->numExtensions,
242                             operand_desc->extensions);
243   if (operand_exts.empty()) return false;
244 
245   return _.HasAnyOfExtensions(operand_exts);
246 }
247 
IsEnabledByCapabilityOpenCL_1_2(ValidationState_t & _,uint32_t capability)248 bool IsEnabledByCapabilityOpenCL_1_2(ValidationState_t& _,
249                                      uint32_t capability) {
250   if (_.HasCapability(spv::Capability::ImageBasic)) {
251     switch (spv::Capability(capability)) {
252       case spv::Capability::LiteralSampler:
253       case spv::Capability::Sampled1D:
254       case spv::Capability::Image1D:
255       case spv::Capability::SampledBuffer:
256       case spv::Capability::ImageBuffer:
257         return true;
258       default:
259         break;
260     }
261     return false;
262   }
263   return false;
264 }
265 
IsEnabledByCapabilityOpenCL_2_0(ValidationState_t & _,uint32_t capability)266 bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _,
267                                      uint32_t capability) {
268   if (_.HasCapability(spv::Capability::ImageBasic)) {
269     switch (spv::Capability(capability)) {
270       case spv::Capability::ImageReadWrite:
271       case spv::Capability::LiteralSampler:
272       case spv::Capability::Sampled1D:
273       case spv::Capability::Image1D:
274       case spv::Capability::SampledBuffer:
275       case spv::Capability::ImageBuffer:
276         return true;
277       default:
278         break;
279     }
280     return false;
281   }
282   return false;
283 }
284 
285 }  // namespace
286 
287 // Validates that capability declarations use operands allowed in the current
288 // context.
CapabilityPass(ValidationState_t & _,const Instruction * inst)289 spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) {
290   if (inst->opcode() != spv::Op::OpCapability) return SPV_SUCCESS;
291 
292   assert(inst->operands().size() == 1);
293 
294   const spv_parsed_operand_t& operand = inst->operand(0);
295 
296   assert(operand.num_words == 1);
297   assert(operand.offset < inst->words().size());
298 
299   const uint32_t capability = inst->word(operand.offset);
300   const auto capability_str = [&_, capability]() {
301     spv_operand_desc desc = nullptr;
302     if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability,
303                                   &desc) != SPV_SUCCESS ||
304         !desc) {
305       return std::string("Unknown");
306     }
307     return std::string(desc->name);
308   };
309 
310   const auto env = _.context()->target_env;
311   const bool opencl_embedded = env == SPV_ENV_OPENCL_EMBEDDED_1_2 ||
312                                env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
313                                env == SPV_ENV_OPENCL_EMBEDDED_2_1 ||
314                                env == SPV_ENV_OPENCL_EMBEDDED_2_2;
315   const std::string opencl_profile = opencl_embedded ? "Embedded" : "Full";
316   if (env == SPV_ENV_VULKAN_1_0) {
317     if (!IsSupportGuaranteedVulkan_1_0(capability) &&
318         !IsSupportOptionalVulkan_1_0(capability) &&
319         !IsEnabledByExtension(_, capability)) {
320       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
321              << "Capability " << capability_str()
322              << " is not allowed by Vulkan 1.0 specification"
323              << " (or requires extension)";
324     }
325   } else if (env == SPV_ENV_VULKAN_1_1) {
326     if (!IsSupportGuaranteedVulkan_1_1(capability) &&
327         !IsSupportOptionalVulkan_1_1(capability) &&
328         !IsEnabledByExtension(_, capability)) {
329       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
330              << "Capability " << capability_str()
331              << " is not allowed by Vulkan 1.1 specification"
332              << " (or requires extension)";
333     }
334   } else if (env == SPV_ENV_VULKAN_1_2) {
335     if (!IsSupportGuaranteedVulkan_1_2(capability) &&
336         !IsSupportOptionalVulkan_1_2(capability) &&
337         !IsEnabledByExtension(_, capability)) {
338       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
339              << "Capability " << capability_str()
340              << " is not allowed by Vulkan 1.2 specification"
341              << " (or requires extension)";
342     }
343   } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) {
344     if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) &&
345         !IsSupportOptionalOpenCL_1_2(capability) &&
346         !IsEnabledByExtension(_, capability) &&
347         !IsEnabledByCapabilityOpenCL_1_2(_, capability)) {
348       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
349              << "Capability " << capability_str()
350              << " is not allowed by OpenCL 1.2 " << opencl_profile
351              << " Profile specification"
352              << " (or requires extension or capability)";
353     }
354   } else if (env == SPV_ENV_OPENCL_2_0 || env == SPV_ENV_OPENCL_EMBEDDED_2_0 ||
355              env == SPV_ENV_OPENCL_2_1 || env == SPV_ENV_OPENCL_EMBEDDED_2_1) {
356     if (!IsSupportGuaranteedOpenCL_2_0(capability, opencl_embedded) &&
357         !IsSupportOptionalOpenCL_1_2(capability) &&
358         !IsEnabledByExtension(_, capability) &&
359         !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
360       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
361              << "Capability " << capability_str()
362              << " is not allowed by OpenCL 2.0/2.1 " << opencl_profile
363              << " Profile specification"
364              << " (or requires extension or capability)";
365     }
366   } else if (env == SPV_ENV_OPENCL_2_2 || env == SPV_ENV_OPENCL_EMBEDDED_2_2) {
367     if (!IsSupportGuaranteedOpenCL_2_2(capability, opencl_embedded) &&
368         !IsSupportOptionalOpenCL_1_2(capability) &&
369         !IsEnabledByExtension(_, capability) &&
370         !IsEnabledByCapabilityOpenCL_2_0(_, capability)) {
371       return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
372              << "Capability " << capability_str()
373              << " is not allowed by OpenCL 2.2 " << opencl_profile
374              << " Profile specification"
375              << " (or requires extension or capability)";
376     }
377   }
378 
379   return SPV_SUCCESS;
380 }
381 
382 }  // namespace val
383 }  // namespace spvtools
384