Skip to content

PerformanceInliner: enable generic inlining of co-routines #27088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions lib/SILOptimizer/Transforms/PerformanceInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,6 @@ bool SILPerformanceInliner::isProfitableToInline(
assert(Callee);
bool IsGeneric = AI.hasSubstitutions();

assert(EnableSILInliningOfGenerics || !IsGeneric);

// Start with a base benefit.
int BaseBenefit = RemovedCallBenefit;

Expand Down Expand Up @@ -555,17 +553,26 @@ static Optional<bool> shouldInlineGeneric(FullApplySite AI) {
if (Callee->getInlineStrategy() == AlwaysInline || Callee->isTransparent())
return true;

// All other generic functions should not be inlined if this kind of inlining
// is disabled.
if (!EnableSILInliningOfGenerics)
return false;

// If all substitutions are concrete, then there is no need to perform the
// generic inlining. Let the generic specializer create a specialized
// function and then decide if it is beneficial to inline it.
if (!AI.getSubstitutionMap().hasArchetypes())
return false;

if (Callee->getLoweredFunctionType()->getCoroutineKind() !=
SILCoroutineKind::None) {
// Co-routines are so expensive (e.g. Array.subscript.read) that we always
// enable inlining them in a generic context. Though the final inlining
// decision is done by the usual heuristics. Therefore we return None and
// not true.
return None;
}

// All other generic functions should not be inlined if this kind of inlining
// is disabled.
if (!EnableSILInliningOfGenerics)
return false;

// It is not clear yet if this function should be decided or not.
return None;
}
Expand Down
14 changes: 5 additions & 9 deletions lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -767,20 +767,16 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI,
return nullptr;
}

if (!EnableSILInliningOfGenerics && AI.hasSubstitutions()) {
// Inlining of generics is not allowed unless it is an @inline(__always)
// or transparent function.
if (Callee->getInlineStrategy() != AlwaysInline && !Callee->isTransparent())
return nullptr;
}

// We cannot inline function with layout constraints on its generic types
// if the corresponding substitution type does not have the same constraints.
// The reason for this restriction is that we'd need to be able to express
// in SIL something like casting a value of generic type T into a value of
// generic type T: _LayoutConstraint, which is impossible currently.
if (EnableSILInliningOfGenerics && AI.hasSubstitutions()) {
if (!isCallerAndCalleeLayoutConstraintsCompatible(AI))
if (AI.hasSubstitutions()) {
if (!isCallerAndCalleeLayoutConstraintsCompatible(AI) &&
// TODO: revisit why we can make an exception for inline-always
// functions. Some tests depend on it.
Callee->getInlineStrategy() != AlwaysInline && !Callee->isTransparent())
return nullptr;
}

Expand Down
34 changes: 34 additions & 0 deletions test/SILOptimizer/inline_generic_coroutines.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %target-swift-frontend -parse-as-library -O -emit-sil %s | %FileCheck %s
// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib

@inline(never)
func useit<T>(_ t: T) {
print(t)
}

// Check that we inline the Array.subscript.read coroutine

// CHECK-LABEL: sil @{{.*}}testit
// CHECK-NOT: begin_apply
// CHECK-NOT: end_apply
// CHECK: } // end sil function {{.*}}testit
public func testit<T>(_ a: [T]) {
for t in a {
useit(t)
}
}

// Check that we inline the ManagedBufferPointer.header.read coroutine

public final class MyBuffer<Element> {
typealias Manager = ManagedBufferPointer<Int, Element>

// CHECK-LABEL: sil @{{.*}}MyBuffer{{.*}}countSivg
// CHECK-NOT: begin_apply
// CHECK-NOT: end_apply
// CHECK: } // end sil function {{.*}}MyBuffer{{.*}}countSivg
public var count: Int {
return Manager(unsafeBufferObject: self).header
}
}