2020#include " swift/SIL/BasicBlockBits.h"
2121#include " swift/SIL/BasicBlockUtils.h"
2222#include " swift/SIL/LinearLifetimeChecker.h"
23+ #include " swift/SIL/OSSALifetimeCompletion.h"
2324#include " swift/SIL/OwnershipUtils.h"
2425#include " swift/SIL/SILBuilder.h"
2526#include " swift/SILOptimizer/PassManager/Passes.h"
@@ -1945,19 +1946,22 @@ class AllocOptimize {
19451946
19461947 InstructionDeleter &deleter;
19471948
1949+ DominanceInfo *domInfo;
1950+
19481951 // / A structure that we use to compute our available values.
19491952 AvailableValueDataflowContext DataflowContext;
19501953
19511954public:
19521955 AllocOptimize (AllocationInst *memory, SmallVectorImpl<PMOMemoryUse> &uses,
19531956 SmallVectorImpl<SILInstruction *> &releases,
1954- DeadEndBlocks &deadEndBlocks, InstructionDeleter &deleter)
1957+ DeadEndBlocks &deadEndBlocks, InstructionDeleter &deleter,
1958+ DominanceInfo *domInfo)
19551959 : Module(memory->getModule ()), TheMemory(memory),
19561960 MemoryType(getMemoryType(memory)),
19571961 NumMemorySubElements(getNumSubElements(
19581962 MemoryType, Module, TypeExpansionContext(*memory->getFunction ()))),
19591963 Uses(uses), Releases(releases), deadEndBlocks(deadEndBlocks),
1960- deleter(deleter),
1964+ deleter(deleter), domInfo(domInfo),
19611965 DataflowContext(TheMemory, NumMemorySubElements, uses, deleter) {}
19621966
19631967 bool optimizeMemoryAccesses ();
@@ -2655,145 +2659,20 @@ bool AllocOptimize::tryToRemoveDeadAllocation() {
26552659 // post-dominating consuming use sets. This can happen if we have an enum that
26562660 // is known dynamically none along a path. This is dynamically correct, but
26572661 // 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+
26592664 while (!valuesNeedingLifetimeCompletion.empty ()) {
26602665 auto optV = valuesNeedingLifetimeCompletion.pop_back_val ();
26612666 if (!optV)
26622667 continue ;
26632668 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+ // Lexical enums can have incomplete lifetimes in non payload paths that
2670+ // don't end in unreachable. Force their lifetime to end immediately after
2671+ // the last use instead.
2672+ bool forceBoundaryCompletion = v->getType ().isOrHasEnum ();
2673+ LLVM_DEBUG (llvm::dbgs () << " Completing lifetime of: " );
2674+ LLVM_DEBUG (v->dump ());
2675+ completion.completeOSSALifetime (v, forceBoundaryCompletion);
27972676 }
27982677
27992678 return true ;
@@ -2863,7 +2742,7 @@ static AllocationInst *getOptimizableAllocation(SILInstruction *i) {
28632742 return alloc;
28642743}
28652744
2866- bool swift::optimizeMemoryAccesses (SILFunction *fn) {
2745+ bool swift::optimizeMemoryAccesses (SILFunction *fn, DominanceInfo *domInfo ) {
28672746 bool changed = false ;
28682747 DeadEndBlocks deadEndBlocks (fn);
28692748
@@ -2892,8 +2771,8 @@ bool swift::optimizeMemoryAccesses(SILFunction *fn) {
28922771 // runs. It creates and deletes instructions other than alloc.
28932772 continue ;
28942773 }
2895- AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks,
2896- deleter );
2774+ AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks, deleter,
2775+ domInfo );
28972776 changed |= allocOptimize.optimizeMemoryAccesses ();
28982777
28992778 // Move onto the next instruction. We know this is safe since we do not
@@ -2904,7 +2783,7 @@ bool swift::optimizeMemoryAccesses(SILFunction *fn) {
29042783 return changed;
29052784}
29062785
2907- bool swift::eliminateDeadAllocations (SILFunction *fn) {
2786+ bool swift::eliminateDeadAllocations (SILFunction *fn, DominanceInfo *domInfo ) {
29082787 if (!fn->hasOwnership ())
29092788 return false ;
29102789
@@ -2935,8 +2814,8 @@ bool swift::eliminateDeadAllocations(SILFunction *fn) {
29352814 if (!collectPMOElementUsesFrom (memInfo, uses, destroys)) {
29362815 continue ;
29372816 }
2938- AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks,
2939- deleter );
2817+ AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks, deleter,
2818+ domInfo );
29402819 if (allocOptimize.tryToRemoveDeadAllocation ()) {
29412820 deleter.cleanupDeadInstructions ();
29422821 ++NumAllocRemoved;
@@ -2958,19 +2837,24 @@ class PredictableMemoryAccessOptimizations : public SILFunctionTransform {
29582837 // / either indicates that this pass missing some opportunities the first time,
29592838 // / or has a pass order dependency on other early passes.
29602839 void run () override {
2840+ auto *func = getFunction ();
2841+ LLVM_DEBUG (llvm::dbgs () << " Looking at: " << func->getName () << " \n " );
2842+ auto *da = getAnalysis<DominanceAnalysis>();
29612843 // TODO: Can we invalidate here just instructions?
2962- if (optimizeMemoryAccesses (getFunction ( )))
2844+ if (optimizeMemoryAccesses (func, da-> get (func )))
29632845 invalidateAnalysis (SILAnalysis::InvalidationKind::FunctionBody);
29642846 }
29652847};
29662848
29672849class PredictableDeadAllocationElimination : public SILFunctionTransform {
29682850 void run () override {
2851+ auto *func = getFunction ();
2852+ LLVM_DEBUG (llvm::dbgs () << " Looking at: " << func->getName () << " \n " );
2853+ auto *da = getAnalysis<DominanceAnalysis>();
29692854 // If we are already canonical or do not have ownership, just bail.
2970- if (getFunction ()->wasDeserializedCanonical () ||
2971- !getFunction ()->hasOwnership ())
2855+ if (func->wasDeserializedCanonical () || !func->hasOwnership ())
29722856 return ;
2973- if (eliminateDeadAllocations (getFunction ( )))
2857+ if (eliminateDeadAllocations (func, da-> get (func )))
29742858 invalidateAnalysis (SILAnalysis::InvalidationKind::FunctionBody);
29752859 }
29762860};
0 commit comments