Skip to content

Commit dfb9bf3

Browse files
let a user select preferred/unpreferred capabilities in a list of enabling capabilities (#81476)
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. One important case is Shader capability that may not be supported by a backend, but always is inserted if "OpDecorate SpecId" is found, because Enabling Capabilities for the latter is the list of Shader and Kernel, where Shader is coming first and thus always selected as the first available option. In this PR we address the problem by keeping current behaviour of selecting the first option among enabling capabilities as is, but giving a user a way to filter capabilities during the selection process via a newly introduced "--avoid-spirv-capabilities" command line option. This option is to avoid selection of certain capabilities if there are other available enabling capabilities. This PR is changing also existing pruneCapabilities() function. It doesn't remove capability from module requirement anymore, but only adds implicitly required capabilities recursively, so its name is changed accordingly. This change fixes the present bug in collecting required by a module capabilities. Before the change, introduced by this PR, pruneCapabilities() function has been removing, for example, Kernel capability from required by a module, because Kernel is initially required and the second time it was needed pruneCapabilities() removed it by mistake.
1 parent 09b80e6 commit dfb9bf3

File tree

3 files changed

+45
-13
lines changed

3 files changed

+45
-13
lines changed

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@ static cl::opt<bool>
3535
cl::desc("Dump MIR with SPIR-V dependencies info"),
3636
cl::Optional, cl::init(false));
3737

38+
static cl::list<SPIRV::Capability::Capability>
39+
AvoidCapabilities("avoid-spirv-capabilities",
40+
cl::desc("SPIR-V capabilities to avoid if there are "
41+
"other options enabling a feature"),
42+
cl::ZeroOrMore, cl::Hidden,
43+
cl::values(clEnumValN(SPIRV::Capability::Shader, "Shader",
44+
"SPIR-V Shader capability")));
45+
// Use sets instead of cl::list to check "if contains" condition
46+
struct AvoidCapabilitiesSet {
47+
SmallSet<SPIRV::Capability::Capability, 4> S;
48+
AvoidCapabilitiesSet() {
49+
for (auto Cap : AvoidCapabilities)
50+
S.insert(Cap);
51+
}
52+
};
53+
3854
char llvm::SPIRVModuleAnalysis::ID = 0;
3955

4056
namespace llvm {
@@ -58,6 +74,8 @@ static SPIRV::Requirements
5874
getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,
5975
unsigned i, const SPIRVSubtarget &ST,
6076
SPIRV::RequirementHandler &Reqs) {
77+
static AvoidCapabilitiesSet
78+
AvoidCaps; // contains capabilities to avoid if there is another option
6179
unsigned ReqMinVer = getSymbolicOperandMinVersion(Category, i);
6280
unsigned ReqMaxVer = getSymbolicOperandMaxVersion(Category, i);
6381
unsigned TargetVer = ST.getSPIRVVersion();
@@ -72,9 +90,26 @@ getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,
7290
return {false, {}, {}, 0, 0};
7391
}
7492
} else if (MinVerOK && MaxVerOK) {
75-
for (auto Cap : ReqCaps) { // Only need 1 of the capabilities to work.
93+
if (ReqCaps.size() == 1) {
94+
auto Cap = ReqCaps[0];
7695
if (Reqs.isCapabilityAvailable(Cap))
7796
return {true, {Cap}, {}, ReqMinVer, ReqMaxVer};
97+
} else {
98+
// By SPIR-V specification: "If an instruction, enumerant, or other
99+
// feature specifies multiple enabling capabilities, only one such
100+
// capability needs to be declared to use the feature." However, one
101+
// capability may be preferred over another. We use command line
102+
// argument(s) and AvoidCapabilities to avoid selection of certain
103+
// capabilities if there are other options.
104+
CapabilityList UseCaps;
105+
for (auto Cap : ReqCaps)
106+
if (Reqs.isCapabilityAvailable(Cap))
107+
UseCaps.push_back(Cap);
108+
for (size_t i = 0, Sz = UseCaps.size(); i < Sz; ++i) {
109+
auto Cap = UseCaps[i];
110+
if (i == Sz - 1 || !AvoidCaps.S.contains(Cap))
111+
return {true, {Cap}, {}, ReqMinVer, ReqMaxVer};
112+
}
78113
}
79114
}
80115
// If there are no capabilities, or we can't satisfy the version or
@@ -432,16 +467,13 @@ void SPIRV::RequirementHandler::getAndAddRequirements(
432467
addRequirements(getSymbolicOperandRequirements(Category, i, ST, *this));
433468
}
434469

435-
void SPIRV::RequirementHandler::pruneCapabilities(
470+
void SPIRV::RequirementHandler::recursiveAddCapabilities(
436471
const CapabilityList &ToPrune) {
437472
for (const auto &Cap : ToPrune) {
438473
AllCaps.insert(Cap);
439-
auto FoundIndex = llvm::find(MinimalCaps, Cap);
440-
if (FoundIndex != MinimalCaps.end())
441-
MinimalCaps.erase(FoundIndex);
442474
CapabilityList ImplicitDecls =
443475
getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap);
444-
pruneCapabilities(ImplicitDecls);
476+
recursiveAddCapabilities(ImplicitDecls);
445477
}
446478
}
447479

@@ -452,7 +484,7 @@ void SPIRV::RequirementHandler::addCapabilities(const CapabilityList &ToAdd) {
452484
continue;
453485
CapabilityList ImplicitDecls =
454486
getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap);
455-
pruneCapabilities(ImplicitDecls);
487+
recursiveAddCapabilities(ImplicitDecls);
456488
MinimalCaps.push_back(Cap);
457489
}
458490
}

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ struct RequirementHandler {
7171
SmallSet<Extension::Extension, 4> AllExtensions;
7272
unsigned MinVersion; // 0 if no min version is defined.
7373
unsigned MaxVersion; // 0 if no max version is defined.
74-
// Remove a list of capabilities from dedupedCaps and add them to AllCaps,
75-
// recursing through their implicitly declared capabilities too.
76-
void pruneCapabilities(const CapabilityList &ToPrune);
74+
// Add capabilities to AllCaps, recursing through their implicitly declared
75+
// capabilities too.
76+
void recursiveAddCapabilities(const CapabilityList &ToPrune);
7777

7878
void initAvailableCapabilitiesForOpenCL(const SPIRVSubtarget &ST);
7979
void initAvailableCapabilitiesForVulkan(const SPIRVSubtarget &ST);

llvm/test/CodeGen/SPIRV/transcoding/spec_const.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
2-
; XFAIL: *
1+
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown --avoid-spirv-capabilities=Shader %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
2+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown --avoid-spirv-capabilities=Shader %s -o - -filetype=obj | spirv-val %}
33

4+
; CHECK-SPIRV-DAG: OpCapability Kernel
45
; CHECK-SPIRV-NOT: OpCapability Matrix
56
; CHECK-SPIRV-NOT: OpCapability Shader
6-
; CHECK-SPIRV: OpCapability Kernel
77

88
; CHECK-SPIRV-DAG: OpDecorate %[[#SC0:]] SpecId 0
99
; CHECK-SPIRV-DAG: OpDecorate %[[#SC1:]] SpecId 1

0 commit comments

Comments
 (0)