Skip to content

[sil-inliner] Respect the @inline(__always) and @_transparent even if inlining of generics is disabled #9794

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 3 commits into from
May 20, 2017
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
31 changes: 19 additions & 12 deletions lib/SILOptimizer/Transforms/PerformanceInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,15 +365,6 @@ static Optional<bool> shouldInlineGeneric(FullApplySite AI) {
assert(!AI.getSubstitutions().empty() &&
"Expected a generic apply");

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 (!hasArchetypes(AI.getSubstitutions()))
return false;

SILFunction *Callee = AI.getReferencedFunction();

// Do not inline @_semantics functions when compiling the stdlib,
Expand All @@ -393,6 +384,17 @@ 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 (!hasArchetypes(AI.getSubstitutions()))
return false;

// It is not clear yet if this function should be decided or not.
return None;
}
Expand All @@ -411,10 +413,10 @@ decideInWarmBlock(FullApplySite AI,

SILFunction *Callee = AI.getReferencedFunction();

if (Callee->getInlineStrategy() == AlwaysInline) {
if (Callee->getInlineStrategy() == AlwaysInline || Callee->isTransparent()) {
DEBUG(
dumpCaller(AI.getFunction());
llvm::dbgs() << " always-inline decision " <<Callee->getName() << '\n';
llvm::dbgs() << " always-inline decision " << Callee->getName() << '\n';
);
return true;
}
Expand All @@ -434,8 +436,13 @@ bool SILPerformanceInliner::decideInColdBlock(FullApplySite AI,
return false;
}

if (Callee->getInlineStrategy() == AlwaysInline)
if (Callee->getInlineStrategy() == AlwaysInline || Callee->isTransparent()) {
DEBUG(
dumpCaller(AI.getFunction());
llvm::dbgs() << " always-inline decision " << Callee->getName() << '\n';
);
return true;
}

int CalleeCost = 0;

Expand Down
6 changes: 4 additions & 2 deletions lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -678,8 +678,10 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI,
}

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

// IRGen cannot handle partial_applies containing opened_existentials
Expand Down
8 changes: 4 additions & 4 deletions test/SILGen/collection_cast_crash.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,28 @@ class KeyClass : Hashable {
}
func ==(lhs: KeyClass, rhs: KeyClass) -> Bool { return true }

// CHECK-LABEL: sil hidden @{{.*}}arrayUpCast{{.*}} <Ct where Ct : MyClass>
// CHECK-LABEL: sil shared @{{.*}}arrayUpCast{{.*}} <Ct where Ct : MyClass>
func arrayUpCast<Ct: MyClass>(_ arr: [Ct]) -> [MyClass] {
// CHECK: apply %{{[0-9]*}}<Ct, MyClass>(%{{[0-9]*}})
return arr
// CHECK: return
}

// CHECK-LABEL: sil hidden @{{.*}}arrayDownCast{{.*}} <Ct where Ct : MyClass>
// CHECK-LABEL: sil shared @{{.*}}arrayDownCast{{.*}} <Ct where Ct : MyClass>
func arrayDownCast<Ct: MyClass>(_ arr: [MyClass]) -> [Ct] {
// CHECK: apply %{{[0-9]*}}<MyClass, Ct>(%{{[0-9]*}})
return arr as! [Ct]
// CHECK: return
}

// CHECK-LABEL: sil hidden @{{.*}}dictUpCast{{.*}} <Ct where Ct : MyClass>
// CHECK-LABEL: sil shared @{{.*}}dictUpCast{{.*}} <Ct where Ct : MyClass>
func dictUpCast<Ct: MyClass>(_ dict: [KeyClass:Ct]) -> [KeyClass:MyClass] {
// CHECK: apply %{{[0-9]*}}<KeyClass, Ct, KeyClass, MyClass>(%{{[0-9]*}})
return dict as [KeyClass:MyClass]
// CHECK: return
}

// CHECK-LABEL: sil hidden @{{.*}}dictDownCast{{.*}} <Ct where Ct : MyClass>
// CHECK-LABEL: sil shared @{{.*}}dictDownCast{{.*}} <Ct where Ct : MyClass>
func dictDownCast<Ct: MyClass>(_ dict: [KeyClass:MyClass]) -> [KeyClass:Ct] {
// CHECK: apply %{{[0-9]*}}<KeyClass, MyClass, KeyClass, Ct>(%{{[0-9]*}})
return dict as! [KeyClass:Ct]
Expand Down
63 changes: 63 additions & 0 deletions test/SILOptimizer/inline_generics.sil
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -inline -sil-inline-generics=true | %FileCheck %s
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -inline -sil-inline-generics=false | %FileCheck --check-prefix=DISABLED-GENERIC-INLINING-CHECK %s

sil_stage canonical

Expand Down Expand Up @@ -112,6 +113,68 @@ bb0(%0 : $T, %1 : $@callee_owned (@owned P) -> Bool):
return %3 : $Bool
} // end sil function 'thunk2'


sil [always_inline] @alwaysInlineGenericCallee : $@convention(thin) <T> (@in T) -> @out T {
bb0(%0 : $*T, %1 : $*T):
copy_addr [take] %1 to [initialization] %0 : $*T
%4 = tuple ()
return %4 : $()
} // end sil function 'alwaysInlineGenericCallee'

sil [transparent] @transparentGenericCallee : $@convention(thin) <T> (@in T) -> @out T {
bb0(%0 : $*T, %1 : $*T):
copy_addr [take] %1 to [initialization] %0 : $*T
%4 = tuple ()
return %4 : $()
} // end sil function 'transparentInlineGenericCallee'


// Check that [always_inline] and [transparent] functions are inlined even if
// inlining of generics is disabled. Regular generic functions should not be
// inlined.
// DISABLED-GENERIC-INLINING-CHECK-LABEL: sil @testComplexInliningOfGenerics
// DISABLED-GENERIC-INLINING-CHECK-NOT: function_ref @{{.*}}
// DISABLED-GENERIC-INLINING-CHECK-NOT: apply
// DISABLED-GENERIC-INLINING-CHECK: [[FUNC:%[0-9]+]] = function_ref @genericFoo
// DISABLED-GENERIC-INLINING-CHECK: apply [[FUNC]]
// DISABLED-GENERIC-INLINING-CHECK-NOT: function_ref
// DISABLED-GENERIC-INLINING-CHECK-NOT: apply
// DISABLED-GENERIC-INLINING-CHECK: end sil function 'testComplexInliningOfGenerics'

// Check that all callees are inlined if inlining of generics is enabled.
// CHECK-LABEL: sil @testComplexInliningOfGenerics
// CHECK-NOT: apply
// CHECK: end sil function 'testComplexInliningOfGenerics'
sil @testComplexInliningOfGenerics : $@convention(thin) <T> (@in T) -> @out T {
bb0(%0 : $*T, %1 : $*T):

// Call an [always_inline] function.
%3 = function_ref @alwaysInlineGenericCallee : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
%4 = alloc_stack $T
copy_addr %1 to [initialization] %4 : $*T
%6 = apply %3<T>(%0, %4) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
dealloc_stack %4 : $*T

// Call a [transparent] function.
%8 = function_ref @transparentGenericCallee : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
%9 = alloc_stack $T
copy_addr %1 to [initialization] %9 : $*T
%10 = apply %8<T>(%0, %9) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
dealloc_stack %9 : $*T

// Call a regular function.
// function_ref genericFoo<A> (A) -> A
%12 = function_ref @genericFoo : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
%13 = alloc_stack $T
copy_addr %1 to [initialization] %13 : $*T
%15 = apply %12<T>(%0, %13) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
dealloc_stack %13 : $*T

destroy_addr %1 : $*T
%18 = tuple ()
return %18 : $()
} // end sil function 'testComplexInliningOfGenerics'

sil_default_witness_table P {
no_default
}
2 changes: 1 addition & 1 deletion test/SILOptimizer/performance_inliner.sil
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ bb2: // Preds: bb0 bb1
return %16 : $() // id: %17
}

sil [transparent] @slowHelper : $@convention(thin) () -> () {
sil @slowHelper : $@convention(thin) () -> () {
bb0:
// make it a non-trivial function
%f = function_ref @unknown_function : $@convention(thin) () -> ()
Expand Down