// 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 "validate.h" #include #include #include "diagnostic.h" #include "opcode.h" #include "val/instruction.h" #include "val/validation_state.h" namespace libspirv { 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 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: return true; } return false; } bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) { switch (capability) { case SpvCapabilityAddresses: case SpvCapabilityFloat16Buffer: case SpvCapabilityGroups: case SpvCapabilityInt16: case SpvCapabilityInt8: case SpvCapabilityKernel: case SpvCapabilityLinkage: case SpvCapabilityVector16: return true; case SpvCapabilityInt64: return !embedded_profile; case SpvCapabilityPipes: 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 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 spv_parsed_instruction_t* inst) { const SpvOp opcode = static_cast(inst->opcode); if (opcode != SpvOpCapability) return SPV_SUCCESS; assert(inst->num_operands == 1); const spv_parsed_operand_t& operand = inst->operands[0]; assert(operand.num_words == 1); assert(operand.offset < inst->num_words); const uint32_t capability = inst->words[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) << "Capability " << capability_str() << " is not allowed by Vulkan 1.0 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) << "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) << "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) << "Capability " << capability_str() << " is not allowed by OpenCL 2.2 " << opencl_profile << " Profile specification" << " (or requires extension or capability)"; } } return SPV_SUCCESS; } } // namespace libspirv