Skip to content

Commit 171987a

Browse files
authored
Merge pull request #28620 from gottesmm/pr-2249363b68d
[semantic-arc] Reapply converting load [copy] -> load_borrow through casts
2 parents 179e3c2 + dce8b6a commit 171987a

File tree

2 files changed

+125
-51
lines changed

2 files changed

+125
-51
lines changed

lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp

Lines changed: 77 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/SILOptimizer/PassManager/Passes.h"
2727
#include "swift/SILOptimizer/PassManager/Transforms.h"
2828
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
29+
#include "swift/SILOptimizer/Utils/ValueLifetime.h"
2930
#include "llvm/ADT/SmallPtrSet.h"
3031
#include "llvm/ADT/SmallVector.h"
3132
#include "llvm/ADT/Statistic.h"
@@ -45,9 +46,9 @@ STATISTIC(NumLoadCopyConvertedToLoadBorrow,
4546
///
4647
/// Semantically this implies that a value is never passed off as +1 to memory
4748
/// or another function implying it can be used everywhere at +0.
48-
static bool isDeadLiveRange(
49-
SILValue v, SmallVectorImpl<DestroyValueInst *> &destroys,
50-
NullablePtr<SmallVectorImpl<SILInstruction *>> forwardingInsts = nullptr) {
49+
static bool
50+
isDeadLiveRange(SILValue v, SmallVectorImpl<SILInstruction *> &destroys,
51+
SmallVectorImpl<SILInstruction *> &forwardingInsts) {
5152
assert(v.getOwnershipKind() == ValueOwnershipKind::Owned);
5253
SmallVector<Operand *, 32> worklist(v->use_begin(), v->use_end());
5354
while (!worklist.empty()) {
@@ -85,8 +86,7 @@ static bool isDeadLiveRange(
8586
//
8687
// NOTE: Today we do not support TermInsts for simplicity... we /could/
8788
// support it though if we need to.
88-
if (forwardingInsts.isNull() || isa<TermInst>(user) ||
89-
!isGuaranteedForwardingInst(user) ||
89+
if (isa<TermInst>(user) || !isGuaranteedForwardingInst(user) ||
9090
1 != count_if(user->getOperandValues(
9191
true /*ignore type dependent operands*/),
9292
[&](SILValue v) {
@@ -99,7 +99,7 @@ static bool isDeadLiveRange(
9999
// Ok, this is a forwarding instruction whose ownership we can flip from
100100
// owned -> guaranteed. Visit its users recursively to see if the the
101101
// users force the live range to be alive.
102-
forwardingInsts.get()->push_back(user);
102+
forwardingInsts.push_back(user);
103103
for (SILValue v : user->getResults()) {
104104
if (v.getOwnershipKind() != ValueOwnershipKind::Owned)
105105
continue;
@@ -158,6 +158,7 @@ struct SemanticARCOptVisitor
158158

159159
SILFunction &F;
160160
Optional<DeadEndBlocks> TheDeadEndBlocks;
161+
ValueLifetimeAnalysis::Frontier lifetimeFrontier;
161162

162163
explicit SemanticARCOptVisitor(SILFunction &F) : F(F) {}
163164

@@ -370,6 +371,49 @@ bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) {
370371
return true;
371372
}
372373

374+
static void convertForwardingInstsFromOwnedToGuaranteed(
375+
SmallVectorImpl<SILInstruction *> &guaranteedForwardingInsts) {
376+
// Then change all of our guaranteed forwarding insts to have guaranteed
377+
// ownership kind instead of what ever they previously had (ignoring trivial
378+
// results);
379+
while (!guaranteedForwardingInsts.empty()) {
380+
auto *i = guaranteedForwardingInsts.pop_back_val();
381+
assert(i->hasResults());
382+
383+
for (SILValue result : i->getResults()) {
384+
if (auto *svi = dyn_cast<OwnershipForwardingSingleValueInst>(result)) {
385+
if (svi->getOwnershipKind() == ValueOwnershipKind::Owned) {
386+
svi->setOwnershipKind(ValueOwnershipKind::Guaranteed);
387+
}
388+
continue;
389+
}
390+
391+
if (auto *ofci = dyn_cast<OwnershipForwardingConversionInst>(result)) {
392+
if (ofci->getOwnershipKind() == ValueOwnershipKind::Owned) {
393+
ofci->setOwnershipKind(ValueOwnershipKind::Guaranteed);
394+
}
395+
continue;
396+
}
397+
398+
if (auto *sei = dyn_cast<OwnershipForwardingSelectEnumInstBase>(result)) {
399+
if (sei->getOwnershipKind() == ValueOwnershipKind::Owned) {
400+
sei->setOwnershipKind(ValueOwnershipKind::Guaranteed);
401+
}
402+
continue;
403+
}
404+
405+
if (auto *mvir = dyn_cast<MultipleValueInstructionResult>(result)) {
406+
if (mvir->getOwnershipKind() == ValueOwnershipKind::Owned) {
407+
mvir->setOwnershipKind(ValueOwnershipKind::Guaranteed);
408+
}
409+
continue;
410+
}
411+
412+
llvm_unreachable("unhandled forwarding instruction?!");
413+
}
414+
}
415+
}
416+
373417
// Eliminate a copy of a borrowed value, if:
374418
//
375419
// 1. All of the copies users do not consume the copy (and thus can accept a
@@ -417,9 +461,9 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
417461
// must be some consuming use that we either do not understand is /actually/
418462
// forwarding or a user that truly represents a necessary consume of the
419463
// value (e.x. storing into memory).
420-
SmallVector<DestroyValueInst *, 16> destroys;
464+
SmallVector<SILInstruction *, 16> destroys;
421465
SmallVector<SILInstruction *, 16> guaranteedForwardingInsts;
422-
if (!isDeadLiveRange(cvi, destroys, &guaranteedForwardingInsts))
466+
if (!isDeadLiveRange(cvi, destroys, guaranteedForwardingInsts))
423467
return false;
424468

425469
// Next check if we do not have any destroys of our copy_value and are
@@ -530,47 +574,8 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst
530574
}
531575

532576
eraseAndRAUWSingleValueInstruction(cvi, cvi->getOperand());
577+
convertForwardingInstsFromOwnedToGuaranteed(guaranteedForwardingInsts);
533578

534-
// Then change all of our guaranteed forwarding insts to have guaranteed
535-
// ownership kind instead of what ever they previously had (ignoring trivial
536-
// results);
537-
while (!guaranteedForwardingInsts.empty()) {
538-
auto *i = guaranteedForwardingInsts.pop_back_val();
539-
540-
assert(i->hasResults());
541-
542-
for (SILValue result : i->getResults()) {
543-
if (auto *svi = dyn_cast<OwnershipForwardingSingleValueInst>(result)) {
544-
if (svi->getOwnershipKind() == ValueOwnershipKind::Owned) {
545-
svi->setOwnershipKind(ValueOwnershipKind::Guaranteed);
546-
}
547-
continue;
548-
}
549-
550-
if (auto *ofci = dyn_cast<OwnershipForwardingConversionInst>(result)) {
551-
if (ofci->getOwnershipKind() == ValueOwnershipKind::Owned) {
552-
ofci->setOwnershipKind(ValueOwnershipKind::Guaranteed);
553-
}
554-
continue;
555-
}
556-
557-
if (auto *sei = dyn_cast<OwnershipForwardingSelectEnumInstBase>(result)) {
558-
if (sei->getOwnershipKind() == ValueOwnershipKind::Owned) {
559-
sei->setOwnershipKind(ValueOwnershipKind::Guaranteed);
560-
}
561-
continue;
562-
}
563-
564-
if (auto *mvir = dyn_cast<MultipleValueInstructionResult>(result)) {
565-
if (mvir->getOwnershipKind() == ValueOwnershipKind::Owned) {
566-
mvir->setOwnershipKind(ValueOwnershipKind::Guaranteed);
567-
}
568-
continue;
569-
}
570-
571-
llvm_unreachable("unhandled forwarding instruction?!");
572-
}
573-
}
574579
++NumEliminatedInsts;
575580
return true;
576581
}
@@ -812,8 +817,9 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
812817
// FIXME: We should consider if it is worth promoting a load [copy]
813818
// -> load_borrow if we can put a copy_value on a cold path and thus
814819
// eliminate RR traffic on a hot path.
815-
SmallVector<DestroyValueInst *, 32> destroyValues;
816-
if (!isDeadLiveRange(li, destroyValues))
820+
SmallVector<SILInstruction *, 32> destroyValues;
821+
SmallVector<SILInstruction *, 16> guaranteedForwardingInsts;
822+
if (!isDeadLiveRange(li, destroyValues, guaranteedForwardingInsts))
817823
return false;
818824

819825
// Then check if our address is ever written to. If it is, then we cannot use
@@ -831,14 +837,34 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
831837
// parameters, we can have multiple destroy_value along the same path. We need
832838
// to find the post-dominating block set of these destroy value to ensure that
833839
// we do not insert multiple end_borrow.
840+
assert(lifetimeFrontier.empty());
841+
ValueLifetimeAnalysis analysis(li, destroyValues);
842+
bool foundCriticalEdges = !analysis.computeFrontier(
843+
lifetimeFrontier, ValueLifetimeAnalysis::DontModifyCFG,
844+
&getDeadEndBlocks());
845+
(void)foundCriticalEdges;
846+
assert(!foundCriticalEdges);
847+
auto loc = RegularLocation::getAutoGeneratedLocation();
848+
while (!lifetimeFrontier.empty()) {
849+
auto *insertPoint = lifetimeFrontier.pop_back_val();
850+
SILBuilderWithScope builder(insertPoint);
851+
builder.createEndBorrow(loc, lbi);
852+
}
853+
854+
// Then delete all of our destroy_value.
834855
while (!destroyValues.empty()) {
835856
auto *dvi = destroyValues.pop_back_val();
836-
SILBuilderWithScope(dvi).createEndBorrow(dvi->getLoc(), lbi);
837857
eraseInstruction(dvi);
838858
++NumEliminatedInsts;
839859
}
840860

861+
// RAUW our other uses from the load to the load_borrow.
841862
eraseAndRAUWSingleValueInstruction(li, lbi);
863+
864+
// And then change the ownership all of our owned forwarding users to be
865+
// guaranteed.
866+
convertForwardingInstsFromOwnedToGuaranteed(guaranteedForwardingInsts);
867+
842868
++NumEliminatedInsts;
843869
++NumLoadCopyConvertedToLoadBorrow;
844870
return true;

test/SILOptimizer/semantic-arc-opts.sil

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct NativeObjectPair {
2323
sil @get_nativeobject_pair : $@convention(thin) () -> @owned NativeObjectPair
2424

2525
class Klass {}
26+
sil @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
2627

2728
struct MyInt {
2829
var value: Builtin.Int32
@@ -437,6 +438,28 @@ bb0(%x : @guaranteed $ClassLet):
437438
return undef : $()
438439
}
439440

441+
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_guaranteed_base_and_forwarding_uses :
442+
// CHECK: ref_element_addr
443+
// CHECK-NEXT: load_borrow
444+
// CHECK-NEXT: unchecked_ref_cast
445+
// CHECK-NEXT: apply
446+
// CHECK-NEXT: end_borrow
447+
// CHECK-NEXT: return
448+
// CHECK: } // end sil function 'dont_copy_let_properties_with_guaranteed_base_and_forwarding_uses'
449+
sil [ossa] @dont_copy_let_properties_with_guaranteed_base_and_forwarding_uses : $@convention(thin) (@guaranteed ClassLet) -> () {
450+
bb0(%x : @guaranteed $ClassLet):
451+
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
452+
453+
%p = ref_element_addr %x : $ClassLet, #ClassLet.aLet
454+
%v = load [copy] %p : $*Klass
455+
%c = unchecked_ref_cast %v : $Klass to $Klass
456+
%b = begin_borrow %c : $Klass
457+
apply %f(%b) : $@convention(thin) (@guaranteed Klass) -> ()
458+
end_borrow %b : $Klass
459+
destroy_value %c : $Klass
460+
return undef : $()
461+
}
462+
440463
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_guaranteed_upcast_base
441464
// CHECK: ref_element_addr
442465
// CHECK-NEXT: load_borrow
@@ -900,3 +923,28 @@ bb2:
900923
bb3:
901924
unreachable
902925
}
926+
927+
// Make sure that we put the end_borrow on the load_borrow, not LHS or RHS.
928+
//
929+
// CHECK-LABEL: sil [ossa] @destructure_load_copy_to_load_borrow : $@convention(thin) (@guaranteed ClassLet) -> () {
930+
// CHECK: bb0([[ARG:%.*]] :
931+
// CHECK: [[INTERIOR_POINTER:%.*]] = ref_element_addr [[ARG]]
932+
// CHECK: [[BORROWED_VAL:%.*]] = load_borrow [[INTERIOR_POINTER]]
933+
// CHECK: ([[LHS:%.*]], [[RHS:%.*]]) = destructure_tuple [[BORROWED_VAL]]
934+
// CHECK: apply {{%.*}}([[LHS]])
935+
// CHECK: apply {{%.*}}([[RHS]])
936+
// CHECK: end_borrow [[BORROWED_VAL]]
937+
// CHECK: } // end sil function 'destructure_load_copy_to_load_borrow'
938+
sil [ossa] @destructure_load_copy_to_load_borrow : $@convention(thin) (@guaranteed ClassLet) -> () {
939+
bb0(%0 : @guaranteed $ClassLet):
940+
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aLetTuple
941+
%2 = load [copy] %1 : $*(Klass, Klass)
942+
(%3, %4) = destructure_tuple %2 : $(Klass, Klass)
943+
%5 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
944+
%6 = apply %5(%3) : $@convention(thin) (@guaranteed Klass) -> ()
945+
%7 = apply %5(%4) : $@convention(thin) (@guaranteed Klass) -> ()
946+
destroy_value %3 : $Klass
947+
destroy_value %4 : $Klass
948+
%9999 = tuple()
949+
return %9999 : $()
950+
}

0 commit comments

Comments
 (0)