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