//===--- OwnershipUtils.cpp -----------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 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 // //===----------------------------------------------------------------------===// #include "swift/SIL/OwnershipUtils.h" #include "swift/Basic/DAGNodeWorklist.h" #include "swift/Basic/Defer.h" #include "swift/Basic/DAGNodeWorklist.h" #include "swift/Basic/SmallPtrSetVector.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/LinearLifetimeChecker.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/Projection.h" #include "swift/SIL/PrunedLiveness.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILInstruction.h" using namespace swift; bool swift::isValueAddressOrTrivial(SILValue v) { return v->getType().isAddress() || v.getOwnershipKind() == OwnershipKind::None; } // These operations forward both owned and guaranteed ownership. static bool isOwnershipForwardingInstructionKind(SILInstructionKind kind) { switch (kind) { case SILInstructionKind::TupleInst: case SILInstructionKind::StructInst: case SILInstructionKind::EnumInst: case SILInstructionKind::DifferentiableFunctionInst: case SILInstructionKind::LinearFunctionInst: case SILInstructionKind::OpenExistentialRefInst: case SILInstructionKind::UpcastInst: case SILInstructionKind::UncheckedValueCastInst: case SILInstructionKind::UncheckedRefCastInst: case SILInstructionKind::ConvertFunctionInst: case SILInstructionKind::RefToBridgeObjectInst: case SILInstructionKind::BridgeObjectToRefInst: case SILInstructionKind::UnconditionalCheckedCastInst: case SILInstructionKind::UncheckedEnumDataInst: case SILInstructionKind::SelectEnumInst: case SILInstructionKind::SwitchEnumInst: case SILInstructionKind::CheckedCastBranchInst: case SILInstructionKind::DestructureStructInst: case SILInstructionKind::DestructureTupleInst: case SILInstructionKind::MarkDependenceInst: case SILInstructionKind::InitExistentialRefInst: return true; default: return false; } } // These operations forward guaranteed ownership, but don't necessarily forward // owned values. static bool isGuaranteedForwardingInstructionKind(SILInstructionKind kind) { switch (kind) { case SILInstructionKind::TupleExtractInst: case SILInstructionKind::StructExtractInst: case SILInstructionKind::DifferentiableFunctionExtractInst: case SILInstructionKind::LinearFunctionExtractInst: case SILInstructionKind::OpenExistentialValueInst: case SILInstructionKind::OpenExistentialBoxValueInst: return true; default: return isOwnershipForwardingInstructionKind(kind); } } bool swift::canOpcodeForwardGuaranteedValues(SILValue value) { // If we have an argument from a transforming terminator, we can forward // guaranteed. if (auto *arg = dyn_cast(value)) if (auto *ti = arg->getSingleTerminator()) if (ti->isTransformationTerminator()) { assert(OwnershipForwardingMixin::isa(ti)); return true; } auto *inst = value->getDefiningInstruction(); if (!inst) return false; bool result = isGuaranteedForwardingInstructionKind(inst->getKind()); if (result) { assert(!isa(inst)); assert(OwnershipForwardingMixin::isa(inst)); } return result; } bool swift::canOpcodeForwardGuaranteedValues(Operand *use) { auto *user = use->getUser(); bool result = isOwnershipForwardingInstructionKind(user->getKind()); if (result) { assert(!isa(user)); assert(OwnershipForwardingMixin::isa(user)); } return result; } static bool isOwnedForwardingValueKind(SILInstructionKind kind) { switch (kind) { case SILInstructionKind::MarkUninitializedInst: return true; default: return isOwnershipForwardingInstructionKind(kind); } } bool swift::canOpcodeForwardOwnedValues(SILValue value) { // If we have a SILArgument and we are the successor block of a transforming // terminator, we are fine. if (auto *arg = dyn_cast(value)) if (auto *predTerm = arg->getSingleTerminator()) if (predTerm->isTransformationTerminator()) { assert(OwnershipForwardingMixin::isa(predTerm)); return true; } auto *inst = value->getDefiningInstruction(); if (!inst) return false; bool result = isOwnedForwardingValueKind(inst->getKind()); if (result) { assert(!isa(inst)); assert(OwnershipForwardingMixin::isa(inst)); } return result; } bool swift::canOpcodeForwardOwnedValues(Operand *use) { auto *user = use->getUser(); bool result = isOwnershipForwardingInstructionKind(user->getKind()); if (result) { assert(OwnershipForwardingMixin::isa(user)); } return result; } //===----------------------------------------------------------------------===// // Guaranteed Use-Point (Lifetime) Discovery //===----------------------------------------------------------------------===// // Find all use points of \p guaranteedValue within its borrow scope. All uses // are naturally dominated by \p guaranteedValue. If a PointerEscape is found, // then no assumption can be made about \p guaranteedValue's lifetime. Therefore // the use points are incomplete and this returns false. // // Accumulate results in \p usePoints, ignoring existing elements. // // Skip over nested borrow scopes. Their scope-ending instructions are their use // points. Transitively find all nested scope-ending instructions by looking // through nested reborrows. Nested reborrows are not use points. bool swift::findInnerTransitiveGuaranteedUses( SILValue guaranteedValue, SmallVectorImpl *usePoints) { auto leafUse = [&](Operand *use) { if (usePoints && use->getOperandOwnership() != OperandOwnership::NonUse) { usePoints->push_back(use); } return true; }; // Push the value's immediate uses. // // TODO: The worklist can be a simple vector without any a membership check if // destructures are changed to be represented as reborrows. Currently a // destructure forwards multiple results! This means that the worklist could // grow exponentially without the membership check. It's fine to do this // membership check locally in this function (within a borrow scope) because // it isn't needed for the immediate uses, only the transitive uses. DAGNodeWorklist worklist; for (Operand *use : guaranteedValue->getUses()) { if (use->getOperandOwnership() != OperandOwnership::NonUse) worklist.insert(use); } // --- Transitively follow forwarded uses and look for escapes. // usePoints grows in this loop. while (Operand *use = worklist.pop()) { switch (use->getOperandOwnership()) { case OperandOwnership::NonUse: case OperandOwnership::TrivialUse: case OperandOwnership::ForwardingConsume: case OperandOwnership::DestroyingConsume: llvm_unreachable("this operand cannot handle an inner guaranteed use"); case OperandOwnership::ForwardingUnowned: case OperandOwnership::PointerEscape: return false; case OperandOwnership::InstantaneousUse: case OperandOwnership::UnownedInstantaneousUse: case OperandOwnership::BitwiseEscape: // Reborrow only happens when this is called on a value that creates a // borrow scope. case OperandOwnership::Reborrow: // EndBorrow either happens when this is called on a value that creates a // borrow scope, or when it is pushed as a use when processing a nested // borrow. case OperandOwnership::EndBorrow: leafUse(use); break; case OperandOwnership::InteriorPointer: return false; #if 0 // FIXME!!! Enable in a following commit that fixes RAUW // 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 (InteriorPointerOperand(use).findTransitiveUses(usePoints) != AddressUseKind::NonEscaping) { return false; } #endif break; case OperandOwnership::ForwardingBorrow: { bool nonLeaf = false; ForwardingOperand(use).visitForwardedValues([&](SILValue result) { // Do not include transitive uses with 'none' ownership if (result.getOwnershipKind() == OwnershipKind::None) return true; for (auto *resultUse : result->getUses()) { if (resultUse->getOperandOwnership() != OperandOwnership::NonUse) { nonLeaf = true; worklist.insert(resultUse); } } return true; }); // e.g. A dead forwarded value, e.g. a switch_enum with only trivial uses, // must itself be a leaf use. if (!nonLeaf) { leafUse(use); } break; } case OperandOwnership::Borrow: BorrowingOperand(use).visitExtendedScopeEndingUses([&](Operand *endUse) { leafUse(endUse); return true; }); break; } } return true; } // Find all use points of \p guaranteedValue within its borrow scope. All use // points will be dominated by \p guaranteedValue. // // Record (non-nested) reborrows as uses. // // BorrowedValues (which introduce a borrow scope) are fundamentally different // than "inner" guaranteed values. Their only use points are their scope-ending // uses. There is no need to transitively process uses. However, unlike inner // guaranteed values, they can have reborrows. To transitively process // reborrows, use findExtendedTransitiveBorrowedUses. bool swift::findTransitiveGuaranteedUses( SILValue guaranteedValue, SmallVectorImpl &usePoints, function_ref visitReborrow) { // Handle local borrow introducers without following uses. // SILFunctionArguments are *not* borrow introducers in this context--we're // trying to find lifetime of values within a function. if (auto borrowedValue = BorrowedValue(guaranteedValue)) { if (borrowedValue.isLocalScope()) { borrowedValue.visitLocalScopeEndingUses([&](Operand *scopeEnd) { // Initially push the reborrow as a use point. visitReborrow may pop it // if it only wants to compute the extended lifetime's use points. usePoints.push_back(scopeEnd); if (scopeEnd->getOperandOwnership() == OperandOwnership::Reborrow) visitReborrow(scopeEnd); return true; }); } return true; } return findInnerTransitiveGuaranteedUses(guaranteedValue, &usePoints); } // Find all use points of \p guaranteedValue within its borrow scope. If the // guaranteed value introduces a borrow scope, then this includes the extended // borrow scope by following reborrows. bool swift:: findExtendedTransitiveGuaranteedUses(SILValue guaranteedValue, SmallVectorImpl &usePoints) { // Multiple paths may reach the same reborrows, and reborrow may even be // recursive, so the working set requires a membership check. SmallPtrSetVector reborrows; auto visitReborrow = [&](Operand *reborrow) { // Pop the reborrow. It should not appear in the use points of the // extend lifetime. assert(reborrow == usePoints.back()); usePoints.pop_back(); auto borrowedPhi = BorrowingOperand(reborrow).getBorrowIntroducingUserResult(); reborrows.insert(borrowedPhi.value); }; if (!findTransitiveGuaranteedUses(guaranteedValue, usePoints, visitReborrow)) return false; // For guaranteed values that do not introduce a borrow scope, reborrows will // be empty at this point. for (unsigned idx = 0; idx < reborrows.size(); ++idx) { bool result = findTransitiveGuaranteedUses(reborrows[idx], usePoints, visitReborrow); // It is impossible to find a Pointer escape while traversing reborrows. assert(result && "visiting reborrows always succeeds"); (void)result; } return true; } //===----------------------------------------------------------------------===// // Borrowing Operand //===----------------------------------------------------------------------===// void BorrowingOperandKind::print(llvm::raw_ostream &os) const { switch (value) { case Kind::Invalid: llvm_unreachable("Using an unreachable?!"); case Kind::BeginBorrow: os << "BeginBorrow"; return; case Kind::BeginApply: os << "BeginApply"; return; case Kind::Branch: os << "Branch"; return; case Kind::Apply: os << "Apply"; return; case Kind::TryApply: os << "TryApply"; return; case Kind::Yield: os << "Yield"; return; } llvm_unreachable("Covered switch isn't covered?!"); } llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os, BorrowingOperandKind kind) { kind.print(os); return os; } void BorrowingOperand::print(llvm::raw_ostream &os) const { os << "BorrowScopeOperand:\n" "Kind: " << kind << "\n" "Value: " << op->get() << "User: " << *op->getUser(); } llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os, const BorrowingOperand &operand) { operand.print(os); return os; } bool BorrowingOperand::hasEmptyRequiredEndingUses() const { switch (kind) { case BorrowingOperandKind::Invalid: llvm_unreachable("Using invalid case"); case BorrowingOperandKind::BeginBorrow: case BorrowingOperandKind::BeginApply: { return op->getUser()->hasUsesOfAnyResult(); } case BorrowingOperandKind::Branch: { auto *br = cast(op->getUser()); return br->getArgForOperand(op)->use_empty(); } // These are instantaneous borrow scopes so there aren't any special end // scope instructions. case BorrowingOperandKind::Apply: case BorrowingOperandKind::TryApply: case BorrowingOperandKind::Yield: return false; } llvm_unreachable("Covered switch isn't covered"); } bool BorrowingOperand::visitScopeEndingUses( function_ref func) const { switch (kind) { case BorrowingOperandKind::Invalid: llvm_unreachable("Using invalid case"); case BorrowingOperandKind::BeginBorrow: for (auto *use : cast(op->getUser())->getUses()) { if (use->isLifetimeEnding()) { if (!func(use)) return false; } } return true; case BorrowingOperandKind::BeginApply: { auto *user = cast(op->getUser()); for (auto *use : user->getTokenResult()->getUses()) { if (!func(use)) return false; } return true; } // These are instantaneous borrow scopes so there aren't any special end // scope instructions. case BorrowingOperandKind::Apply: case BorrowingOperandKind::TryApply: case BorrowingOperandKind::Yield: return true; case BorrowingOperandKind::Branch: { auto *br = cast(op->getUser()); for (auto *use : br->getArgForOperand(op)->getUses()) if (use->isLifetimeEnding()) if (!func(use)) return false; return true; } } llvm_unreachable("Covered switch isn't covered"); } bool BorrowingOperand::visitExtendedScopeEndingUses( function_ref visitor) const { if (hasBorrowIntroducingUser()) { return visitBorrowIntroducingUserResults( [visitor](BorrowedValue borrowedValue) { return borrowedValue.visitExtendedScopeEndingUses(visitor); }); } return visitScopeEndingUses(visitor); } bool BorrowingOperand::visitBorrowIntroducingUserResults( function_ref visitor) const { switch (kind) { case BorrowingOperandKind::Invalid: llvm_unreachable("Using invalid case"); case BorrowingOperandKind::Apply: case BorrowingOperandKind::TryApply: case BorrowingOperandKind::BeginApply: case BorrowingOperandKind::Yield: llvm_unreachable("Never has borrow introducer results!"); case BorrowingOperandKind::BeginBorrow: { auto value = BorrowedValue(cast(op->getUser())); assert(value); return visitor(value); } case BorrowingOperandKind::Branch: { auto *bi = cast(op->getUser()); auto value = BorrowedValue( bi->getDestBB()->getArgument(op->getOperandNumber())); assert(value && "guaranteed-to-unowned conversion not allowed on branches"); return visitor(value); } } llvm_unreachable("Covered switch isn't covered?!"); } BorrowedValue BorrowingOperand::getBorrowIntroducingUserResult() { switch (kind) { case BorrowingOperandKind::Invalid: case BorrowingOperandKind::Apply: case BorrowingOperandKind::TryApply: case BorrowingOperandKind::BeginApply: case BorrowingOperandKind::Yield: return BorrowedValue(); case BorrowingOperandKind::BeginBorrow: return BorrowedValue(cast(op->getUser())); case BorrowingOperandKind::Branch: { auto *bi = cast(op->getUser()); return BorrowedValue(bi->getDestBB()->getArgument(op->getOperandNumber())); } } llvm_unreachable("covered switch"); } void BorrowingOperand::getImplicitUses( SmallVectorImpl &foundUses, std::function *errorFunction) const { visitScopeEndingUses([&](Operand *op) { foundUses.push_back(op); return true; }); } //===----------------------------------------------------------------------===// // Borrow Introducers //===----------------------------------------------------------------------===// void BorrowedValueKind::print(llvm::raw_ostream &os) const { switch (value) { case BorrowedValueKind::Invalid: llvm_unreachable("Using invalid case?!"); case BorrowedValueKind::SILFunctionArgument: os << "SILFunctionArgument"; return; case BorrowedValueKind::BeginBorrow: os << "BeginBorrowInst"; return; case BorrowedValueKind::LoadBorrow: os << "LoadBorrowInst"; return; case BorrowedValueKind::Phi: os << "Phi"; return; } llvm_unreachable("Covered switch isn't covered?!"); } void BorrowedValue::print(llvm::raw_ostream &os) const { os << "BorrowScopeIntroducingValue:\n" "Kind: " << kind << "\n" "Value: " << value; } void BorrowedValue::getLocalScopeEndingInstructions( SmallVectorImpl &scopeEndingInsts) const { assert(isLocalScope() && "Should only call this given a local scope"); switch (kind) { case BorrowedValueKind::Invalid: llvm_unreachable("Using invalid case?!"); case BorrowedValueKind::SILFunctionArgument: llvm_unreachable("Should only call this with a local scope"); case BorrowedValueKind::BeginBorrow: case BorrowedValueKind::LoadBorrow: case BorrowedValueKind::Phi: for (auto *use : value->getUses()) { if (use->isLifetimeEnding()) { scopeEndingInsts.push_back(use->getUser()); } } return; } llvm_unreachable("Covered switch isn't covered?!"); } // Note: BorrowedLifetimeExtender assumes no intermediate values between a // borrow introducer and its reborrow. The borrowed value must be an operand of // the reborrow. bool BorrowedValue::visitLocalScopeEndingUses( function_ref visitor) const { assert(isLocalScope() && "Should only call this given a local scope"); switch (kind) { case BorrowedValueKind::Invalid: llvm_unreachable("Using invalid case?!"); case BorrowedValueKind::SILFunctionArgument: llvm_unreachable("Should only call this with a local scope"); case BorrowedValueKind::LoadBorrow: case BorrowedValueKind::BeginBorrow: case BorrowedValueKind::Phi: for (auto *use : value->getUses()) { if (use->isLifetimeEnding()) { if (!visitor(use)) return false; } } return true; } llvm_unreachable("Covered switch isn't covered?!"); } llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os, BorrowedValueKind kind) { kind.print(os); return os; } llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os, const BorrowedValue &value) { value.print(os); return os; } /// Add this scopes live blocks into the PrunedLiveness result. void BorrowedValue::computeLiveness(PrunedLiveness &liveness) const { liveness.initializeDefBlock(value->getParentBlock()); visitLocalScopeEndingUses([&](Operand *endOp) { liveness.updateForUse(endOp->getUser(), true); return true; }); } bool BorrowedValue::areUsesWithinLocalScope( ArrayRef uses, DeadEndBlocks &deadEndBlocks) const { // First make sure that we actually have a local scope. If we have a non-local // scope, then we have something (like a SILFunctionArgument) where a larger // semantic construct (in the case of SILFunctionArgument, the function // itself) acts as the scope. So we already know that our passed in // instructions must be in the same scope. if (!isLocalScope()) return true; // Compute the local scope's liveness. PrunedLiveness liveness; computeLiveness(liveness); return liveness.areUsesWithinBoundary(uses, deadEndBlocks); } // The visitor \p func is only called on final scope-ending uses, not reborrows. bool BorrowedValue::visitExtendedScopeEndingUses( function_ref visitor) const { assert(isLocalScope()); SmallPtrSetVector reborrows; auto visitEnd = [&](Operand *scopeEndingUse) { if (scopeEndingUse->getOperandOwnership() == OperandOwnership::Reborrow) { BorrowingOperand(scopeEndingUse).visitBorrowIntroducingUserResults( [&](BorrowedValue borrowedValue) { reborrows.insert(borrowedValue.value); return true; }); return true; } return visitor(scopeEndingUse); }; if (!visitLocalScopeEndingUses(visitEnd)) return false; // reborrows grows in this loop. for (unsigned idx = 0; idx < reborrows.size(); ++idx) { if (!BorrowedValue(reborrows[idx]).visitLocalScopeEndingUses(visitEnd)) return false; } return true; } bool BorrowedValue::visitInteriorPointerOperandHelper( function_ref func, BorrowedValue::InteriorPointerOperandVisitorKind kind) const { using Kind = BorrowedValue::InteriorPointerOperandVisitorKind; SmallVector worklist(value->getUses()); while (!worklist.empty()) { auto *op = worklist.pop_back_val(); if (auto interiorPointer = InteriorPointerOperand(op)) { func(interiorPointer); continue; } if (auto borrowingOperand = BorrowingOperand(op)) { switch (kind) { case Kind::NoNestedNoReborrows: // We do not look through nested things and or reborrows, so just // continue. continue; case Kind::YesNestedNoReborrows: // We only look through nested borrowing operands, we never look through // reborrows though. if (borrowingOperand.isReborrow()) continue; break; case Kind::YesNestedYesReborrows: // Look through everything! break; } borrowingOperand.visitBorrowIntroducingUserResults([&](auto bv) { for (auto *use : bv->getUses()) { if (auto intPtrOperand = InteriorPointerOperand(use)) { func(intPtrOperand); continue; } worklist.push_back(use); } return true; }); continue; } auto *user = op->getUser(); if (isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user)) { continue; } // These are interior pointers that have not had support yet added for them. if (isa(user)) { continue; } // Look through object. if (auto *svi = dyn_cast(user)) { if (Projection::isObjectProjection(svi)) { for (SILValue result : user->getResults()) { llvm::copy(result->getUses(), std::back_inserter(worklist)); } continue; } } return false; } return true; } AddressUseKind swift::findTransitiveUsesForAddress(SILValue projectedAddress, SmallVectorImpl *foundUses, std::function *onError) { // If the projectedAddress is dead, it is itself a leaf use. Since we don't // have an operand for it, simply bail. Dead projectedAddress is unexpected. // // TODO: store_borrow is currently an InteriorPointer with no uses, so we end // up bailing. It should be in a dependence scope instead. It's not clear why // it produces an address at all. if (projectedAddress->use_empty()) return AddressUseKind::PointerEscape; SmallVector worklist(projectedAddress->getUses()); AddressUseKind result = AddressUseKind::NonEscaping; auto leafUse = [foundUses](Operand *use) { if (foundUses) foundUses->push_back(use); }; auto transitiveResultUses = [&](Operand *use) { auto *svi = cast(use->getUser()); if (svi->use_empty()) { leafUse(use); } else { worklist.append(svi->use_begin(), svi->use_end()); } }; while (!worklist.empty()) { auto *op = worklist.pop_back_val(); // Skip type dependent operands. if (op->isTypeDependent()) continue; // Then update the worklist with new things to find if we recognize this // inst and then continue. If we fail, we emit an error at the bottom of the // loop that we didn't recognize the user. auto *user = op->getUser(); // TODO: Partial apply should be NonEscaping, but then we need to consider // the apply to be a use point. if (isa(user) || isa(user)) { result = meet(result, AddressUseKind::PointerEscape); continue; } // First, eliminate "end point uses" that we just need to check liveness at // and do not need to check transitive uses of. if (isa(user) || isa(user) || isIncidentalUse(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user)) { leafUse(op); continue; } if (isa(user) || isa(user)) { assert(!user->hasResults()); continue; } // Then handle users that we need to look at transitive uses of. if (Projection::isAddressProjection(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user) || isa(user)) { transitiveResultUses(op); continue; } if (auto *builtin = dyn_cast(user)) { if (auto kind = builtin->getBuiltinKind()) { if (*kind == BuiltinValueKind::TSanInoutAccess) { leafUse(op); continue; } } } // If we have a load_borrow, add it's end scope to the liveness requirement. if (auto *lbi = dyn_cast(user)) { if (foundUses) { for (Operand *use : lbi->getUses()) { if (use->endsLocalBorrowScope()) { leafUse(use); } } } continue; } // TODO: Merge this into the full apply site code below. if (auto *beginApply = dyn_cast(user)) { if (foundUses) { // TODO: the empty check should not be needed when dead begin_apply is // disallowed. if (beginApply->getTokenResult()->use_empty()) { leafUse(op); } else { llvm::copy(beginApply->getTokenResult()->getUses(), std::back_inserter(*foundUses)); } } continue; } if (auto fas = FullApplySite::isa(user)) { leafUse(op); continue; } if (auto *mdi = dyn_cast(user)) { // If this is the base, just treat it as a liveness use. if (op->get() == mdi->getBase()) { leafUse(op); continue; } // If we are the value use, look through it. transitiveResultUses(op); continue; } // We were unable to recognize this user, so return true that we failed. if (onError) { (*onError)(op); } result = AddressUseKind::Unknown; } return result; } //===----------------------------------------------------------------------===// // AddressOwnership //===----------------------------------------------------------------------===// bool AddressOwnership::areUsesWithinLifetime( ArrayRef uses, DeadEndBlocks &deadEndBlocks) const { if (!base.hasLocalOwnershipLifetime()) return true; SILValue root = base.getOwnershipReferenceRoot(); BorrowedValue borrow(root); if (borrow) return borrow.areUsesWithinLocalScope(uses, deadEndBlocks); // --- A reference no borrow scope. Currently happens for project_box. // Compute the reference value's liveness. PrunedLiveness liveness; liveness.computeSSALiveness(root); return liveness.areUsesWithinBoundary(uses, deadEndBlocks); } //===----------------------------------------------------------------------===// // Owned Value Introducers //===----------------------------------------------------------------------===// void OwnedValueIntroducerKind::print(llvm::raw_ostream &os) const { switch (value) { case OwnedValueIntroducerKind::Invalid: llvm_unreachable("Using invalid case?!"); case OwnedValueIntroducerKind::Apply: os << "Apply"; return; case OwnedValueIntroducerKind::BeginApply: os << "BeginApply"; return; case OwnedValueIntroducerKind::TryApply: os << "TryApply"; return; case OwnedValueIntroducerKind::Copy: os << "Copy"; return; case OwnedValueIntroducerKind::LoadCopy: os << "LoadCopy"; return; case OwnedValueIntroducerKind::LoadTake: os << "LoadTake"; return; case OwnedValueIntroducerKind::Phi: os << "Phi"; return; case OwnedValueIntroducerKind::Struct: os << "Struct"; return; case OwnedValueIntroducerKind::Tuple: os << "Tuple"; return; case OwnedValueIntroducerKind::FunctionArgument: os << "FunctionArgument"; return; case OwnedValueIntroducerKind::PartialApplyInit: os << "PartialApplyInit"; return; case OwnedValueIntroducerKind::AllocBoxInit: os << "AllocBoxInit"; return; case OwnedValueIntroducerKind::AllocRefInit: os << "AllocRefInit"; return; } llvm_unreachable("Covered switch isn't covered"); } //===----------------------------------------------------------------------===// // Introducer Searching Routines //===----------------------------------------------------------------------===// bool swift::getAllBorrowIntroducingValues(SILValue inputValue, SmallVectorImpl &out) { if (inputValue.getOwnershipKind() != OwnershipKind::Guaranteed) return false; SmallVector worklist; worklist.emplace_back(inputValue); while (!worklist.empty()) { SILValue value = worklist.pop_back_val(); // First check if v is an introducer. If so, stash it and continue. if (auto scopeIntroducer = BorrowedValue(value)) { out.push_back(scopeIntroducer); continue; } // If v produces .none ownership, then we can ignore it. It is important // that we put this before checking for guaranteed forwarding instructions, // since we want to ignore guaranteed forwarding instructions that in this // specific case produce a .none value. if (value.getOwnershipKind() == OwnershipKind::None) continue; // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isForwardingBorrow(value)) { if (auto *i = value->getDefiningInstruction()) { llvm::copy(i->getOperandValues(true /*skip type dependent ops*/), std::back_inserter(worklist)); continue; } // Otherwise, we should have a block argument that is defined by a single // predecessor terminator. auto *arg = cast(value); auto *termInst = arg->getSingleTerminator(); assert(termInst && termInst->isTransformationTerminator()); assert(termInst->getNumOperands() == 1 && "Transforming terminators should always have a single operand"); worklist.push_back(termInst->getAllOperands()[0].get()); continue; } // Otherwise, this is an introducer we do not understand. Bail and return // false. return false; } return true; } // FIXME: replace this logic with AccessBase::findOwnershipReferenceRoot. BorrowedValue swift::getSingleBorrowIntroducingValue(SILValue inputValue) { if (inputValue.getOwnershipKind() != OwnershipKind::Guaranteed) return {}; SILValue currentValue = inputValue; while (true) { // First check if our initial value is an introducer. If we have one, just // return it. if (auto scopeIntroducer = BorrowedValue(currentValue)) { return scopeIntroducer; } if (currentValue.getOwnershipKind() == OwnershipKind::None) return {}; // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isForwardingBorrow(currentValue)) { if (auto *i = currentValue->getDefiningInstruction()) { auto instOps = i->getOperandValues(true /*ignore type dependent ops*/); // If we have multiple incoming values, return .None. We can't handle // this. auto begin = instOps.begin(); if (std::next(begin) != instOps.end()) { return {}; } // Otherwise, set currentOp to the single operand and continue. currentValue = *begin; continue; } // Otherwise, we should have a block argument that is defined by a single // predecessor terminator. auto *arg = cast(currentValue); auto *termInst = arg->getSingleTerminator(); assert(termInst && termInst->isTransformationTerminator()); assert(termInst->getNumOperands() == 1 && "Transformation terminators should only have single operands"); currentValue = termInst->getAllOperands()[0].get(); continue; } // Otherwise, this is an introducer we do not understand. Bail and return // None. return {}; } llvm_unreachable("Should never hit this"); } bool swift::getAllOwnedValueIntroducers( SILValue inputValue, SmallVectorImpl &out) { if (inputValue.getOwnershipKind() != OwnershipKind::Owned) return false; SmallVector worklist; worklist.emplace_back(inputValue); while (!worklist.empty()) { SILValue value = worklist.pop_back_val(); // First check if v is an introducer. If so, stash it and continue. if (auto introducer = OwnedValueIntroducer::get(value)) { out.push_back(introducer); continue; } // If v produces .none ownership, then we can ignore it. It is important // that we put this before checking for guaranteed forwarding instructions, // since we want to ignore guaranteed forwarding instructions that in this // specific case produce a .none value. if (value.getOwnershipKind() == OwnershipKind::None) continue; // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isForwardingConsume(value)) { if (auto *i = value->getDefiningInstruction()) { llvm::copy(i->getOperandValues(true /*skip type dependent ops*/), std::back_inserter(worklist)); continue; } // Otherwise, we should have a block argument that is defined by a single // predecessor terminator. auto *arg = cast(value); auto *termInst = arg->getSingleTerminator(); assert(termInst && termInst->isTransformationTerminator()); assert(termInst->getNumOperands() == 1 && "Transforming terminators should always have a single operand"); worklist.push_back(termInst->getAllOperands()[0].get()); continue; } // Otherwise, this is an introducer we do not understand. Bail and return // false. return false; } return true; } OwnedValueIntroducer swift::getSingleOwnedValueIntroducer(SILValue inputValue) { if (inputValue.getOwnershipKind() != OwnershipKind::Owned) return {}; SILValue currentValue = inputValue; while (true) { // First check if our initial value is an introducer. If we have one, just // return it. if (auto introducer = OwnedValueIntroducer::get(currentValue)) { return introducer; } // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isForwardingConsume(currentValue)) { if (auto *i = currentValue->getDefiningInstruction()) { auto instOps = i->getOperandValues(true /*ignore type dependent ops*/); // If we have multiple incoming values, return .None. We can't handle // this. auto begin = instOps.begin(); if (std::next(begin) != instOps.end()) { return {}; } // Otherwise, set currentOp to the single operand and continue. currentValue = *begin; continue; } // Otherwise, we should have a block argument that is defined by a single // predecessor terminator. auto *arg = cast(currentValue); auto *termInst = arg->getSingleTerminator(); assert(termInst && termInst->isTransformationTerminator()); assert(termInst->getNumOperands() - termInst->getNumTypeDependentOperands() == 1 && "Transformation terminators should only have single operands"); currentValue = termInst->getAllOperands()[0].get(); continue; } // Otherwise, this is an introducer we do not understand. Bail and return // None. return {}; } llvm_unreachable("Should never hit this"); } //===----------------------------------------------------------------------===// // Forwarding Operand //===----------------------------------------------------------------------===// ForwardingOperand::ForwardingOperand(Operand *use) { if (use->isTypeDependent()) return; if (!OwnershipForwardingMixin::isa(use->getUser())) { return; } #ifndef NDEBUG switch (use->getOperandOwnership()) { case OperandOwnership::ForwardingUnowned: case OperandOwnership::ForwardingConsume: case OperandOwnership::ForwardingBorrow: break; case OperandOwnership::NonUse: case OperandOwnership::TrivialUse: case OperandOwnership::InstantaneousUse: case OperandOwnership::UnownedInstantaneousUse: case OperandOwnership::PointerEscape: case OperandOwnership::BitwiseEscape: case OperandOwnership::Borrow: case OperandOwnership::DestroyingConsume: case OperandOwnership::InteriorPointer: case OperandOwnership::EndBorrow: case OperandOwnership::Reborrow: llvm_unreachable("this isn't the operand being forwarding!"); } #endif this->use = use; } ValueOwnershipKind ForwardingOperand::getForwardingOwnershipKind() const { auto *user = use->getUser(); // NOTE: This if chain is meant to be a covered switch, so make sure to return // in each if itself since we have an unreachable at the bottom to ensure if a // new subclass of OwnershipForwardingInst is added if (auto *ofsvi = dyn_cast(user)) return ofsvi->getForwardingOwnershipKind(); if (auto *ofsvi = dyn_cast(user)) return ofsvi->getForwardingOwnershipKind(); if (auto *ofci = dyn_cast(user)) return ofci->getForwardingOwnershipKind(); if (auto *ofseib = dyn_cast(user)) return ofseib->getForwardingOwnershipKind(); if (auto *ofmvi = dyn_cast(user)) { assert(ofmvi->getNumOperands() == 1); return ofmvi->getForwardingOwnershipKind(); } if (auto *ofti = dyn_cast(user)) { assert(ofti->getNumOperands() == 1); return ofti->getForwardingOwnershipKind(); } llvm_unreachable("Unhandled forwarding inst?!"); } void ForwardingOperand::setForwardingOwnershipKind( ValueOwnershipKind newKind) const { auto *user = use->getUser(); // NOTE: This if chain is meant to be a covered switch, so make sure to return // in each if itself since we have an unreachable at the bottom to ensure if a // new subclass of OwnershipForwardingInst is added if (auto *ofsvi = dyn_cast(user)) return ofsvi->setForwardingOwnershipKind(newKind); if (auto *ofsvi = dyn_cast(user)) return ofsvi->setForwardingOwnershipKind(newKind); if (auto *ofci = dyn_cast(user)) return ofci->setForwardingOwnershipKind(newKind); if (auto *ofseib = dyn_cast(user)) return ofseib->setForwardingOwnershipKind(newKind); if (auto *ofmvi = dyn_cast(user)) { assert(ofmvi->getNumOperands() == 1); if (!ofmvi->getOperand(0)->getType().isTrivial(*ofmvi->getFunction())) { ofmvi->setForwardingOwnershipKind(newKind); // TODO: Refactor this better. if (auto *dsi = dyn_cast(ofmvi)) { for (auto &result : dsi->getAllResultsBuffer()) { if (result.getType().isTrivial(*dsi->getFunction())) continue; result.setOwnershipKind(newKind); } } else { auto *dti = cast(ofmvi); for (auto &result : dti->getAllResultsBuffer()) { if (result.getType().isTrivial(*dti->getFunction())) continue; result.setOwnershipKind(newKind); } } } return; } if (auto *ofti = dyn_cast(user)) { assert(ofti->getNumOperands() == 1); if (!ofti->getOperand()->getType().isTrivial(*ofti->getFunction())) { ofti->setForwardingOwnershipKind(newKind); // Then convert all of its incoming values that are owned to be guaranteed. for (auto &succ : ofti->getSuccessors()) { auto *succBlock = succ.getBB(); // If we do not have any arguments, then continue. if (succBlock->args_empty()) continue; for (auto *succArg : succBlock->getSILPhiArguments()) { // If we have an any value, just continue. if (!succArg->getType().isTrivial(*ofti->getFunction())) continue; succArg->setOwnershipKind(newKind); } } } return; } llvm_unreachable("Out of sync with OperandOwnership"); } void ForwardingOperand::replaceOwnershipKind(ValueOwnershipKind oldKind, ValueOwnershipKind newKind) const { auto *user = use->getUser(); if (auto *fInst = dyn_cast(user)) if (fInst->getForwardingOwnershipKind() == oldKind) return fInst->setForwardingOwnershipKind(newKind); if (auto *fInst = dyn_cast(user)) if (fInst->getForwardingOwnershipKind() == oldKind) return fInst->setForwardingOwnershipKind(newKind); if (auto *ofci = dyn_cast(user)) if (ofci->getForwardingOwnershipKind() == oldKind) return ofci->setForwardingOwnershipKind(newKind); if (auto *ofseib = dyn_cast(user)) if (ofseib->getForwardingOwnershipKind() == oldKind) return ofseib->setForwardingOwnershipKind(newKind); if (auto *ofmvi = dyn_cast(user)) { if (ofmvi->getForwardingOwnershipKind() == oldKind) { ofmvi->setForwardingOwnershipKind(newKind); } // TODO: Refactor this better. if (auto *dsi = dyn_cast(ofmvi)) { for (auto &result : dsi->getAllResultsBuffer()) { if (result.getOwnershipKind() != oldKind) continue; result.setOwnershipKind(newKind); } } else { auto *dti = cast(ofmvi); for (auto &result : dti->getAllResultsBuffer()) { if (result.getOwnershipKind() != oldKind) continue; result.setOwnershipKind(newKind); } } return; } if (auto *ofti = dyn_cast(user)) { if (ofti->getForwardingOwnershipKind() == oldKind) { ofti->setForwardingOwnershipKind(newKind); // Then convert all of its incoming values that are owned to be guaranteed. for (auto &succ : ofti->getSuccessors()) { auto *succBlock = succ.getBB(); // If we do not have any arguments, then continue. if (succBlock->args_empty()) continue; for (auto *succArg : succBlock->getSILPhiArguments()) { // If we have an any value, just continue. if (succArg->getOwnershipKind() == oldKind) { succArg->setOwnershipKind(newKind); } } } } return; } llvm_unreachable("Missing Case! Out of sync with OperandOwnership"); } SILValue ForwardingOperand::getSingleForwardedValue() const { if (auto *svi = dyn_cast(use->getUser())) return svi; return SILValue(); } bool ForwardingOperand::visitForwardedValues( function_ref visitor) { auto *user = use->getUser(); // See if we have a single value instruction... if we do that is always the // transitive result. if (auto *svi = dyn_cast(user)) { return visitor(svi); } if (auto *mvri = dyn_cast(user)) { return llvm::all_of(mvri->getResults(), [&](SILValue value) { if (value.getOwnershipKind() == OwnershipKind::None) return true; return visitor(value); }); } // This is an instruction like switch_enum and checked_cast_br that are // "transforming terminators"... We know that this means that we should at // most have a single phi argument. auto *ti = cast(user); return llvm::all_of(ti->getSuccessorBlocks(), [&](SILBasicBlock *succBlock) { // If we do not have any arguments, then continue. if (succBlock->args_empty()) return true; auto args = succBlock->getSILPhiArguments(); assert(args.size() == 1 && "Transforming terminator with multiple args?!"); return visitor(args[0]); }); } void swift::findTransitiveReborrowBaseValuePairs( BorrowingOperand initialScopedOperand, SILValue origBaseValue, function_ref visitReborrowBaseValuePair) { // We need a SetVector to make sure we don't revisit the same reborrow operand // again. SmallSetVector, 4> worklist; // Populate the worklist with reborrow and the base value initialScopedOperand.visitScopeEndingUses([&](Operand *op) { if (op->getOperandOwnership() == OperandOwnership::Reborrow) { worklist.insert(std::make_tuple(op, origBaseValue)); } return true; }); // Size of worklist changes in this loop for (unsigned idx = 0; idx < worklist.size(); idx++) { Operand *reborrowOp; SILValue baseValue; std::tie(reborrowOp, baseValue) = worklist[idx]; BorrowingOperand borrowingOperand(reborrowOp); assert(borrowingOperand.isReborrow()); auto *branchInst = cast(reborrowOp->getUser()); auto *succBlock = branchInst->getDestBB(); auto *phiArg = cast( succBlock->getArgument(reborrowOp->getOperandNumber())); SILValue newBaseVal = baseValue; // If the previous base value was also passed as a phi arg, that will be // the new base value. for (auto *arg : succBlock->getArguments()) { if (arg->getIncomingPhiValue(branchInst->getParent()) == baseValue) { newBaseVal = arg; break; } } // Call the visitor function visitReborrowBaseValuePair(phiArg, newBaseVal); BorrowedValue scopedValue(phiArg); scopedValue.visitLocalScopeEndingUses([&](Operand *op) { if (op->getOperandOwnership() == OperandOwnership::Reborrow) { worklist.insert(std::make_tuple(op, newBaseVal)); } return true; }); } } void swift::visitTransitiveEndBorrows( BorrowedValue beginBorrow, function_ref visitEndBorrow) { SmallSetVector worklist; worklist.insert(beginBorrow.value); while (!worklist.empty()) { auto val = worklist.pop_back_val(); for (auto *consumingUse : val->getConsumingUses()) { auto *consumingUser = consumingUse->getUser(); if (auto *branch = dyn_cast(consumingUser)) { auto *succBlock = branch->getSingleSuccessorBlock(); auto *phiArg = cast( succBlock->getArgument(consumingUse->getOperandNumber())); worklist.insert(phiArg); } else { visitEndBorrow(cast(consumingUser)); } } } }