Skip to content

Commit edd878b

Browse files
committed
[SPIR-V] Add SPIR-V structurizer
This commit adds an initial SPIR-V structurizer. It leverages the previously merged passes, and the convergence region analysis to determine the correct merge and continue blocks for SPIR-V. The first part does a branch cleanup (simplifying switches, and legalizing them), then merge instructions are added to cycles, convergent and later divergent blocks. Then comes the important part: splitting critical edges, and making sure the divergent construct boundaries don't cross. - we split blocks with multiple headers into 2 blocks. - we split blocks that are a merge blocks for 2 or more constructs: SPIR-V spec disallow a merge block to be shared by 2 loop/switch/condition construct. - we split merge & continue blocks: SPIR-V spec disallow a basic block to be both a continue block, and a merge block. - we remove superfluous headers: when a header doesn't bring more info than the parent on the divergence state, it must be removed. This PR leverages the merged SPIR-V simulator for testing, as long as spirv-val. For now, most DXC structurization tests are passing. The unsupported ones are either caused by unsupported features like switches on boolean types, or switches in region exits, because the MergeExit pass doesn't support those yet (there is a FIXME). This PR is quite large, and the addition not trivial, so I tried to keep it simple. E.G: as soon as the CFG changes, I recompute the dominator trees and other structures instead of updating them.
1 parent 5a658ee commit edd878b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+4774
-252
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
2+
// RUN: spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s
3+
4+
int process() {
5+
// CHECK: entry:
6+
// CHECK: %[[#entry_token:]] = call token @llvm.experimental.convergence.entry()
7+
int val = 0;
8+
9+
// CHECK: for.cond:
10+
// CHECK-NEXT: %[[#]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %[[#entry_token]]) ]
11+
// CHECK: br i1 {{.*}}, label %for.body, label %for.end
12+
for (int i = 0; i < 10; ++i) {
13+
14+
// CHECK: for.body:
15+
// CHECK: br label %for.inc
16+
val = i;
17+
18+
// CHECK: for.inc:
19+
// CHECK: br label %for.cond
20+
}
21+
22+
// CHECK: for.end:
23+
// CHECK: br label %for.cond1
24+
25+
// Infinite loop
26+
for ( ; ; ) {
27+
// CHECK: for.cond1:
28+
// CHECK-NEXT: %[[#]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %[[#entry_token]]) ]
29+
// CHECK: br label %for.cond1
30+
val = 0;
31+
}
32+
33+
// CHECK-NEXT: }
34+
// This loop in unreachable. Not generated.
35+
// Null body
36+
for (int j = 0; j < 10; ++j)
37+
;
38+
return val;
39+
}
40+
41+
[numthreads(1, 1, 1)]
42+
void main() {
43+
process();
44+
}

llvm/include/llvm/IR/IntrinsicsSPIRV.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ let TargetPrefix = "spv" in {
3131
def int_spv_bitcast : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
3232
def int_spv_ptrcast : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_metadata_ty, llvm_i32_ty], [ImmArg<ArgIndex<2>>]>;
3333
def int_spv_switch : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
34+
def int_spv_loop_merge : Intrinsic<[], [llvm_vararg_ty]>;
35+
def int_spv_selection_merge : Intrinsic<[], [llvm_vararg_ty]>;
3436
def int_spv_cmpxchg : Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_vararg_ty]>;
3537
def int_spv_unreachable : Intrinsic<[], []>;
3638
def int_spv_alloca : Intrinsic<[llvm_any_ty], []>;

llvm/lib/Target/SPIRV/Analysis/SPIRVConvergenceRegionAnalysis.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ class ConvergenceRegionAnalyzer {
203203

204204
private:
205205
bool isBackEdge(const BasicBlock *From, const BasicBlock *To) const {
206-
assert(From != To && "From == To. This is awkward.");
206+
if (From == To)
207+
return true;
207208

208209
// We only handle loop in the simplified form. This means:
209210
// - a single back-edge, a single latch.
@@ -230,6 +231,7 @@ class ConvergenceRegionAnalyzer {
230231
auto *Terminator = From->getTerminator();
231232
for (unsigned i = 0; i < Terminator->getNumSuccessors(); ++i) {
232233
auto *To = Terminator->getSuccessor(i);
234+
// Ignore back edges.
233235
if (isBackEdge(From, To))
234236
continue;
235237

@@ -276,7 +278,6 @@ class ConvergenceRegionAnalyzer {
276278
while (ToProcess.size() != 0) {
277279
auto *L = ToProcess.front();
278280
ToProcess.pop();
279-
assert(L->isLoopSimplifyForm());
280281

281282
auto CT = getConvergenceToken(L->getHeader());
282283
SmallPtrSet<BasicBlock *, 8> RegionBlocks(L->block_begin(),

llvm/lib/Target/SPIRV/Analysis/SPIRVConvergenceRegionAnalysis.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ class ConvergenceRegionInfo {
130130
}
131131

132132
const ConvergenceRegion *getTopLevelRegion() const { return TopLevelRegion; }
133+
ConvergenceRegion *getWritableTopLevelRegion() const {
134+
return TopLevelRegion;
135+
}
133136
};
134137

135138
} // namespace SPIRV

llvm/lib/Target/SPIRV/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ add_llvm_target(SPIRVCodeGen
3131
SPIRVMCInstLower.cpp
3232
SPIRVMetadata.cpp
3333
SPIRVModuleAnalysis.cpp
34+
SPIRVStructurizer.cpp
3435
SPIRVPreLegalizer.cpp
3536
SPIRVPostLegalizer.cpp
3637
SPIRVPrepareFunctions.cpp

llvm/lib/Target/SPIRV/SPIRV.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class InstructionSelector;
2020
class RegisterBankInfo;
2121

2222
ModulePass *createSPIRVPrepareFunctionsPass(const SPIRVTargetMachine &TM);
23+
FunctionPass *createSPIRVStructurizerPass();
2324
FunctionPass *createSPIRVMergeRegionExitTargetsPass();
2425
FunctionPass *createSPIRVStripConvergenceIntrinsicsPass();
2526
FunctionPass *createSPIRVRegularizerPass();

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,6 +2296,19 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
22962296
}
22972297
return MIB.constrainAllUses(TII, TRI, RBI);
22982298
}
2299+
case Intrinsic::spv_loop_merge:
2300+
case Intrinsic::spv_selection_merge: {
2301+
const auto Opcode = IID == Intrinsic::spv_selection_merge
2302+
? SPIRV::OpSelectionMerge
2303+
: SPIRV::OpLoopMerge;
2304+
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode));
2305+
for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) {
2306+
assert(I.getOperand(i).isMBB());
2307+
MIB.addMBB(I.getOperand(i).getMBB());
2308+
}
2309+
MIB.addImm(SPIRV::SelectionControl::None);
2310+
return MIB.constrainAllUses(TII, TRI, RBI);
2311+
}
22992312
case Intrinsic::spv_cmpxchg:
23002313
return selectAtomicCmpXchg(ResVReg, ResType, I);
23012314
case Intrinsic::spv_unreachable:

llvm/lib/Target/SPIRV/SPIRVMergeRegionExitTargets.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class SPIRVMergeRegionExitTargets : public FunctionPass {
133133
// Run the pass on the given convergence region, ignoring the sub-regions.
134134
// Returns true if the CFG changed, false otherwise.
135135
bool runOnConvergenceRegionNoRecurse(LoopInfo &LI,
136-
const SPIRV::ConvergenceRegion *CR) {
136+
SPIRV::ConvergenceRegion *CR) {
137137
// Gather all the exit targets for this region.
138138
SmallPtrSet<BasicBlock *, 4> ExitTargets;
139139
for (BasicBlock *Exit : CR->Exits) {
@@ -198,14 +198,19 @@ class SPIRVMergeRegionExitTargets : public FunctionPass {
198198
for (auto Exit : CR->Exits)
199199
replaceBranchTargets(Exit, ExitTargets, NewExitTarget);
200200

201+
CR = CR->Parent;
202+
while (CR) {
203+
CR->Blocks.insert(NewExitTarget);
204+
CR = CR->Parent;
205+
}
206+
201207
return true;
202208
}
203209

204210
/// Run the pass on the given convergence region and sub-regions (DFS).
205211
/// Returns true if a region/sub-region was modified, false otherwise.
206212
/// This returns as soon as one region/sub-region has been modified.
207-
bool runOnConvergenceRegion(LoopInfo &LI,
208-
const SPIRV::ConvergenceRegion *CR) {
213+
bool runOnConvergenceRegion(LoopInfo &LI, SPIRV::ConvergenceRegion *CR) {
209214
for (auto *Child : CR->Children)
210215
if (runOnConvergenceRegion(LI, Child))
211216
return true;
@@ -235,20 +240,17 @@ class SPIRVMergeRegionExitTargets : public FunctionPass {
235240

236241
virtual bool runOnFunction(Function &F) override {
237242
LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
238-
const auto *TopLevelRegion =
243+
auto *TopLevelRegion =
239244
getAnalysis<SPIRVConvergenceRegionAnalysisWrapperPass>()
240245
.getRegionInfo()
241-
.getTopLevelRegion();
246+
.getWritableTopLevelRegion();
242247

243248
// FIXME: very inefficient method: each time a region is modified, we bubble
244249
// back up, and recompute the whole convergence region tree. Once the
245250
// algorithm is completed and test coverage good enough, rewrite this pass
246251
// to be efficient instead of simple.
247252
bool modified = false;
248253
while (runOnConvergenceRegion(LI, TopLevelRegion)) {
249-
TopLevelRegion = getAnalysis<SPIRVConvergenceRegionAnalysisWrapperPass>()
250-
.getRegionInfo()
251-
.getTopLevelRegion();
252254
modified = true;
253255
}
254256

@@ -262,6 +264,8 @@ class SPIRVMergeRegionExitTargets : public FunctionPass {
262264
AU.addRequired<DominatorTreeWrapperPass>();
263265
AU.addRequired<LoopInfoWrapperPass>();
264266
AU.addRequired<SPIRVConvergenceRegionAnalysisWrapperPass>();
267+
268+
AU.addPreserved<SPIRVConvergenceRegionAnalysisWrapperPass>();
265269
FunctionPass::getAnalysisUsage(AU);
266270
}
267271
};

0 commit comments

Comments
 (0)