Skip to content

Devirtualizer: disable the “effectively final” optimization if a function is inlinable #14740

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
Feb 20, 2018
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
10 changes: 6 additions & 4 deletions include/swift/SILOptimizer/Utils/Devirtualize.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,17 @@ DevirtualizationResult tryDevirtualizeApply(ApplySite AI,
bool canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA);
bool isNominalTypeWithUnboundGenericParameters(SILType Ty, SILModule &M);
bool canDevirtualizeClassMethod(FullApplySite AI, SILType ClassInstanceType,
OptRemark::Emitter *ORE = nullptr);
OptRemark::Emitter *ORE = nullptr,
bool isEffectivelyFinalMethod = false);
SILFunction *getTargetClassMethod(SILModule &M, SILType ClassOrMetatypeType,
MethodInst *MI);
DevirtualizationResult devirtualizeClassMethod(FullApplySite AI,
SILValue ClassInstance,
OptRemark::Emitter *ORE);
DevirtualizationResult tryDevirtualizeClassMethod(FullApplySite AI,
SILValue ClassInstance,
OptRemark::Emitter *ORE);
DevirtualizationResult
tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
OptRemark::Emitter *ORE,
bool isEffectivelyFinalMethod = false);
DevirtualizationResult
tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE);
}
Expand Down
29 changes: 22 additions & 7 deletions lib/SILOptimizer/Utils/Devirtualize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,8 @@ SILFunction *swift::getTargetClassMethod(SILModule &M,
/// return true if it is possible to devirtualize, false - otherwise.
bool swift::canDevirtualizeClassMethod(FullApplySite AI,
SILType ClassOrMetatypeType,
OptRemark::Emitter *ORE) {
OptRemark::Emitter *ORE,
bool isEffectivelyFinalMethod) {

DEBUG(llvm::dbgs() << " Trying to devirtualize : " << *AI.getInstruction());

Expand All @@ -519,6 +520,15 @@ bool swift::canDevirtualizeClassMethod(FullApplySite AI,
return false;
}

// We need to disable the “effectively final” opt if a function is inlinable
if (isEffectivelyFinalMethod &&
F->getResilienceExpansion() == ResilienceExpansion::Minimal) {
DEBUG(llvm::dbgs() << " FAIL: Could not optimize function because "
"it is an effectively-final inlinable: "
<< F->getName() << "\n");
return false;
}

// Mandatory inlining does class method devirtualization. I'm not sure if this
// is really needed, but some test rely on this.
// So even for Onone functions we have to do it if the SILStage is raw.
Expand Down Expand Up @@ -701,10 +711,12 @@ DevirtualizationResult swift::devirtualizeClassMethod(FullApplySite AI,
return std::make_pair(ResultValue, NewAI);
}

DevirtualizationResult swift::tryDevirtualizeClassMethod(FullApplySite AI,
SILValue ClassInstance,
OptRemark::Emitter *ORE) {
if (!canDevirtualizeClassMethod(AI, ClassInstance->getType(), ORE))
DevirtualizationResult
swift::tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
OptRemark::Emitter *ORE,
bool isEffectivelyFinalMethod) {
if (!canDevirtualizeClassMethod(AI, ClassInstance->getType(), ORE,
isEffectivelyFinalMethod))
return std::make_pair(nullptr, FullApplySite());
return devirtualizeClassMethod(AI, ClassInstance, ORE);
}
Expand Down Expand Up @@ -1025,7 +1037,8 @@ DevirtualizationResult swift::tryDevirtualizeApply(ApplySite AI,
auto *CD = ClassType.getClassOrBoundGenericClass();

if (isEffectivelyFinalMethod(FAS, ClassType, CD, CHA))
return tryDevirtualizeClassMethod(FAS, Instance, ORE);
return tryDevirtualizeClassMethod(FAS, Instance, ORE,
true /*isEffectivelyFinalMethod*/);

// Try to check if the exact dynamic type of the instance is statically
// known.
Expand Down Expand Up @@ -1091,7 +1104,9 @@ bool swift::canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA)
auto *CD = ClassType.getClassOrBoundGenericClass();

if (isEffectivelyFinalMethod(AI, ClassType, CD, CHA))
return canDevirtualizeClassMethod(AI, Instance->getType());
return canDevirtualizeClassMethod(AI, Instance->getType(),
nullptr /*ORE*/,
true /*isEffectivelyFinalMethod*/);

// Try to check if the exact dynamic type of the instance is statically
// known.
Expand Down
2 changes: 0 additions & 2 deletions test/IRGen/big_types_corner_cases.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ public class BigClass {
}

// CHECK-LABEL: define{{( protected)?}} hidden swiftcc void @"$S22big_types_corner_cases8BigClassC03useE6Struct0aH0yAA0eH0V_tF"(%T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}), %T22big_types_corner_cases8BigClassC* swiftself)
// CHECK: getelementptr inbounds %T22big_types_corner_cases8BigClassC, %T22big_types_corner_cases8BigClassC*
// CHECK: call void @"$SSqWy
// CHECK: [[BITCAST:%.*]] = bitcast i8* {{.*}} to void (%T22big_types_corner_cases9BigStructV*, %swift.refcounted*)*
// CHECK: call swiftcc void [[BITCAST]](%T22big_types_corner_cases9BigStructV* noalias nocapture dereferenceable({{.*}}) %0, %swift.refcounted* swiftself
// CHECK: ret void
Expand Down
57 changes: 57 additions & 0 deletions test/SILOptimizer/devirt_access_serialized.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -devirtualizer | %FileCheck %s

sil_stage canonical

import Builtin
import Swift
import SwiftShims

class X
{
private func ping() -> Int
@objc deinit
init()
}

class Y : X
{
@objc deinit
override init()
}

sil [serialized] @_TFC14devirt_access21X4pingfS0_FT_Si : $@convention(method) (@guaranteed X) -> Int
sil public_external [transparent] @_TFSi33_convertFromBuiltinIntegerLiteralfMSiFBi2048_Si : $@convention(thin) (Builtin.Int2048, @thin Int.Type) -> Int
sil @_TFC14devirt_access21Xd : $@convention(method) (@guaranteed X) -> @owned Builtin.NativeObject
sil @_TFC14devirt_access21XD : $@convention(method) (@guaranteed X) -> ()
sil @_TFC14devirt_access21XcfMS0_FT_S0_ : $@convention(method) (@owned X) -> @owned X
sil @_TFC14devirt_access21XCfMS0_FT_S0_ : $@convention(thin) (@thick X.Type) -> @owned X
sil @_TFC14devirt_access21Yd : $@convention(method) (@guaranteed Y) -> @owned Builtin.NativeObject
sil @_TFC14devirt_access21YD : $@convention(method) (@guaranteed Y) -> ()
sil @_TFC14devirt_access21YcfMS0_FT_S0_ : $@convention(method) (@owned Y) -> @owned Y
sil @_TFC14devirt_access21YCfMS0_FT_S0_ : $@convention(thin) (@thick Y.Type) -> @owned Y

//CHECK-LABEL: sil @Case
//CHECK-NOT: function_ref @_TFC14devirt_access21X4pingfS0_FT_Si
//CHECK: class_method
//CHECK: apply
//CHECK: return
sil @Case : $@convention(thin) (@owned Y) -> Int {
bb0(%0 : $Y):
debug_value %0 : $Y, let, name "a" // id: %1
strong_retain %0 : $Y // id: %2
%3 = upcast %0 : $Y to $X // users: %4, %5
%4 = class_method %3 : $X, #X.ping!1 : (X) -> () -> Int, $@convention(method) (@guaranteed X) -> Int // user: %5
%5 = apply %4(%3) : $@convention(method) (@guaranteed X) -> Int // user: %7
strong_release %0 : $Y // id: %6
return %5 : $Int // id: %7
}

sil_vtable X {
#X.ping!1: @_TFC14devirt_access21X4pingfS0_FT_Si // devirt_access2.X.ping (devirt_access2.X)() -> Swift.Int
#X.init!initializer.1: @_TFC14devirt_access21XcfMS0_FT_S0_ // devirt_access2.X.init (devirt_access2.X.Type)() -> devirt_access2.X
}

sil_vtable Y {
#X.ping!1: @_TFC14devirt_access21X4pingfS0_FT_Si // devirt_access2.X.ping (devirt_access2.X)() -> Swift.Int
#X.init!initializer.1: @_TFC14devirt_access21YcfMS0_FT_S0_ // devirt_access2.Y.init (devirt_access2.Y.Type)() -> devirt_access2.Y
}
6 changes: 2 additions & 4 deletions test/sil-func-extractor/basic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@

// EXTRACT-NOW-LABEL: sil [serialized] @$S5basic7VehicleC3nowSiyF : $@convention(method) (@guaranteed Vehicle) -> Int {
// EXTRACT-NOW: bb0
// EXTRACT-NOW: ref_element_addr
// EXTRACT-NOW-NEXT: begin_access [read] [dynamic]
// EXTRACT-NOW-NEXT: load
// EXTRACT-NOW-NEXT: end_access
// EXTRACT-NOW: class_method
// EXTRACT-NOW-NEXT: apply
// EXTRACT-NOW-NEXT: return

public struct X {
Expand Down