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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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. |
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 | |
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 | |
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 | |
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. |
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 | |