Skip to content

Commit 0336167

Browse files
Merge pull request #39939 from nate-chandler/dce/keep-outer-borrow-scope-alive-when-reborrowing
[DCE] Keep outer borrows alive when reborrowing.
2 parents 8e48fd6 + 0a18c62 commit 0336167

File tree

4 files changed

+299
-15
lines changed

4 files changed

+299
-15
lines changed

include/swift/SIL/MemAccessUtils.h

+5
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ SILValue findReferenceRoot(SILValue ref);
204204
/// Find the first owned root of the reference.
205205
SILValue findOwnershipReferenceRoot(SILValue ref);
206206

207+
/// Look through all ownership forwarding instructions to find the values which
208+
/// were originally borrowed.
209+
void findGuaranteedReferenceRoots(SILValue value,
210+
SmallVectorImpl<SILValue> &roots);
211+
207212
/// Find the aggregate containing the first owned root of the
208213
/// reference. Identical to findOwnershipReferenceRoot, but looks through
209214
/// struct_extract, tuple_extract, etc.

lib/SIL/Utils/MemAccessUtils.cpp

+59-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
#define DEBUG_TYPE "sil-access-utils"
1414

1515
#include "swift/SIL/MemAccessUtils.h"
16-
#include "swift/SIL/SILModule.h"
17-
#include "swift/SIL/SILUndef.h"
18-
#include "swift/SIL/DynamicCasts.h"
16+
#include "swift/Basic/GraphNodeWorklist.h"
1917
#include "swift/SIL/Consumption.h"
18+
#include "swift/SIL/DynamicCasts.h"
2019
#include "swift/SIL/SILInstruction.h"
20+
#include "swift/SIL/SILModule.h"
21+
#include "swift/SIL/SILUndef.h"
2122
#include "llvm/Support/Debug.h"
2223

2324
using namespace swift;
@@ -800,6 +801,61 @@ SILValue swift::findOwnershipReferenceRoot(SILValue ref) {
800801
return ref;
801802
}
802803

804+
void swift::findGuaranteedReferenceRoots(SILValue value,
805+
SmallVectorImpl<SILValue> &roots) {
806+
GraphNodeWorklist<SILValue, 4> worklist;
807+
auto addAllOperandsToWorklist = [&worklist](SILInstruction *inst) -> bool {
808+
if (inst->getNumOperands() > 0) {
809+
for (auto operand : inst->getOperandValues()) {
810+
worklist.insert(operand);
811+
}
812+
return true;
813+
}
814+
return false;
815+
};
816+
worklist.initialize(value);
817+
while (auto value = worklist.pop()) {
818+
if (auto *arg = dyn_cast<SILPhiArgument>(value)) {
819+
if (auto *terminator = arg->getSingleTerminator()) {
820+
worklist.insert(terminator->getOperand(arg->getIndex()));
821+
continue;
822+
}
823+
} else if (auto *inst = value->getDefiningInstruction()) {
824+
if (auto *result =
825+
dyn_cast<FirstArgOwnershipForwardingSingleValueInst>(inst)) {
826+
if (result->getNumOperands() > 0) {
827+
worklist.insert(result->getOperand(0));
828+
continue;
829+
}
830+
} else if (auto *result =
831+
dyn_cast<AllArgOwnershipForwardingSingleValueInst>(inst)) {
832+
if (addAllOperandsToWorklist(result)) {
833+
continue;
834+
}
835+
} else if (auto *result = dyn_cast<OwnershipForwardingTermInst>(inst)) {
836+
assert(false && "value defined by a terminator?!");
837+
} else if (auto *result =
838+
dyn_cast<OwnershipForwardingConversionInst>(inst)) {
839+
worklist.insert(result->getConverted());
840+
continue;
841+
} else if (auto *result =
842+
dyn_cast<OwnershipForwardingSelectEnumInstBase>(inst)) {
843+
if (addAllOperandsToWorklist(result)) {
844+
continue;
845+
}
846+
} else if (auto *result =
847+
dyn_cast<OwnershipForwardingMultipleValueInstruction>(
848+
inst)) {
849+
if (addAllOperandsToWorklist(result)) {
850+
continue;
851+
}
852+
}
853+
}
854+
if (value.getOwnershipKind() == OwnershipKind::Guaranteed)
855+
roots.push_back(value);
856+
}
857+
}
858+
803859
/// Find the first owned aggregate containing the reference, or simply the
804860
/// reference root if no aggregate is found.
805861
///

lib/SILOptimizer/Transforms/DeadCodeElimination.cpp

+13-8
Original file line numberDiff line numberDiff line change
@@ -284,18 +284,23 @@ 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
298-
findReborrowDependencies(cast<BeginBorrowInst>(&I));
303+
findReborrowDependencies(borrowInst);
299304
break;
300305
}
301306
default:

test/SILOptimizer/dead_code_elimination_nontrivial_ossa.sil

+222-4
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
}
@@ -658,7 +663,7 @@ bb4(%3 : @owned $Klass):
658663
sil [ossa] @looping_borrow : $@convention(thin) () -> () {
659664
entry:
660665
%instance_1 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt
661-
%lifetime_1 = begin_borrow [lexical] %instance_1 : $FakeOptional<Klass>
666+
%lifetime_1 = begin_borrow %instance_1 : $FakeOptional<Klass>
662667
br loop_entry(%instance_1 : $FakeOptional<Klass>, %lifetime_1 : $FakeOptional<Klass>)
663668

664669
loop_entry(%18 : @owned $FakeOptional<Klass>, %19 : @guaranteed $FakeOptional<Klass>):
@@ -684,11 +689,11 @@ exit:
684689
// CHECK: bb0({{%[^,]+}} : $Builtin.Int1, [[INSTANCE_1:%[^,]+]] : @owned $Klass, {{%[^,]+}} : @owned $Klass):
685690
// CHECK: [[LIFETIME_1:%[^,]+]] = begin_borrow [[INSTANCE_1]]
686691
// CHECK: copy_value [[LIFETIME_1]]
687-
// CHECK: cond_br {{%[^,]+}}, [[BASIC_BLOCK1:bb[0-9]+]], [[BASIC_BLOCK2:bb[0-9]+]]
688-
// CHECK: [[BASIC_BLOCK1]]:
692+
// CHECK: cond_br {{%[^,]+}}, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
693+
// CHECK: [[LEFT]]:
689694
// CHECK: end_borrow [[LIFETIME_1]]
690695
// CHECK: destroy_value [[INSTANCE_1]]
691-
// CHECK: [[BASIC_BLOCK2]]:
696+
// CHECK: [[RIGHT]]:
692697
// CHECK: end_borrow [[LIFETIME_1]]
693698
// CHECK: destroy_value [[INSTANCE_1]]
694699
// CHECK-LABEL: } // end sil function 'add_end_borrow_after_destroy_value'
@@ -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)