blob: 9fd9653b12c26377ca328c04fe9df9b0405963b3 [file] [log] [blame]
// RUN: %target-sil-opt -enable-sil-verify-all -late-release-hoisting %s | %FileCheck %s
import Builtin
import Swift
sil_stage canonical
//===----------------------------------------------------------------------===//
// Unit tests for reachability to and from local objects.
//===----------------------------------------------------------------------===//
class HasObj {
var o : AnyObject
}
class HasInt64 {
var i : Int64
}
class HasHasObj {
var ho : HasObj
}
// The release of %lo can be hoisted over the load because it does not
// point to any escaping references.
//
// CHECK-LABEL: sil @testLocalNotReachesEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject {
// CHECK: bb0(%0 : $Int64, %1 : $HasObj):
// CHECK: [[LO:%.*]] = alloc_ref $HasInt
// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64, #HasInt64.i
// CHECK: store %0 to [[IADR]] : $*Int64
// CHECK: strong_release [[LO]] : $HasInt
// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o
// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject
// CHECK: return [[O]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testLocalNotReachesEscaped'
sil @testLocalNotReachesEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject {
bb0(%0 : $Int64, %1 : $HasObj):
%lo = alloc_ref $HasInt64
%iadr = ref_element_addr %lo : $HasInt64, #HasInt64.i
store %0 to %iadr : $*Int64
%oadr = ref_element_addr %1 : $HasObj, #HasObj.o
%o = load %oadr : $*AnyObject
strong_release %lo : $HasInt64
return %o : $AnyObject
}
// The release of %lo cannot be hoisted over the load.
//
// CHECK-LABEL: sil @testLocalReachesEscaped : $@convention(thin) (@owned AnyObject) -> AnyObject {
// CHECK: bb0(%0 : $AnyObject):
// CHECK: [[LO:%.*]] = alloc_ref $HasObj
// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o
// CHECK: store %0 to [[IADR]] : $*AnyObject
// CHECK: [[OADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o
// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject
// CHECK: strong_release [[LO]] : $HasObj
// CHECK: return [[O]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testLocalReachesEscaped'
sil @testLocalReachesEscaped : $@convention(thin) (@owned AnyObject) -> AnyObject {
bb0(%0 : $AnyObject):
%lo = alloc_ref $HasObj
%iadr = ref_element_addr %lo : $HasObj, #HasObj.o
store %0 to %iadr : $*AnyObject
%oadr = ref_element_addr %lo : $HasObj, #HasObj.o
%o = load %oadr : $*AnyObject
strong_release %lo : $HasObj
return %o : $AnyObject
}
// The release of %lo can be hoisted above the load from %oadr. There
// is a points-to relation between %lo and %oadr. However, the
// points-to path from %lo to %oadr reaches another reference counted
// object before reaching $oadr.
//
// CHECK-LABEL: sil @testLocalReachesRCEscaped : $@convention(thin) (@owned HasObj) -> AnyObject {
// CHECK: bb0(%0 : $HasObj):
// CHECK: [[LO:%.*]] = alloc_ref $HasHasObj
// CHECK: [[HOADR:%.*]] = ref_element_addr [[LO]] : $HasHasObj, #HasHasObj.ho
// CHECK: strong_retain %0 : $HasObj
// CHECK: store %0 to [[HOADR]] : $*HasObj
// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj
// CHECK: strong_release [[LO]] : $HasHasObj
// CHECK: [[OADR:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o
// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject
// CHECK: return [[O]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testLocalReachesRCEscaped'
sil @testLocalReachesRCEscaped : $@convention(thin) (@owned HasObj) -> AnyObject {
bb0(%0 : $HasObj):
%lo = alloc_ref $HasHasObj
%hoadr = ref_element_addr %lo : $HasHasObj, #HasHasObj.ho
strong_retain %0 : $HasObj
store %0 to %hoadr : $*HasObj
%ho = load %hoadr : $*HasObj
%oadr = ref_element_addr %ho : $HasObj, #HasObj.o
%o = load %oadr : $*AnyObject
// Normally a retain of %o would precede this release of %c. But
// let's assume some aggressive optimization happened.
strong_release %lo : $HasHasObj
return %o : $AnyObject
}
// Two local references, one reachable from the other.
//
// TODO: We do not currently hoist strong_release %hho above
// strong_retain %o because %hho is marked as escaping. However, it is
// only stored to another local object, so in theory it does not need
// to be marked escaping.
//
// CHECK-LABEL: sil @testLocalReachesEscapingLocal : $@convention(thin) (AnyObject) -> AnyObject {
// CHECK: bb0(%0 : $AnyObject):
// CHECK: [[LO:%.*]] = alloc_ref $HasObj
// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o
// CHECK: strong_retain %0 : $AnyObject
// CHECK: store %0 to [[OADR]] : $*AnyObject
// CHECK: [[HHO:%.*]] = alloc_ref $HasHasObj
// CHECK: [[HOADR:%.*]] = ref_element_addr [[HHO]] : $HasHasObj, #HasHasObj.ho
// CHECK: store [[LO]] to [[HOADR]] : $*HasObj
// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj
// CHECK: [[OADR2:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o
// CHECK: [[O:%.*]] = load [[OADR2]] : $*AnyObject
// CHECK: strong_retain [[O]] : $AnyObject
// CHECK: strong_release [[HHO]] : $HasHasObj
// CHECK: return [[O]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testLocalReachesEscapingLocal'
sil @testLocalReachesEscapingLocal : $@convention(thin) (AnyObject) -> AnyObject {
bb0(%0 : $AnyObject):
%ho = alloc_ref $HasObj
%oadr = ref_element_addr %ho : $HasObj, #HasObj.o
strong_retain %0 : $AnyObject
store %0 to %oadr : $*AnyObject
%hho = alloc_ref $HasHasObj
%hoadr = ref_element_addr %hho : $HasHasObj, #HasHasObj.ho
store %ho to %hoadr : $*HasObj
%ho2 = load %hoadr : $*HasObj
%oadr2 = ref_element_addr %ho2 : $HasObj, #HasObj.o
%o = load %oadr2 : $*AnyObject
strong_retain %o : $AnyObject
strong_release %hho : $HasHasObj
return %o : $AnyObject
}
// Two local references, one reachable from the other. We assume that
// the reachable one has its own reference count and hoist
// strong_release %hho above load %oadr.
//
// CHECK-LABEL: sil @testLocalReachesRCLocal : $@convention(thin) (AnyObject) -> AnyObject {
// CHECK: bb0(%0 : $AnyObject):
// CHECK: [[LO:%.*]] = alloc_ref $HasObj
// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o
// CHECK: strong_retain %0 : $AnyObject
// CHECK: store %0 to [[OADR]] : $*AnyObject
// CHECK: [[HHO:%.*]] = alloc_ref $HasHasObj
// CHECK: [[HOADR:%.*]] = ref_element_addr [[HHO]] : $HasHasObj, #HasHasObj.ho
// CHECK: store [[LO]] to [[HOADR]] : $*HasObj
// CHECK: [[HO:%.*]] = load [[HOADR]] : $*HasObj
// CHECK: strong_release [[HHO]] : $HasHasObj
// CHECK: [[OADR2:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o
// CHECK: [[O:%.*]] = load [[OADR2]] : $*AnyObject
// CHECK: strong_retain [[O]] : $AnyObject
// CHECK: return [[O]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testLocalReachesRCLocal'
sil @testLocalReachesRCLocal : $@convention(thin) (AnyObject) -> AnyObject {
bb0(%0 : $AnyObject):
%ho = alloc_ref $HasObj
%oadr = ref_element_addr %ho : $HasObj, #HasObj.o
strong_retain %0 : $AnyObject
store %0 to %oadr : $*AnyObject
%hho = alloc_ref $HasHasObj
%hoadr = ref_element_addr %hho : $HasHasObj, #HasHasObj.ho
store %ho to %hoadr : $*HasObj
%ho2 = load %hoadr : $*HasObj
%oadr2 = ref_element_addr %ho2 : $HasObj, #HasObj.o
%o = load %oadr2 : $*AnyObject
strong_release %hho : $HasHasObj
strong_retain %o : $AnyObject
return %o : $AnyObject
}
// Hoist the release of an escaping object above memory operations on
// a local object that never escapes.
//
// CHECK-LABEL: sil @testEscapeNotReachesLocal : $@convention(thin) (Int64, @owned HasObj) -> Int64 {
// CHECK: bb0(%0 : $Int64, %1 : $HasObj):
// CHECK: strong_release %1 : $HasObj
// CHECK: [[LO:%.*]] = alloc_ref $HasInt64
// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64, #HasInt64.i
// CHECK: store %0 to [[IADR]] : $*Int64
// CHECK: [[I:%.*]] = load [[IADR]] : $*Int64
// CHECK: return [[I]] : $Int64
// CHECK-LABEL: } // end sil function 'testEscapeNotReachesLocal'
sil @testEscapeNotReachesLocal : $@convention(thin) (Int64, @owned HasObj) -> Int64 {
bb0(%0 : $Int64, %1 : $HasObj):
%lo = alloc_ref $HasInt64
%iadr = ref_element_addr %lo : $HasInt64, #HasInt64.i
store %0 to %iadr : $*Int64
%i = load %iadr : $*Int64
strong_release %1 : $HasObj
return %i : $Int64
}
// EscapeAnalysis must consider the local object %lo as globally
// escaping because it is stored into an object that escapes via an
// incoming argument. This creates an aliasing relationship preventing
// the strong_release from being hoisted.
//
// CHECK-LABEL: sil @testEscapeReachesLocal : $@convention(thin) (@owned AnyObject, @owned HasHasObj) -> AnyObject {
// CHECK: bb0(%0 : $AnyObject, %1 : $HasHasObj):
// CHECK: [[LO:%.*]] = alloc_ref $HasObj
// CHECK: [[OADR:%.*]] = ref_element_addr [[LO]] : $HasObj, #HasObj.o
// CHECK: store %0 to [[OADR]] : $*AnyObject
// CHECK: [[HOADR:%.*]] = ref_element_addr %1 : $HasHasObj, #HasHasObj.ho
// CHECK: store [[LO]] to [[HOADR]] : $*HasObj
// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject
// CHECK: strong_release %1 : $HasHasObj
// CHECK: return [[O]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testEscapeReachesLocal'
sil @testEscapeReachesLocal : $@convention(thin) (@owned AnyObject, @owned HasHasObj) -> AnyObject {
bb0(%0 : $AnyObject, %1 : $HasHasObj):
%lo = alloc_ref $HasObj
%oadr = ref_element_addr %lo : $HasObj, #HasObj.o
store %0 to %oadr : $*AnyObject
%hoadr = ref_element_addr %1 : $HasHasObj, #HasHasObj.ho
store %lo to %hoadr : $*HasObj
%o = load %oadr : $*AnyObject
strong_release %1 : $HasHasObj
return %o : $AnyObject
}