13
13
#define DEBUG_TYPE " sil-semantic-arc-opts"
14
14
#include " swift/Basic/BlotSetVector.h"
15
15
#include " swift/Basic/FrozenMultiMap.h"
16
+ #include " swift/Basic/MultiMapCache.h"
16
17
#include " swift/Basic/STLExtras.h"
17
18
#include " swift/SIL/BasicBlockUtils.h"
18
19
#include " swift/SIL/DebugUtils.h"
@@ -477,35 +478,13 @@ LiveRange::hasUnknownConsumingUse(bool assumingAtFixPoint) const {
477
478
// Address Written To Analysis
478
479
// ===----------------------------------------------------------------------===//
479
480
480
- namespace {
481
-
482
- // / A simple analysis that checks if a specific def (in our case an inout
483
- // / argument) is ever written to. This is conservative, local and processes
484
- // / recursively downwards from def->use.
485
- struct IsAddressWrittenToDefUseAnalysis {
486
- llvm::SmallDenseMap<SILValue, bool , 8 > isWrittenToCache;
487
-
488
- bool operator ()(SILValue value) {
489
- auto iter = isWrittenToCache.try_emplace (value, true );
490
-
491
- // If we are already in the map, just return that.
492
- if (!iter.second )
493
- return iter.first ->second ;
494
-
495
- // Otherwise, compute our value, cache it and return.
496
- bool result = isWrittenToHelper (value);
497
- iter.first ->second = result;
498
- return result;
499
- }
500
-
501
- private:
502
- bool isWrittenToHelper (SILValue value);
503
- };
504
-
505
- } // end anonymous namespace
506
-
507
- bool IsAddressWrittenToDefUseAnalysis::isWrittenToHelper (SILValue initialValue) {
481
+ // / Returns true if we were able to ascertain that either the initialValue has
482
+ // / no write uses or all of the write uses were writes that we could understand.
483
+ static bool
484
+ constructCacheValue (SILValue initialValue,
485
+ SmallVectorImpl<Operand *> &wellBehavedWriteAccumulator) {
508
486
SmallVector<Operand *, 8 > worklist (initialValue->getUses ());
487
+
509
488
while (!worklist.empty ()) {
510
489
auto *op = worklist.pop_back_val ();
511
490
SILInstruction *user = op->getUser ();
@@ -521,35 +500,41 @@ bool IsAddressWrittenToDefUseAnalysis::isWrittenToHelper(SILValue initialValue)
521
500
if (auto *oeai = dyn_cast<OpenExistentialAddrInst>(user)) {
522
501
// Mutable access!
523
502
if (oeai->getAccessKind () != OpenedExistentialAccess::Immutable) {
524
- return true ;
503
+ wellBehavedWriteAccumulator. push_back (op) ;
525
504
}
526
505
527
506
// Otherwise, look through it and continue.
528
507
llvm::copy (oeai->getUses (), std::back_inserter (worklist));
529
508
continue ;
530
509
}
531
510
511
+ // Add any destroy_addrs to the resultAccumulator.
512
+ if (isa<DestroyAddrInst>(user)) {
513
+ wellBehavedWriteAccumulator.push_back (op);
514
+ continue ;
515
+ }
516
+
532
517
// load_borrow and incidental uses are fine as well.
533
518
if (isa<LoadBorrowInst>(user) || isIncidentalUse (user)) {
534
519
continue ;
535
520
}
536
521
537
522
// Look through immutable begin_access.
538
523
if (auto *bai = dyn_cast<BeginAccessInst>(user)) {
539
- // If we do not have a read, return true .
524
+ // If we do not have a read, mark this as a write .
540
525
if (bai->getAccessKind () != SILAccessKind::Read) {
541
- return true ;
526
+ wellBehavedWriteAccumulator. push_back (op) ;
542
527
}
543
528
544
529
// Otherwise, add the users to the worklist and continue.
545
530
llvm::copy (bai->getUses (), std::back_inserter (worklist));
546
531
continue ;
547
532
}
548
533
549
- // As long as we do not have a load [take], we are fine .
534
+ // If we have a load, we just need to mark the load [take] as a write .
550
535
if (auto *li = dyn_cast<LoadInst>(user)) {
551
536
if (li->getOwnershipQualifier () == LoadOwnershipQualifier::Take) {
552
- return true ;
537
+ wellBehavedWriteAccumulator. push_back (op) ;
553
538
}
554
539
continue ;
555
540
}
@@ -559,41 +544,45 @@ bool IsAddressWrittenToDefUseAnalysis::isWrittenToHelper(SILValue initialValue)
559
544
// interprocedural analysis that we do not perform here.
560
545
if (auto fas = FullApplySite::isa (user)) {
561
546
if (fas.getArgumentConvention (*op) ==
562
- SILArgumentConvention::Indirect_In_Guaranteed)
547
+ SILArgumentConvention::Indirect_In_Guaranteed) {
563
548
continue ;
549
+ }
564
550
565
- // Otherwise, be conservative and return true.
566
- return true ;
551
+ // Otherwise, be conservative and return that we had a write that we did
552
+ // not understand.
553
+ return false ;
567
554
}
568
555
569
556
// Copy addr that read are just loads.
570
557
if (auto *cai = dyn_cast<CopyAddrInst>(user)) {
571
558
// If our value is the destination, this is a write.
572
559
if (cai->getDest () == op->get ()) {
573
- return true ;
560
+ wellBehavedWriteAccumulator.push_back (op);
561
+ continue ;
574
562
}
575
563
576
564
// Ok, so we are Src by process of elimination. Make sure we are not being
577
565
// taken.
578
566
if (cai->isTakeOfSrc ()) {
579
- return true ;
567
+ wellBehavedWriteAccumulator.push_back (op);
568
+ continue ;
580
569
}
581
570
582
571
// Otherwise, we are safe and can continue.
583
572
continue ;
584
573
}
585
574
586
575
// If we did not recognize the user, just return conservatively that it was
587
- // written to.
576
+ // written to in a way we did not understand .
588
577
LLVM_DEBUG (llvm::dbgs ()
589
578
<< " Function: " << user->getFunction ()->getName () << " \n " );
590
579
LLVM_DEBUG (llvm::dbgs () << " Value: " << op->get ());
591
580
LLVM_DEBUG (llvm::dbgs () << " Unknown instruction!: " << *user);
592
- return true ;
581
+ return false ;
593
582
}
594
583
595
584
// Ok, we finished our worklist and this address is not being written to.
596
- return false ;
585
+ return true ;
597
586
}
598
587
599
588
// ===----------------------------------------------------------------------===//
@@ -623,7 +612,7 @@ struct SemanticARCOptVisitor
623
612
SILFunction &F;
624
613
Optional<DeadEndBlocks> TheDeadEndBlocks;
625
614
ValueLifetimeAnalysis::Frontier lifetimeFrontier;
626
- IsAddressWrittenToDefUseAnalysis isAddressWrittenToDefUseAnalysis ;
615
+ SmallMultiMapCache<SILValue, Operand *> addressToExhaustiveWriteListCache ;
627
616
628
617
// / Are we assuming that we reached a fix point and are re-processing to
629
618
// / prepare to use the phiToIncomingValueMultiMap.
@@ -658,7 +647,8 @@ struct SemanticARCOptVisitor
658
647
using FrozenMultiMapRange =
659
648
decltype (joinedOwnedIntroducerToConsumedOperands)::PairToSecondEltRange;
660
649
661
- explicit SemanticARCOptVisitor (SILFunction &F) : F(F) {}
650
+ explicit SemanticARCOptVisitor (SILFunction &F)
651
+ : F(F), addressToExhaustiveWriteListCache(constructCacheValue) {}
662
652
663
653
DeadEndBlocks &getDeadEndBlocks () {
664
654
if (!TheDeadEndBlocks)
@@ -1663,7 +1653,8 @@ class StorageGuaranteesLoadVisitor
1663
1653
1664
1654
// If we have a modify, check if our value is /ever/ written to. If it is
1665
1655
// never actually written to, then we convert to a load_borrow.
1666
- return answer (ARCOpt.isAddressWrittenToDefUseAnalysis (access));
1656
+ auto result = ARCOpt.addressToExhaustiveWriteListCache .get (access);
1657
+ return answer (!result.hasValue () || result.getValue ().size ());
1667
1658
}
1668
1659
1669
1660
void visitArgumentAccess (SILFunctionArgument *arg) {
@@ -1676,7 +1667,8 @@ class StorageGuaranteesLoadVisitor
1676
1667
// If we have an inout parameter that isn't ever actually written to, return
1677
1668
// false.
1678
1669
if (arg->getKnownParameterInfo ().isIndirectMutating ()) {
1679
- return answer (ARCOpt.isAddressWrittenToDefUseAnalysis (arg));
1670
+ auto result = ARCOpt.addressToExhaustiveWriteListCache .get (arg);
1671
+ return answer (!result.hasValue () || result.getValue ().size ());
1680
1672
}
1681
1673
1682
1674
// TODO: This should be extended:
0 commit comments