//===--- OwnershipOptUtils.cpp --------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// /// /// \file /// /// Ownership Utilities that rely on SILOptimizer functionality. /// //===----------------------------------------------------------------------===// #include "swift/SILOptimizer/Utils/OwnershipOptUtils.h" #include "swift/Basic/Defer.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/LinearLifetimeChecker.h" #include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/Projection.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/ValueLifetime.h" using namespace swift; //===----------------------------------------------------------------------===// // Utility Helper Functions //===----------------------------------------------------------------------===// static void cleanupOperandsBeforeDeletion(SILInstruction *oldValue, InstModCallbacks &callbacks) { SILBuilderWithScope builder(oldValue); for (auto &op : oldValue->getAllOperands()) { if (!op.isLifetimeEnding()) { continue; } switch (op.get().getOwnershipKind()) { case OwnershipKind::Any: llvm_unreachable("Invalid ownership for value"); case OwnershipKind::Owned: { auto *dvi = builder.createDestroyValue(oldValue->getLoc(), op.get()); callbacks.createdNewInst(dvi); continue; } case OwnershipKind::Guaranteed: { // Should only happen once we model destructures as true reborrows. auto *ebi = builder.createEndBorrow(oldValue->getLoc(), op.get()); callbacks.createdNewInst(ebi); continue; } case OwnershipKind::None: continue; case OwnershipKind::Unowned: llvm_unreachable("Unowned object can never be consumed?!"); } llvm_unreachable("Covered switch isn't covered"); } } static SILPhiArgument * insertOwnedBaseValueAlongBranchEdge(BranchInst *bi, SILValue innerCopy, InstModCallbacks &callbacks) { auto *destBB = bi->getDestBB(); // We need to create the phi argument before calling addNewEdgeValueToBranch // since it checks that the destination block has enough arguments for the // argument. auto *phiArg = destBB->createPhiArgument(innerCopy->getType(), OwnershipKind::Owned); addNewEdgeValueToBranch(bi, destBB, innerCopy, callbacks); // Grab our predecessor blocks, ignoring us, add to the branch edge an // undef corresponding to our value. // // We gather all predecessor blocks in a separate array to avoid // iterator invalidation issues as we mess with terminators. SmallVector predecessorBlocks( destBB->getPredecessorBlocks()); for (auto *predBlock : predecessorBlocks) { if (predBlock == innerCopy->getParentBlock()) continue; addNewEdgeValueToBranch( predBlock->getTerminator(), destBB, SILUndef::get(innerCopy->getType(), *destBB->getParent()), callbacks); } return phiArg; } static void getAllNonTrivialUsePointsOfBorrowedValue( SILValue value, SmallVectorImpl &usePoints, SmallVectorImpl &reborrowPoints) { assert(value.getOwnershipKind() == OwnershipKind::Guaranteed); unsigned firstOffset = usePoints.size(); llvm::copy(value->getUses(), std::back_inserter(usePoints)); if (usePoints.size() == firstOffset) return; // NOTE: Use points resizes in this loop so usePoints.size() may be // different every time. for (unsigned i = firstOffset; i < usePoints.size(); ++i) { if (auto fOperand = ForwardingOperand::get(usePoints[i])) { fOperand->visitForwardedValues([&](SILValue transitiveValue) { // Do not include transitive uses with 'none' ownership if (transitiveValue.getOwnershipKind() == OwnershipKind::None) return true; for (auto *transitiveUse : transitiveValue->getUses()) usePoints.push_back(transitiveUse); return true; }); continue; } if (auto borrowingOp = BorrowingOperand::get(usePoints[i])) { // If we have a reborrow, we have no further work to do, our reborrow is // already a use and we will handle the reborrow separately. if (borrowingOp->isReborrow()) continue; // Otherwise, try to grab additional end scope instructions to find more // liveness info. Stash any reborrow uses so that we can eliminate the // reborrow before we are done processing. borrowingOp->visitLocalEndScopeUses([&](Operand *scopeEndingUse) { if (auto scopeEndingBorrowingOp = BorrowingOperand::get(scopeEndingUse)) { if (scopeEndingBorrowingOp->isReborrow()) { reborrowPoints.push_back(scopeEndingUse); return true; } } usePoints.push_back(scopeEndingUse); return true; }); // Now break up all of the reborrows continue; } // If our base guaranteed value does not have any consuming uses (consider // function arguments), we need to be sure to include interior pointer // operands since we may not get a use from a end_scope instruction. if (auto intPtrOperand = InteriorPointerOperand::get(usePoints[i])) { intPtrOperand->getImplicitUses(usePoints); continue; } } } //===----------------------------------------------------------------------===// // Ownership Lifetime Extender //===----------------------------------------------------------------------===// namespace { struct OwnershipLifetimeExtender { OwnershipFixupContext &ctx; /// Create a new copy of \p value assuming that our caller will clean up the /// copy along all paths that go through consuming point. Operationally this /// means that the API will insert compensating destroy_value on the copy /// along all paths that do not go through consuming point. /// /// DISCUSSION: If \p consumingPoint is an instruction that forwards \p value, /// calling this and then RAUWing with \p value guarantee that \p value will /// be consumed by the forwarding instruction's results consuming uses. CopyValueInst *createPlusOneCopy(SILValue value, SILInstruction *consumingPoint); /// Create a copy of \p value that covers all of \p range and insert all /// needed destroy_values. We assume that no uses in \p range consume \p /// value. CopyValueInst *createPlusZeroCopy(SILValue value, ArrayRef range) { return createPlusZeroCopy>(value, range); } /// Create a copy of \p value that covers all of \p range and insert all /// needed destroy_values. We assume that all uses in \p range do not consume /// \p value. /// /// We return our copy_value to the user at +0 to show that they do not need /// to insert cleanup destroys. template CopyValueInst *createPlusZeroCopy(SILValue value, const RangeTy &range); /// Create a new borrow scope for \p newValue that contains all uses in \p /// useRange. We assume that \p useRange does not contain any lifetime ending /// uses. template BeginBorrowInst *createPlusZeroBorrow(SILValue newValue, RangeTy useRange); }; } // end anonymous namespace // Lifetime extend newValue over owned oldValue assuming that our copy will have // its lifetime ended by oldValue's lifetime ending uses after RAUWing by our // caller. CopyValueInst * OwnershipLifetimeExtender::createPlusOneCopy(SILValue value, SILInstruction *consumingPoint) { auto *newValInsertPt = value->getDefiningInsertionPoint(); assert(newValInsertPt); CopyValueInst *copy; if (!isa(value)) { SILBuilderWithScope::insertAfter(newValInsertPt, [&](SILBuilder &builder) { copy = builder.createCopyValue(builder.getInsertionPointLoc(), value); }); } else { SILBuilderWithScope builder(newValInsertPt); copy = builder.createCopyValue(newValInsertPt->getLoc(), value); } auto &callbacks = ctx.callbacks; callbacks.createdNewInst(copy); auto *result = copy; ctx.jointPostDomSetComputer.findJointPostDominatingSet( newValInsertPt->getParent(), consumingPoint->getParent(), // inputBlocksFoundDuringWalk. [&](SILBasicBlock *loopBlock) { // This must be consumingPoint->getParent() since we only have one // consuming use. In this case, we know that this is the consuming // point where we will need a control equivalent copy_value (and that // destroy_value will be put for the out of loop value as appropriate. assert(loopBlock == consumingPoint->getParent()); auto front = loopBlock->begin(); SILBuilderWithScope newBuilder(front); result = newBuilder.createCopyValue(front->getLoc(), copy); callbacks.createdNewInst(result); llvm_unreachable("Should never visit this!"); }, // Input blocks in joint post dom set. We don't care about thse. [&](SILBasicBlock *postDomBlock) { auto front = postDomBlock->begin(); SILBuilderWithScope newBuilder(front); auto *dvi = newBuilder.createDestroyValue(front->getLoc(), copy); callbacks.createdNewInst(dvi); }); return result; } // A copy_value that we lifetime extend with destroy_value over range. We assume // all instructions passed into range do not consume value. template CopyValueInst * OwnershipLifetimeExtender::createPlusZeroCopy(SILValue value, const RangeTy &range) { auto *newValInsertPt = value->getDefiningInsertionPoint(); assert(newValInsertPt); CopyValueInst *copy; if (!isa(value)) { SILBuilderWithScope::insertAfter(newValInsertPt, [&](SILBuilder &builder) { copy = builder.createCopyValue(builder.getInsertionPointLoc(), value); }); } else { SILBuilderWithScope builder(newValInsertPt); copy = builder.createCopyValue(newValInsertPt->getLoc(), value); } auto &callbacks = ctx.callbacks; callbacks.createdNewInst(copy); auto opRange = makeUserRange(range); ValueLifetimeAnalysis lifetimeAnalysis(copy, opRange); ValueLifetimeAnalysis::Frontier frontier; bool result = lifetimeAnalysis.computeFrontier( frontier, ValueLifetimeAnalysis::DontModifyCFG, &ctx.deBlocks); assert(result); while (!frontier.empty()) { auto *insertPt = frontier.pop_back_val(); SILBuilderWithScope frontierBuilder(insertPt); auto *dvi = frontierBuilder.createDestroyValue(insertPt->getLoc(), copy); callbacks.createdNewInst(dvi); } return copy; } template BeginBorrowInst * OwnershipLifetimeExtender::createPlusZeroBorrow(SILValue newValue, RangeTy useRange) { auto *newValInsertPt = newValue->getDefiningInsertionPoint(); assert(newValInsertPt); CopyValueInst *copy = nullptr; BeginBorrowInst *borrow = nullptr; if (!isa(newValue)) { SILBuilderWithScope::insertAfter(newValInsertPt, [&](SILBuilder &builder) { auto loc = builder.getInsertionPointLoc(); copy = builder.createCopyValue(loc, newValue); borrow = builder.createBeginBorrow(loc, copy); }); } else { SILBuilderWithScope builder(newValInsertPt); auto loc = newValInsertPt->getLoc(); copy = builder.createCopyValue(loc, newValue); borrow = builder.createBeginBorrow(loc, copy); } assert(copy && borrow); auto opRange = makeUserRange(useRange); ValueLifetimeAnalysis lifetimeAnalysis(copy, opRange); ValueLifetimeAnalysis::Frontier frontier; bool result = lifetimeAnalysis.computeFrontier( frontier, ValueLifetimeAnalysis::DontModifyCFG, &ctx.deBlocks); assert(result); auto &callbacks = ctx.callbacks; while (!frontier.empty()) { auto *insertPt = frontier.pop_back_val(); SILBuilderWithScope frontierBuilder(insertPt); // Use an auto-generated location here, because insertPt may have an // incompatible LocationKind auto loc = RegularLocation::getAutoGeneratedLocation( insertPt->getLoc().getSourceLoc()); auto *ebi = frontierBuilder.createEndBorrow(loc, borrow); auto *dvi = frontierBuilder.createDestroyValue(loc, copy); callbacks.createdNewInst(ebi); callbacks.createdNewInst(dvi); } return borrow; } //===----------------------------------------------------------------------===// // Reborrow Elimination //===----------------------------------------------------------------------===// static void eliminateReborrowsOfRecursiveBorrows( ArrayRef transitiveReborrows, SmallVectorImpl &usePoints, InstModCallbacks &callbacks) { SmallVector, 8> baseBorrowedValuePair; // Ok, we have transitive reborrows. for (auto borrowingOperand : transitiveReborrows) { // We eliminate the reborrow by creating a new copy+borrow at the reborrow // edge from the base value and using that for the reborrow instead of the // actual value. We of course insert an end_borrow for our original incoming // value. SILValue value = borrowingOperand->get(); auto *bi = cast(borrowingOperand->getUser()); SILBuilderWithScope reborrowBuilder(bi); // Use an auto-generated location here, because the branch may have an // incompatible LocationKind auto loc = RegularLocation::getAutoGeneratedLocation(bi->getLoc().getSourceLoc()); auto *innerCopy = reborrowBuilder.createCopyValue(loc, value); auto *innerBorrow = reborrowBuilder.createBeginBorrow(loc, innerCopy); auto *outerEndBorrow = reborrowBuilder.createEndBorrow(loc, value); callbacks.createdNewInst(innerCopy); callbacks.createdNewInst(innerBorrow); callbacks.createdNewInst(outerEndBorrow); // Then set our borrowing operand to take our innerBorrow instead of value // (whose lifetime we just ended). callbacks.setUseValue(*borrowingOperand, innerBorrow); // Add our outer end borrow as a use point to make sure that we extend our // base value to this point. usePoints.push_back(&outerEndBorrow->getAllOperands()[0]); // Then check if in our destination block, we have further reborrows. If we // do, we need to recursively process them. auto *borrowedArg = const_cast(bi->getArgForOperand(borrowingOperand)); auto *baseArg = insertOwnedBaseValueAlongBranchEdge(bi, innerCopy, callbacks); baseBorrowedValuePair.emplace_back(baseArg, borrowedArg); } // Now recursively update all further reborrows... while (!baseBorrowedValuePair.empty()) { SILPhiArgument *baseArg; SILPhiArgument *borrowedArg; std::tie(baseArg, borrowedArg) = baseBorrowedValuePair.pop_back_val(); for (auto *use : borrowedArg->getConsumingUses()) { // If our consuming use is an end of scope marker, we need to end // the lifetime of our base arg. if (isEndOfScopeMarker(use->getUser())) { SILBuilderWithScope::insertAfter(use->getUser(), [&](SILBuilder &b) { auto *dvi = b.createDestroyValue(b.getInsertionPointLoc(), baseArg); callbacks.createdNewInst(dvi); }); continue; } // Otherwise, we have a reborrow. For now our reborrows must be // phis. Add our owned value as a new argument of that phi along our // edge and undef along all other edges. auto borrowingOp = *BorrowingOperand::get(use); auto *brInst = cast(borrowingOp.op->getUser()); auto *newBorrowedPhi = brInst->getArgForOperand(borrowingOp); auto *newBasePhi = insertOwnedBaseValueAlongBranchEdge(brInst, baseArg, callbacks); baseBorrowedValuePair.emplace_back(newBasePhi, newBorrowedPhi); } } } static void rewriteReborrows(SILValue newBorrowedValue, ArrayRef foundReborrows, InstModCallbacks &callbacks) { // Each initial reborrow that we have is a use of oldValue, so we know // that copy should be valid at the reborrow. SmallVector, 8> baseBorrowedValuePair; for (auto reborrow : foundReborrows) { auto *bi = cast(reborrow.op->getUser()); SILBuilderWithScope reborrowBuilder(bi); // Use an auto-generated location here, because the branch may have an // incompatible LocationKind auto loc = RegularLocation::getAutoGeneratedLocation(bi->getLoc().getSourceLoc()); auto *innerCopy = reborrowBuilder.createCopyValue(loc, newBorrowedValue); auto *innerBorrow = reborrowBuilder.createBeginBorrow(loc, innerCopy); auto *outerEndBorrow = reborrowBuilder.createEndBorrow(loc, reborrow.op->get()); callbacks.createdNewInst(innerCopy); callbacks.createdNewInst(innerBorrow); callbacks.createdNewInst(outerEndBorrow); callbacks.setUseValue(*reborrow, innerBorrow); auto *borrowedArg = const_cast(bi->getArgForOperand(reborrow.op)); auto *baseArg = insertOwnedBaseValueAlongBranchEdge(bi, innerCopy, callbacks); baseBorrowedValuePair.emplace_back(baseArg, borrowedArg); } // Now, follow through all chains of reborrows. while (!baseBorrowedValuePair.empty()) { SILPhiArgument *baseArg; SILPhiArgument *borrowedArg; std::tie(baseArg, borrowedArg) = baseBorrowedValuePair.pop_back_val(); for (auto *use : borrowedArg->getConsumingUses()) { // If our consuming use is an end of scope marker, we need to end // the lifetime of our base arg. if (isEndOfScopeMarker(use->getUser())) { SILBuilderWithScope::insertAfter(use->getUser(), [&](SILBuilder &b) { auto *dvi = b.createDestroyValue(b.getInsertionPointLoc(), baseArg); callbacks.createdNewInst(dvi); }); continue; } // Otherwise, we have a reborrow. For now our reborrows must be // phis. Add our owned value as a new argument of that phi along our // edge and undef along all other edges. auto borrowingOp = *BorrowingOperand::get(use); auto *brInst = cast(borrowingOp.op->getUser()); auto *newBorrowedPhi = brInst->getArgForOperand(borrowingOp); auto *newBasePhi = insertOwnedBaseValueAlongBranchEdge(brInst, baseArg, callbacks); baseBorrowedValuePair.emplace_back(newBasePhi, newBorrowedPhi); } } } //===----------------------------------------------------------------------===// // Ownership Fixup RAUW //===----------------------------------------------------------------------===// /// Given an old value and a new value, lifetime extend new value as appropriate /// so we can RAUW new value with old value and preserve ownership /// invariants. We leave fixing up the lifetime of old value to our caller. namespace { struct OwnershipRAUWUtility { SingleValueInstruction *oldValue; SILValue newValue; OwnershipFixupContext &ctx; SILBasicBlock::iterator handleUnowned(); SILBasicBlock::iterator handleGuaranteed(); SILBasicBlock::iterator perform(); /// Insert copies/borrows as appropriate to eliminate any reborrows of /// borrowed value, given we are going to replace it with newValue. void eliminateReborrows(BorrowedValue oldBorrowedValue, SILValue newValue); OwnershipLifetimeExtender getLifetimeExtender() { return {ctx}; } const InstModCallbacks &getCallbacks() const { return ctx.callbacks; } }; } // anonymous namespace SILBasicBlock::iterator OwnershipRAUWUtility::handleUnowned() { auto &callbacks = ctx.callbacks; switch (newValue.getOwnershipKind()) { case OwnershipKind::None: llvm_unreachable("Should have been handled elsewhere"); case OwnershipKind::Any: llvm_unreachable("Invalid for values"); case OwnershipKind::Unowned: // An unowned value can always be RAUWed with another unowned value. return replaceAllUsesAndErase(oldValue, newValue, callbacks); case OwnershipKind::Guaranteed: { // If we have an unowned value that we want to replace with a guaranteed // value, we need to ensure that the guaranteed value is live at all use // points of the unowned value. If so, just replace and continue. // // TODO: Implement this. // Otherwise, we need to lifetime extend the borrow over all of the use // points. To do so, we copy the value, borrow it, insert an unchecked // ownership conversion to unowned at oldValue and then RAUW. // // We need to insert the conversion to ensure that we do not violate // ownership propagation rules of forwarding insts. SmallVector oldValueUses(oldValue->getUses()); for (auto *use : oldValueUses) { if (auto *ti = dyn_cast(use->getUser())) { if (ti->isFunctionExiting()) { SILBuilderWithScope builder(ti); auto *newInst = builder.createUncheckedOwnershipConversion( ti->getLoc(), use->get(), OwnershipKind::Unowned); callbacks.createdNewInst(newInst); callbacks.setUseValue(use, newInst); } } } auto extender = getLifetimeExtender(); SILValue borrow = extender.createPlusZeroBorrow(newValue, oldValue->getUses()); SILBuilderWithScope builder(oldValue); auto *newInst = builder.createUncheckedOwnershipConversion( oldValue->getLoc(), borrow, OwnershipKind::Unowned); callbacks.createdNewInst(newInst); return replaceAllUsesAndErase(oldValue, newInst, callbacks); } case OwnershipKind::Owned: { // If we have an unowned value that we want to replace with an owned value, // we first check if the owned value is live over all use points of the old // value. If so, just RAUW and continue. // // TODO: Implement this. // Otherwise, insert a copy of the owned value and lifetime extend that over // all uses of the value and then RAUW. SmallVector oldValueUses(oldValue->getUses()); for (auto *use : oldValueUses) { if (auto *ti = dyn_cast(use->getUser())) { if (ti->isFunctionExiting()) { SILBuilderWithScope builder(ti); // We insert this to ensure that we can extend our owned value's // lifetime to before the function end point. auto *newInst = builder.createUncheckedOwnershipConversion( ti->getLoc(), use->get(), OwnershipKind::Unowned); callbacks.createdNewInst(newInst); callbacks.setUseValue(use, newInst); } } } auto extender = getLifetimeExtender(); SILValue copy = extender.createPlusZeroCopy(newValue, oldValue->getUses()); SILBuilderWithScope builder(oldValue); auto *newInst = builder.createUncheckedOwnershipConversion( oldValue->getLoc(), copy, OwnershipKind::Unowned); callbacks.createdNewInst(newInst); auto result = replaceAllUsesAndErase(oldValue, newInst, callbacks); return result; } } llvm_unreachable("covered switch isn't covered?!"); } SILBasicBlock::iterator OwnershipRAUWUtility::handleGuaranteed() { // If we want to replace a guaranteed value with a value of some other // ownership whose def dominates our guaranteed value. We first see if all // uses of the old guaranteed value are within the lifetime of the new // guaranteed value. If so, we can just RAUW and move on. // // TODO: Implement this. // // Otherwise, we need to actually modify the IR. We first always first // lifetime extend newValue to oldValue's transitive uses to set our // workspace. SmallVector usePoints; SmallVector recursiveBorrowScopeReborrows; getAllNonTrivialUsePointsOfBorrowedValue(oldValue, usePoints, recursiveBorrowScopeReborrows); // If we have any transitive reborrows on sub-borrows. if (recursiveBorrowScopeReborrows.size()) eliminateReborrowsOfRecursiveBorrows(recursiveBorrowScopeReborrows, usePoints, ctx.callbacks); auto extender = getLifetimeExtender(); SILValue newBorrowedValue = extender.createPlusZeroBorrow>(newValue, usePoints); // Now we need to handle reborrows by eliminating the reborrows from any // borrowing operands that use old value as well as from oldvalue itself. We // take advantage of a few properties of reborrows: // // 1. A reborrow has to be on a BorrowedValue. This ensures that the same // base value is propagated through chains of reborrows. (In the future // this may not be true when destructures are introduced as reborrow // instructions). // // 2. Given that, we change each reborrows into new copy+borrow from the // owned value that we perform at the reborrow use. What is nice about // this formulation is that it ensures that we are always working with a // non-dominating copy value, allowing us to force our borrowing value to // need a base phi argument (the one of our choosing). if (auto oldValueBorrowedVal = BorrowedValue::get(oldValue)) { SmallVector foundReborrows; if (oldValueBorrowedVal->gatherReborrows(foundReborrows)) { rewriteReborrows(newBorrowedValue, foundReborrows, ctx.callbacks); } } // Then we need to look and see if our oldValue had any transitive uses that // Ok, we now have eliminated any reborrows if we had any. That means that // the uses of oldValue should be completely within the lifetime of our new // borrow. return replaceAllUsesAndErase(oldValue, newBorrowedValue, ctx.callbacks); } SILBasicBlock::iterator OwnershipRAUWUtility::perform() { assert(oldValue->getFunction()->hasOwnership()); assert( OwnershipFixupContext::canFixUpOwnershipForRAUW(oldValue, newValue) && "Should have checked if can perform this operation before calling it?!"); // If our new value is just none, we can pass anything to do it so just RAUW // and return. // // NOTE: This handles RAUWing with undef. if (newValue.getOwnershipKind() == OwnershipKind::None) return replaceAllUsesAndErase(oldValue, newValue, ctx.callbacks); assert(SILValue(oldValue).getOwnershipKind() != OwnershipKind::None); switch (SILValue(oldValue).getOwnershipKind()) { case OwnershipKind::None: // If our old value was none and our new value is not, we need to do // something more complex that we do not support yet, so bail. We should // have not called this function in such a case. llvm_unreachable("Should have been handled elsewhere"); case OwnershipKind::Any: llvm_unreachable("Invalid for values"); case OwnershipKind::Guaranteed: { return handleGuaranteed(); } case OwnershipKind::Owned: { // If we have an owned value that we want to replace with a value with any // other non-None ownership, we need to copy the other value for a // lifetimeEnding RAUW, then RAUW the value, and insert a destroy_value on // the original value. auto extender = getLifetimeExtender(); SILValue copy = extender.createPlusOneCopy(newValue, oldValue); cleanupOperandsBeforeDeletion(oldValue, ctx.callbacks); auto result = replaceAllUsesAndErase(oldValue, copy, ctx.callbacks); return result; } case OwnershipKind::Unowned: { return handleUnowned(); } } llvm_unreachable("Covered switch isn't covered?!"); } //===----------------------------------------------------------------------===// // Ownership Fixup Context //===----------------------------------------------------------------------===// // All callers of our RAUW routines must ensure that their values return true // from this. bool OwnershipFixupContext::canFixUpOwnershipForRAUW(SILValue oldValue, SILValue newValue) { auto newOwnershipKind = newValue.getOwnershipKind(); // If our new kind is ValueOwnershipKind::None, then we are fine. We // trivially support that. This check also ensures that we can always // replace any value with a ValueOwnershipKind::None value. if (newOwnershipKind == OwnershipKind::None) return true; // First check if oldValue is SILUndef. If it is, then we know that: // // 1. SILUndef (and thus oldValue) must have OwnershipKind::None. // 2. newValue is not OwnershipKind::None due to our check above. // // Thus we know that we would be replacing a value with OwnershipKind::None // with a value with non-None ownership. This is a case we don't support, so // we can bail now. if (isa(oldValue)) return false; // Ok, we now know that we do not have SILUndef implying that we must be able // to get a module from our value since we must have an argument or an // instruction. auto *m = oldValue->getModule(); assert(m); // If we are in Raw SIL, just bail at this point. We do not support // ownership fixups. if (m->getStage() == SILStage::Raw) return false; // If our old ownership kind is ValueOwnershipKind::None and our new kind is // not, we may need to do more work that has not been implemented yet. So // bail. // // Due to our requirement that types line up, this can only occur given a // non-trivial typed value with None ownership. This can only happen when // oldValue is a trivial payloaded or no-payload non-trivially typed // enum. That doesn't occur that often so we just bail on it today until we // implement this functionality. auto oldOwnershipKind = SILValue(oldValue).getOwnershipKind(); if (oldOwnershipKind != OwnershipKind::None) return true; // Ok, we have an old ownership kind that is OwnershipKind::None and a new // ownership kind that is not OwnershipKind::None. In that case, for now, do // not perform this transform. return false; } SILBasicBlock::iterator OwnershipFixupContext::replaceAllUsesAndErase(SingleValueInstruction *oldValue, SILValue newValue) { OwnershipRAUWUtility utility{oldValue, newValue, *this}; return utility.perform(); }