Skip to content

Commit 8ba3b7a

Browse files
committed
Fix coro lowering of single predecessor phis
Code assumes that uses of single predecessor phis are not live accross suspend points. Cleanup any single predecessor phis preceeding the code making this assumption. rdar://76020301 Differential Revision: https://reviews.llvm.org/D105488
1 parent 0446a4a commit 8ba3b7a

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,6 +1839,24 @@ static void rewritePHIsForCleanupPad(BasicBlock *CleanupPadBB,
18391839
}
18401840
}
18411841

1842+
static void cleanupSinglePredPHIs(Function &F) {
1843+
SmallVector<PHINode *, 32> Worklist;
1844+
for (auto &BB : F) {
1845+
for (auto &Phi : BB.phis()) {
1846+
if (Phi.getNumIncomingValues() == 1) {
1847+
Worklist.push_back(&Phi);
1848+
} else
1849+
break;
1850+
}
1851+
}
1852+
while (!Worklist.empty()) {
1853+
auto *Phi = Worklist.back();
1854+
Worklist.pop_back();
1855+
auto *OriginalValue = Phi->getIncomingValue(0);
1856+
Phi->replaceAllUsesWith(OriginalValue);
1857+
}
1858+
}
1859+
18421860
static void rewritePHIs(BasicBlock &BB) {
18431861
// For every incoming edge we will create a block holding all
18441862
// incoming values in a single PHI nodes.
@@ -2602,6 +2620,10 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
26022620
}
26032621
}
26042622

2623+
// Later code makes structural assumptions about single predecessors phis e.g
2624+
// that they are not live accross a suspend point.
2625+
cleanupSinglePredPHIs(F);
2626+
26052627
// Transforms multi-edge PHI Nodes, so that any value feeding into a PHI will
26062628
// never has its definition separated from the PHI by the suspend point.
26072629
rewritePHIs(F);
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
; RUN: opt < %s -enable-coroutines -O0 -S | FileCheck --check-prefixes=CHECK %s
2+
; RUN: opt < %s -enable-coroutines -passes='default<O0>' -S | FileCheck --check-prefixes=CHECK %s
3+
4+
%swift.async_func_pointer = type <{ i32, i32 }>
5+
%swift.context = type { %swift.context*, void (%swift.context*)*, i64 }
6+
%T10RR13AC = type <{ %swift.refcounted, %swift.defaultactor }>
7+
%swift.refcounted = type { %swift.type*, i64 }
8+
%swift.type = type { i64 }
9+
%swift.defaultactor = type { [10 x i8*] }
10+
%swift.bridge = type opaque
11+
%swift.error = type opaque
12+
%swift.executor = type {}
13+
14+
@repoTU = hidden global %swift.async_func_pointer <{ i32 trunc (i64 sub (i64 ptrtoint (void (%swift.context*, i64, i64, %T10RR13AC*)* @repo to i64), i64 ptrtoint (%swift.async_func_pointer* @repoTU to i64)) to i32), i32 20 }>, section "__TEXT,__const", align 8
15+
16+
declare void @use(i8*)
17+
18+
; This used to crash.
19+
; CHECK: repo
20+
define hidden swifttailcc void @repo(%swift.context* swiftasync %arg, i64 %arg1, i64 %arg2, %T10RR13AC* swiftself %arg3) #0 {
21+
entry:
22+
%i = alloca %swift.context*, align 8
23+
%i11 = call token @llvm.coro.id.async(i32 20, i32 16, i32 0, i8* bitcast (%swift.async_func_pointer* @repoTU to i8*))
24+
%i12 = call i8* @llvm.coro.begin(token %i11, i8* null)
25+
%i18 = call i8* @llvm.coro.async.resume()
26+
call void @use(i8* %i18)
27+
%i21 = call { i8* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8s(i32 0, i8* %i18, i8* bitcast (i8* (i8*)* @__swift_async_resume_get_context to i8*), i8* bitcast (void (i8*, %swift.executor*, %swift.context*)* @__swift_suspend_point to i8*), i8* %i18, %swift.executor* null, %swift.context* null)
28+
%i22 = extractvalue { i8* } %i21, 0
29+
%i23 = call i8* @__swift_async_resume_get_context(i8* %i22)
30+
%i28 = icmp eq i64 %arg2, 0
31+
br i1 %i28, label %bb126, label %bb
32+
33+
bb: ; preds = %entry
34+
%i29 = inttoptr i64 %arg2 to %swift.bridge*
35+
br label %bb30
36+
37+
bb30: ; preds = %bb
38+
%i31 = phi i64 [ %arg1, %bb ]
39+
%i32 = phi %swift.bridge* [ %i29, %bb ]
40+
%i35 = ptrtoint %swift.bridge* %i32 to i64
41+
%i36 = bitcast %T10RR13AC* %arg3 to %swift.type**
42+
%i37 = load %swift.type*, %swift.type** %i36, align 8
43+
%i38 = bitcast %swift.type* %i37 to void (%swift.context*, i64, i64, %T10RR13AC*)**
44+
%i39 = getelementptr inbounds void (%swift.context*, i64, i64, %T10RR13AC*)*, void (%swift.context*, i64, i64, %T10RR13AC*)** %i38, i64 11
45+
%i40 = load void (%swift.context*, i64, i64, %T10RR13AC*)*, void (%swift.context*, i64, i64, %T10RR13AC*)** %i39, align 8
46+
%i41 = bitcast void (%swift.context*, i64, i64, %T10RR13AC*)* %i40 to %swift.async_func_pointer*
47+
%i42 = getelementptr inbounds %swift.async_func_pointer, %swift.async_func_pointer* %i41, i32 0, i32 0
48+
%i43 = load i32, i32* %i42, align 8
49+
%i44 = sext i32 %i43 to i64
50+
%i45 = ptrtoint i32* %i42 to i64
51+
%i46 = add i64 %i45, %i44
52+
%i47 = inttoptr i64 %i46 to i8*
53+
%i48 = bitcast i8* %i47 to void (%swift.context*, i64, i64, %T10RR13AC*)*
54+
%i52 = call swiftcc i8* @swift_task_alloc(i64 24) #1
55+
%i53 = bitcast i8* %i52 to <{ %swift.context*, void (%swift.context*)*, i32 }>*
56+
%i54 = load %swift.context*, %swift.context** %i, align 8
57+
%i55 = getelementptr inbounds <{ %swift.context*, void (%swift.context*)*, i32 }>, <{ %swift.context*, void (%swift.context*)*, i32 }>* %i53, i32 0, i32 0
58+
store %swift.context* %i54, %swift.context** %i55, align 8
59+
%i56 = call i8* @llvm.coro.async.resume()
60+
call void @use(i8* %i56)
61+
%i57 = bitcast i8* %i56 to void (%swift.context*)*
62+
%i58 = getelementptr inbounds <{ %swift.context*, void (%swift.context*)*, i32 }>, <{ %swift.context*, void (%swift.context*)*, i32 }>* %i53, i32 0, i32 1
63+
store void (%swift.context*)* %i57, void (%swift.context*)** %i58, align 8
64+
%i59 = bitcast i8* %i52 to %swift.context*
65+
%i60 = bitcast void (%swift.context*, i64, i64, %T10RR13AC*)* %i48 to i8*
66+
%i61 = call { i8*, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8p0s_swift.errorss(i32 256, i8* %i56, i8* bitcast (i8* (i8*)* @__swift_async_resume_project_context to i8*), i8* bitcast (void (i8*, %swift.context*, i64, i64, %T10RR13AC*)* @__swift_suspend_dispatch_4 to i8*), i8* %i60, %swift.context* %i59, i64 %i31, i64 0, %T10RR13AC* %arg3)
67+
%i62 = extractvalue { i8*, %swift.error* } %i61, 0
68+
%i63 = call i8* @__swift_async_resume_project_context(i8* %i62)
69+
%i64 = bitcast i8* %i63 to %swift.context*
70+
store %swift.context* %i64, %swift.context** %i, align 8
71+
%i65 = extractvalue { i8*, %swift.error* } %i61, 1
72+
call swiftcc void @swift_task_dealloc(i8* %i52) #1
73+
br i1 %i28, label %bb126, label %bb68
74+
75+
bb68: ; preds = %bb30
76+
%i69 = call i8* @llvm.coro.async.resume()
77+
call void @use(i8* %i69)
78+
%i70 = load %swift.context*, %swift.context** %i, align 8
79+
%i71 = call { i8* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8s(i32 0, i8* %i69, i8* bitcast (i8* (i8*)* @__swift_async_resume_get_context to i8*), i8* bitcast (void (i8*, %swift.executor*, %swift.context*)* @__swift_suspend_point to i8*), i8* %i69, %swift.executor* null, %swift.context* %i70)
80+
%i77 = ptrtoint %swift.bridge* %i32 to i64
81+
%i78 = bitcast %T10RR13AC* %arg3 to %swift.type**
82+
%i79 = load %swift.type*, %swift.type** %i78, align 8
83+
%i80 = bitcast %swift.type* %i79 to void (%swift.context*, i64, i64, %T10RR13AC*)**
84+
%i81 = getelementptr inbounds void (%swift.context*, i64, i64, %T10RR13AC*)*, void (%swift.context*, i64, i64, %T10RR13AC*)** %i80, i64 11
85+
%i82 = load void (%swift.context*, i64, i64, %T10RR13AC*)*, void (%swift.context*, i64, i64, %T10RR13AC*)** %i81, align 8
86+
%i83 = bitcast void (%swift.context*, i64, i64, %T10RR13AC*)* %i82 to %swift.async_func_pointer*
87+
%i84 = getelementptr inbounds %swift.async_func_pointer, %swift.async_func_pointer* %i83, i32 0, i32 0
88+
%i85 = load i32, i32* %i84, align 8
89+
%i86 = sext i32 %i85 to i64
90+
%i87 = ptrtoint i32* %i84 to i64
91+
%i88 = add i64 %i87, %i86
92+
%i89 = inttoptr i64 %i88 to i8*
93+
%i90 = bitcast i8* %i89 to void (%swift.context*, i64, i64, %T10RR13AC*)*
94+
%i94 = call swiftcc i8* @swift_task_alloc(i64 24) #1
95+
%i98 = call i8* @llvm.coro.async.resume()
96+
call void @use(i8* %i98)
97+
%i102 = bitcast void (%swift.context*, i64, i64, %T10RR13AC*)* %i90 to i8*
98+
%i103 = call { i8*, %swift.error* } (i32, i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8p0s_swift.errorss(i32 256, i8* %i98, i8* bitcast (i8* (i8*)* @__swift_async_resume_project_context to i8*), i8* bitcast (void (i8*, %swift.context*, i64, i64, %T10RR13AC*)* @__swift_suspend_dispatch_4.1 to i8*), i8* %i102, %swift.context* null, i64 %i31, i64 0, %T10RR13AC* %arg3)
99+
call swiftcc void @swift_task_dealloc(i8* %i94) #1
100+
br label %bb126
101+
102+
bb126:
103+
%i162 = call i1 (i8*, i1, ...) @llvm.coro.end.async(i8* %i12, i1 false, void (i8*, %swift.context*, %swift.error*)* @__swift_suspend_dispatch_2, i8* bitcast (void (%swift.context*, %swift.error*)* @doIt to i8*), %swift.context* null, %swift.error* null)
104+
unreachable
105+
}
106+
107+
; Function Attrs: nounwind
108+
declare token @llvm.coro.id.async(i32, i32, i32, i8*) #1
109+
110+
; Function Attrs: nounwind
111+
declare i8* @llvm.coro.begin(token, i8* writeonly) #1
112+
113+
; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly
114+
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #3
115+
116+
; Function Attrs: nounwind
117+
declare i8* @llvm.coro.async.resume() #1
118+
119+
; Function Attrs: noinline
120+
define linkonce_odr hidden i8* @__swift_async_resume_get_context(i8* %arg) #4 {
121+
entry:
122+
ret i8* %arg
123+
}
124+
125+
; Function Attrs: nounwind
126+
declare extern_weak swifttailcc void @swift_task_switch(%swift.context*, i8*, %swift.executor*) #1
127+
128+
; Function Attrs: nounwind
129+
define internal swifttailcc void @__swift_suspend_point(i8* %arg, %swift.executor* %arg1, %swift.context* %arg2) #1 {
130+
entry:
131+
musttail call swifttailcc void @swift_task_switch(%swift.context* swiftasync %arg2, i8* %arg, %swift.executor* %arg1) #1
132+
ret void
133+
}
134+
135+
; Function Attrs: nounwind
136+
declare { i8* } @llvm.coro.suspend.async.sl_p0i8s(i32, i8*, i8*, ...) #1
137+
138+
; Function Attrs: nounwind
139+
declare i1 @llvm.coro.end.async(i8*, i1, ...) #1
140+
141+
; Function Attrs: argmemonly nounwind
142+
declare extern_weak swiftcc i8* @swift_task_alloc(i64) #5
143+
144+
; Function Attrs: nounwind readnone
145+
declare i8** @llvm.swift.async.context.addr() #6
146+
147+
; Function Attrs: alwaysinline nounwind
148+
define linkonce_odr hidden i8* @__swift_async_resume_project_context(i8* %arg) #7 {
149+
entry:
150+
%i = bitcast i8* %arg to i8**
151+
%i1 = load i8*, i8** %i, align 8
152+
%i2 = call i8** @llvm.swift.async.context.addr()
153+
store i8* %i1, i8** %i2, align 8
154+
ret i8* %i1
155+
}
156+
157+
; Function Attrs: nounwind
158+
declare { i8*, %swift.error* } @llvm.coro.suspend.async.sl_p0i8p0s_swift.errorss(i32, i8*, i8*, ...) #1
159+
160+
; Function Attrs: argmemonly nounwind
161+
declare extern_weak swiftcc void @swift_task_dealloc(i8*) #5
162+
163+
; Function Attrs: nounwind
164+
define internal swifttailcc void @__swift_suspend_dispatch_4(i8* %arg, %swift.context* %arg1, i64 %arg2, i64 %arg3, %T10RR13AC* %arg4) #1 {
165+
entry:
166+
%i = bitcast i8* %arg to void (%swift.context*, i64, i64, %T10RR13AC*)*
167+
musttail call swifttailcc void %i(%swift.context* swiftasync %arg1, i64 %arg2, i64 %arg3, %T10RR13AC* swiftself %arg4)
168+
ret void
169+
}
170+
171+
declare swifttailcc void @doIt(%swift.context* swiftasync %arg1, %swift.error* swiftself %arg2)
172+
173+
; Function Attrs: nounwind
174+
define internal swifttailcc void @__swift_suspend_dispatch_2(i8* %arg, %swift.context* %arg1, %swift.error* %arg2) #1 {
175+
entry:
176+
%i = bitcast i8* %arg to void (%swift.context*, %swift.error*)*
177+
musttail call swifttailcc void %i(%swift.context* swiftasync %arg1, %swift.error* swiftself %arg2)
178+
ret void
179+
}
180+
181+
; Function Attrs: nounwind
182+
define internal swifttailcc void @__swift_suspend_dispatch_4.1(i8* %arg, %swift.context* %arg1, i64 %arg2, i64 %arg3, %T10RR13AC* %arg4) #1 {
183+
entry:
184+
%i = bitcast i8* %arg to void (%swift.context*, i64, i64, %T10RR13AC*)*
185+
musttail call swifttailcc void %i(%swift.context* swiftasync %arg1, i64 %arg2, i64 %arg3, %T10RR13AC* swiftself %arg4)
186+
ret void
187+
}
188+
189+
attributes #0 = { "frame-pointer"="all" }
190+
attributes #1 = { nounwind }
191+
attributes #2 = { argmemonly nofree nosync nounwind willreturn }
192+
attributes #3 = { argmemonly nofree nosync nounwind willreturn writeonly }
193+
attributes #4 = { noinline "frame-pointer"="all" }
194+
attributes #5 = { argmemonly nounwind }
195+
attributes #6 = { nounwind readnone }
196+
attributes #7 = { alwaysinline nounwind "frame-pointer"="all" }

0 commit comments

Comments
 (0)