| // Copyright (c) 2017 Google Inc. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // Validates OpCapability instruction. | |
| #include <cassert> | |
| #include <string> | |
| #include <unordered_set> | |
| #include "source/diagnostic.h" | |
| #include "source/opcode.h" | |
| #include "source/val/instruction.h" | |
| #include "source/val/validate.h" | |
| #include "source/val/validation_state.h" | |
| namespace spvtools { | |
| namespace val { | |
| namespace { | |
| bool IsSupportGuaranteedVulkan_1_0(uint32_t capability) { | |
| switch (capability) { | |
| case SpvCapabilityMatrix: | |
| case SpvCapabilityShader: | |
| case SpvCapabilityInputAttachment: | |
| case SpvCapabilitySampled1D: | |
| case SpvCapabilityImage1D: | |
| case SpvCapabilitySampledBuffer: | |
| case SpvCapabilityImageBuffer: | |
| case SpvCapabilityImageQuery: | |
| case SpvCapabilityDerivativeControl: | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool IsSupportGuaranteedVulkan_1_1(uint32_t capability) { | |
| if (IsSupportGuaranteedVulkan_1_0(capability)) return true; | |
| switch (capability) { | |
| case SpvCapabilityDeviceGroup: | |
| case SpvCapabilityMultiView: | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool IsSupportGuaranteedVulkan_1_2(uint32_t capability) { | |
| if (IsSupportGuaranteedVulkan_1_1(capability)) return true; | |
| switch (capability) { | |
| case SpvCapabilityShaderNonUniform: | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool IsSupportOptionalVulkan_1_0(uint32_t capability) { | |
| switch (capability) { | |
| case SpvCapabilityGeometry: | |
| case SpvCapabilityTessellation: | |
| case SpvCapabilityFloat64: | |
| case SpvCapabilityInt64: | |
| case SpvCapabilityInt16: | |
| case SpvCapabilityTessellationPointSize: | |
| case SpvCapabilityGeometryPointSize: | |
| case SpvCapabilityImageGatherExtended: | |
| case SpvCapabilityStorageImageMultisample: | |
| case SpvCapabilityUniformBufferArrayDynamicIndexing: | |
| case SpvCapabilitySampledImageArrayDynamicIndexing: | |
| case SpvCapabilityStorageBufferArrayDynamicIndexing: | |
| case SpvCapabilityStorageImageArrayDynamicIndexing: | |
| case SpvCapabilityClipDistance: | |
| case SpvCapabilityCullDistance: | |
| case SpvCapabilityImageCubeArray: | |
| case SpvCapabilitySampleRateShading: | |
| case SpvCapabilitySparseResidency: | |
| case SpvCapabilityMinLod: | |
| case SpvCapabilitySampledCubeArray: | |
| case SpvCapabilityImageMSArray: | |
| case SpvCapabilityStorageImageExtendedFormats: | |
| case SpvCapabilityInterpolationFunction: | |
| case SpvCapabilityStorageImageReadWithoutFormat: | |
| case SpvCapabilityStorageImageWriteWithoutFormat: | |
| case SpvCapabilityMultiViewport: | |
| case SpvCapabilityInt64Atomics: | |
| case SpvCapabilityTransformFeedback: | |
| case SpvCapabilityGeometryStreams: | |
| case SpvCapabilityFloat16: | |
| case SpvCapabilityInt8: | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool IsSupportOptionalVulkan_1_1(uint32_t capability) { | |
| if (IsSupportOptionalVulkan_1_0(capability)) return true; | |
| switch (capability) { | |
| case SpvCapabilityGroupNonUniform: | |
| case SpvCapabilityGroupNonUniformVote: | |
| case SpvCapabilityGroupNonUniformArithmetic: | |
| case SpvCapabilityGroupNonUniformBallot: | |
| case SpvCapabilityGroupNonUniformShuffle: | |
| case SpvCapabilityGroupNonUniformShuffleRelative: | |
| case SpvCapabilityGroupNonUniformClustered: | |
| case SpvCapabilityGroupNonUniformQuad: | |
| case SpvCapabilityDrawParameters: | |
| // Alias SpvCapabilityStorageBuffer16BitAccess. | |
| case SpvCapabilityStorageUniformBufferBlock16: | |
| // Alias SpvCapabilityUniformAndStorageBuffer16BitAccess. | |
| case SpvCapabilityStorageUniform16: | |
| case SpvCapabilityStoragePushConstant16: | |
| case SpvCapabilityStorageInputOutput16: | |
| case SpvCapabilityDeviceGroup: | |
| case SpvCapabilityMultiView: | |
| case SpvCapabilityVariablePointersStorageBuffer: | |
| case SpvCapabilityVariablePointers: | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool IsSupportOptionalVulkan_1_2(uint32_t capability) { | |
| if (IsSupportOptionalVulkan_1_1(capability)) return true; | |
| switch (capability) { | |
| case SpvCapabilityDenormPreserve: | |
| case SpvCapabilityDenormFlushToZero: | |
| case SpvCapabilitySignedZeroInfNanPreserve: | |
| case SpvCapabilityRoundingModeRTE: | |
| case SpvCapabilityRoundingModeRTZ: | |
| case SpvCapabilityVulkanMemoryModel: | |
| case SpvCapabilityVulkanMemoryModelDeviceScope: | |
| case SpvCapabilityStorageBuffer8BitAccess: | |
| case SpvCapabilityUniformAndStorageBuffer8BitAccess: | |
| case SpvCapabilityStoragePushConstant8: | |
| case SpvCapabilityShaderViewportIndex: | |
| case SpvCapabilityShaderLayer: | |
| case SpvCapabilityPhysicalStorageBufferAddresses: | |
| case SpvCapabilityRuntimeDescriptorArray: | |
| case SpvCapabilityUniformTexelBufferArrayDynamicIndexing: | |
| case SpvCapabilityStorageTexelBufferArrayDynamicIndexing: | |
| case SpvCapabilityUniformBufferArrayNonUniformIndexing: | |
| case SpvCapabilitySampledImageArrayNonUniformIndexing: | |
| case SpvCapabilityStorageBufferArrayNonUniformIndexing: | |
| case SpvCapabilityStorageImageArrayNonUniformIndexing: | |
| case SpvCapabilityInputAttachmentArrayNonUniformIndexing: | |
| case SpvCapabilityUniformTexelBufferArrayNonUniformIndexing: | |
| case SpvCapabilityStorageTexelBufferArrayNonUniformIndexing: | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) { | |
| switch (capability) { | |
| case SpvCapabilityAddresses: | |
| case SpvCapabilityFloat16Buffer: | |
| case SpvCapabilityInt16: | |
| case SpvCapabilityInt8: | |
| case SpvCapabilityKernel: | |
| case SpvCapabilityLinkage: | |
| case SpvCapabilityVector16: | |
| return true; | |
| case SpvCapabilityInt64: | |
| return !embedded_profile; | |
| } | |
| return false; | |
| } | |
| bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) { | |
| if (IsSupportGuaranteedOpenCL_1_2(capability, embedded_profile)) return true; | |
| switch (capability) { | |
| case SpvCapabilityDeviceEnqueue: | |
| case SpvCapabilityGenericPointer: | |
| case SpvCapabilityGroups: | |
| case SpvCapabilityPipes: | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool IsSupportGuaranteedOpenCL_2_2(uint32_t capability, bool embedded_profile) { | |
| if (IsSupportGuaranteedOpenCL_2_0(capability, embedded_profile)) return true; | |
| switch (capability) { | |
| case SpvCapabilitySubgroupDispatch: | |
| case SpvCapabilityPipeStorage: | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool IsSupportOptionalOpenCL_1_2(uint32_t capability) { | |
| switch (capability) { | |
| case SpvCapabilityImageBasic: | |
| case SpvCapabilityFloat64: | |
| return true; | |
| } | |
| return false; | |
| } | |
| // Checks if |capability| was enabled by extension. | |
| bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) { | |
| spv_operand_desc operand_desc = nullptr; | |
| _.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, | |
| &operand_desc); | |
| // operand_desc is expected to be not null, otherwise validator would have | |
| // failed at an earlier stage. This 'assert' is 'just in case'. | |
| assert(operand_desc); | |
| ExtensionSet operand_exts(operand_desc->numExtensions, | |
| operand_desc->extensions); | |
| if (operand_exts.IsEmpty()) return false; | |
| return _.HasAnyOfExtensions(operand_exts); | |
| } | |
| bool IsEnabledByCapabilityOpenCL_1_2(ValidationState_t& _, | |
| uint32_t capability) { | |
| if (_.HasCapability(SpvCapabilityImageBasic)) { | |
| switch (capability) { | |
| case SpvCapabilityLiteralSampler: | |
| case SpvCapabilitySampled1D: | |
| case SpvCapabilityImage1D: | |
| case SpvCapabilitySampledBuffer: | |
| case SpvCapabilityImageBuffer: | |
| return true; | |
| } | |
| return false; | |
| } | |
| return false; | |
| } | |
| bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _, | |
| uint32_t capability) { | |
| if (_.HasCapability(SpvCapabilityImageBasic)) { | |
| switch (capability) { | |
| case SpvCapabilityImageReadWrite: | |
| case SpvCapabilityLiteralSampler: | |
| case SpvCapabilitySampled1D: | |
| case SpvCapabilityImage1D: | |
| case SpvCapabilitySampledBuffer: | |
| case SpvCapabilityImageBuffer: | |
| return true; | |
| } | |
| return false; | |
| } | |
| return false; | |
| } | |
| } // namespace | |
| // Validates that capability declarations use operands allowed in the current | |
| // context. | |
| spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) { | |
| if (inst->opcode() != SpvOpCapability) return SPV_SUCCESS; | |
| assert(inst->operands().size() == 1); | |
| const spv_parsed_operand_t& operand = inst->operand(0); | |
| assert(operand.num_words == 1); | |
| assert(operand.offset < inst->words().size()); | |
| const uint32_t capability = inst->word(operand.offset); | |
| const auto capability_str = [&_, capability]() { | |
| spv_operand_desc desc = nullptr; | |
| if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, | |
| &desc) != SPV_SUCCESS || | |
| !desc) { | |
| return std::string("Unknown"); | |
| } | |
| return std::string(desc->name); | |
| }; | |
| const auto env = _.context()->target_env; | |
| const bool opencl_embedded = env == SPV_ENV_OPENCL_EMBEDDED_1_2 || | |
| env == SPV_ENV_OPENCL_EMBEDDED_2_0 || | |
| env == SPV_ENV_OPENCL_EMBEDDED_2_1 || | |
| env == SPV_ENV_OPENCL_EMBEDDED_2_2; | |
| const std::string opencl_profile = opencl_embedded ? "Embedded" : "Full"; | |
| if (env == SPV_ENV_VULKAN_1_0) { | |
| if (!IsSupportGuaranteedVulkan_1_0(capability) && | |
| !IsSupportOptionalVulkan_1_0(capability) && | |
| !IsEnabledByExtension(_, capability)) { | |
| return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) | |
| << "Capability " << capability_str() | |
| << " is not allowed by Vulkan 1.0 specification" | |
| << " (or requires extension)"; | |
| } | |
| } else if (env == SPV_ENV_VULKAN_1_1) { | |
| if (!IsSupportGuaranteedVulkan_1_1(capability) && | |
| !IsSupportOptionalVulkan_1_1(capability) && | |
| !IsEnabledByExtension(_, capability)) { | |
| return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) | |
| << "Capability " << capability_str() | |
| << " is not allowed by Vulkan 1.1 specification" | |
| << " (or requires extension)"; | |
| } | |
| } else if (env == SPV_ENV_VULKAN_1_2) { | |
| if (!IsSupportGuaranteedVulkan_1_2(capability) && | |
| !IsSupportOptionalVulkan_1_2(capability) && | |
| !IsEnabledByExtension(_, capability)) { | |
| return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) | |
| << "Capability " << capability_str() | |
| << " is not allowed by Vulkan 1.2 specification" | |
| << " (or requires extension)"; | |
| } | |
| } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) { | |
| if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) && | |
| !IsSupportOptionalOpenCL_1_2(capability) && | |
| !IsEnabledByExtension(_, capability) && | |
| !IsEnabledByCapabilityOpenCL_1_2(_, capability)) { | |
| return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) | |
| << "Capability " << capability_str() | |
| << " is not allowed by OpenCL 1.2 " << opencl_profile | |
| << " Profile specification" | |
| << " (or requires extension or capability)"; | |
| } | |
| } else if (env == SPV_ENV_OPENCL_2_0 || env == SPV_ENV_OPENCL_EMBEDDED_2_0 || | |
| env == SPV_ENV_OPENCL_2_1 || env == SPV_ENV_OPENCL_EMBEDDED_2_1) { | |
| if (!IsSupportGuaranteedOpenCL_2_0(capability, opencl_embedded) && | |
| !IsSupportOptionalOpenCL_1_2(capability) && | |
| !IsEnabledByExtension(_, capability) && | |
| !IsEnabledByCapabilityOpenCL_2_0(_, capability)) { | |
| return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) | |
| << "Capability " << capability_str() | |
| << " is not allowed by OpenCL 2.0/2.1 " << opencl_profile | |
| << " Profile specification" | |
| << " (or requires extension or capability)"; | |
| } | |
| } else if (env == SPV_ENV_OPENCL_2_2 || env == SPV_ENV_OPENCL_EMBEDDED_2_2) { | |
| if (!IsSupportGuaranteedOpenCL_2_2(capability, opencl_embedded) && | |
| !IsSupportOptionalOpenCL_1_2(capability) && | |
| !IsEnabledByExtension(_, capability) && | |
| !IsEnabledByCapabilityOpenCL_2_0(_, capability)) { | |
| return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) | |
| << "Capability " << capability_str() | |
| << " is not allowed by OpenCL 2.2 " << opencl_profile | |
| << " Profile specification" | |
| << " (or requires extension or capability)"; | |
| } | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| } // namespace val | |
| } // namespace spvtools |