Skip to content

More conservative about when we can move a release across an instruction #2158

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
Apr 13, 2016
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
5 changes: 5 additions & 0 deletions include/swift/SILOptimizer/Analysis/AliasAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ class AliasAnalysis : public SILAnalysis {
return alias(V1, V2, TBAAType1, TBAAType2) == AliasResult::MayAlias;
}

/// \returns True if the release of the \p Ptr can access memory accessed by
/// \p User.
bool mayValueReleaseInterfereWithInstruction(SILInstruction *User,
SILValue Ptr);

/// Use the alias analysis to determine the memory behavior of Inst with
/// respect to V.
///
Expand Down
8 changes: 6 additions & 2 deletions lib/SILOptimizer/Analysis/ARCAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,12 @@ static bool canTerminatorUseValue(TermInst *TI, SILValue Ptr,
return doOperandsAlias(CCBI->getAllOperands(), Ptr, AA);
}

bool swift::mayUseValue(SILInstruction *User, SILValue Ptr,
AliasAnalysis *AA) {

bool swift::mayUseValue(SILInstruction *User, SILValue Ptr, AliasAnalysis *AA) {
// Check whether releasing this value can call deinit and interfere with User.
if (AA->mayValueReleaseInterfereWithInstruction(User, Ptr))
return true;

// If Inst is an instruction that we know can never use values with reference
// semantics, return true.
if (canNeverUseValues(User))
Expand Down
49 changes: 49 additions & 0 deletions lib/SILOptimizer/Analysis/AliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &OS, AliasResult R) {
}
}

SILValue getAccessedMemory(SILInstruction *User) {
if (auto *LI = dyn_cast<LoadInst>(User)) {
return LI->getOperand();
}

if (auto *SI = dyn_cast<StoreInst>(User)) {
return SI->getDest();
}

return SILValue();
}

//===----------------------------------------------------------------------===//
// Unequal Base Object AA
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -672,6 +684,43 @@ bool AliasAnalysis::canBuiltinDecrementRefCount(BuiltinInst *BI, SILValue Ptr) {
return false;
}


bool AliasAnalysis::mayValueReleaseInterfereWithInstruction(SILInstruction *User,
SILValue Ptr) {
// TODO: Its important to make this as precise as possible.
//
// TODO: Eventually we can plug in some analysis on the what the release of
// the Ptr can do, i.e. be more precise about Ptr's deinit.
//
// TODO: If we know the specific release instruction, we can potentially do
// more.
//
// If this instruction can not read or write any memory. Its OK.
if (!User->mayReadOrWriteMemory())
return false;

// These instructions do read or write memory, get memory accessed.
SILValue V = getAccessedMemory(User);
if (!V)
return true;

// Is this a local allocation ?
if (!pointsToLocalObject(V))
return true;

// This is a local allocation.
// The most important check: does the object escape the current function?
auto LO = getUnderlyingObject(V);
auto *ConGraph = EA->getConnectionGraph(User->getFunction());
auto *Node = ConGraph->getNodeOrNull(LO, EA);
if (Node && !Node->escapes())
return false;

// This is either a non-local allocation or a local allocation that escapes.
// We failed to prove anything, it could be read or written by the deinit.
return true;
}

bool swift::isLetPointer(SILValue V) {
// Traverse the "access" path for V and check that it starts with "let"
// and everything along this path is a value-type (i.e. struct or tuple).
Expand Down
115 changes: 57 additions & 58 deletions test/SILOptimizer/cast_folding_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ func test0() -> Bool {
// Check that this cast does not get eliminated, because
// the compiler does not statically know if this object
// is NSNumber can be converted into Int.
// CHECK-LABEL: sil [noinline] @_TF17cast_folding_objc35testMayBeBridgedCastFromObjCtoSwiftFPs9AnyObject_Si
// CHECK: unconditional_checked_cast_addr
// CHECK: return
@inline(never)
public func testMayBeBridgedCastFromObjCtoSwift(_ o: AnyObject) -> Int {
return o as! Int
Expand All @@ -69,9 +66,6 @@ public func testMayBeBridgedCastFromObjCtoSwift(_ o: AnyObject) -> Int {
// Check that this cast does not get eliminated, because
// the compiler does not statically know if this object
// is NSString can be converted into String.
// CHECK-LABEL: sil [noinline] @_TF17cast_folding_objc41testConditionalBridgedCastFromObjCtoSwiftFPs9AnyObject_GSqSS_
// CHECK: unconditional_checked_cast_addr
// CHECK: return
@inline(never)
public func testConditionalBridgedCastFromObjCtoSwift(_ o: AnyObject) -> String? {
return o as? String
Expand Down Expand Up @@ -101,174 +95,179 @@ public func testFailingBridgedCastFromSwiftToObjC(_ s: String) -> NSInteger {
return s as! NSInteger
}

// Check that class instances may be cast to potentially-class metatypes.
// CHECK-LABEL: sil [noinline] @{{.*}}testCastNSObjectToAnyClass{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastNSObjectToAnyClass(_ o: NSObject) -> AnyClass {
return o as! AnyClass
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastNSObjectToClassObject{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastNSObjectToClassObject(_ o: NSObject) -> NSObject.Type {
return o as! NSObject.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastNSObjectToAnyType{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastNSObjectToAnyType(_ o: NSObject) -> Any.Type {
return o as! Any.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastNSObjectToEveryType{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastNSObjectToEveryType<T>(_ o: NSObject) -> T.Type {
return o as! T.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastNSObjectToNonClassType
// CHECK: builtin "int_trap"
@inline(never)
public func testCastNSObjectToNonClassType(_ o: NSObject) -> Int.Type {
return o as! Int.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyObjectToAnyClass{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastAnyObjectToAnyClass(_ o: AnyObject) -> AnyClass {
return o as! AnyClass
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyObjectToClassObject{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastAnyObjectToClassObject(_ o: AnyObject) -> AnyObject.Type {
return o as! AnyObject.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyObjectToAnyType{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastAnyObjectToAnyType(_ o: AnyObject) -> Any.Type {
return o as! Any.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyObjectToEveryType{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastAnyObjectToEveryType<T>(_ o: AnyObject) -> T.Type {
return o as! T.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyObjectToNonClassType
// CHECK: builtin "int_trap"
@inline(never)
public func testCastAnyObjectToNonClassType(_ o: AnyObject) -> Int.Type {
return o as! Int.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToAnyClass{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastAnyToAnyClass(_ o: Any) -> AnyClass {
return o as! AnyClass
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToClassObject{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastAnyToClassObject(_ o: Any) -> AnyObject.Type {
return o as! AnyObject.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToAnyType{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastAnyToAnyType(_ o: Any) -> Any.Type {
return o as! Any.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToEveryType{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastAnyToEveryType<T>(_ o: Any) -> T.Type {
return o as! T.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToNonClassType
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastAnyToNonClassType(_ o: Any) -> Int.Type {
return o as! Int.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToAnyClass{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastEveryToAnyClass<T>(_ o: T) -> AnyClass {
return o as! AnyClass
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToClassObject{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastEveryToClassObject<T>(_ o: T) -> AnyObject.Type {
return o as! AnyObject.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToAnyType{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastEveryToAnyType<T>(_ o: T) -> Any.Type {
return o as! Any.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToEveryType{{.*}}
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastEveryToEveryType<T, U>(_ o: U) -> T.Type {
return o as! T.Type
}

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToNonClassType
// CHECK: unconditional_checked_cast_addr
@inline(never)
public func testCastEveryToNonClassType<T>(_ o: T) -> Int.Type {
return o as! Int.Type
}

print("test0=\(test0())")


// CHECK-LABEL: sil [noinline] @{{.*}}testCastNSObjectToEveryType{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastNSObjectToNonClassType
// CHECK: builtin "int_trap"

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyObjectToEveryType{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyObjectToNonClassType
// CHECK: builtin "int_trap"

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToAnyClass{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToClassObject{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToAnyType{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToEveryType{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyToNonClassType
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToAnyClass{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToClassObject{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToAnyType{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToEveryType{{.*}}
// CHECK: unconditional_checked_cast_addr

// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToNonClassType
// CHECK: unconditional_checked_cast_addr



// Check that compiler understands that this cast always succeeds.
// Since it is can be statically proven that NSString is bridgeable to String,
// _forceBridgeFromObjectiveC from String should be invoked instead of
// a more general, but less effective swift_bridgeNonVerbatimFromObjectiveC, which
// also performs conformance checks at runtime.
// CHECK-LABEL: sil [noinline] @_TTSf4g___TF17cast_folding_objc30testBridgedCastFromObjCtoSwiftFCSo8NSStringSS
// CHECK-NOT: {{ cast}}
// CHECK: metatype $@thick String.Type
// CHECK: function_ref @_TTWSSs21_ObjectiveCBridgeable10FoundationZFS_26_forceBridgeFromObjectiveCfTwx15_ObjectiveCType6resultRGSqx__T_
// CHECK: apply
// CHECK: return
@inline(never)
public func testBridgedCastFromObjCtoSwift(_ ns: NSString) -> String {
return ns as String
}

// Check that compiler understands that this cast always succeeds
// CHECK-LABEL: sil [noinline] @_TTSf4gs___TF17cast_folding_objc30testBridgedCastFromSwiftToObjCFSSCSo8NSString
// CHECK-NOT: {{ cast}}
// CHECK: function_ref @_TFE10FoundationSS19_bridgeToObjectiveC
// CHECK: apply
// CHECK: return
@inline(never)
public func testBridgedCastFromSwiftToObjC(_ s: String) -> NSString {
return s as NSString
}

// CHECK-LABEL: sil [noinline] @_TTSf4g___TF17cast_folding_objc35testMayBeBridgedCastFromObjCtoSwiftFPs9AnyObject_Si
// CHECK: unconditional_checked_cast_addr
// CHECK: return

// CHECK-LABEL: sil [noinline] @_TTSf4g___TF17cast_folding_objc41testConditionalBridgedCastFromObjCtoSwiftFPs9AnyObject_GSqSS_
// CHECK: unconditional_checked_cast_addr
// CHECK: return

// CHECK-LABEL: sil [noinline] @_TTSf4gs___TF17cast_folding_objc30testBridgedCastFromSwiftToObjCFSSCSo8NSString
// CHECK-NOT: {{ cast}}
// CHECK: function_ref @_TFE10FoundationSS19_bridgeToObjectiveC
// CHECK: apply
// CHECK: return
Loading