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