mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Type annotations for instruction operands are omitted, e.g. ``` %3 = struct $S(%1, %2) ``` Operand types are redundant anyway and were only used for sanity checking in the SIL parser. But: operand types _are_ printed if the definition of the operand value was not printed yet. This happens: * if the block with the definition appears after the block where the operand's instruction is located * if a block or instruction is printed in isolation, e.g. in a debugger The old behavior can be restored with `-Xllvm -sil-print-types`. This option is added to many existing test files which check for operand types in their check-lines.
332 lines
13 KiB
Plaintext
332 lines
13 KiB
Plaintext
// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all -late-release-hoisting -module-name=test %s | %FileCheck %s
|
|
|
|
// REQUIRES: swift_in_compiler
|
|
|
|
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 HasInt64Sub : HasObj {
|
|
var i : Int64
|
|
}
|
|
|
|
class HasHasInt64 : HasObj {
|
|
var hi : HasInt64
|
|
}
|
|
|
|
class HasHasObj {
|
|
var ho : HasObj
|
|
}
|
|
|
|
public final class C {}
|
|
|
|
struct FD: ~Copyable {
|
|
var c = C()
|
|
|
|
deinit {}
|
|
}
|
|
|
|
sil @$s4test8HasInt64CfD : $@convention(method) (@owned HasInt64) -> () {
|
|
[%0: noescape **]
|
|
}
|
|
|
|
// 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 $HasInt64
|
|
// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64, #HasInt64.i
|
|
// CHECK: store %0 to [[IADR]] : $*Int64
|
|
// CHECK-NOT-SUPPORTED-YET: strong_release [[LO]] : $HasInt64
|
|
// 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 because it the
|
|
// release of %lo causes all objects pointed to by its fields to
|
|
// escape. Since it has at least one reference-type field, it may
|
|
// point to escaping memory.
|
|
//
|
|
// CHECK-LABEL: sil @testLocalMayReachEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject {
|
|
// CHECK: bb0(%0 : $Int64, %1 : $HasObj):
|
|
// CHECK: [[LO:%.*]] = alloc_ref $HasInt64Sub
|
|
// CHECK: [[IADR:%.*]] = ref_element_addr [[LO]] : $HasInt64Sub, #HasInt64Sub.i
|
|
// CHECK: store %0 to [[IADR]] : $*Int64
|
|
// CHECK: [[OADR:%.*]] = ref_element_addr %1 : $HasObj, #HasObj.o
|
|
// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject
|
|
// CHECK: strong_release [[LO]] : $HasInt64Sub
|
|
// CHECK: return [[O]] : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testLocalMayReachEscaped'
|
|
sil @testLocalMayReachEscaped : $@convention(thin) (Int64, @owned HasObj) -> AnyObject {
|
|
bb0(%0 : $Int64, %1 : $HasObj):
|
|
%lo = alloc_ref $HasInt64Sub
|
|
%iadr = ref_element_addr %lo : $HasInt64Sub, #HasInt64Sub.i
|
|
store %0 to %iadr : $*Int64
|
|
|
|
%oadr = ref_element_addr %1 : $HasObj, #HasObj.o
|
|
%o = load %oadr : $*AnyObject
|
|
strong_release %lo : $HasInt64Sub
|
|
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 cannot be hoisted above the load from %oadr
|
|
// because there is a points-to relation between %lo and %oadr.
|
|
//
|
|
// TODO: To hoist this release, AliasAnalysis also need to prove that
|
|
// it isn't being hoisted above the last use. This information is not
|
|
// properly communicated between AliasAnalysis and EscapeAnalysis.
|
|
//
|
|
// 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: [[OADR:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o
|
|
// CHECK: [[O:%.*]] = load [[OADR]] : $*AnyObject
|
|
// CHECK: strong_release [[LO]] : $HasHasObj
|
|
// 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 cannot assume that
|
|
// the reachable one has its own reference count and hoist
|
|
// strong_release %hho above load %oadr without more information.
|
|
//
|
|
// TODO: To hoist this release, AliasAnalysis also need to prove that
|
|
// it isn't being hoisted above the last use. This information is not
|
|
// properly communicated between AliasAnalysis and EscapeAnalysis.
|
|
//
|
|
// 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: [[OADR2:%.*]] = ref_element_addr [[HO]] : $HasObj, #HasObj.o
|
|
// CHECK: [[O:%.*]] = load [[OADR2]] : $*AnyObject
|
|
// CHECK: strong_release [[HHO]] : $HasHasObj
|
|
// 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
|
|
}
|
|
|
|
// 'strong_release %lo : $HasHasInt64' cannot be hoisted because it
|
|
// contains a reference that appears to escape. With the current
|
|
// information, we can't be sure if that escaping reference points to
|
|
// the load of '%oadr'.
|
|
//
|
|
// TODO: To hoist this release, AliasAnalysis also need to prove that
|
|
// it isn't being hoisted above the last use. This information is not
|
|
// properly communicated between AliasAnalysis and EscapeAnalysis.
|
|
//
|
|
// CHECK-LABEL: sil @testLocalWithReferenceProperty : $@convention(thin) (Int64, @owned HasObj) -> AnyObject {
|
|
// CHECK: bb0(%0 : $Int64, %1 : $HasObj):
|
|
// CHECK: [[O:%.*]] = load {{.*}} : $*AnyObject
|
|
// CHECK: strong_release %{{.*}} : $HasHasInt64
|
|
// CHECK: return [[O]] : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testLocalWithReferenceProperty'
|
|
sil @testLocalWithReferenceProperty : $@convention(thin) (Int64, @owned HasObj) -> AnyObject {
|
|
bb0(%0 : $Int64, %1 : $HasObj):
|
|
%hi = alloc_ref $HasInt64
|
|
%iadr = ref_element_addr %hi : $HasInt64, #HasInt64.i
|
|
store %0 to %iadr : $*Int64
|
|
%lo = alloc_ref $HasHasInt64
|
|
%hiadr = ref_element_addr %lo : $HasHasInt64, #HasHasInt64.hi
|
|
store %hi to %hiadr : $*HasInt64
|
|
|
|
%oadr = ref_element_addr %1 : $HasObj, #HasObj.o
|
|
%o = load %oadr : $*AnyObject
|
|
strong_release %lo : $HasHasInt64
|
|
return %o : $AnyObject
|
|
}
|
|
|
|
// Test that a struct-with-deinit is not RC-identical to its single member.
|
|
//
|
|
// CHECK-LABEL: sil hidden @fdDeinit : $@convention(method) (@owned FD) -> () {
|
|
// CHECK: strong_release %{{.*}} : $C
|
|
// CHECK-LABEL: } // end sil function 'fdDeinit'
|
|
sil hidden @fdDeinit : $@convention(method) (@owned FD) -> () {
|
|
bb0(%0 : $FD):
|
|
%2 = struct_extract %0 : $FD, #FD.c
|
|
strong_release %2 : $C
|
|
%4 = tuple ()
|
|
return %4 : $()
|
|
}
|