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