Files
swift-mirror/test/SILOptimizer/late_release_hoisting.sil
Erik Eckstein 7cceaff5f3 SIL: don't print operand types in textual SIL
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.
2024-11-21 18:49:52 +01:00

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 : $()
}