Skip to content

Commit 66474ed

Browse files
committed
Inliner: inline generic thunks which return a partial_apply.
The returned partial_apply of a thunk is most likely being optimized away if inlined. Because some thunks cannot be specialized (e.g. if an opened existential is in the subsitution list), inline such thunks also in case they are generic. https://bugs.swift.org/browse/SR-12115 rdar://problem/59061452
1 parent 12dfcc4 commit 66474ed

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

lib/SILOptimizer/Transforms/PerformanceInliner.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,15 @@ bool SILPerformanceInliner::isProfitableToInline(
526526
return true;
527527
}
528528

529+
static bool returnsClosure(SILFunction *F) {
530+
for (SILBasicBlock &BB : *F) {
531+
if (auto *RI = dyn_cast<ReturnInst>(BB.getTerminator())) {
532+
return isa<PartialApplyInst>(RI->getOperand());
533+
}
534+
}
535+
return false;
536+
}
537+
529538
/// Checks if a given generic apply should be inlined unconditionally, i.e.
530539
/// without any complex analysis using e.g. a cost model.
531540
/// It returns true if a function should be inlined.
@@ -570,6 +579,13 @@ static Optional<bool> shouldInlineGeneric(FullApplySite AI) {
570579
return None;
571580
}
572581

582+
// The returned partial_apply of a thunk is most likely being optimized away
583+
// if inlined. Because some thunks cannot be specialized (e.g. if an opened
584+
// existential is in the subsitution list), we inline such thunks also in case
585+
// they are generic.
586+
if (Callee->isThunk() && returnsClosure(Callee))
587+
return true;
588+
573589
// All other generic functions should not be inlined if this kind of inlining
574590
// is disabled.
575591
if (!EnableSILInliningOfGenerics)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %target-swift-frontend -primary-file %s -O -sil-verify-all -module-name=test -emit-sil | %FileCheck %s
2+
3+
// Also do an end-to-end test to check all components, including IRGen.
4+
// RUN: %empty-directory(%t)
5+
// RUN: %target-build-swift -O %s -o %t/a.out
6+
// RUN: %target-run %t/a.out | %FileCheck --check-prefix=CHECK-OUTPUT %s
7+
8+
// REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib
9+
10+
protocol P {
11+
func foo() throws -> Int
12+
}
13+
14+
struct S: P {
15+
var a: Int
16+
var b: Int
17+
var c: Int
18+
var d: Int
19+
20+
func foo() throws -> Int {
21+
return a + b + c + d
22+
}
23+
}
24+
25+
func callClosure<R>(_ body: () throws -> R) rethrows -> R {
26+
try body()
27+
}
28+
29+
// Check that the optimizer can eliminat all calls to thunks and directly
30+
// try_apply's the witness method.
31+
32+
// CHECK-LABEL: sil hidden [noinline] @$s4test6testitySiSgAA1P_pF
33+
// CHECK: [[METHOD:%[0-9]+]] = witness_method $@opened{{.*}} #P.foo
34+
// CHECK: try_apply [[METHOD]]<@opened
35+
// CHECK: // end sil function '$s4test6testitySiSgAA1P_pF'
36+
@inline(never)
37+
func testit(_ p: P) -> Int? {
38+
return try? callClosure(p.foo)
39+
}
40+
41+
let p: P = S(a: 1, b: 2, c: 3, d: 4)
42+
43+
// CHECK-OUTPUT: 10
44+
print(testit(p)!)
45+

0 commit comments

Comments
 (0)