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