//===--- AddressWalker.cpp ------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2022 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/AddressWalker.h" #include "swift/Basic/Defer.h" #include "swift/SIL/NodeDatastructures.h" #include "swift/SIL/Projection.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/StackList.h" using namespace swift; AddressUseKind TransitiveAddressWalker::walk(SILValue projectedAddress) && { assert(!didInvalidate); // When we exit, set the result to be invalidated so we can't use this again. SWIFT_DEFER { didInvalidate = true; }; // 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; } StackList worklist(projectedAddress->getFunction()); SmallPtrSet visitedOperands; auto addToWorklist = [&](Operand *use) { if (visitedOperands.insert(use).second) worklist.push_back(use); }; for (auto *use : projectedAddress->getUses()) { addToWorklist(use); } // Record all uses that aren't transitively followed. These are either // instanteneous uses of the addres, or cause a pointer escape. auto transitiveResultUses = [&](Operand *use) { auto *svi = cast(use->getUser()); if (svi->use_empty()) { callVisitUse(use); } else { for (auto *use : svi->getUses()) addToWorklist(use); } }; while (!worklist.empty()) { if (result == AddressUseKind::Unknown) return AddressUseKind::Unknown; 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(); if (auto *ti = dyn_cast(user)) { switch (ti->getTermKind()) { case TermKind::BranchInst: case TermKind::CondBranchInst: // We could have an address phi. To be conservative, just treat this as // a point escape. meet(AddressUseKind::PointerEscape); for (auto succBlockArgList : ti->getSuccessorBlockArgumentLists()) { auto *succ = succBlockArgList[op->getOperandNumber()]; for (auto *use : succ->getUses()) addToWorklist(use); } continue; case TermKind::UnreachableInst: case TermKind::UnwindInst: llvm_unreachable("Should never be used"); case TermKind::SwitchEnumInst: case TermKind::SwitchValueInst: case TermKind::DynamicMethodBranchInst: case TermKind::AwaitAsyncContinuationInst: case TermKind::CheckedCastBranchInst: llvm_unreachable("Never takes an address"); // Point uses. case TermKind::ReturnInst: case TermKind::ThrowInst: case TermKind::YieldInst: case TermKind::TryApplyInst: case TermKind::SwitchEnumAddrInst: case TermKind::CheckedCastAddrBranchInst: callVisitUse(op); continue; } } // TODO: Partial apply should be NonEscaping, but then we need to consider // the apply to be a use point. if (isa(user) || isa(user)) { meet(AddressUseKind::PointerEscape); callVisitUse(op); 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) || 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) || 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)) { callVisitUse(op); continue; } if (isa(user) || isa(user)) { assert(!user->hasResults()); callVisitUse(op); 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) || 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()) { switch (*kind) { case BuiltinValueKind::TSanInoutAccess: case BuiltinValueKind::ResumeThrowingContinuationReturning: case BuiltinValueKind::ResumeNonThrowingContinuationReturning: case BuiltinValueKind::Copy: case BuiltinValueKind::GenericAdd: case BuiltinValueKind::GenericFAdd: case BuiltinValueKind::GenericAnd: case BuiltinValueKind::GenericAShr: case BuiltinValueKind::GenericLShr: case BuiltinValueKind::GenericOr: case BuiltinValueKind::GenericFDiv: case BuiltinValueKind::GenericMul: case BuiltinValueKind::GenericFMul: case BuiltinValueKind::GenericSDiv: case BuiltinValueKind::GenericExactSDiv: case BuiltinValueKind::GenericShl: case BuiltinValueKind::GenericSRem: case BuiltinValueKind::GenericSub: case BuiltinValueKind::GenericFSub: case BuiltinValueKind::GenericUDiv: case BuiltinValueKind::GenericExactUDiv: case BuiltinValueKind::GenericURem: case BuiltinValueKind::GenericFRem: case BuiltinValueKind::GenericXor: case BuiltinValueKind::TaskRunInline: callVisitUse(op); continue; default: break; } } } if (auto fas = FullApplySite::isa(user)) { callVisitUse(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()) { callVisitUse(op); continue; } // If we are the value use, look through it. transitiveResultUses(op); continue; } // We were unable to recognize this user, so set AddressUseKind to unknown // and call onError with the specific user that caused the problem. onError(op); return AddressUseKind::Unknown; } return result; }