// RUN: %target-sil-opt -sil-print-types -update-borrowed-from -copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK // RUN: %target-sil-opt -sil-print-types -update-borrowed-from -mandatory-copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK // 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 // CHECK-NOT: destroy // CHECK-LABEL: } // end sil function 'testBeginApply' sil hidden [ossa] @testBeginApply : $@convention(thin) (@inout Struct) -> () { bb0(%0 : $*Struct): %2 = keypath $WritableKeyPath, (root $Struct; stored_property #Struct.st : $Int) debug_value %2 : $WritableKeyPath, let, name "kp" %4 = copy_value %2 : $WritableKeyPath %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(%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 destroy_value %2 : $WritableKeyPath %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 } %boxadr = project_box %box : $<τ_0_0> { var τ_0_0 } , 0 store %0 to [init] %boxadr : $*B %load = load [copy] %boxadr : $*B %copy = copy_value %box : $<τ_0_0> { var τ_0_0 } destroy_value %box : $<τ_0_0> { var τ_0_0 } destroy_value %copy : $<τ_0_0> { var τ_0_0 } 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). // // TODO: with lifetime completion, both copies should be deleted. // // CHECK-LABEL: sil hidden [ossa] @testDeadBorrow : {{.*}} { // CHECK: bb0: // CHECK: copy_value // CHECK: destroy_value // CHECK: copy_value // 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 : $() } // 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 : $() } // CHECK-LABEL: sil [ossa] @look_through_end_init_let_ref : // CHECK: [[E:%.*]] = end_init_let_ref // CHECK: [[D:%.*]] = function_ref @dummy // CHECK: apply [[D]] // CHECK: destroy_value [[E]] // CHECK-LABEL: } // end sil function 'look_through_end_init_let_ref' sil [ossa] @look_through_end_init_let_ref : $@convention(thin) () -> () { bb0: %0 = alloc_ref $C %1 = move_value [lexical] %0 %2 = end_init_let_ref %1 %4 = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> () apply %4(%2) : $@convention(thin) (@guaranteed C) -> () %6 = function_ref @dummy : $@convention(thin) () -> () apply %6() : $@convention(thin) () -> () destroy_value %2 %3 = tuple () return %3 }