Skip to content

Commit 1de0c05

Browse files
authored
Merge pull request #65947 from eeckstein/fix-dead-async-methods-5.9
[5.9] IRGen: fix async vtable stubs
2 parents aba8d13 + c7bc2a2 commit 1de0c05

File tree

4 files changed

+117
-27
lines changed

4 files changed

+117
-27
lines changed

lib/IRGen/GenDecl.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,8 +2102,20 @@ void IRGenModule::emitVTableStubs() {
21022102
}
21032103

21042104
// For each eliminated method symbol create an alias to the stub.
2105-
auto *alias = llvm::GlobalAlias::create(llvm::GlobalValue::ExternalLinkage,
2106-
F.getName(), stub);
2105+
llvm::GlobalValue *alias = nullptr;
2106+
if (F.isAsync()) {
2107+
// TODO: We cannot directly create a pointer to `swift_deletedAsyncMethodError`
2108+
// to workaround a linker crash.
2109+
// Instead use the stub, which calls swift_deletedMethodError. This works because
2110+
// swift_deletedMethodError takes no parameters and simply aborts the program.
2111+
auto asyncLayout = getAsyncContextLayout(*this, const_cast<SILFunction *>(&F));
2112+
auto entity = LinkEntity::forSILFunction(const_cast<SILFunction *>(&F));
2113+
auto *fnPtr = emitAsyncFunctionPointer(*this, stub, entity, asyncLayout.getSize());
2114+
alias = fnPtr;
2115+
} else {
2116+
alias = llvm::GlobalAlias::create(llvm::GlobalValue::ExternalLinkage,
2117+
F.getName(), stub);
2118+
}
21072119

21082120
if (F.getEffectiveSymbolLinkage() == SILLinkage::Hidden)
21092121
alias->setVisibility(llvm::GlobalValue::HiddenVisibility);

test/IRGen/Inputs/report_dead_method_call/main.swift

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,24 @@ func testProto(_ c: Container) {
1010
c.p.abc()
1111
}
1212

13+
@inline(never)
14+
func testProtoAsync(_ c: Container) async {
15+
// call the dead witness method abcAsync()
16+
await c.p.abcAsync()
17+
}
18+
1319
@inline(never)
1420
func testClass(_ c: ClassContainer) {
1521
// call the dead vtable method def()
1622
c.p.def()
1723
}
1824

25+
@inline(never)
26+
func testClassAsync(_ c: ClassContainer) async {
27+
// call the dead vtable method defAsync()
28+
await c.p.defAsync()
29+
}
30+
1931
public class PublicDerived : PublicBase {
2032
// The vtable of PublicDerived contains a reference to PublicBase.ghi()
2133
}
@@ -31,22 +43,55 @@ func testPublicClass(_ c: PublicBase) {
3143
c.ghi()
3244
}
3345

34-
let ReportDeadMethodCallTestSuite = TestSuite("ReportDeadMethodCall")
35-
36-
ReportDeadMethodCallTestSuite.test("Call class") {
37-
expectCrashLater()
38-
callClass()
46+
@inline(never)
47+
func callPublicClassAsync() async {
48+
await testPublicClassAsync(PublicDerived())
3949
}
4050

41-
ReportDeadMethodCallTestSuite.test("Call proto") {
42-
expectCrashLater()
43-
callProto()
51+
@inline(never)
52+
func testPublicClassAsync(_ c: PublicBase) async {
53+
// call the dead private vtable method ghiAsync()
54+
await c.ghiAsync()
4455
}
4556

46-
ReportDeadMethodCallTestSuite.test("Call public class") {
47-
expectCrashLater()
48-
callPublicClass()
49-
}
57+
@main struct Main {
58+
static func main() async {
59+
60+
let tests = TestSuite("ReportDeadMethodCall")
61+
62+
tests.test("Call class") {
63+
expectCrashLater(withMessage: "Fatal error: Call of deleted method")
64+
callClass()
65+
}
66+
67+
tests.test("Call class async") {
68+
// TODO: it should crash with the error message and not with sigsegv
69+
expectCrashLater()
70+
await callClassAsync()
71+
}
72+
73+
tests.test("Call proto") {
74+
expectCrashLater(withMessage: "Fatal error: Call of deleted method")
75+
callProto()
76+
}
77+
78+
tests.test("Call proto async") {
79+
// TODO: it should crash with the error message and not with sigsegv
80+
expectCrashLater(withMessage: "")
81+
await callProtoAsync()
82+
}
83+
84+
tests.test("Call public class") {
85+
expectCrashLater(withMessage: "Fatal error: Call of deleted method")
86+
callPublicClass()
87+
}
88+
89+
tests.test("Call public class async") {
90+
expectCrashLater(withMessage: "Fatal error: Call of deleted method")
91+
await callPublicClassAsync()
92+
}
5093

51-
runAllTests()
94+
await runAllTestsAsync()
95+
}
96+
}
5297

test/IRGen/report_dead_method_call.swift

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,31 @@
22

33
// We compile with -O (optimizations) and -disable-access-control (which
44
// allows use to "call" methods that are removed by dead code elimination).
5-
// RUN: %target-build-swift %S/Inputs/report_dead_method_call/main.swift %s -O -Xfrontend -disable-access-control -o %t/report_dead_method_call
5+
// RUN: %target-build-swift -parse-as-library %S/Inputs/report_dead_method_call/main.swift %s -O -Xfrontend -disable-access-control -Xfrontend -disable-availability-checking -o %t/report_dead_method_call
66

77
// The private, unused methods are optimized away. The test calls these
88
// methods anyway (since it has overridden the access control), so we
99
// expect them to produce "Fatal error: Call of deleted method" when run.
1010
// RUN: %target-codesign %t/report_dead_method_call
1111
// RUN: %target-run %t/report_dead_method_call
12+
1213
// REQUIRES: executable_test
14+
// REQUIRES: concurrency
15+
// UNSUPPORTED: freestanding
16+
// REQUIRES: concurrency_runtime
17+
// UNSUPPORTED: back_deployment_runtime
1318

1419
// UNSUPPORTED: swift_test_mode_optimize_none_with_implicit_dynamic
1520
// UNSUPPORTED: swift_test_mode_optimize_with_implicit_dynamic
1621

1722
private protocol PrivateProto {
1823
func abc()
24+
func abcAsync() async
1925
}
2026

2127
struct PrivateStructC : PrivateProto {
22-
func abc() {
23-
}
28+
func abc() {}
29+
func abcAsync() async {}
2430
}
2531

2632
struct Container {
@@ -33,14 +39,19 @@ func callProto() {
3339
testProto(Container())
3440
}
3541

42+
@inline(never)
43+
func callProtoAsync() async {
44+
await testProtoAsync(Container())
45+
}
46+
3647
private class Base {
37-
func def() {
38-
}
48+
func def() {}
49+
func defAsync() async {}
3950
}
4051

4152
private class Derived : Base {
42-
override func def() {
43-
}
53+
override func def() {}
54+
override func defAsync() async {}
4455
}
4556

4657
struct ClassContainer {
@@ -53,8 +64,14 @@ func callClass() {
5364
testClass(ClassContainer())
5465
}
5566

67+
@inline(never)
68+
func callClassAsync() async {
69+
await testClassAsync(ClassContainer())
70+
}
71+
5672
public class PublicBase {
57-
private func ghi() {
58-
}
73+
private func ghi() { }
74+
75+
private func ghiAsync() async {}
5976
}
6077

test/IRGen/static-vtable-stubs.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
// RUN: %empty-directory(%t)
22
// RUN: split-file --leading-lines %s %t
3-
// RUN: %swift-target-frontend -parse-as-library -static -O -module-name M -c -primary-file %t/A.swift %t/B.swift -S -emit-ir -o - | %FileCheck %t/A.swift -check-prefix CHECK
4-
// RUN: %swift-target-frontend -parse-as-library -static -O -module-name M -c %t/A.swift -primary-file %t/B.swift -S -emit-ir -o - | %FileCheck %t/B.swift -check-prefix CHECK
3+
// RUN: %swift-target-frontend -disable-availability-checking -parse-as-library -static -O -module-name M -c -primary-file %t/A.swift %t/B.swift -S -emit-ir -o - | %FileCheck %t/A.swift -check-prefix CHECK
4+
// RUN: %swift-target-frontend -disable-availability-checking -parse-as-library -static -O -module-name M -c %t/A.swift -primary-file %t/B.swift -S -emit-ir -o - | %FileCheck %t/B.swift -check-prefix CHECK
5+
6+
// Verify that we can link successfully.
7+
// RUN: %target-build-swift -Xfrontend -disable-availability-checking -O %t/A.swift %t/B.swift -o %t/a.out
8+
9+
// REQUIRES: concurrency
510

611
//--- A.swift
712
open class C {
8-
private var i: [ObjectIdentifier:Any] = [:]
13+
private var i: [ObjectIdentifier:Any] = [:]
14+
15+
private func foo() async {}
916
}
1017

18+
// CHECK: @"$s1M1CC3foo33_{{.*}}Tu" = hidden global %swift.async_func_pointer <{ {{.*}} @_swift_dead_method_stub
19+
1120
// CHECK: @"$s1M1CC1i33_807E3D81CC6CDD898084F3279464DDF9LLSDySOypGvg" = hidden alias void (), void ()* @_swift_dead_method_stub
1221
// CHECK: @"$s1M1CC1i33_807E3D81CC6CDD898084F3279464DDF9LLSDySOypGvs" = hidden alias void (), void ()* @_swift_dead_method_stub
1322
// CHECK: @"$s1M1CC1i33_807E3D81CC6CDD898084F3279464DDF9LLSDySOypGvM" = hidden alias void (), void ()* @_swift_dead_method_stub
@@ -19,3 +28,10 @@ final class D: C {
1928
// CHECK: declare swiftcc %swift.bridge* @"$s1M1CC1i33_807E3D81CC6CDD898084F3279464DDF9LLSDySOypGvg"(%T1M1CC* swiftself) #0
2029
// CHECK: declare swiftcc void @"$s1M1CC1i33_807E3D81CC6CDD898084F3279464DDF9LLSDySOypGvs"(%swift.bridge*, %T1M1CC* swiftself) #0
2130
// CHECK: declare swiftcc { i8*, %TSD* } @"$s1M1CC1i33_807E3D81CC6CDD898084F3279464DDF9LLSDySOypGvM"(i8* noalias dereferenceable(32), %T1M1CC* swiftself) #0
31+
32+
@main
33+
struct Main {
34+
static func main() {
35+
}
36+
}
37+

0 commit comments

Comments
 (0)