Files
swift-mirror/test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil
Erik Eckstein 98805c9141 Optimizer: let the InstructionDeleter respect deinit-barriers by default
The InstructionDeleter can remove instructions including their destroys and then insert compensating destroys at a new place.
This is effectively destroy-hoisting which doesn't respect deinit-barriers. Therefore it's not done for lexical lifetimes.
However, since https://github.com/swiftlang/swift/pull/85334, the optimizer should treat _all_ lifetimes as fixed and not only lexical lifetimes.

This change adds a `assumeFixedLifetimes` flag to InstructionDeleter which is on by default.
Only mandatory passes (like OSLogOptimization) should turn this off.
2025-12-03 15:53:56 +01:00

1414 lines
49 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all -update-borrowed-from -dce %s | %FileCheck %s
// REQUIRES: swift_in_compiler
sil_stage canonical
import Builtin
import Swift
class Klass {
}
enum EitherNoneOrAnyObject {
case none
case any(AnyObject)
}
struct NonTrivialStruct {
var val:Klass
}
enum FakeOptional<T> {
case none
case some(T)
}
struct Wrapper1 {
var val1: Wrapper2
}
struct Wrapper2 {
var val2: Klass
}
sil [ossa] @$testtryapplyklassgen : $@convention(thin) () -> (@owned Klass, @error any Error)
sil [ossa] @$use_klass1 : $@convention(thin) (@owned Klass) -> ()
sil [ossa] @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
sil [ossa] @$use_nontrivialstruct1 : $@convention(thin) (@owned NonTrivialStruct) -> ()
sil [ossa] @$use_nontrivialstruct2 : $@convention(thin) (@guaranteed NonTrivialStruct) -> ()
// We cannot DCE a function argument
// CHECK-LABEL: sil [ossa] @dce_dontoptarg1 :
// CHECK: destroy_value %0
// CHECK-LABEL: } // end sil function 'dce_dontoptarg1'
sil [ossa] @dce_dontoptarg1 : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
destroy_value %0 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_dontoptarg2 :
// CHECK: copy_value
// CHECK: destroy_value
// CHECK-LABEL: } // end sil function 'dce_dontoptarg2'
sil [ossa] @dce_dontoptarg2 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = copy_value %0 : $Klass
br bb1(%1 : $Klass)
bb1(%2 : @owned $Klass):
%3 = copy_value %2 : $Klass
%4 = function_ref @$use_klass1 : $@convention(thin) (@owned Klass) -> ()
%5 = apply %4(%3) : $@convention(thin) (@owned Klass) -> ()
destroy_value %2 : $Klass
%res = tuple ()
return %res : $()
}
// Don't dce due to a useful dependency due to apply
// CHECK-LABEL: sil [ossa] @dce_dontoptrevdep1 :
// CHECK: [[RES:%.*]] = load_borrow %0
// CHECK: end_borrow [[RES]]
// CHECK-LABEL: } // end sil function 'dce_dontoptrevdep1'
sil [ossa] @dce_dontoptrevdep1 : $@convention(thin) (@in Klass) -> () {
bb0(%0 : $*Klass):
%1 = load_borrow %0 : $*Klass
%2 = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%3 = apply %2(%1) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %1 : $Klass
destroy_addr %0 : $*Klass
%res = tuple ()
return %res : $()
}
// Don't dce due to a useful dependency due to apply
// CHECK-LABEL: sil [ossa] @dce_dontoptendborrow1 :
// CHECK: [[RES:%.*]] = begin_borrow %0
// CHECK: end_borrow
// CHECK-LABEL: } // end sil function 'dce_dontoptendborrow1'
sil [ossa] @dce_dontoptendborrow1 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = begin_borrow %0 : $Klass
br bb1(%1 : $Klass)
bb1(%2 : @guaranteed $Klass):
%3 = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %2 : $Klass
%res = tuple ()
return %res : $()
}
// Cannot optimize dead non phi args
// CHECK-LABEL: sil [ossa] @dce_dontoptarg3 :
// CHECK: destroy_value
// CHECK-LABEL: } // end sil function 'dce_dontoptarg3'
sil [ossa] @dce_dontoptarg3 : $@convention(thin) (@guaranteed Klass) -> @error any Error {
bb0(%0 : @guaranteed $Klass):
%2 = function_ref @$testtryapplyklassgen : $@convention(thin) () -> (@owned Klass, @error any Error)
try_apply %2() : $@convention(thin) () -> (@owned Klass, @error any Error), normal bb1, error bb2
bb1(%3 : @owned $Klass):
destroy_value %3 : $Klass
%res = tuple ()
return %res : $()
bb2(%4 : $Error):
throw %4 : $Error
}
// CHECK-LABEL: sil [ossa] @dce_deadcopy1 :
// CHECK-NOT: copy_value
// CHECK: destroy_value %0
// CHECK-LABEL: } // end sil function 'dce_deadcopy1'
sil [ossa] @dce_deadcopy1 : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = copy_value %0 : $Klass
destroy_value %1 : $Klass
destroy_value %0 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadcopy2 :
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'dce_deadcopy2'
sil [ossa] @dce_deadcopy2 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = copy_value %0 : $Klass
destroy_value %1 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadcopy3 :
// CHECK: bb0([[ARG:%.*]])
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: [[RES:%.*]] = tuple ()
// CHECK-NEXT: return [[RES]]
// CHECK-LABEL: } // end sil function 'dce_deadcopy3'
sil [ossa] @dce_deadcopy3 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = copy_value %0 : $Klass
br bb1(%1 : $Klass)
bb1(%2 : @owned $Klass):
destroy_value %2 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadcopy4 :
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'dce_deadcopy4'
sil [ossa] @dce_deadcopy4 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = copy_value %0 : $Klass
cond_br undef, bb1, bb2
bb1:
br bb3(%1 : $Klass)
bb2:
br bb3(%1 : $Klass)
bb3(%2 : @owned $Klass):
destroy_value %2 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadcopy5 :
// CHECK: bb0([[ARG:%.*]])
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: [[RES:%.*]] = tuple ()
// CHECK-NEXT: return [[RES]]
// CHECK-LABEL: } // end sil function 'dce_deadcopy5'
sil [ossa] @dce_deadcopy5 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = copy_value %0 : $Klass
cond_br undef, bb1, bb2
bb3(%2 : @owned $Klass):
destroy_value %2 : $Klass
%res = tuple ()
return %res : $()
bb1:
br bb3(%1 : $Klass)
bb2:
br bb3(%1 : $Klass)
}
// CHECK-LABEL: sil [ossa] @dce_deadcopy6 :
// CHECK: bb0([[ARG:%.*]])
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: [[RES:%.*]] = tuple ()
// CHECK-NEXT: return [[RES]]
// CHECK-LABEL: } // end sil function 'dce_deadcopy6'
sil [ossa] @dce_deadcopy6 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = copy_value %0 : $Klass
cond_br undef, bb1a, bb2a
bb1a:
br bb1(%1 : $Klass)
bb2a:
br bb2(%1 : $Klass)
bb1(%1a : @owned $Klass):
br bb3(%1a : $Klass)
bb2(%1b : @owned $Klass):
br bb3(%1b : $Klass)
bb3(%2 : @owned $Klass):
destroy_value %2 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadcopy7 :
// CHECK-NOT: load [copy]
// CHECK-LABEL: } // end sil function 'dce_deadcopy7'
sil [ossa] @dce_deadcopy7 : $@convention(thin) (@in Klass) -> () {
bb0(%0 : $*Klass):
%1 = load [copy] %0 : $*Klass
br bb1(%1 : $Klass)
bb1(%2 : @owned $Klass):
destroy_value %2 : $Klass
destroy_addr %0 : $*Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_destructure1 :
// CHECK: bb0(%0 : {{.*}})
// CHECK-NEXT: destroy_value %0
// CHECK-NEXT: [[RES:%.*]] = tuple ()
// CHECK-NEXT: return [[RES]]
// CHECK-LABEL: } // end sil function 'dce_destructure1'
sil [ossa] @dce_destructure1 : $@convention(thin) (@owned NonTrivialStruct) -> () {
bb0(%0 : @owned $NonTrivialStruct):
(%1) = destructure_struct %0 : $NonTrivialStruct
destroy_value %1 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_destructure2 :
// CHECK: bb0(%0 : {{.*}})
// CHECK-NEXT: [[RES:%.*]] = tuple ()
// CHECK-NEXT: return [[RES]]
// CHECK-LABEL: } // end sil function 'dce_destructure2'
sil [ossa] @dce_destructure2 : $@convention(thin) (@guaranteed NonTrivialStruct) -> () {
bb0(%0 : @guaranteed $NonTrivialStruct):
(%1) = destructure_struct %0 : $NonTrivialStruct
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_destructure3 :
// CHECK: bb0(%0 : {{.*}})
// CHECK-NOT: destructure_struct
// CHECK-LABEL: } // end sil function 'dce_destructure3'
sil [ossa] @dce_destructure3 : $@convention(thin) (@guaranteed NonTrivialStruct) -> () {
bb0(%0 : @guaranteed $NonTrivialStruct):
%1 = copy_value %0 : $NonTrivialStruct
%func = function_ref @$use_nontrivialstruct2 : $@convention(thin) (@guaranteed NonTrivialStruct) -> ()
%funcres = apply %func(%1) : $@convention(thin) (@guaranteed NonTrivialStruct) -> ()
(%2) = destructure_struct %1 : $NonTrivialStruct
destroy_value %2 : $Klass
%res = tuple ()
return %res : $()
}
// This test shows that when we delete a dead instruction which has lifetime ending ops,
// we need to insert destroys of ops to end their lifetime correctly
// CHECK-LABEL: sil [ossa] @dce_insertdestroy1 :
// CHECK-NOT: struct
// CHECK-LABEL: } // end sil function 'dce_insertdestroy1'
sil [ossa] @dce_insertdestroy1 : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = copy_value %0 : $Klass
%func = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%funcres = apply %func(%1) : $@convention(thin) (@guaranteed Klass) -> ()
%3 = struct $NonTrivialStruct(%1 : $Klass)
destroy_value %3 : $NonTrivialStruct
destroy_value %0 : $Klass
%res = tuple ()
return %res : $()
}
// This test shows that when we delete a dead phi arg and its incoming values are live,
// we need to insert destroys of the incoming values in its pred blocks.
// CHECK-LABEL: sil [ossa] @dce_insertdestroy2 :
// CHECK: bb1(%3 : {{.*}})
// CHECK-NEXT: destroy_value %3
// CHECK-NEXT: br bb3
// CHECK-LABEL: } // end sil function 'dce_insertdestroy2'
sil [ossa] @dce_insertdestroy2 : $@convention(thin) (@guaranteed Klass) -> @error any Error {
bb0(%0 : @guaranteed $Klass):
%2 = function_ref @$testtryapplyklassgen : $@convention(thin) () -> (@owned Klass, @error any Error)
try_apply %2() : $@convention(thin) () -> (@owned Klass, @error any Error), normal bb1, error bb2
bb1(%3 : @owned $Klass):
br bb3(%3 : $Klass)
bb2(%4 : $Error):
throw %4 : $Error
bb3(%5 : @owned $Klass):
destroy_value %5 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_multiplerevdepdests :
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'dce_multiplerevdepdests'
sil [ossa] @dce_multiplerevdepdests : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = copy_value %0 : $Klass
br bb1(%1 : $Klass)
bb1(%2 : @owned $Klass):
cond_br undef, bb2, bb3
bb2:
destroy_value %2 : $Klass
br bb4
bb3:
destroy_value %2 : $Klass
br bb4
bb4:
%res = tuple()
return %res : $()
}
struct TestStruct {
var val:Klass
var index:Int
}
// CHECK-LABEL: sil [ossa] @dce_destructurenotfullydead :
// CHECK-NOT: copy_value
// CHECK-LABEL: } // end sil function 'dce_destructurenotfullydead'
sil [ossa] @dce_destructurenotfullydead : $@convention(thin) (@owned TestStruct) -> Int {
bb0(%0 : @owned $TestStruct):
%stk = alloc_stack $TestStruct
store %0 to [init] %stk : $*TestStruct
%copy = load [take] %stk : $*TestStruct
(%2, %3) = destructure_struct %copy : $TestStruct
%4 = struct $TestStruct (%2 : $Klass, %3 : $Int)
destroy_value %4 : $TestStruct
dealloc_stack %stk : $*TestStruct
return %3 : $Int
}
// CHECK-LABEL: sil [ossa] @dce_borrowlifetime1 :
// CHECK: bb1([[ARG1:%.*]] : @owned $NonTrivialStruct, [[ARG2:%.*]] : @reborrow $NonTrivialStruct):
// CHECK-LABEL: } // end sil function 'dce_borrowlifetime1'
sil [ossa] @dce_borrowlifetime1 : $@convention(thin) (@guaranteed NonTrivialStruct) -> @owned NonTrivialStruct {
bb0(%0 : @guaranteed $NonTrivialStruct):
%copy = copy_value %0 : $NonTrivialStruct
%borrow = begin_borrow %copy : $NonTrivialStruct
br bb1(%copy : $NonTrivialStruct, %borrow : $NonTrivialStruct)
bb1(%copy2 : @owned $NonTrivialStruct, %borrow2 : @guaranteed $NonTrivialStruct):
%newcopy = copy_value %borrow2 : $NonTrivialStruct
end_borrow %borrow2 : $NonTrivialStruct
destroy_value %copy2 : $NonTrivialStruct
return %newcopy : $NonTrivialStruct
}
// CHECK-LABEL: sil [ossa] @dce_borrowlifetime2 :
// CHECK: bb1:
// CHECK-LABEL: } // end sil function 'dce_borrowlifetime2'
sil [ossa] @dce_borrowlifetime2 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%copy = copy_value %0 : $Klass
%borrow = begin_borrow %copy : $Klass
%2 = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%3 = apply %2(%borrow) : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %2(%copy) : $@convention(thin) (@guaranteed Klass) -> ()
br bb1(%copy : $Klass, %borrow : $Klass)
bb1(%copy2 : @owned $Klass, %borrow2 : @guaranteed $Klass):
end_borrow %borrow2 : $Klass
destroy_value %copy2 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_borrowlifetime3 :
// CHECK: bb1:
// CHECK-LABEL: } // end sil function 'dce_borrowlifetime3'
sil [ossa] @dce_borrowlifetime3 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%copy = copy_value %0 : $Klass
%borrow = begin_borrow %copy : $Klass
%2 = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%3 = apply %2(%borrow) : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %2(%copy) : $@convention(thin) (@guaranteed Klass) -> ()
br bb1(%borrow : $Klass, %copy : $Klass)
bb1(%borrow2 : @guaranteed $Klass, %copy2 : @owned $Klass):
end_borrow %borrow2 : $Klass
destroy_value %copy2 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_borrowlifetime4 :
// CHECK: bb1:
// CHECK-LABEL: } // end sil function 'dce_borrowlifetime4'
sil [ossa] @dce_borrowlifetime4 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%copy = copy_value %0 : $Klass
%borrow = begin_borrow %copy : $Klass
%2 = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%3 = apply %2(%borrow) : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %2(%copy) : $@convention(thin) (@guaranteed Klass) -> ()
br bb1(%borrow : $Klass)
bb1(%borrow2 : @guaranteed $Klass):
end_borrow %borrow2 : $Klass
br bb2(%copy : $Klass)
bb2(%copy2 : @owned $Klass):
destroy_value %copy2 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_borrowlifetime5 :
// CHECK: bb1:
// CHECK-LABEL: } // end sil function 'dce_borrowlifetime5'
sil [ossa] @dce_borrowlifetime5 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%copy1 = copy_value %0 : $Klass
%copy2 = copy_value %0 : $Klass
%borrow1 = begin_borrow %copy1 : $Klass
%borrow2 = begin_borrow %copy2 : $Klass
%2 = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%3 = apply %2(%borrow1) : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %2(%borrow2) : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %2(%copy1) : $@convention(thin) (@guaranteed Klass) -> ()
%6 = apply %2(%copy2) : $@convention(thin) (@guaranteed Klass) -> ()
br bb1(%copy1 : $Klass, %borrow1 : $Klass, %borrow2 : $Klass)
bb1(%newcopy : @owned $Klass, %newborrow1 : @guaranteed $Klass, %newborrow2 : @guaranteed $Klass):
end_borrow %newborrow1 : $Klass
end_borrow %newborrow2 : $Klass
destroy_value %newcopy : $Klass
destroy_value %copy2 : $Klass
%res = tuple ()
return %res : $()
}
// Nested borrows are currently not optimized in DCE
sil [ossa] @dce_nestedborrowlifetime1 : $@convention(thin) (@guaranteed NonTrivialStruct) -> @owned NonTrivialStruct {
bb0(%0 : @guaranteed $NonTrivialStruct):
%borrowo = begin_borrow %0 : $NonTrivialStruct
%borrow = begin_borrow %borrowo : $NonTrivialStruct
br bb1(%borrowo : $NonTrivialStruct, %borrow : $NonTrivialStruct)
bb1(%newborrowo : @guaranteed $NonTrivialStruct, %borrow2 : @guaranteed $NonTrivialStruct):
%newcopy = copy_value %borrow2 : $NonTrivialStruct
end_borrow %borrow2 : $NonTrivialStruct
end_borrow %newborrowo : $NonTrivialStruct
return %newcopy : $NonTrivialStruct
}
sil [ossa] @dce_nestedborrowlifetime2 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%borrowo = begin_borrow %0 : $Klass
%borrow = begin_borrow %borrowo : $Klass
%2 = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%3 = apply %2(%borrow) : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %2(%borrowo) : $@convention(thin) (@guaranteed Klass) -> ()
br bb1(%borrowo : $Klass, %borrow : $Klass)
bb1(%newborrow : @guaranteed $Klass, %borrow2 : @guaranteed $Klass):
end_borrow %borrow2 : $Klass
end_borrow %newborrow : $Klass
%res = tuple ()
return %res : $()
}
// This test shows it is non trivial to find the insert point of an outer reborrow.
// Here %newborrowo and %newborrowi are both dead phis.
// First end_borrow for the incoming value of %newborrowi is added
// It is non straight forward to find the insert pt for the end_borrow of the incoming value of %newborrowo
// This may not be important once OSSACanonicalizeOwned supports rewrite of multi-block borrows.
sil [ossa] @dce_nestedborrowlifetime3 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%borrowo = begin_borrow %0 : $Klass
%borrow = begin_borrow %borrowo : $Klass
%2 = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%3 = apply %2(%borrow) : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %2(%borrowo) : $@convention(thin) (@guaranteed Klass) -> ()
br bb1(%borrow : $Klass, %borrowo : $Klass)
bb1(%newborrowi : @guaranteed $Klass, %newborrowo : @guaranteed $Klass):
end_borrow %newborrowi : $Klass
end_borrow %newborrowo : $Klass
%res = tuple ()
return %res : $()
}
sil [ossa] @dce_nestedborrowlifetime4 : $@convention(thin) (@guaranteed Wrapper1, @guaranteed Wrapper1) -> () {
bb0(%0 : @guaranteed $Wrapper1, %1 : @guaranteed $Wrapper1):
cond_br undef, bb1, bb2
bb1:
%outer1 = begin_borrow %0 : $Wrapper1
%ex1 = struct_extract %outer1 : $Wrapper1, #Wrapper1.val1
%ex11 = struct_extract %ex1 : $Wrapper2, #Wrapper2.val2
br bb3(%ex11 : $Klass, %outer1 : $Wrapper1)
bb2:
%outer2 = begin_borrow %1 : $Wrapper1
%ex2 = struct_extract %outer2 : $Wrapper1, #Wrapper1.val1
%ex21 = struct_extract %ex2 : $Wrapper2, #Wrapper2.val2
br bb3(%ex21 : $Klass, %outer2 : $Wrapper1)
bb3(%phi1 : @guaranteed $Klass, %phi2 : @guaranteed $Wrapper1):
%f = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%phi1) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %phi2 : $Wrapper1
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @dce_nestedborrowlifetime5 : $@convention(thin) (@guaranteed Wrapper1) -> () {
bb0(%0 : @guaranteed $Wrapper1):
%outer1 = begin_borrow %0 : $Wrapper1
%inner1 = begin_borrow %outer1 : $Wrapper1
%ex1 = struct_extract %inner1 : $Wrapper1, #Wrapper1.val1
%ex11 = struct_extract %ex1 : $Wrapper2, #Wrapper2.val2
br bb2(%ex11 : $Klass, %inner1 : $Wrapper1)
bb2(%phi1 : @guaranteed $Klass, %phi2 : @guaranteed $Wrapper1):
%f = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%phi1) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %phi2 : $Wrapper1
end_borrow %outer1 : $Wrapper1
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @infinite_loop :
// CHECK-NOT: copy_value
// CHECK-LABEL: } // end sil function 'infinite_loop'
sil [ossa] @infinite_loop : $@convention(thin) (@guaranteed NonTrivialStruct, @guaranteed NonTrivialStruct) -> () {
bb0(%0 : @guaranteed $NonTrivialStruct, %1 : @guaranteed $NonTrivialStruct):
cond_br undef, bb1, bb4
bb1:
%copy0 = copy_value %0 : $NonTrivialStruct
%borrow0 = begin_borrow %copy0 : $NonTrivialStruct
br bb3(%borrow0 : $NonTrivialStruct, %copy0 : $NonTrivialStruct)
bb3(%newborrow : @guaranteed $NonTrivialStruct, %newowned : @owned $NonTrivialStruct):
br bb3(%newborrow : $NonTrivialStruct, %newowned : $NonTrivialStruct)
bb4:
%ret = tuple ()
return %ret : $()
}
// CHECK-LABEL: sil [ossa] @dce_reborrow_with_different_basevalues :
// CHECK: bb3([[ARG1:%.*]] : @reborrow $NonTrivialStruct, [[ARG2:%.*]] : @owned $NonTrivialStruct, [[ARG3:%.*]] : @owned $NonTrivialStruct):
// CHECK-LABEL: } // end sil function 'dce_reborrow_with_different_basevalues'
sil [ossa] @dce_reborrow_with_different_basevalues : $@convention(thin) (@guaranteed NonTrivialStruct, @guaranteed NonTrivialStruct) -> @owned NonTrivialStruct {
bb0(%0 : @guaranteed $NonTrivialStruct, %1 : @guaranteed $NonTrivialStruct):
cond_br undef, bb1, bb2
bb1:
%copy0a = copy_value %0 : $NonTrivialStruct
%borrow0a = begin_borrow %copy0a : $NonTrivialStruct
%copy1a = copy_value %1 : $NonTrivialStruct
br bb3(%borrow0a : $NonTrivialStruct, %copy0a : $NonTrivialStruct, %copy1a : $NonTrivialStruct)
bb2:
%copy1b = copy_value %1 : $NonTrivialStruct
%borrow0b = begin_borrow %copy1b : $NonTrivialStruct
%copy0b = copy_value %0 : $NonTrivialStruct
br bb3(%borrow0b : $NonTrivialStruct, %copy0b : $NonTrivialStruct, %copy1b : $NonTrivialStruct)
bb3(%newborrow : @guaranteed $NonTrivialStruct, %newowned1 : @owned $NonTrivialStruct, %newowned2 : @owned $NonTrivialStruct):
%res = copy_value %newborrow : $NonTrivialStruct
end_borrow %newborrow : $NonTrivialStruct
destroy_value %newowned1 : $NonTrivialStruct
destroy_value %newowned2 : $NonTrivialStruct
return %res : $NonTrivialStruct
}
// CHECK-LABEL: sil [ossa] @dce_deadterm1 :
// CHECK-NOT: switch_enum
// CHECK-LABEL: } // end sil function 'dce_deadterm1'
sil [ossa] @dce_deadterm1 : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : @owned $FakeOptional<Builtin.NativeObject>):
%0a = alloc_stack $FakeOptional<Builtin.NativeObject>
store %0 to [init] %0a : $*FakeOptional<Builtin.NativeObject>
%1 = load [take] %0a : $*FakeOptional<Builtin.NativeObject>
switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%2 : @owned $Builtin.NativeObject):
destroy_value %2 : $Builtin.NativeObject
br bb3
bb2:
br bb3
bb3:
dealloc_stack %0a : $*FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadterm2 :
// CHECK-NOT: load [copy]
// CHECK-NOT: switch_enum
// CHECK-LABEL: } // end sil function 'dce_deadterm2'
sil [ossa] @dce_deadterm2 : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : @owned $FakeOptional<Builtin.NativeObject>):
%0a = alloc_stack $FakeOptional<Builtin.NativeObject>
store %0 to [init] %0a : $*FakeOptional<Builtin.NativeObject>
%1 = load [copy] %0a : $*FakeOptional<Builtin.NativeObject>
switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%2 : @owned $Builtin.NativeObject):
destroy_value %2 : $Builtin.NativeObject
br bb3
bb2:
br bb3
bb3:
destroy_addr %0a : $*FakeOptional<Builtin.NativeObject>
dealloc_stack %0a : $*FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadendlifetime1 :
// CHECK-NOT: end_lifetime
// CHECK-LABEL: } // end sil function 'dce_deadendlifetime1'
sil [ossa] @dce_deadendlifetime1 : $@convention(thin) () -> () {
bb0:
cond_br undef, bb1, bb2
bb1:
br bb3(undef : $Klass)
bb2:
br bb3(undef : $Klass)
bb3(%2 : @owned $Klass):
end_lifetime %2 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadendlifetime2 :
// CHECK-NOT: end_lifetime
// CHECK-LABEL: } // end sil function 'dce_deadendlifetime2'
sil [ossa] @dce_deadendlifetime2 : $@convention(thin) () -> () {
bb0:
cond_br undef, bb1, bb2
bb1:
br bb3(undef : $Klass)
bb2:
br bb3(undef : $Klass)
bb3(%2 : @owned $Klass):
br bb4(%2 : $Klass)
bb4(%3 : @owned $Klass):
end_lifetime %3 : $Klass
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @looping_borrow : $@convention(thin) () -> () {
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'looping_borrow'
sil [ossa] @looping_borrow : $@convention(thin) () -> () {
entry:
%instance_1 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt
%lifetime_1 = begin_borrow %instance_1 : $FakeOptional<Klass>
br loop_entry(%instance_1 : $FakeOptional<Klass>, %lifetime_1 : $FakeOptional<Klass>)
loop_entry(%18 : @owned $FakeOptional<Klass>, %19 : @guaranteed $FakeOptional<Klass>):
br loop_body
loop_body:
cond_br undef, loop_back, loop_exit
loop_back:
br loop_entry(%18 : $FakeOptional<Klass>, %19 : $FakeOptional<Klass>)
loop_exit:
end_borrow %19 : $FakeOptional<Klass>
destroy_value %18 : $FakeOptional<Klass>
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil hidden [ossa] @add_end_borrow_after_destroy_value : {{.*}} {
// CHECK: bb0({{%[^,]+}} : $Builtin.Int1, [[INSTANCE_1:%[^,]+]] : @owned $Klass, {{%[^,]+}} : @owned $Klass):
// CHECK: [[LIFETIME_1:%[^,]+]] = begin_borrow [[INSTANCE_1]]
// CHECK: copy_value [[LIFETIME_1]]
// CHECK: cond_br {{%[^,]+}}, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: end_borrow [[LIFETIME_1]]
// CHECK: destroy_value [[INSTANCE_1]]
// CHECK: [[RIGHT]]:
// CHECK: end_borrow [[LIFETIME_1]]
// CHECK: destroy_value [[INSTANCE_1]]
// CHECK-LABEL: } // end sil function 'add_end_borrow_after_destroy_value'
sil hidden [ossa] @add_end_borrow_after_destroy_value : $@convention(thin) (Builtin.Int1, @owned Klass, @owned Klass) -> () {
bb0(%condition : $Builtin.Int1, %instance_1 : @owned $Klass, %instance_2 : @owned $Klass):
%lifetime_1 = begin_borrow %instance_1 : $Klass
%copy_1 = copy_value %lifetime_1 : $Klass
%stack_addr = alloc_stack $Klass
store %copy_1 to [init] %stack_addr : $*Klass
destroy_addr %stack_addr : $*Klass
dealloc_stack %stack_addr : $*Klass
cond_br %condition, bb1, bb2
bb1:
end_borrow %lifetime_1 : $Klass
destroy_value %instance_1 : $Klass
%lifetime_2 = begin_borrow %instance_2 : $Klass
br bb3(%instance_2 : $Klass, %lifetime_2 : $Klass)
bb2:
destroy_value %instance_2 : $Klass
br bb3(%instance_1 : $Klass, %lifetime_1 : $Klass)
bb3(%original : @owned $Klass, %lifetime : @guaranteed $Klass):
end_borrow %lifetime : $Klass
destroy_value %original : $Klass
%result = tuple ()
return %result : $()
}
// CHECK-LABEL: sil [ossa] @borrow_none : {{.*}} {
// CHECK: {{bb[0-9]+}}:
// CHECK-NEXT: br [[EXIT:bb[0-9]+]]
// CHECK: [[EXIT]]:
// CHECK-NEXT: [[RETVAL:%[^,]+]] = tuple ()
// CHECK-NEXT: return [[RETVAL]] : $()
// CHECK-LABEL: } // end sil function 'borrow_none'
sil [ossa] @borrow_none : $@convention(thin) () -> () {
entry:
%outer_lifetime_1 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt
switch_enum %outer_lifetime_1 : $FakeOptional<Klass>, case #FakeOptional.some!enumelt: work, case #FakeOptional.none!enumelt: to_exit, forwarding: @guaranteed
to_exit:
br exit
work(%outer_lifetime_2 : @guaranteed $Klass):
%inner_lifetime_1 = begin_borrow %outer_lifetime_2 : $Klass
end_borrow %inner_lifetime_1 : $Klass
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @reborrowed_begin_borrow : {{.*}} {
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $Klass):
// CHECK: [[OUTER_LIFETIME_1:%[^,]+]] = begin_borrow [[INSTANCE]]
// CHECK: br [[EXIT:bb[0-9]+]]([[OUTER_LIFETIME_1]] : $Klass)
// CHECK: [[EXIT]]([[OUTER_LIFETIME_2:%[^,]+]] : @reborrow $Klass):
// CHECK: [[R:%.*]] = borrowed [[OUTER_LIFETIME_2]] : $Klass from (%0 : $Klass)
// CHECK: end_borrow [[R]]
// CHECK: destroy_value [[INSTANCE]]
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
// CHECK: return [[RETVAL]]
// CHECK-LABEL: } // end sil function 'reborrowed_begin_borrow'
sil [ossa] @reborrowed_begin_borrow : $@convention(thin) (@owned Klass) -> () {
entry(%instance : @owned $Klass):
%outer_lifetime_1 = begin_borrow %instance : $Klass
%inner_lifetime_1 = begin_borrow %outer_lifetime_1 : $Klass
br exit(%outer_lifetime_1 : $Klass, %inner_lifetime_1 : $Klass)
exit(%outer_lifetime_2 : @guaranteed $Klass, %inner_lifetime_2 : @guaranteed $Klass):
end_borrow %inner_lifetime_2 : $Klass
end_borrow %outer_lifetime_2 : $Klass
destroy_value %instance : $Klass
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @reborrowed_guaranteed_phi : {{.*}} {
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $Klass):
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [[INSTANCE]]
// CHECK: br [[WORK:bb[0-9]+]]([[LIFETIME]] : $Klass)
// CHECK: [[WORK]]([[LIFETIME_1:%[^,]+]] : @reborrow $Klass):
// CHECK: [[R:%.*]] = borrowed [[LIFETIME_1]] : $Klass from (%0 : $Klass)
// CHECK: br [[EXIT:bb[0-9]+]]([[R]] : $Klass)
// CHECK: [[EXIT]]([[LIFETIME_2:%[^,]+]] : @reborrow $Klass):
// CHECK: [[R2:%.*]] = borrowed [[LIFETIME_2]] : $Klass from (%0 : $Klass)
// CHECK: end_borrow [[R2]]
// CHECK: destroy_value [[INSTANCE]]
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
// CHECK: return [[RETVAL]] : $()
// CHECK-LABEL: } // end sil function 'reborrowed_guaranteed_phi'
sil [ossa] @reborrowed_guaranteed_phi : $@convention(thin) (@owned Klass) -> () {
entry(%instance : @owned $Klass):
%outer_lifetime_1 = begin_borrow %instance : $Klass
br work(%outer_lifetime_1 : $Klass)
work(%outer_lifetime_2 : @guaranteed $Klass):
%inner_lifetime_1 = begin_borrow %outer_lifetime_2 : $Klass
br exit(%outer_lifetime_2 : $Klass, %inner_lifetime_1 : $Klass)
exit(%outer_lifetime_3 : @guaranteed $Klass, %inner_lifetime_2 : @guaranteed $Klass):
end_borrow %inner_lifetime_2 : $Klass
end_borrow %outer_lifetime_3 : $Klass
destroy_value %instance : $Klass
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @reborrow_guaranteed_phi2 : {{.*}} {
// CHECK-NOT: switch_enum
// CHECK-LABEL: } // end sil function 'reborrow_guaranteed_phi2'
sil [ossa] @reborrow_guaranteed_phi2 : $@convention(thin) (@owned EitherNoneOrAnyObject) -> () {
entry(%0 : @owned $EitherNoneOrAnyObject):
%borrow_either = begin_borrow %0 : $EitherNoneOrAnyObject
switch_enum %borrow_either : $EitherNoneOrAnyObject, case #EitherNoneOrAnyObject.none!enumelt: none_block, case #EitherNoneOrAnyObject.any!enumelt: any_block
any_block(%borrow : @guaranteed $AnyObject):
%2 = begin_borrow %borrow : $AnyObject
end_borrow %2 : $AnyObject
end_borrow %borrow_either : $EitherNoneOrAnyObject
destroy_value %0 : $EitherNoneOrAnyObject
br exit
none_block:
end_borrow %borrow_either : $EitherNoneOrAnyObject
destroy_value %0 : $EitherNoneOrAnyObject
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @reborrowed_guaranteed_phi3 :
// CHECK: begin_borrow
// CHECK-NOT: begin_borrow
// CHECK-LABEL: } // end sil function 'reborrowed_guaranteed_phi3'
sil [ossa] @reborrowed_guaranteed_phi3 : $@convention(thin) (@owned Klass) -> () {
entry(%instance : @owned $Klass):
%outer_lifetime_1 = begin_borrow %instance : $Klass
%inner_lifetime_1 = begin_borrow %outer_lifetime_1 : $Klass
br exit(%inner_lifetime_1 : $Klass)
exit(%inner_lifetime_2 : @guaranteed $Klass):
end_borrow %inner_lifetime_2 : $Klass
end_borrow %outer_lifetime_1 : $Klass
destroy_value %instance : $Klass
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @reborrow_load_borrow : {{.*}} {
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : $*Klass):
// CHECK: [[LIFETIME:%[^,]+]] = load_borrow [[INSTANCE]]
// CHECK: br [[BASIC_BLOCK1:bb[0-9]+]]([[LIFETIME]] : $Klass)
// CHECK: [[BASIC_BLOCK1]]([[LIFETIME_2:%[^,]+]] : @reborrow $Klass):
// CHECK: [[R:%.*]] = borrowed [[LIFETIME_2]] : $Klass from ()
// CHECK: end_borrow [[R]]
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
// CHECK: return [[RETVAL]]
// CHECK-LABEL: } // end sil function 'reborrow_load_borrow'
sil [ossa] @reborrow_load_borrow : $@convention(method) (@in Klass) -> () {
bb0(%0 : $*Klass):
%1 = load_borrow %0 : $*Klass
%2 = begin_borrow %1 : $Klass
br bb1(%1 : $Klass, %2 : $Klass)
bb1(%4 : @guaranteed $Klass, %5 : @guaranteed $Klass):
end_borrow %5 : $Klass
end_borrow %4 : $Klass
%8 = tuple ()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @reborrow_load_borrow2 : {{.*}} {
// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] : $*Klass):
// CHECK: [[LIFETIME:%[^,]+]] = load_borrow [[ADDR]] : $*Klass
// CHECK: br [[EXIT:bb[0-9]+]]([[LIFETIME]] : $Klass)
// CHECK: [[EXIT]]([[LIFETIME_2:%[^,]+]] : @reborrow $Klass):
// CHECK: [[R:%.*]] = borrowed [[LIFETIME_2]] : $Klass from ()
// CHECK: end_borrow [[R]] : $Klass
// CHECK: [[EXIT:%[^,]+]] = tuple ()
// CHECK: return [[EXIT]] : $()
// CHECK-LABEL: } // end sil function 'reborrow_load_borrow2'
sil [ossa] @reborrow_load_borrow2 : $@convention(method) (@in Klass) -> () {
bb0(%0 : $*Klass):
%1 = load_borrow %0 : $*Klass
%2 = begin_borrow %1 : $Klass
br bb1(%2 : $Klass, %1 : $Klass)
bb1(%4 : @guaranteed $Klass, %5 : @guaranteed $Klass):
end_borrow %4 : $Klass
end_borrow %5 : $Klass
%8 = tuple ()
return %8 : $()
}
class Wrapper {
let val: Klass
}
class DoubleWrapper {
let s: StructWrapper
}
struct StructWrapper {
let val: Klass
}
// CHECK-LABEL: sil [ossa] @reborrow_load_borrow3 : $@convention(method) (@owned Wrapper) -> () {
// CHECK: load_borrow
// CHECK: end_borrow
// CHECK: br bb1
// CHECK-LABEL: } // end sil function 'reborrow_load_borrow3'
sil [ossa] @reborrow_load_borrow3 : $@convention(method) (@owned Wrapper) -> () {
bb0(%0 : @owned $Wrapper):
%1 = begin_borrow %0 : $Wrapper
%2 = ref_element_addr %1 : $Wrapper, #Wrapper.val
%3 = load_borrow %2 : $*Klass
%f = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
br bb1(%1 : $Wrapper, %3 : $Klass)
bb1(%4 : @guaranteed $Wrapper, %5 : @guaranteed $Klass):
end_borrow %5 : $Klass
end_borrow %4 : $Wrapper
destroy_value %0 : $Wrapper
%8 = tuple ()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @reborrow_load_borrow4 : $@convention(method) (@owned DoubleWrapper) -> () {
// CHECK: load_borrow
// CHECK: end_borrow
// CHECK: br bb1
// CHECK-LABEL: } // end sil function 'reborrow_load_borrow4'
sil [ossa] @reborrow_load_borrow4 : $@convention(method) (@owned DoubleWrapper) -> () {
bb0(%0 : @owned $DoubleWrapper):
%1 = begin_borrow %0 : $DoubleWrapper
%2 = ref_element_addr %1 : $DoubleWrapper, #DoubleWrapper.s
%4 = struct_element_addr %2 : $*StructWrapper, #StructWrapper.val
%5 = load_borrow %4 : $*Klass
%f = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%5) : $@convention(thin) (@guaranteed Klass) -> ()
br bb1(%1 : $DoubleWrapper, %5 : $Klass)
bb1(%6 : @guaranteed $DoubleWrapper, %7 : @guaranteed $Klass):
end_borrow %7 : $Klass
end_borrow %6 : $DoubleWrapper
destroy_value %0 : $DoubleWrapper
%8 = tuple ()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @borrow_guaranteed_tuple : {{.*}} {
// CHECK: {{bb[0-9]+}}([[INSTANCE_1:%[^,]+]] : @owned $Klass, [[INSTANCE_2:%[^,]+]] : @owned $Klass):
// CHECK: destroy_value [[INSTANCE_2]]
// CHECK: destroy_value [[INSTANCE_1]]
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
// CHECK: return [[RETVAL]] : $()
// CHECK-LABEL: } // end sil function 'borrow_guaranteed_tuple'
sil [ossa] @borrow_guaranteed_tuple : $@convention(thin) (@owned Klass, @owned Klass) -> () {
entry(%instance_1 : @owned $Klass, %instance_2 : @owned $Klass):
%lifetime_1_1 = begin_borrow %instance_1 : $Klass
%lifetime_2_1 = begin_borrow %instance_2 : $Klass
%tuple = tuple $(Klass, Klass) (%lifetime_1_1, %lifetime_2_1)
br exit(%lifetime_1_1 : $Klass, %lifetime_2_1 : $Klass, %tuple : $(Klass, Klass))
exit(%lifetime_1_2 : @guaranteed $Klass, %lifetime_2_2 : @guaranteed $Klass, %tuple_2 : @guaranteed $(Klass, Klass)):
end_borrow %lifetime_2_2 : $Klass
destroy_value %instance_2 : $Klass
end_borrow %lifetime_1_2 : $Klass
destroy_value %instance_1 : $Klass
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @borrow_guaranteed_struct : {{.*}} {
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $Klass):
// CHECK: destroy_value [[INSTANCE]]
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
// CHECK: return [[RETVAL]] : $()
// CHECK-LABEL: } // end sil function 'borrow_guaranteed_struct'
sil [ossa] @borrow_guaranteed_struct : $@convention(thin) (@owned Klass) -> () {
entry(%instance_1 : @owned $Klass):
%lifetime_1 = begin_borrow %instance_1 : $Klass
%struct = struct $NonTrivialStruct (%lifetime_1 : $Klass)
br exit(%lifetime_1 : $Klass, %struct : $NonTrivialStruct)
exit(%lifetime_2 : @guaranteed $Klass, %struct_2 : @guaranteed $NonTrivialStruct):
end_borrow %lifetime_2 : $Klass
destroy_value %instance_1 : $Klass
%retval = tuple ()
return %retval : $()
}
sil [ossa] @testBorrowedSwitch : $@convention(thin) (Optional<@sil_unmanaged AnyObject>) -> () {
bb0(%0 : $Optional<@sil_unmanaged AnyObject>):
switch_enum %0 : $Optional<@sil_unmanaged AnyObject>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
bb1(%2 : $@sil_unmanaged AnyObject):
%3 = unmanaged_to_ref %2 : $@sil_unmanaged AnyObject to $AnyObject
%4 = unchecked_ownership_conversion %3 : $AnyObject, @unowned to @guaranteed
%5 = begin_borrow [lexical] %4 : $AnyObject
end_borrow %5 : $AnyObject
end_borrow %4 : $AnyObject
br bb3
bb2:
br bb3
bb3:
%242 = tuple ()
return %242 : $()
}
// rdar://87985420 - crash in nullptr returned by findGuaranteedReferenceRoots
// Test that a borrow scope introduced by unchecked_ownership_conversion is not
// deleted by DCE.
sil [ossa] @testUncheckedConvertToGuaranteed : $@convention(thin) (@sil_unmanaged AnyObject) -> () {
bb0(%0 : $@sil_unmanaged AnyObject):
%3 = unmanaged_to_ref %0 : $@sil_unmanaged AnyObject to $AnyObject
%4 = unchecked_ownership_conversion %3 : $AnyObject, @unowned to @guaranteed
%5 = begin_borrow [lexical] %4 : $AnyObject
end_borrow %5 : $AnyObject
end_borrow %4 : $AnyObject
%242 = tuple ()
return %242 : $()
}
sil @foo : $@convention(thin) () -> ()
// CHECK-LABEL: sil [ossa] @test_escape1 : {{.*}} {
// CHECK: br bb1
// CHECK: end_borrow
// CHECK-LABEL: } // end sil function 'test_escape1'
sil [ossa] @test_escape1 : $@convention(thin) () -> () {
bb0:
%13 = function_ref @foo : $@convention(thin) () -> ()
%14 = partial_apply [callee_guaranteed] %13() : $@convention(thin) () -> ()
%15 = convert_escape_to_noescape %14 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%17 = begin_borrow %14 : $@callee_guaranteed () -> ()
%18 = mark_dependence %15 : $@noescape @callee_guaranteed () -> () on %17 : $@callee_guaranteed () -> ()
br bb1(%18 : $@noescape @callee_guaranteed () -> (), %17 : $@callee_guaranteed () -> (), %14 : $@callee_guaranteed () -> ())
bb1(%21 : @owned $@noescape @callee_guaranteed () -> (), %22 : @guaranteed $@callee_guaranteed () -> (), %23 : @owned $@callee_guaranteed () -> ()):
%27 = apply %21() : $@noescape @callee_guaranteed () -> ()
destroy_value %21 : $@noescape @callee_guaranteed () -> ()
end_borrow %22 : $@callee_guaranteed () -> ()
destroy_value %23 : $@callee_guaranteed () -> ()
%28 = tuple ()
return %28: $()
}
// CHECK-LABEL: sil [ossa] @test_escape2 : {{.*}} {
// CHECK: br bb1
// CHECK: destroy_value
// CHECK: destroy_value
// CHECK-LABEL: } // end sil function 'test_escape2'
sil [ossa] @test_escape2 : $@convention(thin) () -> () {
bb0:
%13 = function_ref @foo : $@convention(thin) () -> ()
%14 = partial_apply [callee_guaranteed] %13() : $@convention(thin) () -> ()
%15 = convert_escape_to_noescape %14 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%18 = mark_dependence %15 : $@noescape @callee_guaranteed () -> () on %14 : $@callee_guaranteed () -> ()
br bb1(%18 : $@noescape @callee_guaranteed () -> (), %14 : $@callee_guaranteed () -> ())
bb1(%21 : @owned $@noescape @callee_guaranteed () -> (), %23 : @owned $@callee_guaranteed () -> ()):
%27 = apply %21() : $@noescape @callee_guaranteed () -> ()
destroy_value %21 : $@noescape @callee_guaranteed () -> ()
destroy_value %23 : $@callee_guaranteed () -> ()
%28 = tuple ()
return %28: $()
}
sil @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
// CHECK-LABEL: sil [ossa] @test_forwarded_phi1 :
// CHECK: br bb3
// CHECK: end_borrow
// CHECK-LABEL: } // end sil function 'test_forwarded_phi1'
sil [ossa] @test_forwarded_phi1 : $@convention(thin) (@owned Wrapper1) -> () {
bb0(%0 : @owned $Wrapper1):
%outer = begin_borrow %0 : $Wrapper1
br bb1
bb1:
%ex1 = struct_extract %outer : $Wrapper1, #Wrapper1.val1
br bb2(%ex1 : $Wrapper2, %outer : $Wrapper1)
bb2(%phi1 : @guaranteed $Wrapper2, %phi2 : @guaranteed $Wrapper1):
%ex2 = struct_extract %phi1 : $Wrapper2, #Wrapper2.val2
br bb3(%ex2 : $Klass, %phi2 : $Wrapper1)
bb3(%phi3 : @guaranteed $Klass, %phi4 : @guaranteed $Wrapper1):
%f = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%phi3) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %phi4 : $Wrapper1
destroy_value %0 : $Wrapper1
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @test_forwarded_phi2 :
// CHECK: br bb3
// CHECK: end_borrow
// CHECK-LABEL: } // end sil function 'test_forwarded_phi2'
sil [ossa] @test_forwarded_phi2 : $@convention(thin) (@owned Wrapper1) -> () {
bb0(%0 : @owned $Wrapper1):
%outer = begin_borrow %0 : $Wrapper1
br bb1
bb1:
br bb2(%outer : $Wrapper1)
bb2(%phi1 : @guaranteed $Wrapper1):
%ex1 = struct_extract %phi1 : $Wrapper1, #Wrapper1.val1 // user: %4
%ex2 = struct_extract %ex1 : $Wrapper2, #Wrapper2.val2
br bb3(%ex2 : $Klass, %phi1 : $Wrapper1)
bb3(%phi2 : @guaranteed $Klass, %phi3 : @guaranteed $Wrapper1):
%f = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%phi2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %phi3 : $Wrapper1
destroy_value %0 : $Wrapper1
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @test_forwarded_phi3 :
// CHECK: end_borrow
// CHECK: br bb3
// CHECK-LABEL: } // end sil function 'test_forwarded_phi3'
sil [ossa] @test_forwarded_phi3 : $@convention(thin) (@owned Wrapper1) -> () {
bb0(%0 : @owned $Wrapper1):
%outer = begin_borrow %0 : $Wrapper1
br bb1
bb1:
br bb2(%outer : $Wrapper1)
bb2(%phi1 : @guaranteed $Wrapper1):
%ex1 = struct_extract %phi1 : $Wrapper1, #Wrapper1.val1 // user: %4
%ex2 = struct_extract %ex1 : $Wrapper2, #Wrapper2.val2
%f = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%ex2) : $@convention(thin) (@guaranteed Klass) -> ()
br bb3(%ex2 : $Klass, %phi1 : $Wrapper1)
bb3(%phi2 : @guaranteed $Klass, %phi3 : @guaranteed $Wrapper1):
end_borrow %phi3 : $Wrapper1
destroy_value %0 : $Wrapper1
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @test_loadborrow_dep :
// CHECK: br bb3
// CHECK: end_borrow
// CHECK-LABEL: } // end sil function 'test_loadborrow_dep'
sil [ossa] @test_loadborrow_dep : $@convention(thin) (@in Wrapper1) -> () {
bb0(%0 : $*Wrapper1):
%outer = load_borrow %0 : $*Wrapper1
br bb1
bb1:
%ex1 = struct_extract %outer : $Wrapper1, #Wrapper1.val1
br bb2(%ex1 : $Wrapper2, %outer : $Wrapper1)
bb2(%phi1 : @guaranteed $Wrapper2, %phi2 : @guaranteed $Wrapper1):
%ex2 = struct_extract %phi1 : $Wrapper2, #Wrapper2.val2
br bb3(%ex2 : $Klass, %phi2 : $Wrapper1)
bb3(%phi3 : @guaranteed $Klass, %phi4 : @guaranteed $Wrapper1):
%f = function_ref @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%phi3) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %phi4 : $Wrapper1
destroy_addr %0 : $*Wrapper1
%9999 = tuple()
return %9999 : $()
}
class C {}
sil [ossa] @getC : $@convention(thin) () -> (@owned C)
// CHECK-LABEL: sil [ossa] @dont_dce_lexical_phi :
// CHECK: destroy_value
// CHECK-LABEL: } // end sil function 'dont_dce_lexical_phi'
sil [ossa] @dont_dce_lexical_phi : $() -> () {
entry:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
cond_br undef, left, right
left:
%c1 = apply %getC() : $@convention(thin) () -> (@owned C)
%m1 = move_value [lexical] %c1 : $C
br exit(%m1 : $C)
right:
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
br exit(%c2 : $C)
exit(%cm : @owned $C):
destroy_value %cm : $C
%retval = tuple ()
return %retval : $()
}
class KlassWithDeinit {
init()
deinit
}
// DCE should not delete dead allocations, leave it to DOE
// CHECK-LABEL: sil [ossa] @dont_delete_allocation :
// CHECK: alloc_ref
// CHECK-LABEL: } // end sil function 'dont_delete_allocation'
sil [ossa] @dont_delete_allocation : $@convention(thin) () -> () {
bb0:
%a = alloc_ref $KlassWithDeinit
destroy_value %a : $KlassWithDeinit
%t = tuple ()
return %t : $()
}
// CHECK-LABEL: sil [ossa] @dce_dead_guaranteedphi :
// bb2:
// CHECK-LABEL: } // end sil function 'dce_dead_guaranteedphi'
sil [ossa] @dce_dead_guaranteedphi : $@convention(thin) (@owned NonTrivialStruct) -> () {
bb0(%0 : @owned $NonTrivialStruct):
%b = begin_borrow %0 : $NonTrivialStruct
%func = function_ref @$use_nontrivialstruct2 : $@convention(thin) (@guaranteed NonTrivialStruct) -> ()
%funcres = apply %func(%b) : $@convention(thin) (@guaranteed NonTrivialStruct) -> ()
br bb2(%b : $NonTrivialStruct, %b : $NonTrivialStruct)
bb2(%phi1 : @guaranteed $NonTrivialStruct, %phi2 : @guaranteed $NonTrivialStruct):
end_borrow %phi1 : $NonTrivialStruct
destroy_value %0 : $NonTrivialStruct
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadterm4 :
// CHECK-NOT: switch_enum
// CHECK-LABEL: } // end sil function 'dce_deadterm4'
sil [ossa] @dce_deadterm4 : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : @owned $FakeOptional<Builtin.NativeObject>):
%0a = alloc_stack $FakeOptional<Builtin.NativeObject>
store %0 to [init] %0a : $*FakeOptional<Builtin.NativeObject>
%1 = load [take] %0a : $*FakeOptional<Builtin.NativeObject>
%2 = copy_value %1 : $FakeOptional<Builtin.NativeObject>
apply undef(%2) : $@convention(thin) (@guaranteed FakeOptional<Builtin.NativeObject>) -> ()
switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%3 : @owned $Builtin.NativeObject):
destroy_value %3 : $Builtin.NativeObject
br bb3
bb2:
br bb3
bb3:
switch_enum %2 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb4, case #FakeOptional.none!enumelt: bb5
bb4(%4 : @owned $Builtin.NativeObject):
destroy_value %4 : $Builtin.NativeObject
br bb6
bb5:
br bb6
bb6:
dealloc_stack %0a : $*FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadterm5 :
// CHECK: switch_enum
// CHECK-NOT: switch_enum
// CHECK-LABEL: } // end sil function 'dce_deadterm5'
sil [ossa] @dce_deadterm5 : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : @owned $FakeOptional<Builtin.NativeObject>):
%0a = alloc_stack $FakeOptional<Builtin.NativeObject>
store %0 to [init] %0a : $*FakeOptional<Builtin.NativeObject>
%1 = load [take] %0a : $*FakeOptional<Builtin.NativeObject>
%2 = copy_value %1 : $FakeOptional<Builtin.NativeObject>
apply undef(%2) : $@convention(thin) (@guaranteed FakeOptional<Builtin.NativeObject>) -> ()
switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%3 : @owned $Builtin.NativeObject):
destroy_value %3 : $Builtin.NativeObject
br bb3
bb2:
switch_enum %2 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb4, case #FakeOptional.none!enumelt: bb5
bb3:
destroy_value %2 : $FakeOptional<Builtin.NativeObject>
br bb6
bb4(%4 : @owned $Builtin.NativeObject):
destroy_value %4 : $Builtin.NativeObject
br bb6
bb5:
br bb6
bb6:
dealloc_stack %0a : $*FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @dce_deadforwarding :
// CHECK-NOT: cond_br
// CHECK-LABEL: } // end sil function 'dce_deadforwarding'
sil [ossa] @dce_deadforwarding : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = copy_value %0 : $Klass
%func = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%funcres = apply %func(%1) : $@convention(thin) (@guaranteed Klass) -> ()
cond_br undef, bb1, bb2
bb1:
%3 = struct $NonTrivialStruct(%1 : $Klass)
destroy_value %3 : $NonTrivialStruct
br bb3
bb2:
%4 = struct $NonTrivialStruct(%1 : $Klass)
destroy_value %4 : $NonTrivialStruct
br bb3
bb3:
%res = tuple ()
return %res : $()
}
// Ensure no verification error
sil [ossa] @dce_borrowedfromuser : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () {
bb0(%0 : @guaranteed $FakeOptional<Klass>):
%1 = copy_value %0
%2 = begin_borrow %1
%3 = begin_borrow %1
br bb1(%2, %3)
bb1(%5 : @reborrow $FakeOptional<Klass>, %6 : @reborrow $FakeOptional<Klass>):
%7 = borrowed %6 from (%1)
%8 = borrowed %5 from (%1)
switch_enum %7, case #FakeOptional.some!enumelt: bb2, case #FakeOptional.none!enumelt: bb3
bb2(%10 : @guaranteed $Klass):
%11 = function_ref @$use_klass2 : $@convention(thin) (@guaranteed Klass) -> ()
%12 = apply %11(%10) : $@convention(thin) (@guaranteed Klass) -> ()
br bb4
bb3:
br bb4
bb4:
br bb5(%8, %7)
bb5(%16 : @reborrow $FakeOptional<Klass>, %17 : @reborrow $FakeOptional<Klass>):
%18 = borrowed %17 from (%1)
%19 = borrowed %16 from (%1)
end_borrow %19
end_borrow %18
destroy_value %1
%23 = tuple ()
return %23
}