Skip to content

Commit 0a18c62

Browse files
committed
[DCE] Keep outer borrows alive when reborrowing.
Previously, when encountering a borrow of a guaranteed value, the end_borrows of that reborrow were marked alive. Only doing that enables end_borrows of the outer borrow scope can be marked as dead. The result is that uses of the reborrowed value (including its end_borrow) can outstrip the outer borrow scope, which is illegal. Here, the outer borrow scope's end_borrows are marked alive. To do that, the originally borrowed values have to be identified via findGuaranteedReferenceRoots.
1 parent 30a159b commit 0a18c62

File tree

2 files changed

+230
-7
lines changed

2 files changed

+230
-7
lines changed

lib/SILOptimizer/Transforms/DeadCodeElimination.cpp

+12-7
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,19 @@ void DCE::markLive() {
284284
// Nested borrow handling can be complex in the presence of reborrows.
285285
// So it is not handled currently.
286286
auto *borrowInst = cast<BeginBorrowInst>(&I);
287-
if (borrowInst->getOperand().getOwnershipKind() !=
288-
OwnershipKind::Owned) {
287+
if (borrowInst->getOperand().getOwnershipKind() ==
288+
OwnershipKind::Guaranteed) {
289289
markInstructionLive(borrowInst);
290-
// Visit all end_borrows and mark them live
291-
visitTransitiveEndBorrows(BorrowedValue(borrowInst),
292-
[&](EndBorrowInst *endBorrow) {
293-
markInstructionLive(endBorrow);
294-
});
290+
// Visit the end_borrows of all the borrow scopes that this
291+
// begin_borrow could be borrowing.
292+
SmallVector<SILValue, 4> roots;
293+
findGuaranteedReferenceRoots(borrowInst->getOperand(), roots);
294+
for (auto root : roots) {
295+
visitTransitiveEndBorrows(BorrowedValue(root),
296+
[&](EndBorrowInst *endBorrow) {
297+
markInstructionLive(endBorrow);
298+
});
299+
}
295300
continue;
296301
}
297302
// If not populate reborrowDependencies for this borrow

test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil

+218
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ class Klass {
99

1010
}
1111

12+
enum EitherNoneOrAnyObject {
13+
case none
14+
case any(AnyObject)
15+
}
16+
1217
struct NonTrivialStruct {
1318
var val:Klass
1419
}
@@ -720,3 +725,216 @@ bb3(%original : @owned $Klass, %lifetime : @guaranteed $Klass):
720725
%result = tuple ()
721726
return %result : $()
722727
}
728+
729+
// CHECK-LABEL: sil [ossa] @borrow_none : {{.*}} {
730+
// CHECK: {{bb[0-9]+}}:
731+
// CHECK: [[INSTANCE:%[^,]+]] = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt
732+
// CHECK: switch_enum [[INSTANCE]] : $FakeOptional<Klass>, case #FakeOptional.some!enumelt: [[WORK:bb[0-9]+]], case #FakeOptional.none!enumelt: [[TO_EXIT:bb[0-9]+]], forwarding: @guaranteed
733+
// CHECK: [[TO_EXIT]]:
734+
// CHECK: br [[EXIT:bb[0-9]+]]
735+
// CHECK: [[WORK]]([[PAYLOAD:%[^,]+]] : @guaranteed $Klass):
736+
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [[PAYLOAD]]
737+
// CHECK: end_borrow [[LIFETIME]]
738+
// CHECK: br [[EXIT]]
739+
// CHECK: [[EXIT]]:
740+
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
741+
// CHECK: return [[RETVAL]] : $()
742+
// CHECK-LABEL: } // end sil function 'borrow_none'
743+
sil [ossa] @borrow_none : $@convention(thin) () -> () {
744+
entry:
745+
%outer_lifetime_1 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt
746+
switch_enum %outer_lifetime_1 : $FakeOptional<Klass>, case #FakeOptional.some!enumelt: work, case #FakeOptional.none!enumelt: to_exit, forwarding: @guaranteed
747+
to_exit:
748+
br exit
749+
work(%outer_lifetime_2 : @guaranteed $Klass):
750+
%inner_lifetime_1 = begin_borrow %outer_lifetime_2 : $Klass
751+
end_borrow %inner_lifetime_1 : $Klass
752+
br exit
753+
exit:
754+
%retval = tuple ()
755+
return %retval : $()
756+
}
757+
758+
// CHECK-LABEL: sil [ossa] @reborrowed_begin_borrow : {{.*}} {
759+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $Klass):
760+
// CHECK: [[OUTER_LIFETIME_1:%[^,]+]] = begin_borrow [[INSTANCE]]
761+
// CHECK: br [[EXIT:bb[0-9]+]]([[OUTER_LIFETIME_1]] : $Klass)
762+
// CHECK: [[EXIT]]([[OUTER_LIFETIME_2:%[^,]+]] : @guaranteed $Klass):
763+
// CHECK: end_borrow [[OUTER_LIFETIME_2]]
764+
// CHECK: destroy_value [[INSTANCE]]
765+
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
766+
// CHECK: return [[RETVAL]]
767+
// CHECK-LABEL: } // end sil function 'reborrowed_begin_borrow'
768+
sil [ossa] @reborrowed_begin_borrow : $@convention(thin) (@owned Klass) -> () {
769+
entry(%instance : @owned $Klass):
770+
%outer_lifetime_1 = begin_borrow %instance : $Klass
771+
%inner_lifetime_1 = begin_borrow %outer_lifetime_1 : $Klass
772+
br exit(%outer_lifetime_1 : $Klass, %inner_lifetime_1 : $Klass)
773+
exit(%outer_lifetime_2 : @guaranteed $Klass, %inner_lifetime_2 : @guaranteed $Klass):
774+
end_borrow %inner_lifetime_2 : $Klass
775+
end_borrow %outer_lifetime_2 : $Klass
776+
destroy_value %instance : $Klass
777+
%retval = tuple ()
778+
return %retval : $()
779+
}
780+
781+
// CHECK-LABEL: sil [ossa] @reborrowed_guaranteed_phi : {{.*}} {
782+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $Klass):
783+
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [[INSTANCE]]
784+
// CHECK: br [[WORK:bb[0-9]+]]([[LIFETIME]] : $Klass)
785+
// CHECK: [[WORK]]([[LIFETIME_1:%[^,]+]] : @guaranteed $Klass):
786+
// CHECK: br [[EXIT:bb[0-9]+]]([[LIFETIME_1]] : $Klass)
787+
// CHECK: [[EXIT]]([[LIFETIME_2:%[^,]+]] : @guaranteed $Klass):
788+
// CHECK: end_borrow [[LIFETIME_2]]
789+
// CHECK: destroy_value [[INSTANCE]]
790+
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
791+
// CHECK: return [[RETVAL]] : $()
792+
// CHECK-LABEL: } // end sil function 'reborrowed_guaranteed_phi'
793+
sil [ossa] @reborrowed_guaranteed_phi : $@convention(thin) (@owned Klass) -> () {
794+
entry(%instance : @owned $Klass):
795+
%outer_lifetime_1 = begin_borrow %instance : $Klass
796+
br work(%outer_lifetime_1 : $Klass)
797+
work(%outer_lifetime_2 : @guaranteed $Klass):
798+
%inner_lifetime_1 = begin_borrow %outer_lifetime_2 : $Klass
799+
br exit(%outer_lifetime_2 : $Klass, %inner_lifetime_1 : $Klass)
800+
exit(%outer_lifetime_3 : @guaranteed $Klass, %inner_lifetime_2 : @guaranteed $Klass):
801+
end_borrow %inner_lifetime_2 : $Klass
802+
end_borrow %outer_lifetime_3 : $Klass
803+
destroy_value %instance : $Klass
804+
%retval = tuple ()
805+
return %retval : $()
806+
}
807+
808+
// CHECK-LABEL: sil [ossa] @reborrow_guaranteed_phi2 : {{.*}} {
809+
// CHECK: {{bb[0-9]+}}([[EITHER:%[^,]+]] : @owned $EitherNoneOrAnyObject):
810+
// CHECK: [[BORROW_EITHER:%[^,]+]] = begin_borrow [[EITHER]]
811+
// CHECK: switch_enum [[BORROW_EITHER]] : $EitherNoneOrAnyObject, case #EitherNoneOrAnyObject.none!enumelt: [[NONE_BLOCK:bb[0-9]+]], case #EitherNoneOrAnyObject.any!enumelt: [[SOME_BLOCK:bb[0-9]+]]
812+
// CHECK: [[SOME_BLOCK]]([[PAYLOAD:%[^,]+]] : @guaranteed $AnyObject):
813+
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [[PAYLOAD]]
814+
// CHECK: end_borrow [[LIFETIME]]
815+
// CHECK: end_borrow [[BORROW_EITHER]]
816+
// CHECK: destroy_value [[EITHER]]
817+
// CHECK: br [[EXIT:bb[0-9]+]]
818+
// CHECK: [[NONE_BLOCK]]:
819+
// CHECK: end_borrow [[BORROW_EITHER]]
820+
// CHECK: destroy_value [[EITHER]]
821+
// CHECK: br [[EXIT]]
822+
// CHECK: [[EXIT]]:
823+
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
824+
// CHECK: return [[RETVAL]]
825+
// CHECK-LABEL: } // end sil function 'reborrow_guaranteed_phi2'
826+
sil [ossa] @reborrow_guaranteed_phi2 : $@convention(thin) (@owned EitherNoneOrAnyObject) -> () {
827+
entry(%0 : @owned $EitherNoneOrAnyObject):
828+
%borrow_either = begin_borrow %0 : $EitherNoneOrAnyObject
829+
switch_enum %borrow_either : $EitherNoneOrAnyObject, case #EitherNoneOrAnyObject.none!enumelt: none_block, case #EitherNoneOrAnyObject.any!enumelt: any_block
830+
any_block(%borrow : @guaranteed $AnyObject):
831+
%2 = begin_borrow %borrow : $AnyObject
832+
end_borrow %2 : $AnyObject
833+
end_borrow %borrow_either : $EitherNoneOrAnyObject
834+
destroy_value %0 : $EitherNoneOrAnyObject
835+
br exit
836+
none_block:
837+
end_borrow %borrow_either : $EitherNoneOrAnyObject
838+
destroy_value %0 : $EitherNoneOrAnyObject
839+
br exit
840+
exit:
841+
%retval = tuple ()
842+
return %retval : $()
843+
}
844+
845+
// CHECK-LABEL: sil [ossa] @reborrow_load_borrow : {{.*}} {
846+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : $*Klass):
847+
// CHECK: [[LIFETIME:%[^,]+]] = load_borrow [[INSTANCE]]
848+
// CHECK: br [[BASIC_BLOCK1:bb[0-9]+]]([[LIFETIME]] : $Klass)
849+
// CHECK: [[BASIC_BLOCK1]]([[LIFETIME_2:%[^,]+]] : @guaranteed $Klass):
850+
// CHECK: end_borrow [[LIFETIME_2]]
851+
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
852+
// CHECK: return [[RETVAL]]
853+
// CHECK-LABEL: } // end sil function 'reborrow_load_borrow'
854+
sil [ossa] @reborrow_load_borrow : $@convention(method) (@in Klass) -> () {
855+
bb0(%0 : $*Klass):
856+
%1 = load_borrow %0 : $*Klass
857+
%2 = begin_borrow %1 : $Klass
858+
br bb1(%1 : $Klass, %2 : $Klass)
859+
bb1(%4 : @guaranteed $Klass, %5 : @guaranteed $Klass):
860+
end_borrow %5 : $Klass
861+
end_borrow %4 : $Klass
862+
%8 = tuple ()
863+
return %8 : $()
864+
}
865+
866+
// CHECK-LABEL: sil [ossa] @reborrow_load_borrow2 : {{.*}} {
867+
// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] : $*Klass):
868+
// CHECK: [[LIFETIME:%[^,]+]] = load_borrow [[ADDR]] : $*Klass
869+
// CHECK: br [[EXIT:bb[0-9]+]]([[LIFETIME]] : $Klass)
870+
// CHECK: [[EXIT]]([[LIFETIME_2:%[^,]+]] : @guaranteed $Klass):
871+
// CHECK: end_borrow [[LIFETIME_2]] : $Klass
872+
// CHECK: [[EXIT:%[^,]+]] = tuple ()
873+
// CHECK: return [[EXIT]] : $()
874+
// CHECK-LABEL: } // end sil function 'reborrow_load_borrow2'
875+
sil [ossa] @reborrow_load_borrow2 : $@convention(method) (@in Klass) -> () {
876+
bb0(%0 : $*Klass):
877+
%1 = load_borrow %0 : $*Klass
878+
%2 = begin_borrow %1 : $Klass
879+
br bb1(%2 : $Klass, %1 : $Klass)
880+
bb1(%4 : @guaranteed $Klass, %5 : @guaranteed $Klass):
881+
end_borrow %4 : $Klass
882+
end_borrow %5 : $Klass
883+
%8 = tuple ()
884+
return %8 : $()
885+
}
886+
887+
888+
// CHECK-LABEL: sil [ossa] @borrow_guaranteed_tuple : {{.*}} {
889+
// CHECK: {{bb[0-9]+}}([[INSTANCE_1:%[^,]+]] : @owned $Klass, [[INSTANCE_2:%[^,]+]] : @owned $Klass):
890+
// CHECK: [[LIFETIME_1_1:%[^,]+]] = begin_borrow [[INSTANCE_1]]
891+
// CHECK: [[LIFETIME_2_1:%[^,]+]] = begin_borrow [[INSTANCE_2]]
892+
// CHECK: br [[EXIT:bb[0-9]+]]([[LIFETIME_1_1]] : $Klass, [[LIFETIME_2_1]] : $Klass)
893+
// CHECK: [[EXIT]]([[LIFETIME_1_2:%[^,]+]] : @guaranteed $Klass, [[LIFETIME_2_2:%[^,]+]] : @guaranteed $Klass):
894+
// CHECK: end_borrow [[LIFETIME_2_2]]
895+
// CHECK: destroy_value [[INSTANCE_2]]
896+
// CHECK: end_borrow [[LIFETIME_1_2]]
897+
// CHECK: destroy_value [[INSTANCE_1]]
898+
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
899+
// CHECK: return [[RETVAL]] : $()
900+
// CHECK-LABEL: } // end sil function 'borrow_guaranteed_tuple'
901+
sil [ossa] @borrow_guaranteed_tuple : $@convention(thin) (@owned Klass, @owned Klass) -> () {
902+
entry(%instance_1 : @owned $Klass, %instance_2 : @owned $Klass):
903+
%lifetime_1_1 = begin_borrow %instance_1 : $Klass
904+
%lifetime_2_1 = begin_borrow %instance_2 : $Klass
905+
%tuple = tuple $(Klass, Klass) (%lifetime_1_1, %lifetime_2_1)
906+
%tuple_lifetime_1 = begin_borrow %tuple : $(Klass, Klass)
907+
br exit(%lifetime_1_1 : $Klass, %lifetime_2_1 : $Klass, %tuple_lifetime_1 : $(Klass, Klass))
908+
exit(%lifetime_1_2 : @guaranteed $Klass, %lifetime_2_2 : @guaranteed $Klass, %tuple_lifetime_2 : @guaranteed $(Klass, Klass)):
909+
end_borrow %tuple_lifetime_2 : $(Klass, Klass)
910+
end_borrow %lifetime_2_2 : $Klass
911+
destroy_value %instance_2 : $Klass
912+
end_borrow %lifetime_1_2 : $Klass
913+
destroy_value %instance_1 : $Klass
914+
%retval = tuple ()
915+
return %retval : $()
916+
}
917+
918+
// CHECK-LABEL: sil [ossa] @borrow_guaranteed_struct : {{.*}} {
919+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $Klass):
920+
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [[INSTANCE]]
921+
// CHECK: br bb1([[LIFETIME]] : $Klass)
922+
// CHECK: bb1([[LIFETIME_2:%[^,]+]] : @guaranteed $Klass):
923+
// CHECK: end_borrow [[LIFETIME_2]]
924+
// CHECK: destroy_value [[INSTANCE]]
925+
// CHECK: [[RETVAL:%[^,]+]] = tuple ()
926+
// CHECK: return [[RETVAL]] : $()
927+
// CHECK-LABEL: } // end sil function 'borrow_guaranteed_struct'
928+
sil [ossa] @borrow_guaranteed_struct : $@convention(thin) (@owned Klass) -> () {
929+
entry(%instance_1 : @owned $Klass):
930+
%lifetime_1 = begin_borrow %instance_1 : $Klass
931+
%struct = struct $NonTrivialStruct (%lifetime_1 : $Klass)
932+
%struct_lifetime_1 = begin_borrow %struct : $NonTrivialStruct
933+
br exit(%lifetime_1 : $Klass, %struct_lifetime_1 : $NonTrivialStruct)
934+
exit(%lifetime_2 : @guaranteed $Klass, %struct_lifetime_2 : @guaranteed $NonTrivialStruct):
935+
end_borrow %struct_lifetime_2 : $NonTrivialStruct
936+
end_borrow %lifetime_2 : $Klass
937+
destroy_value %instance_1 : $Klass
938+
%retval = tuple ()
939+
return %retval : $()
940+
}

0 commit comments

Comments
 (0)