Skip to content

Commit 6853289

Browse files
authored
Merge pull request #29784 from eeckstein/inline-generic-curry-thunks
Inliner: inline generic thunks which return a partial_apply.
2 parents aa417c3 + 66474ed commit 6853289

File tree

3 files changed

+61
-49
lines changed

3 files changed

+61
-49
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)

lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -556,49 +556,6 @@ static bool calleeIsSelfRecursive(SILFunction *Callee) {
556556
return false;
557557
}
558558

559-
// Returns true if the callee contains a partial apply instruction,
560-
// whose substitutions list would contain opened existentials after
561-
// inlining.
562-
static bool calleeHasPartialApplyWithOpenedExistentials(FullApplySite AI) {
563-
if (!AI.hasSubstitutions())
564-
return false;
565-
566-
SILFunction *Callee = AI.getReferencedFunctionOrNull();
567-
assert(Callee && "Trying to optimize a dynamic function?!");
568-
569-
auto SubsMap = AI.getSubstitutionMap();
570-
571-
// Bail if there are no open existentials in the list of substitutions.
572-
bool HasNoOpenedExistentials = true;
573-
for (auto Replacement : SubsMap.getReplacementTypes()) {
574-
if (Replacement->hasOpenedExistential()) {
575-
HasNoOpenedExistentials = false;
576-
break;
577-
}
578-
}
579-
580-
if (HasNoOpenedExistentials)
581-
return false;
582-
583-
for (auto &BB : *Callee) {
584-
for (auto &I : BB) {
585-
if (auto PAI = dyn_cast<PartialApplyInst>(&I)) {
586-
if (!PAI->hasSubstitutions())
587-
continue;
588-
589-
// Check if any of substitutions would contain open existentials
590-
// after inlining.
591-
auto PAISubMap = PAI->getSubstitutionMap();
592-
PAISubMap = PAISubMap.subst(SubsMap);
593-
if (PAISubMap.hasOpenedExistential())
594-
return true;
595-
}
596-
}
597-
}
598-
599-
return false;
600-
}
601-
602559
// Returns true if a given apply site should be skipped during the
603560
// early inlining pass.
604561
//
@@ -781,12 +738,6 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI,
781738
return nullptr;
782739
}
783740

784-
// IRGen cannot handle partial_applies containing opened_existentials
785-
// in its substitutions list.
786-
if (calleeHasPartialApplyWithOpenedExistentials(AI)) {
787-
return nullptr;
788-
}
789-
790741
return Callee;
791742
}
792743

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)