Skip to content

Commit 279585a

Browse files
committed
[Coro] [async] Disable inlining in async coroutine splitting for swifttailcc thunks
The call to the inlining utility does not update the call graph. Leading to assertion failures when calling the call graph utility to update the call graph. Instead rely on an inline pass to run after coro splitting and use alwaysinline annotations. We can only do this if the calling convention used for the thunks is `swifttailcc` otherwise we would break clients that use other calling conventions. Previous instance of this PR was #80904. github.com/swiftlang/swift/issues/68708
1 parent 899855d commit 279585a

File tree

3 files changed

+184
-21
lines changed

3 files changed

+184
-21
lines changed

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,16 +256,18 @@ static bool replaceCoroEndAsync(AnyCoroEndInst *End) {
256256
// Insert the return instruction.
257257
Builder.SetInsertPoint(End);
258258
Builder.CreateRetVoid();
259-
InlineFunctionInfo FnInfo;
260259

261260
// Remove the rest of the block, by splitting it into an unreachable block.
262261
auto *BB = End->getParent();
263262
BB->splitBasicBlock(End);
264263
BB->getTerminator()->eraseFromParent();
265264

266-
auto InlineRes = InlineFunction(*MustTailCall, FnInfo);
267-
assert(InlineRes.isSuccess() && "Expected inlining to succeed");
268-
(void)InlineRes;
265+
if (MustTailCallFunc->getCallingConv() != CallingConv::SwiftTail) {
266+
InlineFunctionInfo FnInfo;
267+
auto InlineRes = InlineFunction(*MustTailCall, FnInfo);
268+
assert(InlineRes.isSuccess() && "Expected inlining to succeed");
269+
(void)InlineRes;
270+
}
269271

270272
// We have cleaned up the coro.end block above.
271273
return false;
@@ -1882,8 +1884,11 @@ static void splitAsyncCoroutine(Function &F, coro::Shape &Shape,
18821884
auto *TailCall = coro::createMustTailCall(Suspend->getDebugLoc(), Fn, TTI,
18831885
FnArgs, Builder);
18841886
Builder.CreateRetVoid();
1885-
InlineFunctionInfo FnInfo;
1886-
(void)InlineFunction(*TailCall, FnInfo);
1887+
1888+
if (Fn->getCallingConv() != CallingConv::SwiftTail) {
1889+
InlineFunctionInfo FnInfo;
1890+
(void)InlineFunction(*TailCall, FnInfo);
1891+
}
18871892

18881893
// Replace the lvm.coro.async.resume intrisic call.
18891894
replaceAsyncResumeFunction(Suspend, Continuation);
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
; RUN: opt < %s -passes='default<O2>' -S | FileCheck --check-prefixes=CHECK %s
2+
; RUN: opt < %s -O0 -S | FileCheck --check-prefixes=CHECK-O0 %s
3+
4+
5+
; CHECK-NOT: llvm.coro.suspend.async
6+
; CHECK-O0-NOT: llvm.coro.suspend.async
7+
8+
; This test used to crash during updating the call graph in coro splitting.
9+
10+
target datalayout = "p:64:64:64"
11+
12+
%swift.async_func_pointer = type <{ i32, i32 }>
13+
14+
@"$s1d3fooyySbYaFTu" = hidden global %swift.async_func_pointer <{ i32 trunc (i64 sub (i64 ptrtoint (ptr @"$s1d3fooyySbYaF" to i64), i64 ptrtoint (ptr @"$s1d3fooyySbYaFTu" to i64)) to i32), i32 16 }>
15+
@"$s1d3baryySbYaFTu" = hidden global %swift.async_func_pointer <{ i32 trunc (i64 sub (i64 ptrtoint (ptr @"$s1d3baryySbYaF" to i64), i64 ptrtoint (ptr @"$s1d3baryySbYaFTu" to i64)) to i32), i32 16 }>
16+
17+
define swifttailcc void @"$s1d3fooyySbYaF"(ptr swiftasync %0, i1 %1) {
18+
entry:
19+
%2 = alloca ptr, align 8
20+
%c.debug = alloca i1, align 8
21+
%3 = call token @llvm.coro.id.async(i32 16, i32 16, i32 0, ptr @"$s1d3fooyySbYaFTu")
22+
%4 = call ptr @llvm.coro.begin(token %3, ptr null)
23+
store ptr %0, ptr %2, align 8
24+
call void @llvm.memset.p0.i64(ptr align 8 %c.debug, i8 0, i64 1, i1 false)
25+
store i1 %1, ptr %c.debug, align 8
26+
call void asm sideeffect "", "r"(ptr %c.debug)
27+
%5 = load i32, ptr getelementptr inbounds (%swift.async_func_pointer, ptr @"$s1d3baryySbYaFTu", i32 0, i32 1), align 8
28+
%6 = zext i32 %5 to i64
29+
%7 = call swiftcc ptr @swift_task_alloc(i64 %6) #4
30+
call void @llvm.lifetime.start.p0(i64 -1, ptr %7)
31+
%8 = load ptr, ptr %2, align 8
32+
%9 = getelementptr inbounds <{ ptr, ptr }>, ptr %7, i32 0, i32 0
33+
store ptr %8, ptr %9, align 8
34+
%10 = call ptr @llvm.coro.async.resume()
35+
%11 = getelementptr inbounds <{ ptr, ptr }>, ptr %7, i32 0, i32 1
36+
store ptr %10, ptr %11, align 8
37+
%12 = call { ptr } (i32, ptr, ptr, ...) @llvm.coro.suspend.async.sl_p0s(i32 0, ptr %10, ptr @__swift_async_resume_project_context, ptr @"$s1d3fooyySbYaF.0", ptr @"$s1d3baryySbYaF", ptr %7, i1 %1)
38+
%13 = extractvalue { ptr } %12, 0
39+
%14 = call ptr @__swift_async_resume_project_context(ptr %13)
40+
store ptr %14, ptr %2, align 8
41+
call swiftcc void @swift_task_dealloc(ptr %7) #4
42+
call void @llvm.lifetime.end.p0(i64 -1, ptr %7)
43+
%15 = load ptr, ptr %2, align 8
44+
%16 = getelementptr inbounds <{ ptr, ptr }>, ptr %15, i32 0, i32 1
45+
%17 = load ptr, ptr %16, align 8
46+
%18 = load ptr, ptr %2, align 8
47+
%19 = call i1 (ptr, i1, ...) @llvm.coro.end.async(ptr %4, i1 false, ptr @"$s1d3fooyySbYaF.0.1", ptr %17, ptr %18)
48+
unreachable
49+
}
50+
51+
declare token @llvm.coro.id.async(i32, i32, i32, ptr) #1
52+
53+
declare void @llvm.trap() #2
54+
55+
declare ptr @llvm.coro.begin(token, ptr) #1
56+
57+
declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1 immarg) #3
58+
59+
define hidden swifttailcc void @"$s1d3baryySbYaF"(ptr swiftasync %0, i1 %1) {
60+
entry:
61+
%2 = alloca ptr, align 8
62+
%c.debug = alloca i1, align 8
63+
%3 = call token @llvm.coro.id.async(i32 16, i32 16, i32 0, ptr @"$s1d3baryySbYaFTu")
64+
%4 = call ptr @llvm.coro.begin(token %3, ptr null)
65+
store ptr %0, ptr %2, align 8
66+
call void @llvm.memset.p0.i64(ptr align 8 %c.debug, i8 0, i64 1, i1 false)
67+
store i1 %1, ptr %c.debug, align 8
68+
call void asm sideeffect "", "r"(ptr %c.debug)
69+
br i1 %1, label %5, label %17
70+
71+
5: ; preds = %entry
72+
%6 = xor i1 %1, true
73+
%7 = load i32, ptr getelementptr inbounds (%swift.async_func_pointer, ptr @"$s1d3fooyySbYaFTu", i32 0, i32 1), align 8
74+
%8 = zext i32 %7 to i64
75+
%9 = call swiftcc ptr @swift_task_alloc(i64 %8) #4
76+
call void @llvm.lifetime.start.p0(i64 -1, ptr %9)
77+
%10 = load ptr, ptr %2, align 8
78+
%11 = getelementptr inbounds <{ ptr, ptr }>, ptr %9, i32 0, i32 0
79+
store ptr %10, ptr %11, align 8
80+
%12 = call ptr @llvm.coro.async.resume()
81+
%13 = getelementptr inbounds <{ ptr, ptr }>, ptr %9, i32 0, i32 1
82+
store ptr %12, ptr %13, align 8
83+
%14 = call { ptr } (i32, ptr, ptr, ...) @llvm.coro.suspend.async.sl_p0s(i32 0, ptr %12, ptr @__swift_async_resume_project_context, ptr @"$s1d3baryySbYaF.0.2", ptr @"$s1d3fooyySbYaF", ptr %9, i1 %6)
84+
%15 = extractvalue { ptr } %14, 0
85+
%16 = call ptr @__swift_async_resume_project_context(ptr %15)
86+
store ptr %16, ptr %2, align 8
87+
call swiftcc void @swift_task_dealloc(ptr %9) #4
88+
call void @llvm.lifetime.end.p0(i64 -1, ptr %9)
89+
br label %18
90+
91+
17: ; preds = %entry
92+
br label %18
93+
94+
18: ; preds = %5, %17
95+
%19 = load ptr, ptr %2, align 8
96+
%20 = getelementptr inbounds <{ ptr, ptr }>, ptr %19, i32 0, i32 1
97+
%21 = load ptr, ptr %20, align 8
98+
%22 = load ptr, ptr %2, align 8
99+
%23 = call i1 (ptr, i1, ...) @llvm.coro.end.async(ptr %4, i1 false, ptr @"$s1d3baryySbYaF.0", ptr %21, ptr %22)
100+
unreachable
101+
}
102+
103+
declare swiftcc ptr @swift_task_alloc(i64) #4
104+
105+
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #5
106+
107+
declare ptr @llvm.coro.async.resume() #6
108+
109+
define linkonce_odr hidden ptr @__swift_async_resume_project_context(ptr %0) #7 {
110+
entry:
111+
%1 = load ptr, ptr %0, align 8
112+
%2 = call ptr @llvm.swift.async.context.addr()
113+
store ptr %1, ptr %2, align 8
114+
ret ptr %1
115+
}
116+
117+
declare ptr @llvm.swift.async.context.addr() #1
118+
119+
define internal swifttailcc void @"$s1d3fooyySbYaF.0"(ptr %0, ptr %1, i1 %2) #8 {
120+
entry:
121+
musttail call swifttailcc void %0(ptr swiftasync %1, i1 %2)
122+
ret void
123+
}
124+
125+
declare { ptr } @llvm.coro.suspend.async.sl_p0s(i32, ptr, ptr, ...) #6
126+
127+
declare swiftcc void @swift_task_dealloc(ptr) #4
128+
129+
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #5
130+
131+
define internal swifttailcc void @"$s1d3fooyySbYaF.0.1"(ptr %0, ptr %1) #8 {
132+
entry:
133+
musttail call swifttailcc void %0(ptr swiftasync %1)
134+
ret void
135+
}
136+
137+
declare i1 @llvm.coro.end.async(ptr, i1, ...) #1
138+
139+
define internal swifttailcc void @"$s1d3baryySbYaF.0"(ptr %0, ptr %1) #8 {
140+
entry:
141+
musttail call swifttailcc void %0(ptr swiftasync %1)
142+
ret void
143+
}
144+
145+
define internal swifttailcc void @"$s1d3baryySbYaF.0.2"(ptr %0, ptr %1, i1 %2) #8 {
146+
entry:
147+
musttail call swifttailcc void %0(ptr swiftasync %1, i1 %2)
148+
ret void
149+
}
150+
151+
attributes #1 = { nounwind }
152+
attributes #2 = { cold noreturn nounwind }
153+
attributes #3 = { nocallback nofree nounwind willreturn}
154+
attributes #4 = { nounwind }
155+
attributes #5 = { nocallback nofree nosync nounwind willreturn }
156+
attributes #6 = { nomerge nounwind }
157+
attributes #7 = { alwaysinline nounwind }
158+
attributes #8 = { alwaysinline nounwind }

llvm/test/Transforms/Coroutines/swift-async-dbg.ll

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
; RUN: opt -mtriple='arm64-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s
2-
; RUN: opt -mtriple='x86_64' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s
3-
; RUN: opt -mtriple='i386-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s --check-prefix=NOENTRY
4-
; RUN: opt -mtriple='armv7-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s --check-prefix=NOENTRY
1+
; RUN: opt -mtriple='arm64-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg),always-inline' -o - | FileCheck %s
2+
; RUN: opt -mtriple='x86_64' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg),always-inline' -o - | FileCheck %s
3+
; RUN: opt -mtriple='i386-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg),always-inline' -o - | FileCheck %s --check-prefix=NOENTRY
4+
; RUN: opt -mtriple='armv7-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg),always-inline' -o - | FileCheck %s --check-prefix=NOENTRY
55

66
;; Replicate those tests with non-instruction debug markers.
7-
; RUN: opt --try-experimental-debuginfo-iterators -mtriple='arm64-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s
8-
; RUN: opt --try-experimental-debuginfo-iterators -mtriple='x86_64' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s
9-
; RUN: opt --try-experimental-debuginfo-iterators -mtriple='i386-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s --check-prefix=NOENTRY
10-
; RUN: opt --try-experimental-debuginfo-iterators -mtriple='armv7-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg)' -o - | FileCheck %s --check-prefix=NOENTRY
7+
; RUN: opt --try-experimental-debuginfo-iterators -mtriple='arm64-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg),always-inline' -o - | FileCheck %s
8+
; RUN: opt --try-experimental-debuginfo-iterators -mtriple='x86_64' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg),always-inline' -o - | FileCheck %s
9+
; RUN: opt --try-experimental-debuginfo-iterators -mtriple='i386-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg),always-inline' -o - | FileCheck %s --check-prefix=NOENTRY
10+
; RUN: opt --try-experimental-debuginfo-iterators -mtriple='armv7-' %s -S -passes='module(coro-early),cgscc(coro-split,simplifycfg),always-inline' -o - | FileCheck %s --check-prefix=NOENTRY
1111

1212
; NOENTRY-NOT: OP_llvm_entry_value
1313

@@ -93,37 +93,37 @@ define swifttailcc void @coroutineA(ptr swiftasync %arg) !dbg !48 {
9393
@coroutineBTu = global <{i32, i32}> <{ i32 trunc (i64 sub (i64 ptrtoint (ptr @"coroutineB" to i64), i64 ptrtoint (ptr @"coroutineBTu" to i64)) to i32), i32 16 }>, align 8
9494
@coroutineATu = global <{i32, i32}> <{ i32 trunc (i64 sub (i64 ptrtoint (ptr @"coroutineA" to i64), i64 ptrtoint (ptr @"coroutineATu" to i64)) to i32), i32 16 }>, align 8
9595

96-
define weak_odr hidden ptr @__swift_async_resume_get_context(ptr %arg) !dbg !64 {
96+
define weak_odr hidden ptr @__swift_async_resume_get_context(ptr %arg) alwaysinline !dbg !64 {
9797
ret ptr %arg, !dbg !65
9898
}
99-
define hidden swifttailcc void @coroutineA.1(ptr %arg, i64 %arg1, i64 %arg2, ptr %arg3) !dbg !66 {
99+
define hidden swifttailcc void @coroutineA.1(ptr %arg, i64 %arg1, i64 %arg2, ptr %arg3) alwaysinline !dbg !66 {
100100
musttail call swifttailcc void @swift_task_switch(ptr swiftasync %arg3, ptr %arg, i64 %arg1, i64 %arg2), !dbg !67
101101
ret void, !dbg !67
102102
}
103103

104-
define weak_odr hidden ptr @__swift_async_resume_project_context(ptr %arg) !dbg !68 {
104+
define weak_odr hidden ptr @__swift_async_resume_project_context(ptr %arg) alwaysinline !dbg !68 {
105105
%i1 = load ptr, ptr %arg, align 8, !dbg !69
106106
%i2 = call ptr @llvm.swift.async.context.addr(), !dbg !69
107107
store ptr %i1, ptr %i2, align 8, !dbg !69
108108
ret ptr %i1, !dbg !69
109109
}
110-
define hidden swifttailcc void @coroutineA.0(ptr %arg, ptr %arg1) !dbg !70 {
110+
define hidden swifttailcc void @coroutineA.0(ptr %arg, ptr %arg1) alwaysinline !dbg !70 {
111111
musttail call swifttailcc void %arg(ptr swiftasync %arg1), !dbg !71
112112
ret void, !dbg !71
113113
}
114-
define hidden swifttailcc void @coroutineA.0.1(ptr %arg, ptr %arg1) !dbg !72 {
114+
define hidden swifttailcc void @coroutineA.0.1(ptr %arg, ptr %arg1) alwaysinline !dbg !72 {
115115
musttail call swifttailcc void %arg(ptr swiftasync %arg1), !dbg !73
116116
ret void, !dbg !73
117117
}
118-
define swifttailcc void @coroutineB(ptr swiftasync %arg) !dbg !37 {
118+
define swifttailcc void @coroutineB(ptr swiftasync %arg) alwaysinline !dbg !37 {
119119
%i2 = call token @llvm.coro.id.async(i32 16, i32 16, i32 0, ptr nonnull @coroutineBTu)
120120
%i3 = call ptr @llvm.coro.begin(token %i2, ptr null)
121121
%i6 = getelementptr inbounds <{ ptr, ptr }>, ptr %arg, i64 0, i32 1, !dbg !42
122122
%i712 = load ptr, ptr %i6, align 8, !dbg !42
123123
%i10 = call i1 (ptr, i1, ...) @llvm.coro.end.async(ptr %i3, i1 false, ptr nonnull @coroutineB.0, ptr %i712, ptr %arg), !dbg !42
124124
unreachable, !dbg !42
125125
}
126-
define hidden swifttailcc void @coroutineB.0(ptr %arg, ptr %arg1) !dbg !44 {
126+
define hidden swifttailcc void @coroutineB.0(ptr %arg, ptr %arg1) alwaysinline !dbg !44 {
127127
musttail call swifttailcc void %arg(ptr swiftasync %arg1), !dbg !47
128128
ret void, !dbg !47
129129
}

0 commit comments

Comments
 (0)