Skip to content

Commit 00285f6

Browse files
committed
[SPIR-V] Sort basic blocks to match the SPIR-V spec
The SPIR-V spec required basic blocks to respect some kind of ordering (A block dominating another cannot be after in the binary layout). Signed-off-by: Nathan Gauër <[email protected]>
1 parent a6d81cd commit 00285f6

13 files changed

+235
-106
lines changed

llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
#include "SPIRVUtils.h"
2020
#include "llvm/ADT/PostOrderIterator.h"
2121
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
22+
#include "llvm/CodeGen/MachinePostDominators.h"
2223
#include "llvm/IR/Attributes.h"
2324
#include "llvm/IR/Constants.h"
2425
#include "llvm/IR/DebugInfoMetadata.h"
2526
#include "llvm/IR/IntrinsicsSPIRV.h"
2627
#include "llvm/Target/TargetIntrinsicInfo.h"
28+
#include <stack>
2729

2830
#define DEBUG_TYPE "spirv-postlegalizer"
2931

@@ -150,6 +152,51 @@ static void processNewInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
150152
}
151153
}
152154

155+
// Do a preorder traversal of the CFG starting from the BB |Start|.
156+
// point. Calls |op| on each basic block encountered during the traversal.
157+
void visit(MachineFunction &MF, MachineBasicBlock &Start,
158+
std::function<void(MachineBasicBlock *)> op) {
159+
std::stack<MachineBasicBlock *> ToVisit;
160+
SmallPtrSet<MachineBasicBlock *, 8> Seen;
161+
162+
ToVisit.push(&Start);
163+
Seen.insert(ToVisit.top());
164+
while (ToVisit.size() != 0) {
165+
MachineBasicBlock *MBB = ToVisit.top();
166+
ToVisit.pop();
167+
168+
op(MBB);
169+
170+
for (auto Succ : MBB->successors()) {
171+
if (Seen.contains(Succ))
172+
continue;
173+
ToVisit.push(Succ);
174+
Seen.insert(Succ);
175+
}
176+
}
177+
}
178+
179+
// Do a preorder traversal of the CFG starting from the given function's entry
180+
// point. Calls |op| on each basic block encountered during the traversal.
181+
void visit(MachineFunction &MF, std::function<void(MachineBasicBlock *)> op) {
182+
visit(MF, *MF.begin(), op);
183+
}
184+
185+
// Sorts basic blocks by dominance to respect the SPIR-V spec.
186+
void sortBlocks(MachineFunction &MF) {
187+
MachineDominatorTree MDT(MF);
188+
189+
std::unordered_map<MachineBasicBlock *, size_t> Order;
190+
size_t Index = 0;
191+
visit(MF, [&Order, &Index](MachineBasicBlock *MBB) { Order[MBB] = Index++; });
192+
193+
auto Comparator = [&Order](MachineBasicBlock &LHS, MachineBasicBlock &RHS) {
194+
return Order[&LHS] < Order[&RHS];
195+
};
196+
197+
MF.sort(Comparator);
198+
}
199+
153200
bool SPIRVPostLegalizer::runOnMachineFunction(MachineFunction &MF) {
154201
// Initialize the type registry.
155202
const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>();
@@ -158,6 +205,7 @@ bool SPIRVPostLegalizer::runOnMachineFunction(MachineFunction &MF) {
158205
MachineIRBuilder MIB(MF);
159206

160207
processNewInstrs(MF, GR, MIB);
208+
sortBlocks(MF);
161209

162210
return true;
163211
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK
2+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
3+
4+
; Checks SPIR-V blocks are correctly reordered so that dominators shows up
5+
; before others in the binary layout.
6+
7+
define void @main() {
8+
; CHECK: OpLabel
9+
; CHECK: OpBranch %[[#l1:]]
10+
11+
; CHECK: %[[#l1]] = OpLabel
12+
; CHECK: OpBranch %[[#l2:]]
13+
14+
; CHECK: %[[#l2]] = OpLabel
15+
; CHECK: OpBranch %[[#end:]]
16+
17+
; CHECK: %[[#end]] = OpLabel
18+
; CHECK: OpReturn
19+
entry:
20+
br label %l1
21+
22+
l2:
23+
br label %end
24+
25+
l1:
26+
br label %l2
27+
28+
end:
29+
ret void
30+
}

llvm/test/CodeGen/SPIRV/branching/OpSwitchBranches.ll

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,38 @@ entry:
1010
i32 3, label %case3
1111
]
1212

13-
; CHECK-SPIRV: %[[#CASE1]] = OpLabel
1413
case1:
1514
store i32 1, ptr %alloc
16-
; CHECK-SPIRV: OpBranch %[[#END:]]
1715
br label %end
1816

19-
; CHECK-SPIRV: %[[#CASE2]] = OpLabel
2017
case2:
2118
store i32 2, ptr %alloc
22-
; CHECK-SPIRV: OpBranch %[[#END]]
2319
br label %end
2420

25-
; CHECK-SPIRV: %[[#CASE3]] = OpLabel
2621
case3:
2722
store i32 3, ptr %alloc
28-
; CHECK-SPIRV: OpBranch %[[#END]]
2923
br label %end
3024

31-
; CHECK-SPIRV: %[[#DEFAULT]] = OpLabel
3225
default:
3326
store i32 0, ptr %alloc
34-
; CHECK-SPIRV: OpBranch %[[#END]]
3527
br label %end
3628

37-
; CHECK-SPIRV: %[[#END]] = OpLabel
3829
end:
3930
%result = load i32, ptr %alloc
4031
ret i32 %result
32+
33+
; CHECK-SPIRV: %[[#CASE3]] = OpLabel
34+
; CHECK-SPIRV: OpBranch %[[#END:]]
35+
36+
; CHECK-SPIRV: %[[#END]] = OpLabel
37+
; CHECK-SPIRV: OpReturnValue
38+
39+
; CHECK-SPIRV: %[[#CASE2]] = OpLabel
40+
; CHECK-SPIRV: OpBranch %[[#END]]
41+
42+
; CHECK-SPIRV: %[[#CASE1]] = OpLabel
43+
; CHECK-SPIRV: OpBranch %[[#END]]
44+
45+
; CHECK-SPIRV: %[[#DEFAULT]] = OpLabel
46+
; CHECK-SPIRV: OpBranch %[[#END]]
4147
}
Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
2+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
23

34
define spir_kernel void @test_two_switch_same_register(i32 %value) {
45
; CHECK-SPIRV: OpSwitch %[[#REGISTER:]] %[[#DEFAULT1:]] 1 %[[#CASE1:]] 0 %[[#CASE2:]]
@@ -7,36 +8,42 @@ define spir_kernel void @test_two_switch_same_register(i32 %value) {
78
i32 0, label %case2
89
]
910

10-
; CHECK-SPIRV: %[[#CASE1]] = OpLabel
1111
case1:
12-
; CHECK-SPIRV-NEXT: OpBranch %[[#DEFAULT1]]
1312
br label %default1
1413

15-
; CHECK-SPIRV: %[[#CASE2]] = OpLabel
1614
case2:
17-
; CHECK-SPIRV-NEXT: OpBranch %[[#DEFAULT1]]
1815
br label %default1
1916

20-
; CHECK-SPIRV: %[[#DEFAULT1]] = OpLabel
2117
default1:
22-
; CHECK-SPIRV-NEXT: OpSwitch %[[#REGISTER]] %[[#DEFAULT2:]] 0 %[[#CASE3:]] 1 %[[#CASE4:]]
2318
switch i32 %value, label %default2 [
2419
i32 0, label %case3
2520
i32 1, label %case4
2621
]
2722

28-
; CHECK-SPIRV: %[[#CASE3]] = OpLabel
2923
case3:
30-
; CHECK-SPIRV-NEXT: OpBranch %[[#DEFAULT2]]
3124
br label %default2
3225

33-
; CHECK-SPIRV: %[[#CASE4]] = OpLabel
3426
case4:
35-
; CHECK-SPIRV-NEXT: OpBranch %[[#DEFAULT2]]
3627
br label %default2
3728

38-
; CHECK-SPIRV: %[[#DEFAULT2]] = OpLabel
3929
default2:
40-
; CHECK-SPIRV-NEXT: OpReturn
4130
ret void
31+
32+
; CHECK-SPIRV: %[[#CASE2]] = OpLabel
33+
; CHECK-SPIRV-NEXT: OpBranch %[[#DEFAULT1]]
34+
35+
; CHECK-SPIRV: %[[#CASE1]] = OpLabel
36+
; CHECK-SPIRV-NEXT: OpBranch %[[#DEFAULT1]]
37+
38+
; CHECK-SPIRV: %[[#DEFAULT1]] = OpLabel
39+
; CHECK-SPIRV-NEXT: OpSwitch %[[#REGISTER]] %[[#DEFAULT2:]] 0 %[[#CASE3:]] 1 %[[#CASE4:]]
40+
41+
; CHECK-SPIRV: %[[#CASE4:]] = OpLabel
42+
; CHECK-SPIRV-NEXT: OpBranch %[[#DEFAULT2]]
43+
44+
; CHECK-SPIRV: %[[#CASE3]] = OpLabel
45+
; CHECK-SPIRV-NEXT: OpBranch %[[#DEFAULT2]]
46+
47+
; CHECK-SPIRV: %[[#DEFAULT2]] = OpLabel
48+
; CHECK-SPIRV-NEXT: OpReturn
4249
}

llvm/test/CodeGen/SPIRV/branching/if-merging.ll

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,16 @@ merge_label:
3737
; CHECK: [[COND:%.+]] = OpIEqual [[BOOL]] [[A]] [[B]]
3838
; CHECK: OpBranchConditional [[COND]] [[TRUE_LABEL:%.+]] [[FALSE_LABEL:%.+]]
3939

40-
; CHECK: [[TRUE_LABEL]] = OpLabel
41-
; CHECK: [[V1:%.+]] = OpFunctionCall [[I32]] [[FOO]]
42-
; CHECK: OpBranch [[MERGE_LABEL:%.+]]
43-
4440
; CHECK: [[FALSE_LABEL]] = OpLabel
4541
; CHECK: [[V2:%.+]] = OpFunctionCall [[I32]] [[BAR]]
46-
; CHECK: OpBranch [[MERGE_LABEL]]
42+
; CHECK: OpBranch [[MERGE_LABEL:%.+]]
4743

4844
; CHECK: [[MERGE_LABEL]] = OpLabel
49-
; CHECK-NEXT: [[V:%.+]] = OpPhi [[I32]] [[V1]] [[TRUE_LABEL]] [[V2]] [[FALSE_LABEL]]
45+
; CHECK-NEXT: [[V:%.+]] = OpPhi [[I32]] [[V1:%.+]] [[TRUE_LABEL]] [[V2]] [[FALSE_LABEL]]
5046
; CHECK: OpReturnValue [[V]]
47+
48+
; CHECK: [[TRUE_LABEL]] = OpLabel
49+
; CHECK: [[V1]] = OpFunctionCall [[I32]] [[FOO]]
50+
; CHECK: OpBranch [[MERGE_LABEL]]
51+
5152
; CHECK-NEXT: OpFunctionEnd

llvm/test/CodeGen/SPIRV/branching/if-non-merging.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ false_label:
2121
; CHECK: [[ENTRY:%.+]] = OpLabel
2222
; CHECK: [[COND:%.+]] = OpIEqual [[BOOL]] [[A]] [[B]]
2323
; CHECK: OpBranchConditional [[COND]] [[TRUE_LABEL:%.+]] [[FALSE_LABEL:%.+]]
24-
; CHECK: [[TRUE_LABEL]] = OpLabel
25-
; CHECK: OpReturnValue [[TRUE]]
2624
; CHECK: [[FALSE_LABEL]] = OpLabel
2725
; CHECK: OpReturnValue [[FALSE]]
26+
; CHECK: [[TRUE_LABEL]] = OpLabel
27+
; CHECK: OpReturnValue [[TRUE]]

llvm/test/CodeGen/SPIRV/branching/switch-range-check.ll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
33

44
; CHECK: OpFunction
5+
; CHECK: OpBranchConditional %[[#]] %[[#if_then:]] %[[#if_end:]]
6+
; CHECK: %[[#if_end]] = OpLabel
57
; CHECK: %[[#Var:]] = OpPhi
68
; CHECK: OpSwitch %[[#Var]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]] [[#]] %[[#]]
7-
; CHECK-COUNT-11: OpBranch
9+
; CHECK-COUNT-11: OpLabel
810
; CHECK-NOT: OpBranch
911
; CHECK: OpReturn
12+
; CHECK: %[[#if_then]] = OpLabel
13+
; CHECK: OpBranch %[[#if_end]]
1014
; CHECK-NEXT: OpFunctionEnd
1115

1216
define spir_func void @foo(i64 noundef %addr, i64 noundef %as) {

llvm/test/CodeGen/SPIRV/phi-ptrcast-dominate.ll

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,89 +6,103 @@
66
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
77
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
88

9+
910
; CHECK-DAG: OpName %[[#Case1:]] "case1"
1011
; CHECK-DAG: OpName %[[#Case2:]] "case2"
1112
; CHECK-DAG: OpName %[[#Case3:]] "case3"
12-
; CHECK: %[[#Case1]] = OpFunction
13-
; CHECK: OpBranchConditional
14-
; CHECK: OpPhi
15-
; CHECK: OpBranch
16-
; CHECK-COUNT-2: OpBranchConditional
17-
; CHECK: OpFunctionEnd
18-
; CHECK: %[[#Case2]] = OpFunction
19-
; CHECK: OpBranchConditional
20-
; CHECK: OpPhi
21-
; CHECK: OpBranch
22-
; CHECK-COUNT-2: OpBranchConditional
23-
; CHECK: OpFunctionEnd
24-
; CHECK: %[[#Case3]] = OpFunction
25-
; CHECK: OpBranchConditional
26-
; CHECK: OpPhi
27-
; CHECK: OpBranch
28-
; CHECK: OpInBoundsPtrAccessChain
29-
; CHECK: OpBranchConditional
30-
; CHECK: OpInBoundsPtrAccessChain
31-
; CHECK: OpBranchConditional
32-
; CHECK: OpFunctionEnd
3313

3414
%struct1 = type { i64 }
3515
%struct2 = type { i64, i64 }
3616

3717
@.str.1 = private unnamed_addr addrspace(1) constant [3 x i8] c"OK\00", align 1
3818
@.str.2 = private unnamed_addr addrspace(1) constant [6 x i8] c"WRONG\00", align 1
3919

20+
; CHECK: %[[#Case1]] = OpFunction
4021
define spir_func void @case1(i1 %b1, i1 %b2, i1 %b3) {
4122
entry:
23+
; CHECK: OpBranchConditional %[[#]] %[[#l1:]] %[[#l2:]]
4224
br i1 %b1, label %l1, label %l2
4325

4426
l1:
4527
%str = phi ptr addrspace(1) [ @.str.1, %entry ], [ @.str.2, %l2 ], [ @.str.2, %l3 ]
4628
br label %exit
4729

30+
; CHECK: %[[#l2]] = OpLabel
31+
; CHECK: OpBranchConditional %[[#]] %[[#l1:]] %[[#l3:]]
4832
l2:
4933
br i1 %b2, label %l1, label %l3
5034

35+
; CHECK: %[[#l3]] = OpLabel
36+
; CHECK: OpBranchConditional %[[#]] %[[#l1:]] %[[#exit:]]
5137
l3:
5238
br i1 %b3, label %l1, label %exit
5339

40+
; CHECK: %[[#exit]] = OpLabel
41+
; CHECK: OpReturn
5442
exit:
5543
ret void
44+
45+
; CHECK: %[[#l1]] = OpLabel
46+
; CHECK-NEXT: OpPhi
47+
; CHECK: OpBranch %[[#exit:]]
5648
}
5749

50+
; CHECK: %[[#Case2]] = OpFunction
5851
define spir_func void @case2(i1 %b1, i1 %b2, i1 %b3, ptr addrspace(1) byval(%struct1) %str1, ptr addrspace(1) byval(%struct2) %str2) {
5952
entry:
53+
; CHECK: OpBranchConditional %[[#]] %[[#l1:]] %[[#l2:]]
6054
br i1 %b1, label %l1, label %l2
6155

6256
l1:
6357
%str = phi ptr addrspace(1) [ %str1, %entry ], [ %str2, %l2 ], [ %str2, %l3 ]
6458
br label %exit
6559

60+
; CHECK: %[[#l2]] = OpLabel
61+
; CHECK: OpBranchConditional %[[#]] %[[#l1:]] %[[#l3:]]
6662
l2:
6763
br i1 %b2, label %l1, label %l3
6864

65+
; CHECK: %[[#l3]] = OpLabel
66+
; CHECK: OpBranchConditional %[[#]] %[[#l1:]] %[[#exit:]]
6967
l3:
7068
br i1 %b3, label %l1, label %exit
7169

70+
; CHECK: %[[#exit]] = OpLabel
71+
; CHECK: OpReturn
7272
exit:
7373
ret void
7474
}
7575

76+
; CHECK: %[[#Case3]] = OpFunction
7677
define spir_func void @case3(i1 %b1, i1 %b2, i1 %b3, ptr addrspace(1) byval(%struct1) %_arg_str1, ptr addrspace(1) byval(%struct2) %_arg_str2) {
7778
entry:
7879
br i1 %b1, label %l1, label %l2
7980

81+
; CHECK: OpBranchConditional %[[#]] %[[#l1:]] %[[#l2:]]
8082
l1:
8183
%str = phi ptr addrspace(1) [ %_arg_str1, %entry ], [ %str2, %l2 ], [ %str3, %l3 ]
8284
br label %exit
8385

86+
; CHECK: %[[#l2]] = OpLabel
87+
; CHECK: OpInBoundsPtrAccessChain
88+
; CHECK: OpBranchConditional %[[#]] %[[#l1:]] %[[#l3:]]
8489
l2:
8590
%str2 = getelementptr inbounds %struct2, ptr addrspace(1) %_arg_str2, i32 1
8691
br i1 %b2, label %l1, label %l3
8792

93+
; CHECK: %[[#l3]] = OpLabel
94+
; CHECK: OpInBoundsPtrAccessChain
95+
; CHECK: OpBranchConditional %[[#]] %[[#l1:]] %[[#exit:]]
8896
l3:
8997
%str3 = getelementptr inbounds %struct2, ptr addrspace(1) %_arg_str2, i32 2
9098
br i1 %b3, label %l1, label %exit
9199

100+
; CHECK: %[[#exit]] = OpLabel
101+
; CHECK: OpReturn
92102
exit:
93103
ret void
104+
105+
; CHECK: %[[#l1]] = OpLabel
106+
; CHECK-NEXT: OpPhi
107+
; CHECK: OpBranch %[[#exit:]]
94108
}

0 commit comments

Comments
 (0)