diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp index a18aae1761c83..9f14ea5dbe19b 100644 --- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp @@ -35,6 +35,22 @@ static cl::opt cl::desc("Dump MIR with SPIR-V dependencies info"), cl::Optional, cl::init(false)); +static cl::list + AvoidCapabilities("avoid-spirv-capabilities", + cl::desc("SPIR-V capabilities to avoid if there are " + "other options enabling a feature"), + cl::ZeroOrMore, cl::Hidden, + cl::values(clEnumValN(SPIRV::Capability::Shader, "Shader", + "SPIR-V Shader capability"))); +// Use sets instead of cl::list to check "if contains" condition +struct AvoidCapabilitiesSet { + SmallSet S; + AvoidCapabilitiesSet() { + for (auto Cap : AvoidCapabilities) + S.insert(Cap); + } +}; + char llvm::SPIRVModuleAnalysis::ID = 0; namespace llvm { @@ -58,6 +74,8 @@ static SPIRV::Requirements getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category, unsigned i, const SPIRVSubtarget &ST, SPIRV::RequirementHandler &Reqs) { + static AvoidCapabilitiesSet + AvoidCaps; // contains capabilities to avoid if there is another option unsigned ReqMinVer = getSymbolicOperandMinVersion(Category, i); unsigned ReqMaxVer = getSymbolicOperandMaxVersion(Category, i); unsigned TargetVer = ST.getSPIRVVersion(); @@ -72,9 +90,26 @@ getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category, return {false, {}, {}, 0, 0}; } } else if (MinVerOK && MaxVerOK) { - for (auto Cap : ReqCaps) { // Only need 1 of the capabilities to work. + if (ReqCaps.size() == 1) { + auto Cap = ReqCaps[0]; if (Reqs.isCapabilityAvailable(Cap)) return {true, {Cap}, {}, ReqMinVer, ReqMaxVer}; + } else { + // By SPIR-V specification: "If an instruction, enumerant, or other + // feature specifies multiple enabling capabilities, only one such + // capability needs to be declared to use the feature." However, one + // capability may be preferred over another. We use command line + // argument(s) and AvoidCapabilities to avoid selection of certain + // capabilities if there are other options. + CapabilityList UseCaps; + for (auto Cap : ReqCaps) + if (Reqs.isCapabilityAvailable(Cap)) + UseCaps.push_back(Cap); + for (size_t i = 0, Sz = UseCaps.size(); i < Sz; ++i) { + auto Cap = UseCaps[i]; + if (i == Sz - 1 || !AvoidCaps.S.contains(Cap)) + return {true, {Cap}, {}, ReqMinVer, ReqMaxVer}; + } } } // If there are no capabilities, or we can't satisfy the version or @@ -432,16 +467,13 @@ void SPIRV::RequirementHandler::getAndAddRequirements( addRequirements(getSymbolicOperandRequirements(Category, i, ST, *this)); } -void SPIRV::RequirementHandler::pruneCapabilities( +void SPIRV::RequirementHandler::recursiveAddCapabilities( const CapabilityList &ToPrune) { for (const auto &Cap : ToPrune) { AllCaps.insert(Cap); - auto FoundIndex = llvm::find(MinimalCaps, Cap); - if (FoundIndex != MinimalCaps.end()) - MinimalCaps.erase(FoundIndex); CapabilityList ImplicitDecls = getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap); - pruneCapabilities(ImplicitDecls); + recursiveAddCapabilities(ImplicitDecls); } } @@ -452,7 +484,7 @@ void SPIRV::RequirementHandler::addCapabilities(const CapabilityList &ToAdd) { continue; CapabilityList ImplicitDecls = getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap); - pruneCapabilities(ImplicitDecls); + recursiveAddCapabilities(ImplicitDecls); MinimalCaps.push_back(Cap); } } diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h index b05526b06e7da..708384fc55f52 100644 --- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h +++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h @@ -71,9 +71,9 @@ struct RequirementHandler { SmallSet AllExtensions; unsigned MinVersion; // 0 if no min version is defined. unsigned MaxVersion; // 0 if no max version is defined. - // Remove a list of capabilities from dedupedCaps and add them to AllCaps, - // recursing through their implicitly declared capabilities too. - void pruneCapabilities(const CapabilityList &ToPrune); + // Add capabilities to AllCaps, recursing through their implicitly declared + // capabilities too. + void recursiveAddCapabilities(const CapabilityList &ToPrune); void initAvailableCapabilitiesForOpenCL(const SPIRVSubtarget &ST); void initAvailableCapabilitiesForVulkan(const SPIRVSubtarget &ST); diff --git a/llvm/test/CodeGen/SPIRV/transcoding/spec_const.ll b/llvm/test/CodeGen/SPIRV/transcoding/spec_const.ll index c47dccb35e14d..8ce76534c50db 100644 --- a/llvm/test/CodeGen/SPIRV/transcoding/spec_const.ll +++ b/llvm/test/CodeGen/SPIRV/transcoding/spec_const.ll @@ -1,9 +1,9 @@ -; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV -; XFAIL: * +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown --avoid-spirv-capabilities=Shader %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown --avoid-spirv-capabilities=Shader %s -o - -filetype=obj | spirv-val %} +; CHECK-SPIRV-DAG: OpCapability Kernel ; CHECK-SPIRV-NOT: OpCapability Matrix ; CHECK-SPIRV-NOT: OpCapability Shader -; CHECK-SPIRV: OpCapability Kernel ; CHECK-SPIRV-DAG: OpDecorate %[[#SC0:]] SpecId 0 ; CHECK-SPIRV-DAG: OpDecorate %[[#SC1:]] SpecId 1