20
20
#include " swift/SIL/BasicBlockBits.h"
21
21
#include " swift/SIL/BasicBlockUtils.h"
22
22
#include " swift/SIL/LinearLifetimeChecker.h"
23
+ #include " swift/SIL/OSSALifetimeCompletion.h"
23
24
#include " swift/SIL/OwnershipUtils.h"
24
25
#include " swift/SIL/SILBuilder.h"
25
26
#include " swift/SILOptimizer/PassManager/Passes.h"
@@ -1945,19 +1946,22 @@ class AllocOptimize {
1945
1946
1946
1947
InstructionDeleter &deleter;
1947
1948
1949
+ DominanceInfo *domInfo;
1950
+
1948
1951
// / A structure that we use to compute our available values.
1949
1952
AvailableValueDataflowContext DataflowContext;
1950
1953
1951
1954
public:
1952
1955
AllocOptimize (AllocationInst *memory, SmallVectorImpl<PMOMemoryUse> &uses,
1953
1956
SmallVectorImpl<SILInstruction *> &releases,
1954
- DeadEndBlocks &deadEndBlocks, InstructionDeleter &deleter)
1957
+ DeadEndBlocks &deadEndBlocks, InstructionDeleter &deleter,
1958
+ DominanceInfo *domInfo)
1955
1959
: Module(memory->getModule ()), TheMemory(memory),
1956
1960
MemoryType(getMemoryType(memory)),
1957
1961
NumMemorySubElements(getNumSubElements(
1958
1962
MemoryType, Module, TypeExpansionContext(*memory->getFunction ()))),
1959
1963
Uses(uses), Releases(releases), deadEndBlocks(deadEndBlocks),
1960
- deleter(deleter),
1964
+ deleter(deleter), domInfo(domInfo),
1961
1965
DataflowContext(TheMemory, NumMemorySubElements, uses, deleter) {}
1962
1966
1963
1967
bool optimizeMemoryAccesses ();
@@ -2655,145 +2659,19 @@ bool AllocOptimize::tryToRemoveDeadAllocation() {
2655
2659
// post-dominating consuming use sets. This can happen if we have an enum that
2656
2660
// is known dynamically none along a path. This is dynamically correct, but
2657
2661
// can not be represented in OSSA so we insert these destroys along said path.
2658
- SmallVector<SILBasicBlock *, 32 > consumingUseBlocks;
2662
+ OSSALifetimeCompletion completion (TheMemory->getFunction (), domInfo);
2663
+
2659
2664
while (!valuesNeedingLifetimeCompletion.empty ()) {
2660
2665
auto optV = valuesNeedingLifetimeCompletion.pop_back_val ();
2661
2666
if (!optV)
2662
2667
continue ;
2663
2668
SILValue v = *optV;
2664
- if (v->getOwnershipKind () != OwnershipKind::Owned)
2665
- continue ;
2666
-
2667
- // FIXME: This doesn't handle situations where non-consuming uses are in
2668
- // blocks in which the consuming use is already deleted. Replace the
2669
- // following code with an general OSSA utility for owned lifetime
2670
- // fixup. ValueLifetimeAnalysis does this easily, but we also need to handle
2671
- // reborrows by adding copies. An OwnershipOptUtils utility will handle
2672
- // that soon.
2673
-
2674
- // First see if our value doesn't have any uses. In such a case, just
2675
- // insert a destroy_value at the next instruction and return.
2676
- if (v->use_empty ()) {
2677
- auto *next = v->getNextInstruction ();
2678
- auto loc = RegularLocation::getAutoGeneratedLocation ();
2679
- SILBuilderWithScope localBuilder (next);
2680
- localBuilder.createDestroyValue (loc, v);
2681
- continue ;
2682
- }
2683
-
2684
- // Otherwise, we first see if we have any consuming uses at all. If we do,
2685
- // then we know that any such consuming uses since we have an owned value
2686
- // /must/ be strongly control equivalent to our value and unreachable from
2687
- // each other, so we can just use findJointPostDominatingSet to complete
2688
- // the set.
2689
- consumingUseBlocks.clear ();
2690
- for (auto *use : v->getConsumingUses ())
2691
- consumingUseBlocks.push_back (use->getParentBlock ());
2692
-
2693
- if (!consumingUseBlocks.empty ()) {
2694
- findJointPostDominatingSet (
2695
- v->getParentBlock (), consumingUseBlocks, [](SILBasicBlock *) {},
2696
- [&](SILBasicBlock *result) {
2697
- auto loc = RegularLocation::getAutoGeneratedLocation ();
2698
- SILBuilderWithScope builder (result);
2699
- builder.createDestroyValue (loc, v);
2700
- });
2701
- continue ;
2702
- }
2703
-
2704
- // If we do not have at least one consuming use, we need to do something
2705
- // different. This situation can occur given a non-trivial enum typed
2706
- // stack allocation that:
2707
- //
2708
- // 1. Had a destroy_addr eliminated along a path where we dynamically know
2709
- // that the stack allocation is storing a trivial case.
2710
- //
2711
- // 2. Had some other paths where due to dead end blocks, no destroy_addr
2712
- // is needed.
2713
- //
2714
- // To fix this, we just treat all uses as consuming blocks and insert
2715
- // destroys using the joint post dominance set computer and insert
2716
- // destroys at the end of all input blocks in the post dom set and at the
2717
- // beginning of any leaking blocks.
2718
- {
2719
- // TODO: Can we just pass this in to findJointPostDominatingSet instead
2720
- // of recomputing it there? Maybe an overload that lets us do this?
2721
- BasicBlockSet foundUseBlocks (v->getFunction ());
2722
- for (auto *use : v->getUses ()) {
2723
- auto *block = use->getParentBlock ();
2724
- if (!foundUseBlocks.insert (block))
2725
- continue ;
2726
- consumingUseBlocks.push_back (block);
2727
- }
2728
- }
2729
- findJointPostDominatingSet (
2730
- v->getParentBlock (), consumingUseBlocks,
2731
- [&](SILBasicBlock *foundInputBlock) {
2732
- // This is a block that is reachable from another use. We are not
2733
- // interested in these.
2734
- },
2735
- [&](SILBasicBlock *leakingBlock) {
2736
- auto loc = RegularLocation::getAutoGeneratedLocation ();
2737
- SILBuilderWithScope builder (leakingBlock);
2738
- builder.createDestroyValue (loc, v);
2739
- },
2740
- [&](SILBasicBlock *inputBlockInPostDomSet) {
2741
- auto *termInst = inputBlockInPostDomSet->getTerminator ();
2742
- switch (termInst->getTermKind ()) {
2743
- case TermKind::UnreachableInst:
2744
- // We do not care about input blocks that end in unreachables. We
2745
- // are going to leak down them so do not insert a destroy_value
2746
- // there.
2747
- return ;
2748
-
2749
- // NOTE: Given that our input value is owned, our branch can only
2750
- // accept the use as a non-consuming use if the branch is forwarding
2751
- // unowned ownership. Luckily for use, we checked early if we had
2752
- // any such uses and bailed, so we know the branch can not use our
2753
- // value. This is just avoiding a corner case that we don't need to
2754
- // handle.
2755
- case TermKind::BranchInst:
2756
- LLVM_FALLTHROUGH;
2757
- // NOTE: We put cond_br here since in OSSA, cond_br can never have
2758
- // a non-trivial value operand, meaning we can insert before.
2759
- case TermKind::CondBranchInst:
2760
- LLVM_FALLTHROUGH;
2761
- case TermKind::ReturnInst:
2762
- case TermKind::ThrowInst:
2763
- case TermKind::ThrowAddrInst:
2764
- case TermKind::UnwindInst:
2765
- case TermKind::YieldInst: {
2766
- // These terminators can never be non-consuming uses of an owned
2767
- // value since we would be leaking the owned value no matter what
2768
- // we do. Given that, we can assume that what ever the
2769
- // non-consuming use actually was, must be before this
2770
- // instruction. So insert the destroy_value at the end of the
2771
- // block, before the terminator.
2772
- auto loc = RegularLocation::getAutoGeneratedLocation ();
2773
- SILBuilderWithScope localBuilder (termInst);
2774
- localBuilder.createDestroyValue (loc, v);
2775
- return ;
2776
- }
2777
- case TermKind::TryApplyInst:
2778
- case TermKind::SwitchValueInst:
2779
- case TermKind::SwitchEnumInst:
2780
- case TermKind::SwitchEnumAddrInst:
2781
- case TermKind::DynamicMethodBranchInst:
2782
- case TermKind::AwaitAsyncContinuationInst:
2783
- case TermKind::CheckedCastBranchInst:
2784
- case TermKind::CheckedCastAddrBranchInst: {
2785
- // Otherwise, we insert the destroy_addr /after/ the
2786
- // terminator. All of these are guaranteed to have each successor
2787
- // to have the block as its only predecessor block.
2788
- SILBuilderWithScope::insertAfter (termInst, [&](auto &b) {
2789
- auto loc = RegularLocation::getAutoGeneratedLocation ();
2790
- b.createDestroyValue (loc, v);
2791
- });
2792
- return ;
2793
- }
2794
- }
2795
- llvm_unreachable (" Case that did not return in its body?!" );
2796
- });
2669
+ // enums can have incomplete lifetimes in non payload paths that don't end
2670
+ // in unreachable.
2671
+ bool forceBoundaryCompletion = v->getType ().isOrHasEnum ();
2672
+ LLVM_DEBUG (llvm::dbgs () << " Completing lifetime of: " );
2673
+ LLVM_DEBUG (v->dump ());
2674
+ completion.completeOSSALifetime (v, forceBoundaryCompletion);
2797
2675
}
2798
2676
2799
2677
return true ;
@@ -2863,7 +2741,7 @@ static AllocationInst *getOptimizableAllocation(SILInstruction *i) {
2863
2741
return alloc;
2864
2742
}
2865
2743
2866
- bool swift::optimizeMemoryAccesses (SILFunction *fn) {
2744
+ bool swift::optimizeMemoryAccesses (SILFunction *fn, DominanceInfo *domInfo ) {
2867
2745
bool changed = false ;
2868
2746
DeadEndBlocks deadEndBlocks (fn);
2869
2747
@@ -2892,8 +2770,8 @@ bool swift::optimizeMemoryAccesses(SILFunction *fn) {
2892
2770
// runs. It creates and deletes instructions other than alloc.
2893
2771
continue ;
2894
2772
}
2895
- AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks,
2896
- deleter );
2773
+ AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks, deleter,
2774
+ domInfo );
2897
2775
changed |= allocOptimize.optimizeMemoryAccesses ();
2898
2776
2899
2777
// Move onto the next instruction. We know this is safe since we do not
@@ -2904,7 +2782,7 @@ bool swift::optimizeMemoryAccesses(SILFunction *fn) {
2904
2782
return changed;
2905
2783
}
2906
2784
2907
- bool swift::eliminateDeadAllocations (SILFunction *fn) {
2785
+ bool swift::eliminateDeadAllocations (SILFunction *fn, DominanceInfo *domInfo ) {
2908
2786
if (!fn->hasOwnership ())
2909
2787
return false ;
2910
2788
@@ -2935,8 +2813,8 @@ bool swift::eliminateDeadAllocations(SILFunction *fn) {
2935
2813
if (!collectPMOElementUsesFrom (memInfo, uses, destroys)) {
2936
2814
continue ;
2937
2815
}
2938
- AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks,
2939
- deleter );
2816
+ AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks, deleter,
2817
+ domInfo );
2940
2818
if (allocOptimize.tryToRemoveDeadAllocation ()) {
2941
2819
deleter.cleanupDeadInstructions ();
2942
2820
++NumAllocRemoved;
@@ -2958,19 +2836,22 @@ class PredictableMemoryAccessOptimizations : public SILFunctionTransform {
2958
2836
// / either indicates that this pass missing some opportunities the first time,
2959
2837
// / or has a pass order dependency on other early passes.
2960
2838
void run () override {
2839
+ auto *func = getFunction ();
2840
+ auto *da = getAnalysis<DominanceAnalysis>();
2961
2841
// TODO: Can we invalidate here just instructions?
2962
- if (optimizeMemoryAccesses (getFunction ( )))
2842
+ if (optimizeMemoryAccesses (func, da-> get (func )))
2963
2843
invalidateAnalysis (SILAnalysis::InvalidationKind::FunctionBody);
2964
2844
}
2965
2845
};
2966
2846
2967
2847
class PredictableDeadAllocationElimination : public SILFunctionTransform {
2968
2848
void run () override {
2849
+ auto *func = getFunction ();
2850
+ auto *da = getAnalysis<DominanceAnalysis>();
2969
2851
// If we are already canonical or do not have ownership, just bail.
2970
- if (getFunction ()->wasDeserializedCanonical () ||
2971
- !getFunction ()->hasOwnership ())
2852
+ if (func->wasDeserializedCanonical () || !func->hasOwnership ())
2972
2853
return ;
2973
- if (eliminateDeadAllocations (getFunction ( )))
2854
+ if (eliminateDeadAllocations (func, da-> get (func )))
2974
2855
invalidateAnalysis (SILAnalysis::InvalidationKind::FunctionBody);
2975
2856
}
2976
2857
};
0 commit comments