Skip to content

Commit 1ed65fe

Browse files
authored
[SPIR-V] Add SPIR-V structurizer (#107408)
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. --------- Signed-off-by: Nathan Gauër <[email protected]>
1 parent 9a32f28 commit 1ed65fe

File tree

68 files changed

+8709
-314
lines changed

Some content is hidden

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

68 files changed

+8709
-314
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
@@ -32,6 +32,7 @@ add_llvm_target(SPIRVCodeGen
3232
SPIRVMCInstLower.cpp
3333
SPIRVMetadata.cpp
3434
SPIRVModuleAnalysis.cpp
35+
SPIRVStructurizer.cpp
3536
SPIRVPreLegalizer.cpp
3637
SPIRVPostLegalizer.cpp
3738
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/SPIRVInstrInfo.td

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -617,15 +617,13 @@ def OpFwidthCoarse: UnOp<"OpFwidthCoarse", 215>;
617617

618618
def OpPhi: Op<245, (outs ID:$res), (ins TYPE:$type, ID:$var0, ID:$block0, variable_ops),
619619
"$res = OpPhi $type $var0 $block0">;
620-
def OpLoopMerge: Op<246, (outs), (ins ID:$merge, ID:$continue, LoopControl:$lc, variable_ops),
620+
def OpLoopMerge: Op<246, (outs), (ins unknown:$merge, unknown:$continue, LoopControl:$lc, variable_ops),
621621
"OpLoopMerge $merge $continue $lc">;
622-
def OpSelectionMerge: Op<247, (outs), (ins ID:$merge, SelectionControl:$sc),
622+
def OpSelectionMerge: Op<247, (outs), (ins unknown:$merge, SelectionControl:$sc),
623623
"OpSelectionMerge $merge $sc">;
624624
def OpLabel: Op<248, (outs ID:$label), (ins), "$label = OpLabel">;
625625
let isBarrier = 1, isTerminator=1 in {
626626
def OpBranch: Op<249, (outs), (ins unknown:$label), "OpBranch $label">;
627-
}
628-
let isTerminator=1 in {
629627
def OpBranchConditional: Op<250, (outs), (ins ID:$cond, unknown:$true, unknown:$false, variable_ops),
630628
"OpBranchConditional $cond $true $false">;
631629
def OpSwitch: Op<251, (outs), (ins ID:$sel, ID:$dflt, variable_ops), "OpSwitch $sel $dflt">;

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2425,6 +2425,19 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
24252425
}
24262426
return MIB.constrainAllUses(TII, TRI, RBI);
24272427
}
2428+
case Intrinsic::spv_loop_merge:
2429+
case Intrinsic::spv_selection_merge: {
2430+
const auto Opcode = IID == Intrinsic::spv_selection_merge
2431+
? SPIRV::OpSelectionMerge
2432+
: SPIRV::OpLoopMerge;
2433+
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode));
2434+
for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) {
2435+
assert(I.getOperand(i).isMBB());
2436+
MIB.addMBB(I.getOperand(i).getMBB());
2437+
}
2438+
MIB.addImm(SPIRV::SelectionControl::None);
2439+
return MIB.constrainAllUses(TII, TRI, RBI);
2440+
}
24282441
case Intrinsic::spv_cmpxchg:
24292442
return selectAtomicCmpXchg(ResVReg, ResType, I);
24302443
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
};

llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -175,23 +175,6 @@ void visit(MachineFunction &MF, std::function<void(MachineBasicBlock *)> op) {
175175
visit(MF, *MF.begin(), op);
176176
}
177177

178-
// Sorts basic blocks by dominance to respect the SPIR-V spec.
179-
void sortBlocks(MachineFunction &MF) {
180-
MachineDominatorTree MDT(MF);
181-
182-
std::unordered_map<MachineBasicBlock *, size_t> Order;
183-
Order.reserve(MF.size());
184-
185-
size_t Index = 0;
186-
visit(MF, [&Order, &Index](MachineBasicBlock *MBB) { Order[MBB] = Index++; });
187-
188-
auto Comparator = [&Order](MachineBasicBlock &LHS, MachineBasicBlock &RHS) {
189-
return Order[&LHS] < Order[&RHS];
190-
};
191-
192-
MF.sort(Comparator);
193-
}
194-
195178
bool SPIRVPostLegalizer::runOnMachineFunction(MachineFunction &MF) {
196179
// Initialize the type registry.
197180
const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>();
@@ -200,7 +183,6 @@ bool SPIRVPostLegalizer::runOnMachineFunction(MachineFunction &MF) {
200183
MachineIRBuilder MIB(MF);
201184

202185
processNewInstrs(MF, GR, MIB);
203-
sortBlocks(MF);
204186

205187
return true;
206188
}

0 commit comments

Comments
 (0)