mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Compute, update and handle borrowed-from instruction in various utilities and passes. Also, used borrowed-from to simplify `gatherBorrowIntroducers` and `gatherEnclosingValues`. Replace those utilities by `Value.getBorrowIntroducers` and `Value.getEnclosingValues`, which return a lazily computed Sequence of borrowed/enclosing values.
1074 lines
41 KiB
Plaintext
1074 lines
41 KiB
Plaintext
// RUN: %target-sil-opt -update-borrowed-from -copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK,CHECK-OPT
|
|
// RUN: %target-sil-opt -update-borrowed-from -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
|
|
}
|
|
|
|
struct MOS : ~Copyable {
|
|
deinit {}
|
|
}
|
|
|
|
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) -> ()
|
|
sil [ossa] @getMOS : $() -> (@owned MOS)
|
|
|
|
// -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 as $()
|
|
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-NOT: copy_value
|
|
// CHECK: begin_borrow
|
|
// CHECK: destroy_value
|
|
// 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 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @dontShortenDeadMoveOnlyLifetime : {{.*}} {
|
|
// CHECK: [[GET:%[^,]+]] = function_ref @getMOS
|
|
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
|
|
// CHECK: [[MOS:%[^,]+]] = apply [[GET]]()
|
|
// CHECK: [[MOV:%[^,]+]] = move_value [lexical] [[MOS]]
|
|
// CHECK: apply [[BARRIER]]()
|
|
// CHECK: destroy_value [[MOV]]
|
|
// CHECK-LABEL: } // end sil function 'dontShortenDeadMoveOnlyLifetime'
|
|
sil [ossa] @dontShortenDeadMoveOnlyLifetime : $@convention(thin) () -> () {
|
|
%get = function_ref @getMOS : $@convention(thin) () -> (@owned MOS)
|
|
%barrier = function_ref @barrier : $@convention(thin) () -> ()
|
|
%mos = apply %get() : $@convention(thin) () -> (@owned MOS)
|
|
// Note: This must be lexical so that it doesn't get eliminated as redundant.
|
|
%mov = move_value [lexical] %mos : $MOS
|
|
apply %barrier() : $@convention(thin) () -> ()
|
|
destroy_value %mov : $MOS
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|