mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
When an optimization updates borrowed-from instruction it might be necessary to remove the old enclosing values from the borrowed-from instructions. An optimization might transform the SIL in a way that an existing enclosing value is not valid anymore. Fixes a compiler crash: rdar://142991910
368 lines
13 KiB
Plaintext
368 lines
13 KiB
Plaintext
// RUN: %target-sil-opt -sil-print-types -update-borrowed-from -copy-propagation -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK,CHECK-OPT
|
|
|
|
// REQUIRES: swift_in_compiler
|
|
|
|
class X {}
|
|
|
|
enum FakeOptional<T> {
|
|
case some(T)
|
|
case none
|
|
}
|
|
|
|
sil [ossa] @getX : $@convention(thin) () -> (@owned X)
|
|
sil [ossa] @holdX : $@convention(thin) (@guaranteed X) -> ()
|
|
|
|
// CHECK-LABEL: sil [ossa] @nohoist_destroy_over_reborrow_endborrow : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}([[REBORROW:%[^,]+]] : @reborrow $X, [[VALUE:%[^,]+]] : @owned $X):
|
|
// CHECK: [[BF:%.*]] = borrowed [[REBORROW]] : $X from ([[VALUE]] : $X)
|
|
// CHECK: end_borrow [[BF]]
|
|
// CHECK: destroy_value [[VALUE]]
|
|
// CHECK-LABEL: } // end sil function 'nohoist_destroy_over_reborrow_endborrow'
|
|
sil [ossa] @nohoist_destroy_over_reborrow_endborrow : $@convention(thin) () -> () {
|
|
entry:
|
|
%get = function_ref @getX : $@convention(thin) () -> @owned X
|
|
%value_1 = apply %get() : $@convention(thin) () -> @owned X
|
|
%lifetime = begin_borrow %value_1 : $X
|
|
br body(%lifetime : $X, %value_1 : $X)
|
|
|
|
body(%reborrow : @guaranteed $X, %value_2 : @owned $X):
|
|
end_borrow %reborrow : $X
|
|
destroy_value %value_2 : $X
|
|
br exit
|
|
|
|
exit:
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @hoist_destroy_over_unrelated_endborrow : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}([[UNRELATED:%[^,]+]] : @reborrow $X, [[VALUE:%[^,]+]] : @owned $X):
|
|
// CHECK: [[BF:%.*]] = borrowed [[UNRELATED]] : $X from (%0 : $X)
|
|
// CHECK: destroy_value [[VALUE]]
|
|
// CHECK: end_borrow [[BF]]
|
|
// CHECK-LABEL: } // end sil function 'hoist_destroy_over_unrelated_endborrow'
|
|
sil [ossa] @hoist_destroy_over_unrelated_endborrow : $@convention(thin) (@guaranteed X) -> () {
|
|
entry(%unrelated : @guaranteed $X):
|
|
%get = function_ref @getX : $@convention(thin) () -> @owned X
|
|
%value_1 = apply %get() : $@convention(thin) () -> @owned X
|
|
%borrow_1 = begin_borrow %unrelated : $X
|
|
br body(%borrow_1 : $X, %value_1 : $X)
|
|
|
|
body(%borrow_2 : @guaranteed $X, %value_2 : @owned $X):
|
|
%hold = function_ref @holdX : $@convention(thin) (@guaranteed X) -> ()
|
|
apply %hold(%borrow_2) : $@convention(thin) (@guaranteed X) -> ()
|
|
end_borrow %borrow_2 : $X
|
|
destroy_value %value_2 : $X
|
|
br exit
|
|
|
|
exit:
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @nohoist_destroy_over_reborrow_reborrow_endborrow : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : @reborrow $X, {{%[^,]+}} : @owned $X):
|
|
// CHECK: {{bb[0-9]+}}([[REBORROW:%[^,]+]] : @reborrow $X, [[VALUE:%[^,]+]] : @owned $X):
|
|
// CHECK: [[BF:%.*]] = borrowed [[REBORROW]] : $X from ([[VALUE]] : $X)
|
|
// CHECK: end_borrow [[BF]]
|
|
// CHECK: destroy_value [[VALUE]]
|
|
// CHECK-LABEL: } // end sil function 'nohoist_destroy_over_reborrow_reborrow_endborrow'
|
|
sil [ossa] @nohoist_destroy_over_reborrow_reborrow_endborrow : $@convention(thin) () -> () {
|
|
entry:
|
|
%get = function_ref @getX : $@convention(thin) () -> @owned X
|
|
%value_1 = apply %get() : $@convention(thin) () -> @owned X
|
|
%lifetime = begin_borrow %value_1 : $X
|
|
br body1(%lifetime : $X, %value_1 : $X)
|
|
|
|
body1(%lifetime_2 : @guaranteed $X, %value_2 : @owned $X):
|
|
br body2(%lifetime_2 : $X, %value_2 : $X)
|
|
|
|
body2(%lifetime_3 : @guaranteed $X, %value_3 : @owned $X):
|
|
end_borrow %lifetime_3 : $X
|
|
destroy_value %value_3 : $X
|
|
br exit
|
|
|
|
exit:
|
|
%14 = tuple ()
|
|
return %14 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @hoist_destroy_over_unrelated_reborrow_reborrow_endborrow : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : @reborrow $X, {{%[^,]+}} : @owned $X):
|
|
// CHECK: {{bb[0-9]+}}([[REBORROW:%[^,]+]] : @reborrow $X, [[VALUE:%[^,]+]] : @owned $X):
|
|
// CHECK: [[BF:%.*]] = borrowed [[REBORROW]] : $X from (%0 : $X)
|
|
// CHECK: destroy_value [[VALUE]]
|
|
// CHECK: end_borrow [[BF]]
|
|
// CHECK-LABEL: } // end sil function 'hoist_destroy_over_unrelated_reborrow_reborrow_endborrow'
|
|
sil [ossa] @hoist_destroy_over_unrelated_reborrow_reborrow_endborrow : $@convention(thin) (@guaranteed X) -> () {
|
|
entry(%unrelated : @guaranteed $X):
|
|
%get = function_ref @getX : $@convention(thin) () -> @owned X
|
|
%value_1 = apply %get() : $@convention(thin) () -> @owned X
|
|
%borrow_1 = begin_borrow %unrelated : $X
|
|
br body1(%borrow_1 : $X, %value_1 : $X)
|
|
|
|
body1(%borrow_2 : @guaranteed $X, %value_2 : @owned $X):
|
|
br body2(%borrow_2 : $X, %value_2 : $X)
|
|
|
|
body2(%borrow_3 : @guaranteed $X, %value_3 : @owned $X):
|
|
%hold = function_ref @holdX : $@convention(thin) (@guaranteed X) -> ()
|
|
apply %hold(%borrow_3) : $@convention(thin) (@guaranteed X) -> ()
|
|
end_borrow %borrow_3 : $X
|
|
destroy_value %value_3 : $X
|
|
br exit
|
|
|
|
exit:
|
|
%14 = tuple ()
|
|
return %14 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @nohoist_destroy_over_forward_reborrow_endborrow : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}([[REBORROW:%[^,]+]] : @reborrow $X, [[VALUE:%[^,]+]] : @owned $X):
|
|
// CHECK: [[BF:%.*]] = borrowed [[REBORROW]] : $X from ([[VALUE]] : $X)
|
|
// CHECK: end_borrow [[BF]]
|
|
// CHECK: destroy_value [[VALUE]]
|
|
// CHECK-LABEL: } // end sil function 'nohoist_destroy_over_forward_reborrow_endborrow'
|
|
sil [ossa] @nohoist_destroy_over_forward_reborrow_endborrow : $@convention(thin) () -> () {
|
|
entry:
|
|
%get = function_ref @getX : $@convention(thin) () -> @owned X
|
|
%value_1 = apply %get() : $@convention(thin) () -> @owned X
|
|
br body1(%value_1 : $X)
|
|
|
|
body1(%value_2 : @owned $X):
|
|
%lifetime_2 = begin_borrow %value_2 : $X
|
|
br body2(%lifetime_2 : $X, %value_2 : $X)
|
|
|
|
body2(%lifetime_3 : @guaranteed $X, %value_3 : @owned $X):
|
|
end_borrow %lifetime_3 : $X
|
|
destroy_value %value_3 : $X
|
|
br exit
|
|
|
|
exit:
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @nohoist_destroy_over_reborrow_loop : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}([[REBORROW:%[^,]+]] : @reborrow $FakeOptional<X>, [[VALUE:%[^,]+]] : @owned $FakeOptional<X>):
|
|
// CHECK: [[BF:%.*]] = borrowed [[REBORROW]] : $FakeOptional<X> from ([[VALUE]] : $FakeOptional<X>)
|
|
// CHECK: cond_br undef, [[LOOP:bb[0-9]+]], [[BODY:bb[0-9]+]]
|
|
// CHECK: [[LOOP]]:
|
|
// CHECK: end_borrow [[BF]] : $FakeOptional<X>
|
|
// CHECK: destroy_value [[VALUE]] : $FakeOptional<X>
|
|
// CHECK: [[BODY]]:
|
|
// CHECK: end_borrow [[BF]] : $FakeOptional<X>
|
|
// CHECK: destroy_value [[VALUE]] : $FakeOptional<X>
|
|
// CHECK-LABEL: } // end sil function 'nohoist_destroy_over_reborrow_loop'
|
|
sil [ossa] @nohoist_destroy_over_reborrow_loop : $@convention(thin) () -> () {
|
|
entry:
|
|
%none1 = enum $FakeOptional<X>, #FakeOptional.none!enumelt
|
|
%none2 = enum $FakeOptional<X>, #FakeOptional.none!enumelt
|
|
%none_borrow_2 = begin_borrow %none2 : $FakeOptional<X>
|
|
br head(%none_borrow_2 : $FakeOptional<X>, %none1 : $FakeOptional<X>)
|
|
|
|
head(%reborrow : @guaranteed $FakeOptional<X>, %value : @owned $FakeOptional<X>):
|
|
cond_br undef, body, exit
|
|
|
|
body:
|
|
%get = function_ref @getX : $@convention(thin) () -> (@owned X)
|
|
%value_0 = apply %get() : $@convention(thin) () -> (@owned X)
|
|
end_borrow %reborrow : $FakeOptional<X>
|
|
destroy_value %value : $FakeOptional<X>
|
|
%some_value_0 = enum $FakeOptional<X>, #FakeOptional.some!enumelt, %value_0 : $X
|
|
%some_borrow_0 = begin_borrow %some_value_0 : $FakeOptional<X>
|
|
br head(%some_borrow_0 : $FakeOptional<X>, %some_value_0 : $FakeOptional<X>)
|
|
|
|
exit:
|
|
%retval = tuple ()
|
|
end_borrow %reborrow : $FakeOptional<X>
|
|
destroy_value %value : $FakeOptional<X>
|
|
return %retval : $()
|
|
}
|
|
|
|
// Ensure that an adjacent reborrow phi being reborrowed doesn't get counted as
|
|
// a non-lifetime ending use and extend liveness beyond the instruction where
|
|
// the reborrow occurs.
|
|
// CHECK-LABEL: sil [ossa] @reborrow_adjacent_to_consume_doesnt_extend_lifetime : {{.*}} {
|
|
// CHECK: bb1([[REBORROW:%[^,]+]] : @reborrow $X, [[VALUE:%[^,]+]] :
|
|
// CHECK: [[R:%.*]] = borrowed [[REBORROW]] : $X from ([[VALUE]] : $X)
|
|
// CHECK: {{bb[0-9]+}}:
|
|
// CHECK: end_borrow [[R]]
|
|
// CHECK: destroy_value [[VALUE]]
|
|
// CHECK-LABEL: } // end sil function 'reborrow_adjacent_to_consume_doesnt_extend_lifetime'
|
|
sil [ossa] @reborrow_adjacent_to_consume_doesnt_extend_lifetime : $@convention(thin) (@owned X) -> () {
|
|
bb0(%0 : @owned $X):
|
|
%1 = begin_borrow %0 : $X
|
|
br bb1(%1 : $X, %0 : $X)
|
|
|
|
bb1(%3 : @guaranteed $X, %4 : @owned $X):
|
|
cond_br undef, bb2, bb10
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
br bb4(%3 : $X, %4 : $X)
|
|
|
|
bb4(%8 : @guaranteed $X, %9 : @owned $X):
|
|
cond_br undef, bb5, bb8
|
|
|
|
bb5:
|
|
br bb6
|
|
|
|
bb6:
|
|
br bb7
|
|
|
|
bb7:
|
|
%13 = function_ref @getX : $@convention(thin) () -> @owned X
|
|
%14 = apply %13() : $@convention(thin) () -> @owned X
|
|
end_borrow %8 : $X
|
|
destroy_value %9 : $X
|
|
%17 = begin_borrow %14 : $X
|
|
br bb4(%17 : $X, %14 : $X)
|
|
|
|
bb8:
|
|
br bb9
|
|
|
|
bb9:
|
|
br bb1(%8 : $X, %9 : $X)
|
|
|
|
bb10:
|
|
%23 = tuple ()
|
|
end_borrow %3 : $X
|
|
destroy_value %4 : $X
|
|
return %23 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @nohoist_destroy_over_endborrow_nonadjacent_reborrow : {{.*}} {
|
|
// CHECK: end_borrow {{%[^,]+}}
|
|
// CHECK: destroy_value {{%[^,]+}}
|
|
// CHECK-LABEL: } // end sil function 'nohoist_destroy_over_endborrow_nonadjacent_reborrow'
|
|
sil [ossa] @nohoist_destroy_over_endborrow_nonadjacent_reborrow : $@convention(thin) (@owned X) -> () {
|
|
entry(%value_1 : @owned $X):
|
|
%lifetime_1 = begin_borrow %value_1 : $X
|
|
br consume_and_reborrow(%lifetime_1 : $X, %value_1 : $X)
|
|
|
|
consume_and_reborrow(%lifetime_2 : @guaranteed $X, %value_2 : @owned $X):
|
|
br reborrow_only(%lifetime_2 : $X)
|
|
|
|
reborrow_only(%lifetime_3 : @guaranteed $X):
|
|
br exit
|
|
|
|
exit:
|
|
%retval = tuple ()
|
|
end_borrow %lifetime_3 : $X
|
|
destroy_value %value_2 : $X
|
|
return %retval : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @nohoist_destroy_over_endborrow_nonadjacent_reborrow_2 : {{.*}} {
|
|
// CHECK: end_borrow {{%[^,]+}}
|
|
// CHECK: destroy_value {{%[^,]+}}
|
|
// CHECK-LABEL: } // end sil function 'nohoist_destroy_over_endborrow_nonadjacent_reborrow_2'
|
|
sil [ossa] @nohoist_destroy_over_endborrow_nonadjacent_reborrow_2 : $@convention(thin) (@owned X) -> () {
|
|
entry(%value_1 : @owned $X):
|
|
%lifetime_1 = begin_borrow %value_1 : $X
|
|
br consume_and_reborrow(%lifetime_1 : $X, %value_1 : $X)
|
|
|
|
consume_and_reborrow(%lifetime_2 : @guaranteed $X, %value_2 : @owned $X):
|
|
br reborrow_only_1(%lifetime_2 : $X)
|
|
|
|
reborrow_only_1(%lifetime_3 : @guaranteed $X):
|
|
br reborrow_only_2(%lifetime_3 : $X)
|
|
|
|
reborrow_only_2(%lifetime_4 : @guaranteed $X):
|
|
br exit
|
|
|
|
exit:
|
|
%retval = tuple ()
|
|
end_borrow %lifetime_4 : $X
|
|
destroy_value %value_2 : $X
|
|
return %retval : $()
|
|
}
|
|
|
|
// The owned phi %5 has an inner adjacent phi %4, so its end_borrow is
|
|
// considered a use of the owned lifetime. The end_borrow/destroy
|
|
// maintain their position.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @reborrow_adjacent_to_ownedphi : $@convention(thin) (@owned X) -> () {
|
|
// CHECK: end_borrow
|
|
// CHECK: destroy_value
|
|
sil [ossa] @reborrow_adjacent_to_ownedphi : $@convention(thin) (@owned X) -> () {
|
|
bb0(%0 : @owned $X):
|
|
%1 = begin_borrow %0 : $X
|
|
br bb1(%1 : $X, %0 : $X)
|
|
|
|
bb1(%4 : @guaranteed $X, %5 : @owned $X):
|
|
end_borrow %4 : $X
|
|
destroy_value %5 : $X
|
|
%23 = tuple ()
|
|
return %23 : $()
|
|
}
|
|
|
|
// The owned phi %5 has an inner adjacent phi %4, so its reborrow use,
|
|
// %6 is considered a use of the owned value. Since %5 is also
|
|
// consumed by an owned phi in bb2, the owned lifetime extends no further.
|
|
//
|
|
// Lifetime analysis is successful; the copy/destroy in bb1 are removed. The
|
|
// end_borrow/destroy maintain their position.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @reborrow_adjacent_to_consume : $@convention(thin) () -> () {
|
|
// CHECK: bb1(%{{.*}} : @reborrow $X, %{{.*}} : @owned $X):
|
|
// CHECK-NEXT: borrowed
|
|
// CHECK-NEXT: br bb2
|
|
// CHECK: bb2(%{{.*}} : @reborrow $X, %{{.*}} : @owned $X):
|
|
// CHECK-NEXT: borrowed
|
|
// CHECK-NEXT: end_borrow
|
|
// CHECK-NEXT: destroy_value
|
|
sil [ossa] @reborrow_adjacent_to_consume : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = apply undef() : $@convention(thin) () -> (@owned X)
|
|
%1 = begin_borrow %0 : $X
|
|
br bb1(%1 : $X, %0 : $X)
|
|
|
|
bb1(%4 : @guaranteed $X, %5 : @owned $X):
|
|
%copy = copy_value %5 : $X
|
|
destroy_value %copy : $X
|
|
br bb2(%4 : $X, %5 : $X)
|
|
|
|
bb2(%6 : @guaranteed $X, %7 : @owned $X):
|
|
end_borrow %6 : $X
|
|
destroy_value %7 : $X
|
|
%23 = tuple ()
|
|
return %23 : $()
|
|
}
|
|
|
|
// The owned phi %5 has an inner adjacent phi %4, so its reborrow use,
|
|
// %6 is considered a use of the owned value. Since %5 is not consumed
|
|
// by an owned phi in bb2, the owned lifetime extends to the uses of
|
|
// the second reborrow, %6. The copy/destroy in bb2 are removed. The
|
|
// end_borrow/destroy maintain their position.
|
|
//
|
|
// Lifetime analysis is successful; the copy/destroy in bb2 are removed.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @reborrow_no_adjacent_consume : $@convention(thin) () -> () {
|
|
// CHECK: bb1(%{{.*}} : @reborrow $X, %{{.*}} : @owned $X):
|
|
// CHECK-NEXT: borrowed
|
|
// CHECK-NEXT: br bb2
|
|
// CHECK: bb2(%{{.*}} : @reborrow $X):
|
|
// CHECK-NEXT: borrowed
|
|
// CHECK-NEXT: end_borrow
|
|
// CHECK-NEXT: destroy_value
|
|
sil [ossa] @reborrow_no_adjacent_consume : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = apply undef() : $@convention(thin) () -> (@owned X)
|
|
%1 = begin_borrow %0 : $X
|
|
br bb1(%1 : $X, %0 : $X)
|
|
|
|
bb1(%4 : @guaranteed $X, %5 : @owned $X):
|
|
br bb2(%4 : $X)
|
|
|
|
bb2(%6 : @guaranteed $X):
|
|
%copy = copy_value %5 : $X
|
|
destroy_value %copy : $X
|
|
end_borrow %6 : $X
|
|
destroy_value %5 : $X
|
|
%23 = tuple ()
|
|
return %23 : $()
|
|
}
|