diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index bcba456820cf8..f9a6d8b74dbd5 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -41,16 +41,50 @@ STATISTIC(NumLoadCopyConvertedToLoadBorrow, // Utility //===----------------------------------------------------------------------===// -/// Return true if v only has invalidating uses that are destroy_value. Such an -/// owned value is said to represent a dead "live range". -/// -/// Semantically this implies that a value is never passed off as +1 to memory -/// or another function implying it can be used everywhere at +0. -static bool -isDeadLiveRange(SILValue v, SmallVectorImpl &destroys, - SmallVectorImpl &forwardingInsts) { - assert(v.getOwnershipKind() == ValueOwnershipKind::Owned); - SmallVector worklist(v->use_begin(), v->use_end()); +//===----------------------------------------------------------------------===// +// Live Range Modeling +//===----------------------------------------------------------------------===// + +namespace { + +class LiveRange { + /// A list of destroy_values of the live range. + SmallVector destroys; + + /// A list of forwarding instructions that forward our destroys ownership, but + /// that are also able to forward guaranteed ownership. + SmallVector generalForwardingInsts; + + /// Consuming users that we were not able to understand as a forwarding + /// instruction or a destroy_value. These must be passed a strongly control + /// equivalent +1 value. + SmallVector unknownConsumingUsers; + +public: + LiveRange(SILValue value); + + LiveRange(const LiveRange &) = delete; + LiveRange &operator=(const LiveRange &) = delete; + + /// Return true if v only has invalidating uses that are destroy_value. Such + /// an owned value is said to represent a dead "live range". + /// + /// Semantically this implies that a value is never passed off as +1 to memory + /// or another function implying it can be used everywhere at +0. + bool hasConsumingUse() const { return unknownConsumingUsers.size(); } + + ArrayRef getDestroys() const { return destroys; } + ArrayRef getNonConsumingForwardingInsts() const { + return generalForwardingInsts; + } +}; + +} // end anonymous namespace + +LiveRange::LiveRange(SILValue value) + : destroys(), generalForwardingInsts(), unknownConsumingUsers() { + SmallVector worklist(value->getUses()); + while (!worklist.empty()) { auto *op = worklist.pop_back_val(); @@ -93,13 +127,14 @@ isDeadLiveRange(SILValue v, SmallVectorImpl &destroys, return v.getOwnershipKind() == ValueOwnershipKind::Owned; })) { - return false; + unknownConsumingUsers.push_back(user); + continue; } // Ok, this is a forwarding instruction whose ownership we can flip from // owned -> guaranteed. Visit its users recursively to see if the the // users force the live range to be alive. - forwardingInsts.push_back(user); + generalForwardingInsts.push_back(user); for (SILValue v : user->getResults()) { if (v.getOwnershipKind() != ValueOwnershipKind::Owned) continue; @@ -118,10 +153,6 @@ isDeadLiveRange(SILValue v, SmallVectorImpl &destroys, continue; } } - - // We visited all of our users and were able to prove that all of them were - // benign. Return true. - return true; } //===----------------------------------------------------------------------===// @@ -372,12 +403,13 @@ bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) { } static void convertForwardingInstsFromOwnedToGuaranteed( - SmallVectorImpl &guaranteedForwardingInsts) { + ArrayRef guaranteedForwardingInsts) { // Then change all of our guaranteed forwarding insts to have guaranteed // ownership kind instead of what ever they previously had (ignoring trivial // results); while (!guaranteedForwardingInsts.empty()) { - auto *i = guaranteedForwardingInsts.pop_back_val(); + auto *i = guaranteedForwardingInsts.back(); + guaranteedForwardingInsts = guaranteedForwardingInsts.drop_back(); assert(i->hasResults()); for (SILValue result : i->getResults()) { @@ -461,9 +493,8 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst // must be some consuming use that we either do not understand is /actually/ // forwarding or a user that truly represents a necessary consume of the // value (e.x. storing into memory). - SmallVector destroys; - SmallVector guaranteedForwardingInsts; - if (!isDeadLiveRange(cvi, destroys, guaranteedForwardingInsts)) + LiveRange lr(cvi); + if (lr.hasConsumingUse()) return false; // Next check if we do not have any destroys of our copy_value and are @@ -526,6 +557,7 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst return borrowScope.isLocalScope(); }); + auto destroys = lr.getDestroys(); if (destroys.empty() && haveAnyLocalScopes) { return false; } @@ -568,13 +600,15 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst // Otherwise, we know that our copy_value/destroy_values are all completely // within the guaranteed value scope. First delete the destroys/copies. while (!destroys.empty()) { - auto *dvi = destroys.pop_back_val(); + auto *dvi = destroys.back(); + destroys = destroys.drop_back(); eraseInstruction(dvi); ++NumEliminatedInsts; } eraseAndRAUWSingleValueInstruction(cvi, cvi->getOperand()); - convertForwardingInstsFromOwnedToGuaranteed(guaranteedForwardingInsts); + convertForwardingInstsFromOwnedToGuaranteed( + lr.getNonConsumingForwardingInsts()); ++NumEliminatedInsts; return true; @@ -842,14 +876,14 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { // FIXME: We should consider if it is worth promoting a load [copy] // -> load_borrow if we can put a copy_value on a cold path and thus // eliminate RR traffic on a hot path. - SmallVector destroyValues; - SmallVector guaranteedForwardingInsts; - if (!isDeadLiveRange(li, destroyValues, guaranteedForwardingInsts)) + LiveRange lr(li); + if (lr.hasConsumingUse()) return false; // Then check if our address is ever written to. If it is, then we cannot use // the load_borrow because the stored value may be released during the loaded // value's live range. + auto destroyValues = lr.getDestroys(); if (isWrittenTo(li, destroyValues)) return false; @@ -878,7 +912,8 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { // Then delete all of our destroy_value. while (!destroyValues.empty()) { - auto *dvi = destroyValues.pop_back_val(); + auto *dvi = destroyValues.back(); + destroyValues = destroyValues.drop_back(); eraseInstruction(dvi); ++NumEliminatedInsts; } @@ -888,7 +923,8 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { // And then change the ownership all of our owned forwarding users to be // guaranteed. - convertForwardingInstsFromOwnedToGuaranteed(guaranteedForwardingInsts); + convertForwardingInstsFromOwnedToGuaranteed( + lr.getNonConsumingForwardingInsts()); ++NumEliminatedInsts; ++NumLoadCopyConvertedToLoadBorrow;