15
15
#include " swift/SIL/InstructionUtils.h"
16
16
#include " swift/SIL/MemAccessUtils.h"
17
17
#include " swift/SIL/SILVisitor.h"
18
+ #include " swift/SIL/OwnershipUtils.h"
19
+ #include " swift/SIL/BasicBlockBits.h"
18
20
#include " swift/SILOptimizer/Analysis/AliasAnalysis.h"
19
21
#include " swift/SILOptimizer/Analysis/EscapeAnalysis.h"
20
22
#include " swift/SILOptimizer/Analysis/SideEffectAnalysis.h"
@@ -28,6 +30,11 @@ using namespace swift;
28
30
// memory usage of this cache.
29
31
static const int MemoryBehaviorAnalysisMaxCacheSize = 16384 ;
30
32
33
+ // The maximum size of instsInImmutableScopes.
34
+ // Usually more than enough. But in corner cases it can grow quadratically (in
35
+ // case of many large nested immutable scopes).
36
+ static const int ImmutableScopeInstsMaxSize = 18384 ;
37
+
31
38
// ===----------------------------------------------------------------------===//
32
39
// Memory Behavior Implementation
33
40
// ===----------------------------------------------------------------------===//
@@ -536,12 +543,162 @@ AliasAnalysis::computeMemoryBehavior(SILInstruction *Inst, SILValue V) {
536
543
return Result;
537
544
}
538
545
546
+ // / If \p V is an address of an immutable memory, return the begin of the
547
+ // / scope where the memory can be considered to be immutable.
548
+ // /
549
+ // / This is either a ``begin_access [read]`` in case V is the result of the
550
+ // / begin_access or a projection of it.
551
+ // / Or it is the begin of a borrow scope (begin_borrow, load_borrow, a
552
+ // / guaranteed function argument) of an immutable copy-on-write buffer.
553
+ // / For example:
554
+ // / %b = begin_borrow %array_buffer
555
+ // / %V = ref_element_addr [immutable] %b : $BufferType, #BufferType.someField
556
+ // /
557
+ static SILValue getBeginScopeInst (SILValue V) {
558
+ SILValue accessScope = getAccessScope (V);
559
+ if (auto *access = dyn_cast<BeginAccessInst>(accessScope)) {
560
+ if (access->getAccessKind () == SILAccessKind::Read &&
561
+ access->getEnforcement () != SILAccessEnforcement::Unsafe)
562
+ return access;
563
+ return SILValue ();
564
+ }
565
+ SILValue accessBase = getAccessBase (V);
566
+ SILValue object;
567
+ if (auto *elementAddr = dyn_cast<RefElementAddrInst>(accessBase)) {
568
+ if (!elementAddr->isImmutable ())
569
+ return SILValue ();
570
+ object = elementAddr->getOperand ();
571
+ } else if (auto *tailAddr = dyn_cast<RefTailAddrInst>(accessBase)) {
572
+ if (!tailAddr->isImmutable ())
573
+ return SILValue ();
574
+ object = tailAddr->getOperand ();
575
+ } else {
576
+ return SILValue ();
577
+ }
578
+ if (BorrowedValue borrowedObj = getSingleBorrowIntroducingValue (object)) {
579
+ return borrowedObj.value ;
580
+ }
581
+ return SILValue ();
582
+ }
583
+
584
+ // / Collect all instructions which are inside an immutable scope.
585
+ // /
586
+ // / The \p beginScopeInst is either a ``begin_access [read]`` or the begin of a
587
+ // / borrow scope (begin_borrow, load_borrow) of an immutable copy-on-write
588
+ // / buffer.
589
+ void AliasAnalysis::computeImmutableScope (SingleValueInstruction *beginScopeInst,
590
+ ValueIndexTy beginIdx) {
591
+ BasicBlockSet visitedBlocks (beginScopeInst->getFunction ());
592
+ llvm::SmallVector<std::pair<SILInstruction *, SILBasicBlock *>, 16 > workList;
593
+
594
+ auto addEndScopeInst = [&](SILInstruction *endScope) {
595
+ workList.push_back ({endScope, endScope->getParent ()});
596
+ bool isNew = visitedBlocks.insert (endScope->getParent ());
597
+ (void )isNew;
598
+ assert (isNew);
599
+ };
600
+
601
+ // First step: add all scope-ending instructions to the worklist.
602
+ if (auto *beginAccess = dyn_cast<BeginAccessInst>(beginScopeInst)) {
603
+ for (EndAccessInst *endAccess : beginAccess->getEndAccesses ()) {
604
+ addEndScopeInst (endAccess);
605
+ }
606
+ } else {
607
+ visitTransitiveEndBorrows (BorrowedValue (beginScopeInst), addEndScopeInst);
608
+ }
609
+
610
+ // Second step: walk up the control flow until the beginScopeInst and add
611
+ // all (potentially) memory writing instructions to instsInImmutableScopes.
612
+ while (!workList.empty ()) {
613
+ auto instAndBlock = workList.pop_back_val ();
614
+ SILBasicBlock *block = instAndBlock.second ;
615
+ // If the worklist entry doesn't have an instruction, start at the end of
616
+ // the block.
617
+ auto iter = instAndBlock.first ? instAndBlock.first ->getIterator ()
618
+ : block->end ();
619
+ // Walk up the instruction list - either to the begin of the block or until
620
+ // we hit the beginScopeInst.
621
+ while (true ) {
622
+ if (iter == block->begin ()) {
623
+ assert (block != block->getParent ()->getEntryBlock () &&
624
+ " didn't find the beginScopeInst when walking up the CFG" );
625
+ // Add all predecessor blocks to the worklist.
626
+ for (SILBasicBlock *pred : block->getPredecessorBlocks ()) {
627
+ if (visitedBlocks.insert (pred))
628
+ workList.push_back ({nullptr , pred});
629
+ }
630
+ break ;
631
+ }
632
+ --iter;
633
+ SILInstruction *inst = &*iter;
634
+ if (inst == beginScopeInst) {
635
+ // When we are at the beginScopeInst we terminate the CFG walk.
636
+ break ;
637
+ }
638
+ if (inst->mayWriteToMemory ()) {
639
+ instsInImmutableScopes.insert ({beginIdx,
640
+ InstructionToIndex.getIndex (inst)});
641
+ }
642
+ }
643
+ }
644
+ }
645
+
646
+ // / Returns true if \p inst is in an immutable scope of V.
647
+ // /
648
+ // / That means that even if we don't know anything about inst, we can be sure
649
+ // / that inst cannot write to V.
650
+ // / An immutable scope is for example a read-only begin_access/end_access scope.
651
+ // / Another example is a borrow scope of an immutable copy-on-write buffer.
652
+ bool AliasAnalysis::isInImmutableScope (SILInstruction *inst, SILValue V) {
653
+ if (!V->getType ().isAddress ())
654
+ return false ;
655
+
656
+ SILValue beginScope = getBeginScopeInst (V);
657
+ if (!beginScope)
658
+ return false ;
659
+
660
+ if (auto *funcArg = dyn_cast<SILFunctionArgument>(beginScope)) {
661
+ // The immutable scope (= an guaranteed argument) spans over the whole
662
+ // function. We don't need to do any scope computation in this case.
663
+ assert (funcArg->getArgumentConvention ().isGuaranteedConvention ());
664
+ return true ;
665
+ }
666
+
667
+ auto *beginScopeInst = dyn_cast<SingleValueInstruction>(beginScope);
668
+ if (!beginScopeInst)
669
+ return false ;
670
+
671
+ ValueIndexTy beginIdx = InstructionToIndex.getIndex (beginScopeInst);
672
+
673
+ // Recompute the scope if not done yet.
674
+ if (immutableScopeComputed.insert (beginIdx).second ) {
675
+ // In practice this will never happen. Just be be sure to not run into
676
+ // quadratic complexity in obscure corner cases.
677
+ if (instsInImmutableScopes.size () > ImmutableScopeInstsMaxSize)
678
+ return false ;
679
+
680
+ computeImmutableScope (beginScopeInst, beginIdx);
681
+ }
682
+
683
+ ValueIndexTy instIdx = InstructionToIndex.getIndex (inst);
684
+ return instsInImmutableScopes.contains ({beginIdx, instIdx});
685
+ }
686
+
539
687
MemBehavior
540
688
AliasAnalysis::computeMemoryBehaviorInner (SILInstruction *Inst, SILValue V) {
541
689
LLVM_DEBUG (llvm::dbgs () << " GET MEMORY BEHAVIOR FOR:\n " << *Inst << " "
542
690
<< *V);
543
691
assert (SEA && " SideEffectsAnalysis must be initialized!" );
544
- return MemoryBehaviorVisitor (this , SEA, EA, V).visit (Inst);
692
+
693
+ MemBehavior result = MemoryBehaviorVisitor (this , SEA, EA, V).visit (Inst);
694
+
695
+ // If the "regular" alias analysis thinks that Inst may modify V, check if
696
+ // Inst is in an immutable scope of V.
697
+ if (result > MemBehavior::MayRead && isInImmutableScope (Inst, V)) {
698
+ return (result == MemBehavior::MayWrite) ? MemBehavior::None
699
+ : MemBehavior::MayRead;
700
+ }
701
+ return result;
545
702
}
546
703
547
704
MemBehaviorKeyTy AliasAnalysis::toMemoryBehaviorKey (SILInstruction *V1,
0 commit comments