mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
When canonicalizing a lexical lifetime, don't treat applies which consume a copy of the value as deinit barriers. Doing so forces another copy of the def being canonicalized to remain after the apply. Instead, allow the lifetime to be transferred into the callee. This is the same behavior that already exists for lexical lifetimes represented with the `begin_borrow [lexical]` + `copy_value` instruction sequence.
1050 lines
40 KiB
Plaintext
1050 lines
40 KiB
Plaintext
// RUN: %target-sil-opt -copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK,CHECK-OPT
|
|
// RUN: %target-sil-opt -mandatory-copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK,CHECK-ONONE
|
|
|
|
// REQUIRES: asserts
|
|
|
|
// Most CopyPropagation tests are still in copy_propagation_opaque.sil.
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
class B { }
|
|
|
|
class C {
|
|
var a: Builtin.Int64
|
|
}
|
|
|
|
sil [ossa] @dummy : $@convention(thin) () -> ()
|
|
sil [ossa] @barrier : $@convention(thin) () -> ()
|
|
sil [ossa] @getOwnedC : $@convention(thin) () -> (@owned C)
|
|
sil [ossa] @getOwnedB : $@convention(thin) () -> (@owned B)
|
|
sil [ossa] @takeOwnedC : $@convention(thin) (@owned C) -> ()
|
|
sil [ossa] @takeOwnedCTwice : $@convention(thin) (@owned C, @owned C) -> ()
|
|
sil [ossa] @takeOwnedCAndGuaranteedC : $@convention(thin) (@owned C, @guaranteed C) -> ()
|
|
sil [ossa] @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()
|
|
sil [ossa] @borrowB : $@convention(thin) (@guaranteed B) -> ()
|
|
sil [ossa] @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
|
|
// -O hoists the destroy
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testDestroyAfterCall : {{.*}} {
|
|
// CHECK: bb0:
|
|
// CHECK: [[ARG:%.*]] = apply
|
|
// CHECK: destroy_value [[ARG]] : $B
|
|
// CHECK: apply
|
|
// CHECK-LABEL: } // end sil function 'testDestroyAfterCall'
|
|
sil [ossa] @testDestroyAfterCall : $@convention(thin) () -> () {
|
|
bb0:
|
|
%getOwnedB = function_ref @getOwnedB : $@convention(thin) () -> (@owned B)
|
|
%arg = apply %getOwnedB() : $@convention(thin) () -> (@owned B)
|
|
%f = function_ref @dummy : $@convention(thin) () -> ()
|
|
%call = apply %f() : $@convention(thin) () -> ()
|
|
destroy_value %arg : $B
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// -O removes the copy/destroy
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testDestroyAfterConsumingStore : {{.*}} {
|
|
// CHECK: bb0:
|
|
// CHECK: [[ARG:%.*]] = apply
|
|
// CHECK: [[ADR:%.*]] = alloc_stack $C
|
|
// CHECK-NOT: copy_value
|
|
// CHECK: store [[ARG]] to [init] [[ADR]] : $*C
|
|
// CHECK-NOT: destroy_value
|
|
// CHECK: destroy_addr
|
|
// CHECK-LABEL: } // end sil function 'testDestroyAfterConsumingStore'
|
|
sil [ossa] @testDestroyAfterConsumingStore : $@convention(thin) () -> () {
|
|
bb0:
|
|
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
|
|
%arg = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
|
|
%adr = alloc_stack $C
|
|
%copy = copy_value %arg : $C
|
|
store %copy to [init] %adr : $*C
|
|
destroy_value %arg : $C
|
|
destroy_addr %adr : $*C
|
|
dealloc_stack %adr : $*C
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// -O removes the copy/destroy
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testDestroyAfterConsumingStoreAndCall : {{.*}} {
|
|
// CHECK: bb0:
|
|
// CHECK: [[ARG:%.*]] = apply
|
|
// CHECK: [[ADR:%.*]] = alloc_stack $C
|
|
// CHECK-NOT: copy_value
|
|
// CHECK: store %{{.*}} to [init] [[ADR]] : $*C
|
|
// CHECK-NOT: destroy_value
|
|
// CHECK: apply
|
|
// CHECK: destroy_addr
|
|
// CHECK-LABEL: } // end sil function 'testDestroyAfterConsumingStoreAndCall'
|
|
sil [ossa] @testDestroyAfterConsumingStoreAndCall : $@convention(thin) () -> () {
|
|
bb0:
|
|
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
|
|
%arg = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
|
|
%adr = alloc_stack $C
|
|
%copy = copy_value %arg : $C
|
|
store %copy to [init] %adr : $*C
|
|
%f = function_ref @dummy : $@convention(thin) () -> ()
|
|
%call = apply %f() : $@convention(thin) () -> ()
|
|
destroy_value %arg : $C
|
|
destroy_addr %adr : $*C
|
|
dealloc_stack %adr : $*C
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// -O removes the copy/destroy
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testDestroyAfterConsumingCall : {{.*}} {
|
|
// CHECK: bb0:
|
|
// CHECK: [[ARG:%.*]] = apply
|
|
// CHECK-NOT: copy_value
|
|
// CHECK: apply {{.*}} : $@convention(thin) (@owned C) -> ()
|
|
// CHECK-NOT: destroy_value
|
|
// CHECK-LABEL: } // end sil function 'testDestroyAfterConsumingCall'
|
|
sil [ossa] @testDestroyAfterConsumingCall : $@convention(thin) () -> () {
|
|
bb0:
|
|
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
|
|
%arg = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
|
|
%copy = copy_value %arg : $C
|
|
%f = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> ()
|
|
%call = apply %f(%copy) : $@convention(thin) (@owned C) -> ()
|
|
destroy_value %arg : $C
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// -O removes the copy/destroy
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testDestroyAfterConsumingCallAndCall : {{.*}} {
|
|
// CHECK: bb0:
|
|
// CHECK: [[ARG:%.*]] = apply
|
|
// CHECK-NOT: copy_value
|
|
// CHECK: apply %{{.*}}(%{{.*}}) : $@convention(thin) (@owned C) -> ()
|
|
// CHECK-NOT: destroy_value
|
|
// CHECK: apply
|
|
// CHECK-LABEL: } // end sil function 'testDestroyAfterConsumingCallAndCall'
|
|
sil [ossa] @testDestroyAfterConsumingCallAndCall : $@convention(thin) () -> () {
|
|
bb0:
|
|
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
|
|
%arg = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
|
|
%copy = copy_value %arg : $C
|
|
%f1 = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> ()
|
|
%call1 = apply %f1(%copy) : $@convention(thin) (@owned C) -> ()
|
|
%f2 = function_ref @dummy : $@convention(thin) () -> ()
|
|
%call2 = apply %f2() : $@convention(thin) () -> ()
|
|
destroy_value %arg : $C
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil [ossa] @testPhi : $@convention(thin) () -> @owned C {
|
|
// CHECK: bb0:
|
|
// CHECK: [[C:%.*]] = apply %0() : $@convention(thin) () -> @owned C
|
|
// CHECK: debug_value [[C]] : $C, let, name "Hello Tom"
|
|
// CHECK: apply %{{.*}}([[C]]) : $@convention(thin) (@guaranteed C) -> ()
|
|
// CHECK: cond_br undef, bb1, bb2
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: copy_value
|
|
// CHECK-NOT: destroy_value
|
|
// CHECK: br bb3([[C]] : $C)
|
|
// CHECK: bb2:
|
|
// CHECK: destroy_value %1 : $C
|
|
// CHECK: apply %0() : $@convention(thin) () -> @owned C
|
|
// CHECK: br bb3(
|
|
// CHECK: bb3(%{{.*}} : @owned $C)
|
|
// CHECK-NEXT: br bb4(%{{.*}} : $C)
|
|
// CHECK: bb4(%{{.*}} : @owned $C):
|
|
// CHECK-NEXT: return %{{.*}} : $C
|
|
// CHECK-LABEL: } // end sil function 'testPhi'
|
|
sil [ossa] @testPhi : $@convention(thin) () -> @owned C {
|
|
bb0:
|
|
%f1 = function_ref @getOwnedC : $@convention(thin) () -> @owned C
|
|
%s = apply %f1() : $@convention(thin) () -> @owned C
|
|
debug_value %s : $C, let, name "Hello Tom"
|
|
%f2 = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()
|
|
%use = apply %f2(%s) : $@convention(thin) (@guaranteed C) -> ()
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%cp = copy_value %s : $C
|
|
br bb3(%cp : $C)
|
|
|
|
bb2:
|
|
%s2 = apply %f1() : $@convention(thin) () -> @owned C
|
|
br bb3(%s2 : $C)
|
|
|
|
bb3(%arg3 : @owned $C):
|
|
destroy_value %s : $C
|
|
br bb4(%arg3 : $C)
|
|
|
|
bb4(%arg4 : @owned $C):
|
|
return %arg4 : $C
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @debugValueAfterDestroy : {{.*}} {
|
|
// CHECK: [[INSTANCE:%[^,]+]] = apply
|
|
// CHECK: debug_value [[INSTANCE]]
|
|
// CHECK: destroy_value [[INSTANCE]]
|
|
// CHECK-LABEL: } // end sil function 'debugValueAfterDestroy'
|
|
sil [ossa] @debugValueAfterDestroy : $@convention(thin) () -> () {
|
|
%getOwnedB = function_ref @getOwnedB : $@convention(thin) () -> (@owned B)
|
|
%dummy = function_ref @dummy : $@convention(thin) () -> ()
|
|
%borrowB = function_ref @borrowB : $@convention(thin) (@guaranteed B) -> ()
|
|
%b = apply %getOwnedB() : $@convention(thin) () -> (@owned B)
|
|
apply %dummy() : $@convention(thin) () -> ()
|
|
apply %dummy() : $@convention(thin) () -> ()
|
|
apply %dummy() : $@convention(thin) () -> ()
|
|
apply %borrowB(%b) : $@convention(thin) (@guaranteed B) -> ()
|
|
|
|
debug_value %b : $B
|
|
destroy_value %b : $B
|
|
|
|
apply %dummy() : $@convention(thin) () -> ()
|
|
apply %dummy() : $@convention(thin) () -> ()
|
|
apply %dummy() : $@convention(thin) () -> ()
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @deadArgDef : {{.*}} {
|
|
// CHECK: br [[EXIT:bb[0-9]+]]
|
|
// CHECK: [[EXIT]]([[INSTANCE:%[^,]+]] :
|
|
// CHECK-NEXT: destroy_value [[INSTANCE]]
|
|
// CHECK: apply {{%[^,]+}}
|
|
// CHECK: apply {{%[^,]+}}
|
|
// CHECK: apply {{%[^,]+}}
|
|
// CHECK-LABEL: } // end sil function 'deadArgDef'
|
|
sil [ossa] @deadArgDef : $@convention(thin) () -> () {
|
|
%getOwnedB = function_ref @getOwnedB : $@convention(thin) () -> (@owned B)
|
|
%dummy = function_ref @dummy : $@convention(thin) () -> ()
|
|
%borrowB = function_ref @borrowB : $@convention(thin) (@guaranteed B) -> ()
|
|
%b = apply %getOwnedB() : $@convention(thin) () -> (@owned B)
|
|
apply %borrowB(%b) : $@convention(thin) (@guaranteed B) -> ()
|
|
br exit(%b : $B)
|
|
|
|
exit(%b2 : @owned $B):
|
|
apply %dummy() : $@convention(thin) () -> ()
|
|
apply %dummy() : $@convention(thin) () -> ()
|
|
apply %dummy() : $@convention(thin) () -> ()
|
|
destroy_value %b2 : $B
|
|
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Test begin/end_apply
|
|
// -----------------------------------------------------------------------------
|
|
|
|
struct Struct {
|
|
var st: Int
|
|
}
|
|
|
|
sil @swift_modifyAtWritableKeyPath : $@yield_once @convention(thin) <τ_0_0, τ_0_1> (@inout τ_0_0, @guaranteed WritableKeyPath<τ_0_0, τ_0_1>) -> @yields @inout τ_0_1
|
|
sil @modifyInt : $@convention(thin) (@inout Int) -> ()
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @testBeginApply : $@convention(thin) (@inout Struct) -> () {
|
|
// CHECK: begin_apply
|
|
// CHECK-NOT: destroy
|
|
// CHECK: apply
|
|
// CHECK-NOT: destroy
|
|
// CHECK: end_apply
|
|
// CHECK-NOT: destroy
|
|
// CHECK: destroy_value %{{.*}} : $WritableKeyPath<Struct, Int>
|
|
// CHECK-NOT: destroy
|
|
// CHECK-LABEL: } // end sil function 'testBeginApply'
|
|
sil hidden [ossa] @testBeginApply : $@convention(thin) (@inout Struct) -> () {
|
|
bb0(%0 : $*Struct):
|
|
%2 = keypath $WritableKeyPath<Struct, Int>, (root $Struct; stored_property #Struct.st : $Int)
|
|
debug_value %2 : $WritableKeyPath<Struct, Int>, let, name "kp"
|
|
%4 = copy_value %2 : $WritableKeyPath<Struct, Int>
|
|
%5 = function_ref @swift_modifyAtWritableKeyPath : $@yield_once @convention(thin) <τ_0_0, τ_0_1> (@inout τ_0_0, @guaranteed WritableKeyPath<τ_0_0, τ_0_1>) -> @yields @inout τ_0_1
|
|
(%6, %7) = begin_apply %5<Struct, Int>(%0, %4) : $@yield_once @convention(thin) <τ_0_0, τ_0_1> (@inout τ_0_0, @guaranteed WritableKeyPath<τ_0_0, τ_0_1>) -> @yields @inout τ_0_1
|
|
%8 = function_ref @modifyInt : $@convention(thin) (@inout Int) -> ()
|
|
%9 = apply %8(%6) : $@convention(thin) (@inout Int) -> ()
|
|
end_apply %7
|
|
destroy_value %4 : $WritableKeyPath<Struct, Int>
|
|
destroy_value %2 : $WritableKeyPath<Struct, Int>
|
|
%13 = tuple ()
|
|
return %13 : $()
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Test project_box
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// FIXME: project_box is currently a PointerEscape, so box live ranges are not canonicalized.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testProjectBox : $@convention(thin) (@owned B) -> @owned B {
|
|
// CHECK: copy_value
|
|
// CHECK: destroy_value
|
|
// CHECK: destroy_value
|
|
// CHECK-LABEL: } // end sil function 'testProjectBox'
|
|
sil [ossa] @testProjectBox : $@convention(thin) (@owned B) -> @owned B {
|
|
bb0(%0 : @owned $B):
|
|
%box = alloc_box $<τ_0_0> { var τ_0_0 } <B>
|
|
%boxadr = project_box %box : $<τ_0_0> { var τ_0_0 } <B>, 0
|
|
store %0 to [init] %boxadr : $*B
|
|
%load = load [copy] %boxadr : $*B
|
|
%copy = copy_value %box : $<τ_0_0> { var τ_0_0 } <B>
|
|
destroy_value %box : $<τ_0_0> { var τ_0_0 } <B>
|
|
destroy_value %copy : $<τ_0_0> { var τ_0_0 } <B>
|
|
return %load : $B
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Test mark_dependence
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// FIXME: mark_dependence is currently a PointerEscape, so dependent live ranges are not canonicalized.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testMarkDependence : {{.*}} {
|
|
// CHECK: copy_value
|
|
// CHECK: destroy_value
|
|
// CHECK: destroy_value
|
|
// CHECK-LABEL: } // end sil function 'testMarkDependence'
|
|
sil [ossa] @testMarkDependence : $@convention(thin) (@inout Builtin.Int64) -> Builtin.Int64 {
|
|
bb0(%0 : $*Builtin.Int64):
|
|
%getOwnedB = function_ref @getOwnedB : $@convention(thin) () -> (@owned B)
|
|
%1 = apply %getOwnedB() : $@convention(thin) () -> (@owned B)
|
|
%ptr = mark_dependence %0 : $*Builtin.Int64 on %1 : $B
|
|
%val = load [trivial] %ptr : $*Builtin.Int64
|
|
%copy = copy_value %1 : $B
|
|
destroy_value %1 : $B
|
|
destroy_value %copy : $B
|
|
return %val : $Builtin.Int64
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Test OperandOwnership::BitwiseEscape
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// CHECK-LABEL: sil [ossa] @testBitwiseEscape : $@convention(thin) (@guaranteed C) -> Builtin.RawPointer {
|
|
// CHECK-NOT: copy_value
|
|
// CHECK-NOT: destroy_value
|
|
// CHECK-LABEL: } // end sil function 'testBitwiseEscape'
|
|
sil [ossa] @testBitwiseEscape : $@convention(thin) (@guaranteed C) -> Builtin.RawPointer {
|
|
bb0(%0 : @guaranteed $C):
|
|
%raw = ref_to_raw_pointer %0 : $C to $Builtin.RawPointer
|
|
%copy = copy_value %0 : $C
|
|
destroy_value %copy : $C
|
|
return %raw : $Builtin.RawPointer
|
|
}
|
|
|
|
// =============================================================================
|
|
// Test extending liveness though overlapping access scopes.
|
|
// =============================================================================
|
|
|
|
class X {}
|
|
class Y {}
|
|
|
|
sil [ossa] @getObject : $@convention(thin) () -> @owned AnyObject
|
|
|
|
// No overlap (access ignored):
|
|
// def
|
|
// use
|
|
// begin_access
|
|
// end_access
|
|
// destroy
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testNoOverlapInLiveBlock : $@convention(thin) () -> () {
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK-NOT: copy_value
|
|
// CHECK: store [[DEF]] to [init]
|
|
// CHECK: begin_access
|
|
// CHECK: end_access
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testNoOverlapInLiveBlock'
|
|
sil [ossa] @testNoOverlapInLiveBlock : $@convention(thin) () -> () {
|
|
bb0:
|
|
%box = alloc_box ${ var AnyObject }, var, name "x"
|
|
%adr = project_box %box : ${ var AnyObject }, 0
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
// use
|
|
store %def to [init] %adr : $*AnyObject
|
|
// end canonical lifetime
|
|
%access = begin_access [read] [dynamic] %adr : $*AnyObject
|
|
%obj = load [copy] %access : $*AnyObject
|
|
end_access %access : $*AnyObject
|
|
br bb1
|
|
|
|
bb1:
|
|
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
destroy_value %copy : $AnyObject
|
|
destroy_value %obj : $AnyObject
|
|
destroy_value %box : ${ var AnyObject }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// No overlap (access ignored):
|
|
// def
|
|
// use
|
|
// br...
|
|
// bb...
|
|
// begin_access
|
|
// end_access
|
|
// destroy
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testNoOverlapInDeadBlock : $@convention(thin) () -> () {
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK-NOT: copy_value
|
|
// CHECK: store [[DEF]] to [init] %{{.*}} : $*AnyObject
|
|
// CHECK: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK: begin_access
|
|
// CHECK: end_access
|
|
// CHECK: br bb2
|
|
// CHECK: bb2:
|
|
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testNoOverlapInDeadBlock'
|
|
sil [ossa] @testNoOverlapInDeadBlock : $@convention(thin) () -> () {
|
|
bb0:
|
|
%box = alloc_box ${ var AnyObject }, var, name "x"
|
|
%adr = project_box %box : ${ var AnyObject }, 0
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
// use
|
|
store %def to [init] %adr : $*AnyObject
|
|
// end canonical lifetime
|
|
br bb1
|
|
|
|
bb1:
|
|
%access = begin_access [read] [dynamic] %adr : $*AnyObject
|
|
%obj = load [copy] %access : $*AnyObject
|
|
end_access %access : $*AnyObject
|
|
br bb2
|
|
|
|
bb2:
|
|
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
destroy_value %copy : $AnyObject
|
|
destroy_value %obj : $AnyObject
|
|
destroy_value %box : ${ var AnyObject }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// Overlapping (must extend pruned liveness):
|
|
//
|
|
// %def
|
|
// begin_access // access scope unrelated to def
|
|
// use %def // pruned liveness ends here
|
|
// end_access
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testOverlapInLiveBlock : $@convention(thin) () -> () {
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK: begin_access
|
|
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
|
|
// CHECK: store [[COPY]] to [init] %{{.*}} : $*AnyObject
|
|
// CHECK: end_access
|
|
// CHECK: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testOverlapInLiveBlock'
|
|
sil [ossa] @testOverlapInLiveBlock : $@convention(thin) () -> () {
|
|
bb0:
|
|
%box = alloc_box ${ var AnyObject }, var, name "x"
|
|
%adr = project_box %box : ${ var AnyObject }, 0
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
%access = begin_access [read] [dynamic] %adr : $*AnyObject
|
|
// use
|
|
store %def to [init] %adr : $*AnyObject
|
|
%obj = load [copy] %access : $*AnyObject
|
|
end_access %access : $*AnyObject
|
|
// Branch to avoid reusing the destroy_value
|
|
br bb1
|
|
|
|
bb1:
|
|
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
destroy_value %copy : $AnyObject
|
|
destroy_value %obj : $AnyObject
|
|
destroy_value %box : ${ var AnyObject }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// Overlapping (must extend pruned liveness):
|
|
//
|
|
// %def
|
|
// begin_access // access scope unrelated to def
|
|
// use %def // pruned liveness ends here
|
|
// br...
|
|
// bb...
|
|
// end_access
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testOverlapInDeadBlock : $@convention(thin) () -> () {
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK: begin_access
|
|
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
|
|
// CHECK: store [[COPY]] to [init] %{{.*}} : $*AnyObject
|
|
// CHECK: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK: end_access
|
|
// CHECK: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK: br bb2
|
|
// CHECK: bb2:
|
|
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testOverlapInDeadBlock'
|
|
sil [ossa] @testOverlapInDeadBlock : $@convention(thin) () -> () {
|
|
bb0:
|
|
%box = alloc_box ${ var AnyObject }, var, name "x"
|
|
%adr = project_box %box : ${ var AnyObject }, 0
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
%access = begin_access [read] [dynamic] %adr : $*AnyObject
|
|
// use
|
|
store %def to [init] %adr : $*AnyObject
|
|
br bb1
|
|
|
|
bb1:
|
|
%obj = load [copy] %access : $*AnyObject
|
|
end_access %access : $*AnyObject
|
|
br bb2
|
|
|
|
bb2:
|
|
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
destroy_value %copy : $AnyObject
|
|
destroy_value %obj : $AnyObject
|
|
destroy_value %box : ${ var AnyObject }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// Fully Overlapping (must extend pruned liveness):
|
|
//
|
|
// begin_access // access scope unrelated to def
|
|
// %def
|
|
// use %def // pruned liveness ends here
|
|
// end_access
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testFullOverlapInDefBlock : $@convention(thin) () -> () {
|
|
// CHECK: begin_access
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
|
|
// CHECK: store [[COPY]] to [init] %{{.*}} : $*AnyObject
|
|
// CHECK: end_access
|
|
// CHECK: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testFullOverlapInDefBlock'
|
|
sil [ossa] @testFullOverlapInDefBlock : $@convention(thin) () -> () {
|
|
bb0:
|
|
%box = alloc_box ${ var AnyObject }, var, name "x"
|
|
%adr = project_box %box : ${ var AnyObject }, 0
|
|
%access = begin_access [read] [dynamic] %adr : $*AnyObject
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
// use
|
|
store %def to [init] %adr : $*AnyObject
|
|
%obj = load [copy] %access : $*AnyObject
|
|
end_access %access : $*AnyObject
|
|
// Branch to avoid reusing the destroy_value
|
|
br bb1
|
|
|
|
bb1:
|
|
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
destroy_value %copy : $AnyObject
|
|
destroy_value %obj : $AnyObject
|
|
destroy_value %box : ${ var AnyObject }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// Fully Overlapping (must extend pruned liveness):
|
|
//
|
|
// begin_access // access scope unrelated to def
|
|
// br...
|
|
// bb...
|
|
// %def
|
|
// use %def // pruned liveness ends here
|
|
// end_access
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testFullOverlapBeforeDefBlock : $@convention(thin) () -> () {
|
|
// CHECK: begin_access
|
|
// CHECK: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
|
|
// CHECK: store [[COPY]] to [init] %{{.*}} : $*AnyObject
|
|
// CHECK: end_access
|
|
// CHECK: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK: br bb2
|
|
// CHECK: bb2:
|
|
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testFullOverlapBeforeDefBlock'
|
|
sil [ossa] @testFullOverlapBeforeDefBlock : $@convention(thin) () -> () {
|
|
bb0:
|
|
%box = alloc_box ${ var AnyObject }, var, name "x"
|
|
%adr = project_box %box : ${ var AnyObject }, 0
|
|
%access = begin_access [read] [dynamic] %adr : $*AnyObject
|
|
br bb1
|
|
|
|
bb1:
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
// use
|
|
store %def to [init] %adr : $*AnyObject
|
|
%obj = load [copy] %access : $*AnyObject
|
|
end_access %access : $*AnyObject
|
|
// Branch to avoid reusing the destroy_value
|
|
br bb2
|
|
|
|
bb2:
|
|
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
destroy_value %copy : $AnyObject
|
|
destroy_value %obj : $AnyObject
|
|
destroy_value %box : ${ var AnyObject }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// Original Overlapping (unnecessarily extends pruned liveness):
|
|
//
|
|
// %def
|
|
// begin_access // access scope unrelated to def
|
|
// use %def // pruned liveness ends here
|
|
// destroy %def
|
|
// end_access
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testOriginalOverlapInLiveBlock : $@convention(thin) () -> () {
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK: begin_access
|
|
// CHECK: store [[DEF]] to [init] %{{.*}} : $*AnyObject
|
|
// CHECK: end_access
|
|
// CHECK: br bb1
|
|
// CHECK-LABEL: } // end sil function 'testOriginalOverlapInLiveBlock'
|
|
sil [ossa] @testOriginalOverlapInLiveBlock : $@convention(thin) () -> () {
|
|
bb0:
|
|
%box = alloc_box ${ var AnyObject }, var, name "x"
|
|
%adr = project_box %box : ${ var AnyObject }, 0
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
%access = begin_access [read] [dynamic] %adr : $*AnyObject
|
|
// use
|
|
store %def to [init] %adr : $*AnyObject
|
|
destroy_value %copy : $AnyObject
|
|
%obj = load [copy] %access : $*AnyObject
|
|
end_access %access : $*AnyObject
|
|
br bb1
|
|
|
|
bb1:
|
|
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
destroy_value %obj : $AnyObject
|
|
destroy_value %box : ${ var AnyObject }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// Original Overlapping (unnecessarily extends pruned liveness):
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testOriginalOverlapInDeadBlock : $@convention(thin) () -> () {
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK: begin_access
|
|
// CHECK: store [[DEF]] to [init] %{{.*}} : $*AnyObject
|
|
// CHECK: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK: end_access
|
|
// CHECK-LABEL: } // end sil function 'testOriginalOverlapInDeadBlock'
|
|
sil [ossa] @testOriginalOverlapInDeadBlock : $@convention(thin) () -> () {
|
|
bb0:
|
|
%box = alloc_box ${ var AnyObject }, var, name "x"
|
|
%adr = project_box %box : ${ var AnyObject }, 0
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
%access = begin_access [read] [dynamic] %adr : $*AnyObject
|
|
// use
|
|
store %def to [init] %adr : $*AnyObject
|
|
br bb1
|
|
|
|
bb1:
|
|
destroy_value %copy : $AnyObject
|
|
%obj = load [copy] %access : $*AnyObject
|
|
end_access %access : $*AnyObject
|
|
// Use %obj so it doesn't get deleted because it's unused a trigger a cascade
|
|
// of deletions.
|
|
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
|
|
destroy_value %obj : $AnyObject
|
|
destroy_value %box : ${ var AnyObject }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// Interleaved access (requires iterative lifetime extension):
|
|
//
|
|
// %def
|
|
// begin_access X
|
|
// use %def // Initial pruned lifetime boundary
|
|
// begin_access Y
|
|
// end_access X // Lifetime boundary after first extension
|
|
// end_access Y // Lifetime boundary after second extension
|
|
// destroy %def
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testInterleavedAccessScope : $@convention(thin) (@inout AnyObject) -> () {
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK: begin_access {{.*}} : $*X
|
|
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
|
|
// CHECK: store [[COPY]] to [assign] %{{.*}} : $*AnyObject
|
|
// CHECK: begin_access {{.*}} : $*Y
|
|
// CHECK: end_access {{.*}} : $*X
|
|
// CHECK: end_access {{.*}} : $*Y
|
|
// CHECK: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: destroy_value {{.*}} : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testInterleavedAccessScope'
|
|
sil [ossa] @testInterleavedAccessScope : $@convention(thin) (@inout AnyObject) -> () {
|
|
bb0(%0 : $*AnyObject):
|
|
%x = alloc_box ${ var X }, var, name "x"
|
|
%xadr = project_box %x : ${ var X }, 0
|
|
%y = alloc_box ${ var Y }, var, name "y"
|
|
%yadr = project_box %y : ${ var Y }, 0
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
%accessX = begin_access [read] [dynamic] %xadr : $*X
|
|
// use
|
|
store %def to [assign] %0 : $*AnyObject
|
|
%accessY = begin_access [read] [dynamic] %yadr : $*Y
|
|
// accessX overlaps pruned liveness on the first iteration
|
|
end_access %accessX : $*X
|
|
// accessY only overlaps pruned liveness on the second iteration
|
|
end_access %accessY : $*Y
|
|
br bb1
|
|
|
|
bb1:
|
|
destroy_value %copy : $AnyObject
|
|
destroy_value %y : ${ var Y }
|
|
destroy_value %x : ${ var X }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// Interleaved non-local access (requires iterative lifetime extension):
|
|
//
|
|
// %def
|
|
// begin_access X
|
|
// use %def // Initial pruned lifetime boundary
|
|
// br bb1
|
|
// bb1:
|
|
// begin_access Y
|
|
// br bb2
|
|
// bb2:
|
|
// end_access X // Lifetime boundary after first extension
|
|
// br bb3
|
|
// bb3:
|
|
// end_access Y // Lifetime boundary after second extension
|
|
// br bb4
|
|
// bb4:
|
|
// destroy %def
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testInterleavedNonLocalAccessScope : $@convention(thin) (@inout AnyObject) -> () {
|
|
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
|
|
// CHECK: begin_access {{.*}} : $*X
|
|
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
|
|
// CHECK: store [[COPY]] to [assign] %{{.*}} : $*AnyObject
|
|
// CHECK: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK: begin_access {{.*}} : $*Y
|
|
// CHECK: br bb2
|
|
// CHECK: bb2:
|
|
// CHECK: end_access {{.*}} : $*X
|
|
// CHECK: br bb3
|
|
// CHECK: bb3:
|
|
// CHECK: end_access {{.*}} : $*Y
|
|
// CHECK: destroy_value [[DEF]] : $AnyObject
|
|
// CHECK: br bb4
|
|
// CHECK: bb4:
|
|
// CHECK-NOT: destroy_value {{.*}} : $AnyObject
|
|
// CHECK-LABEL: } // end sil function 'testInterleavedNonLocalAccessScope'
|
|
sil [ossa] @testInterleavedNonLocalAccessScope : $@convention(thin) (@inout AnyObject) -> () {
|
|
bb0(%0 : $*AnyObject):
|
|
%x = alloc_box ${ var X }, var, name "x"
|
|
%xadr = project_box %x : ${ var X }, 0
|
|
%y = alloc_box ${ var Y }, var, name "y"
|
|
%yadr = project_box %y : ${ var Y }, 0
|
|
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
|
|
// def
|
|
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
|
|
%copy = copy_value %def : $AnyObject
|
|
%accessX = begin_access [read] [dynamic] %xadr : $*X
|
|
// use
|
|
store %def to [assign] %0 : $*AnyObject
|
|
br bb1
|
|
|
|
bb1:
|
|
%accessY = begin_access [read] [dynamic] %yadr : $*Y
|
|
br bb2
|
|
|
|
bb2:
|
|
// accessX overlaps pruned liveness on the first iteration
|
|
end_access %accessX : $*X
|
|
br bb3
|
|
|
|
bb3:
|
|
// accessY only overlaps pruned liveness on the second iteration
|
|
end_access %accessY : $*Y
|
|
br bb4
|
|
|
|
bb4:
|
|
destroy_value %copy : $AnyObject
|
|
destroy_value %y : ${ var Y }
|
|
destroy_value %x : ${ var X }
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// Test a dead begin_borrow (with no scope ending uses). Make sure
|
|
// copy-propagation doesn't end the lifetime before the dead borrow.
|
|
//
|
|
// CHECK-LABEL: sil hidden [ossa] @testDeadBorrow : {{.*}} {
|
|
// CHECK: bb0:
|
|
// CHECK: copy_value %1 : $C
|
|
// CHECK: destroy_value
|
|
// CHECK: copy_value %1 : $C
|
|
// CHECK: begin_borrow
|
|
// CHECK: unreachable
|
|
// CHECK-LABEL: } // end sil function 'testDeadBorrow'
|
|
sil hidden [ossa] @testDeadBorrow : $@convention(thin) () -> () {
|
|
bb0:
|
|
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
|
|
%0 = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
|
|
%1 = copy_value %0 : $C
|
|
destroy_value %1 : $C
|
|
%6 = copy_value %0 : $C
|
|
%7 = begin_borrow %6 : $C
|
|
unreachable
|
|
}
|
|
|
|
// Test that copy propagation doesn't hoist a destroy_value corresponding to
|
|
// a move value [lexical] over a barrier.
|
|
// CHECK-LABEL: sil [ossa] @dont_hoist_move_value_lexical_destroy_over_barrier_apply : {{.*}} {
|
|
// CHECK: [[GET_OWNED_C:%.*]] = function_ref @getOwnedC
|
|
// CHECK: [[INSTANCE:%.*]] = apply [[GET_OWNED_C]]
|
|
// CHECK: [[LIFETIME:%[^,]+]] = move_value [lexical] [[INSTANCE]]
|
|
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
|
|
// CHECK: [[TAKE_GUARANTEED_C:%[^,]+]] = function_ref @takeGuaranteedC
|
|
// CHECK: apply [[TAKE_GUARANTEED_C]]([[LIFETIME]])
|
|
// CHECK: apply [[BARRIER]]()
|
|
// CHECK: destroy_value [[LIFETIME]]
|
|
// CHECK-LABEL: } // end sil function 'dont_hoist_move_value_lexical_destroy_over_barrier_apply'
|
|
sil [ossa] @dont_hoist_move_value_lexical_destroy_over_barrier_apply : $@convention(thin) () -> () {
|
|
entry:
|
|
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
|
|
%instance = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
|
|
%lifetime = move_value [lexical] %instance : $C
|
|
%barrier = function_ref @barrier : $@convention(thin) () -> ()
|
|
%takeGuaranteedC = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()
|
|
apply %takeGuaranteedC(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
|
|
apply %barrier() : $@convention(thin) () -> ()
|
|
destroy_value %lifetime : $C
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// Canonicalize the moved-from value when a move_value is deemed redundant and
|
|
// eliminated.
|
|
// CHECK-LABEL: sil [ossa] @canonicalize_source_of_redundant_move_value : {{.*}} {
|
|
// CHECK: [[GET_OWNED_C:%[^,]+]] = function_ref @getOwnedC
|
|
// CHECK: [[TAKE_OWNED_C:%[^,]+]] = function_ref @takeOwnedC
|
|
// CHECK: [[C:%[^,]+]] = apply [[GET_OWNED_C]]() : $@convention(thin) () -> @owned C
|
|
// CHECK: apply [[TAKE_OWNED_C]]([[C]]) : $@convention(thin) (@owned C) -> ()
|
|
// CHECK-LABEL: } // end sil function 'canonicalize_source_of_redundant_move_value'
|
|
sil [ossa] @canonicalize_source_of_redundant_move_value : $@convention(thin) () -> () {
|
|
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
|
|
%takeOwnedC = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> ()
|
|
%c = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
|
|
%c2 = move_value %c : $C
|
|
%c3 = copy_value %c2 : $C
|
|
apply %takeOwnedC(%c3) : $@convention(thin) (@owned C) -> ()
|
|
destroy_value %c2 : $C
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// Verify that a dead copy_value is deleted.
|
|
// CHECK-LABEL: sil [ossa] @delete_dead_reborrow_copy : {{.*}} {
|
|
// CHECK-NOT: copy_value
|
|
// CHECK-LABEL: } // end sil function 'delete_dead_reborrow_copy'
|
|
sil [ossa] @delete_dead_reborrow_copy : $@convention(thin) (@owned X) -> () {
|
|
bb0(%instance : @owned $X):
|
|
%lifetime = begin_borrow %instance : $X
|
|
br bb1(%lifetime : $X)
|
|
|
|
bb1(%reborrow : @guaranteed $X):
|
|
%dead_copy = copy_value %reborrow : $X
|
|
end_borrow %reborrow : $X
|
|
destroy_value %dead_copy : $X
|
|
br exit
|
|
|
|
exit:
|
|
destroy_value %instance : $X
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C
|
|
// CHECK-NOT: copy_value
|
|
// CHECK: begin_access
|
|
// CHECK-NOT: copy_value
|
|
// CHECK: store [[INSTANCE]] to [init] {{%[^,]+}}
|
|
// CHECK-LABEL: } // end sil function 'dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block'
|
|
sil [ossa] @dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block : $@convention(thin) (@owned C) -> () {
|
|
bb0(%instance : @owned $C):
|
|
%addr = alloc_stack [lexical] $C, var
|
|
%borrow = begin_borrow %instance : $C
|
|
%copy = copy_value %borrow : $C
|
|
end_borrow %borrow : $C
|
|
destroy_value %instance : $C
|
|
%access = begin_access [static] [modify] %addr : $*C
|
|
store %copy to [init] %addr : $*C
|
|
end_access %access : $*C
|
|
destroy_addr %addr : $*C
|
|
dealloc_stack %addr : $*C
|
|
%retval = tuple()
|
|
return %retval : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @dont_extend_beyond_nonoverlapping_endaccess_after_destroyvalue_in_consuming_block : $@convention(thin) () -> () {
|
|
// CHECK: begin_access
|
|
// CHECK: destroy_value
|
|
// CHECK: end_access
|
|
// CHECK-NOT: destroy_value
|
|
// CHECK-LABEL: } // end sil function 'dont_extend_beyond_nonoverlapping_endaccess_after_destroyvalue_in_consuming_block'
|
|
sil [ossa] @dont_extend_beyond_nonoverlapping_endaccess_after_destroyvalue_in_consuming_block : $@convention(thin) () -> () {
|
|
bb0:
|
|
%instance = apply undef() : $@convention(thin) () -> @owned C
|
|
%addr = alloc_stack [lexical] $C, var
|
|
%access = begin_access [static] [modify] %addr : $*C
|
|
apply undef(%instance) : $@convention(thin) (@guaranteed C) -> ()
|
|
destroy_value %instance : $C
|
|
end_access %access : $*C
|
|
dealloc_stack %addr : $*C
|
|
%retval = tuple()
|
|
return %retval : $()
|
|
}
|
|
|
|
sil @closureGetter : $@convention(thin) () -> @owned Optional<@Sendable @callee_guaranteed () -> ()>
|
|
|
|
// This pattern is produced by SILGen doUncheckedConversion.
|
|
// rdar://100527903 (Overrelease of optional closure when using shorthand syntax)
|
|
//
|
|
// Copy propagation cannot handle a PointerEscape.
|
|
//
|
|
// CHECK-LABEL: sil hidden [ossa] @testBitwiseClosureEscape : $@convention(thin) () -> () {
|
|
// CHECK: [[V:%.*]] = apply %0() : $@convention(thin) () -> @owned Optional<@Sendable @callee_guaranteed () -> ()>
|
|
// CHECK: [[CP1:%.*]] = copy_value [[V]] : $Optional<@Sendable @callee_guaranteed () -> ()>
|
|
// CHECK: destroy_value [[V]] : $Optional<@Sendable @callee_guaranteed () -> ()>
|
|
// CHECK: [[CAST:%.*]] = unchecked_bitwise_cast [[CP1]] : $Optional<@Sendable @callee_guaranteed () -> ()> to $Optional<@callee_guaranteed () -> ()>
|
|
// CHECK-NEXT: [[CASTCP:%.*]] = copy_value [[CAST]] : $Optional<@callee_guaranteed () -> ()>
|
|
// CHECK: store [[CASTCP]] to [init]
|
|
// CHECK: destroy_value [[CP1]] : $Optional<@Sendable @callee_guaranteed () -> ()>
|
|
// CHECK-LABEL: } // end sil function 'testBitwiseClosureEscape'
|
|
sil hidden [ossa] @testBitwiseClosureEscape : $@convention(thin) () -> () {
|
|
bb0:
|
|
%2 = function_ref @closureGetter : $@convention(thin) () -> @owned Optional<@Sendable @callee_guaranteed () -> ()>
|
|
%3 = apply %2() : $@convention(thin) () -> @owned Optional<@Sendable @callee_guaranteed () -> ()>
|
|
%13 = copy_value %3 : $Optional<@Sendable @callee_guaranteed () -> ()>
|
|
%14 = alloc_stack $Optional<@callee_guaranteed () -> ()>
|
|
destroy_value %3 : $Optional<@Sendable @callee_guaranteed () -> ()>
|
|
%17 = unchecked_bitwise_cast %13 : $Optional<@Sendable @callee_guaranteed () -> ()> to $Optional<@callee_guaranteed () -> ()>
|
|
%18 = copy_value %17 : $Optional<@callee_guaranteed () -> ()>
|
|
store %18 to [init] %14 : $*Optional<@callee_guaranteed () -> ()>
|
|
destroy_addr %14 : $*Optional<@callee_guaranteed () -> ()>
|
|
destroy_value %13 : $Optional<@Sendable @callee_guaranteed () -> ()>
|
|
dealloc_stack %14 : $*Optional<@callee_guaranteed () -> ()>
|
|
%99 = tuple ()
|
|
return %99 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @hoist_destroy_of_copy_of_lexical_over_deinit_barrier : $@convention(thin) (@owned C) -> () {
|
|
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned
|
|
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
|
|
// CHECK: [[BORROW:%[^,]+]] = function_ref @takeGuaranteedC
|
|
// CHECK: apply [[BORROW]]([[INSTANCE]])
|
|
// The destroy of the copy should be hoisted over the deinit barrier, and then
|
|
// canonicalization of the lexical value should remove the copy.
|
|
// CHECK: destroy_value [[INSTANCE]]
|
|
// CHECK: apply [[BARRIER]]()
|
|
// CHECK-LABEL: } // end sil function 'hoist_destroy_of_copy_of_lexical_over_deinit_barrier'
|
|
sil [ossa] @hoist_destroy_of_copy_of_lexical_over_deinit_barrier : $(@owned C) -> () {
|
|
entry(%instance : @owned $C):
|
|
%barrier = function_ref @barrier : $@convention(thin) () -> ()
|
|
%borrow = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()
|
|
%copy = copy_value %instance : $C
|
|
apply %borrow(%instance) : $@convention(thin) (@guaranteed C) -> ()
|
|
destroy_value %instance : $C
|
|
apply %barrier() : $@convention(thin) () -> ()
|
|
destroy_value %copy : $C
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @forward_owned_lexical_value_to_callee : {{.*}} {
|
|
// CHECK-NOT: copy_value
|
|
// CHECK-LABEL: } // end sil function 'forward_owned_lexical_value_to_callee'
|
|
sil [ossa] @forward_owned_lexical_value_to_callee : $@convention(thin) (@owned C) -> () {
|
|
entry(%instance : @owned $C):
|
|
%copy = copy_value %instance : $C
|
|
%consume = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> ()
|
|
apply %consume(%copy) : $@convention(thin) (@owned C) -> ()
|
|
destroy_value %instance : $C
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @cantForwardBecauseBorrowing : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] :
|
|
// CHECK: [[COPY:%[^,]+]] = copy_value [[INSTANCE]]
|
|
// CHECK: [[CONSUME_AND_BORROW:%[^,]+]] = function_ref @takeOwnedCAndGuaranteedC
|
|
// CHECK: apply [[CONSUME_AND_BORROW]]([[COPY]], [[INSTANCE]])
|
|
// CHECK: destroy_value [[INSTANCE]]
|
|
// CHECK-LABEL: } // end sil function 'cantForwardBecauseBorrowing'
|
|
sil [ossa] @cantForwardBecauseBorrowing :$@convention(thin) (@owned C) -> () {
|
|
entry(%instance : @owned $C):
|
|
%copy = copy_value %instance : $C
|
|
%consumeAndBorrow = function_ref @takeOwnedCAndGuaranteedC : $@convention(thin) (@owned C, @guaranteed C) -> ()
|
|
apply %consumeAndBorrow(%copy, %instance) : $@convention(thin) (@owned C, @guaranteed C) -> ()
|
|
destroy_value %instance : $C
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|