22; RUN: opt -S -passes=structurizecfg %s -o - | FileCheck %s
33
44; Structurize as usual, but don't tear callbr and its destination blocks apart.
5+ ;
6+ ; Note: currently, callbr blocks and their corresponding target blocks
7+ ; themselves are not handled by the structurizer.* If the CFG turns out to be
8+ ; unstructured at the end, the CFG lowering (si-annotate-control-flow) will
9+ ; detect this. For the currently intended use cases of callbr in the context of
10+ ; the AMDGPU backend, this is not a limitation (cf.
11+ ; https://discourse.llvm.org/t/rfc-add-callbr-intrinsic-support/86087).
12+ ;
13+ ; Note 2: while callbr and its targets remain untouched, everything else is
14+ ; handled as usual, even if it is nested in a callbr region.
15+ ;
16+ ; *FIXME: this will be fixed in the future. Callbr can be handled as follows:
17+ ; Input IR:
18+ ; ```
19+ ; define void @foo_callbr() {
20+ ; callbr void asm "", "!i"() to label %fallthrough [label %indirect, ...]
21+ ; fallthrough:
22+ ; br label %exit
23+ ; indirect:
24+ ; br label %exit
25+ ; ...
26+ ; exit:
27+ ; ret void
28+ ; }
29+ ; ```
30+ ;
31+ ; Output IR:
32+ ; ```
33+ ; define void @foo_callbr() {
34+ ; callbr void asm "", "!i"()
35+ ; to label %fallthrough [label %fake.indirect, label %fake.indirect1, label %fake.indirect2, ...]
36+ ; fake.indirect: ; preds = %0
37+ ; br label %Flow
38+ ; fake.indirect1: ; preds = %0
39+ ; br label %Flow
40+ ; fake.indirect2: ; preds = %0
41+ ; br label %Flow
42+ ; ...
43+ ; Flow: ; preds = %fallthrough, %fake.indirect[0-N]
44+ ; %1 = phi i1 [ false, %fallthrough ], [ true, %fake.indirect ], [ false, %fake.indirect[1-N] ]
45+ ; br i1 %1, label %indirect, label %Flow1
46+ ; Flow1: ; preds = %Flow, %indirect
47+ ; %2 = phi i1 [ false, %Flow], [ true, %fake.indirect1 ], [ false, %indirect ]
48+ ; br i1 %2, label %indirect1, label %Flow2
49+ ; Flow2: ; preds = %Flow, %indirect1
50+ ; %2 = phi i1 [ false, %Flow], [ true, %fake.indirect2 ], [ false, %indirect1 ]
51+ ; br i1 %2, label %indirect2, label %Flow3
52+ ; ...
53+ ; fallthrough: ; preds = %0
54+ ; br label %Flow
55+ ; indirect: ; preds = %Flow
56+ ; br label %Flow1
57+ ; indirect1: ; preds = %Flow1
58+ ; br label %Flow2
59+ ; indirect2: : preds = %Flow2
60+ ; br label %Flow3
61+ ; ...
62+ ; exit: ; preds = %indirectN, %FlowN
63+ ; ret void
64+ ; }
65+ ; ```
66+ ;
67+ ; Output IR as ASCII-art:
68+ ; %0
69+ ; ---------------------
70+ ; | | | |
71+ ; v v v v
72+ ; f f.i f.i1 f.i2
73+ ; | | | |
74+ ; v v v v
75+ ; ---------------------
76+ ; %Flow
77+ ; | \
78+ ; | %indirect
79+ ; | /
80+ ; %Flow1
81+ ; | \
82+ ; | %indirect1
83+ ; | /
84+ ; %Flow2
85+ ; | \
86+ ; | %indirect2
87+ ; | /
88+ ; %exit
89+ ;
90+
91+ ; Only callbr, nothing to do.
92+ define void @callbr_simple () {
93+ ; CHECK-LABEL: define void @callbr_simple() {
94+ ; CHECK-NEXT: [[CALLBR:.*:]]
95+ ; CHECK-NEXT: callbr void asm "", "!i"()
96+ ; CHECK-NEXT: to label %[[INDIRECT:.*]] [label %indirect]
97+ ; CHECK: [[INDIRECT]]:
98+ ; CHECK-NEXT: br label %[[EXIT:.*]]
99+ ; CHECK: [[INDIRECT1:.*:]]
100+ ; CHECK-NEXT: br label %[[EXIT]]
101+ ; CHECK: [[EXIT]]:
102+ ; CHECK-NEXT: ret void
103+ ;
104+ callbr:
105+ callbr void asm "" , "!i" () to label %fallthrough [label %indirect ]
106+ fallthrough:
107+ br label %exit
108+ indirect:
109+ br label %exit
110+ exit:
111+ ret void
112+ }
113+
114+ ; Callbr nested in non-callbr: non-callbr is transformed
115+ define void @callbr_in_non_callbr (i1 %c ) {
116+ ; CHECK-LABEL: define void @callbr_in_non_callbr(
117+ ; CHECK-SAME: i1 [[C:%.*]]) {
118+ ; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true
119+ ; CHECK-NEXT: br i1 [[C_INV]], label %[[NOCALLBR:.*]], label %[[FLOW:.*]]
120+ ; CHECK: [[FLOW]]:
121+ ; CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[NOCALLBR]] ], [ true, [[TMP0:%.*]] ]
122+ ; CHECK-NEXT: br i1 [[TMP1]], label %[[CALLBR:.*]], label %[[EXIT:.*]]
123+ ; CHECK: [[CALLBR]]:
124+ ; CHECK-NEXT: callbr void asm "", "!i"()
125+ ; CHECK-NEXT: to label %[[INDIRECT:.*]] [label %indirect]
126+ ; CHECK: [[INDIRECT]]:
127+ ; CHECK-NEXT: br label %[[EXIT]]
128+ ; CHECK: [[INDIRECT1:.*:]]
129+ ; CHECK-NEXT: br label %[[EXIT]]
130+ ; CHECK: [[NOCALLBR]]:
131+ ; CHECK-NEXT: br label %[[FLOW]]
132+ ; CHECK: [[EXIT]]:
133+ ; CHECK-NEXT: ret void
134+ ;
135+ br i1 %c , label %callbr , label %nocallbr
136+ callbr:
137+ callbr void asm "" , "!i" () to label %fallthrough [label %indirect ]
138+ fallthrough:
139+ br label %exit
140+ indirect:
141+ br label %exit
142+ nocallbr:
143+ br label %exit
144+ exit:
145+ ret void
146+ }
147+
148+ ; Callbr parent of non-callbr: non-callbr is transformed
149+ define void @non_callbr_in_callbr (i1 %c ) {
150+ ; CHECK-LABEL: define void @non_callbr_in_callbr(
151+ ; CHECK-SAME: i1 [[C:%.*]]) {
152+ ; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true
153+ ; CHECK-NEXT: callbr void asm "", "!i"()
154+ ; CHECK-NEXT: to label %[[INDIRECT:.*]] [label %indirect]
155+ ; CHECK: [[INDIRECT]]:
156+ ; CHECK-NEXT: br i1 [[C_INV]], label %[[FALLTHROUGH2:.*]], label %[[FLOW:.*]]
157+ ; CHECK: [[FLOW]]:
158+ ; CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[FALLTHROUGH2]] ], [ true, %[[INDIRECT]] ]
159+ ; CHECK-NEXT: br i1 [[TMP1]], label %[[FALLTHROUGH1:.*]], label %[[FLOW1:.*]]
160+ ; CHECK: [[FALLTHROUGH1]]:
161+ ; CHECK-NEXT: br label %[[FLOW1]]
162+ ; CHECK: [[FALLTHROUGH2]]:
163+ ; CHECK-NEXT: br label %[[FLOW]]
164+ ; CHECK: [[INDIRECT1:.*:]]
165+ ; CHECK-NEXT: br label %[[EXIT:.*]]
166+ ; CHECK: [[FLOW1]]:
167+ ; CHECK-NEXT: br label %[[EXIT]]
168+ ; CHECK: [[EXIT]]:
169+ ; CHECK-NEXT: ret void
170+ ;
171+ callbr void asm "" , "!i" () to label %fallthrough [label %indirect ]
172+ fallthrough:
173+ br i1 %c , label %fallthrough1 , label %fallthrough2
174+ fallthrough1:
175+ br label %exit
176+ fallthrough2:
177+ br label %exit
178+ indirect:
179+ br label %exit
180+ exit:
181+ ret void
182+ }
5183
6- define void @callbr_inline_asm (i32 %c , i1 %d , i1 %e ) {
7- ; CHECK-LABEL: define void @callbr_inline_asm(
8- ; CHECK-SAME: i32 [[C:%.*]], i1 [[D:%.*]], i1 [[E:%.*]]) {
9- ; CHECK-NEXT: [[D_INV:%.*]] = xor i1 [[D]], true
10- ; CHECK-NEXT: br i1 [[D_INV]], label %[[NOCALLBR:.*]], label %[[FLOW3:.*]]
184+ ; Callbr surrounded by non-callbr: all three regular branches are handled
185+ ; correctly
186+ define void @callbr_nested_in_non_callbr (i1 %c , i1 %d , i1 %e , i1 %f ) {
187+ ; CHECK-LABEL: define void @callbr_nested_in_non_callbr(
188+ ; CHECK-SAME: i1 [[C:%.*]], i1 [[D:%.*]], i1 [[E:%.*]], i1 [[F:%.*]]) {
189+ ; CHECK-NEXT: [[C_INV:%.*]] = xor i1 [[C]], true
190+ ; CHECK-NEXT: br i1 [[C_INV]], label %[[NOCALLBR:.*]], label %[[FLOW3:.*]]
11191; CHECK: [[FLOW3]]:
12192; CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[FLOW:.*]] ], [ true, [[TMP0:%.*]] ]
13193; CHECK-NEXT: br i1 [[TMP1]], label %[[CALLBR:.*]], label %[[RET:.*]]
14194; CHECK: [[CALLBR]]:
15195; CHECK-NEXT: callbr void asm "", "!i"()
16196; CHECK-NEXT: to label %[[INDIRECT:.*]] [label %indirect]
17197; CHECK: [[INDIRECT]]:
18- ; CHECK-NEXT: br i1 [[E ]], label %[[FALLTHROUGH1:.*]], label %[[FLOW2:.*]]
198+ ; CHECK-NEXT: br i1 [[D ]], label %[[FALLTHROUGH1:.*]], label %[[FLOW2:.*]]
19199; CHECK: [[FALLTHROUGH1]]:
20200; CHECK-NEXT: br label %[[FLOW2]]
21201; CHECK: [[INDIRECT2:.*:]]
22202; CHECK-NEXT: br i1 [[E]], label %[[INDIRECT1:.*]], label %[[FLOW1:.*]]
23203; CHECK: [[INDIRECT1]]:
24204; CHECK-NEXT: br label %[[FLOW1]]
25205; CHECK: [[NOCALLBR]]:
26- ; CHECK-NEXT: br i1 [[E ]], label %[[NOCALLBR1:.*]], label %[[FLOW]]
206+ ; CHECK-NEXT: br i1 [[F ]], label %[[NOCALLBR1:.*]], label %[[FLOW]]
27207; CHECK: [[NOCALLBR1]]:
28208; CHECK-NEXT: br label %[[FLOW]]
29209; CHECK: [[FLOW]]:
@@ -35,19 +215,19 @@ define void @callbr_inline_asm(i32 %c, i1 %d, i1 %e) {
35215; CHECK: [[RET]]:
36216; CHECK-NEXT: ret void
37217;
38- br i1 %d , label %callbr , label %nocallbr
218+ br i1 %c , label %callbr , label %nocallbr
39219callbr:
40220 callbr void asm "" , "!i" () to label %fallthrough [label %indirect ]
41221fallthrough:
42- br i1 %e , label %fallthrough1 , label %ret
222+ br i1 %d , label %fallthrough1 , label %ret
43223fallthrough1:
44224 br label %ret
45225indirect:
46226 br i1 %e , label %indirect1 , label %ret
47227indirect1:
48228 br label %ret
49229nocallbr:
50- br i1 %e , label %nocallbr1 , label %ret
230+ br i1 %f , label %nocallbr1 , label %ret
51231nocallbr1:
52232 br label %ret
53233ret:
0 commit comments