mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
LinearLifetimeChecker - make DeadEndBlocks optional
This commit is contained in:
@@ -57,10 +57,23 @@ private:
|
||||
friend class SILOwnershipVerifier;
|
||||
friend class SILValueOwnershipChecker;
|
||||
|
||||
DeadEndBlocks &deadEndBlocks;
|
||||
// TODO: migrate away from using dead end blocks for OSSA values. end_borrow
|
||||
// or destroy_value should ideally exist on all paths. However, deadEndBlocks
|
||||
// may still be useful for checking memory lifetime for address uses.
|
||||
DeadEndBlocks *deadEndBlocks;
|
||||
|
||||
public:
|
||||
LinearLifetimeChecker(DeadEndBlocks &deadEndBlocks)
|
||||
/// \p deadEndBlocks should be provided for lifetimes that do not require
|
||||
/// consuming uses on dead-end paths, which end in an unreachable terminator.
|
||||
/// OSSA values require consumes on all paths, so \p deadEndBlocks are *not*
|
||||
/// required for OSSA lifetimes. Memory lifetimes and access scopes only
|
||||
/// require destroys on non-dead-end paths.
|
||||
///
|
||||
/// TODO: The verifier currently requires OSSA borrow scopes to end on all
|
||||
/// paths. Owned OSSA lifetimes may still be missing destroys on dead-end
|
||||
/// paths. Once owned values are fully enforced, the same invariant will hold
|
||||
/// for all OSSA values.
|
||||
LinearLifetimeChecker(DeadEndBlocks *deadEndBlocks = nullptr)
|
||||
: deadEndBlocks(deadEndBlocks) {}
|
||||
|
||||
/// Returns true that \p value forms a linear lifetime with consuming uses \p
|
||||
|
||||
@@ -85,7 +85,7 @@ class DeadEndBlocks;
|
||||
class GuaranteedPhiVerifier {
|
||||
/// A cache of dead-end basic blocks that we use to determine if we can
|
||||
/// ignore "leaks".
|
||||
DeadEndBlocks &deadEndBlocks;
|
||||
DeadEndBlocks *deadEndBlocks = nullptr;
|
||||
/// The builder that the checker uses to emit error messages, crash if asked
|
||||
/// for, or supply back interesting info to the caller.
|
||||
LinearLifetimeChecker::ErrorBuilder errorBuilder;
|
||||
@@ -96,7 +96,7 @@ class GuaranteedPhiVerifier {
|
||||
dependentPhiToBaseValueMap;
|
||||
|
||||
public:
|
||||
GuaranteedPhiVerifier(const SILFunction *func, DeadEndBlocks &deadEndBlocks,
|
||||
GuaranteedPhiVerifier(const SILFunction *func, DeadEndBlocks *deadEndBlocks,
|
||||
LinearLifetimeChecker::ErrorBuilder errorBuilder)
|
||||
: deadEndBlocks(deadEndBlocks), errorBuilder(errorBuilder) {}
|
||||
|
||||
|
||||
@@ -161,12 +161,12 @@ struct State {
|
||||
/// Once we have setup all of our consuming/non-consuming blocks and have
|
||||
/// validated that all intra-block dataflow is safe, perform the inter-block
|
||||
/// dataflow.
|
||||
void performDataflow(DeadEndBlocks &deBlocks);
|
||||
void performDataflow(DeadEndBlocks *deBlocks);
|
||||
|
||||
/// After we have performed the dataflow, check the end state of our dataflow
|
||||
/// for validity. If this is a linear typed value, return true. Return false
|
||||
/// otherwise.
|
||||
void checkDataflowEndState(DeadEndBlocks &deBlocks);
|
||||
void checkDataflowEndState(DeadEndBlocks *deBlocks);
|
||||
|
||||
void dumpConsumingUsers() const {
|
||||
llvm::errs() << "Consuming Users:\n";
|
||||
@@ -410,7 +410,7 @@ void State::checkPredsForDoubleConsume(SILBasicBlock *userBlock) {
|
||||
// Dataflow
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void State::performDataflow(DeadEndBlocks &deBlocks) {
|
||||
void State::performDataflow(DeadEndBlocks *deBlocks) {
|
||||
LLVM_DEBUG(llvm::dbgs() << " Beginning to check dataflow constraints\n");
|
||||
// Until the worklist is empty...
|
||||
while (!worklist.empty()) {
|
||||
@@ -446,7 +446,7 @@ void State::performDataflow(DeadEndBlocks &deBlocks) {
|
||||
|
||||
// Then check if the successor is a transitively unreachable block. In
|
||||
// such a case, we ignore it since we are going to leak along that path.
|
||||
if (deBlocks.isDeadEnd(succBlock))
|
||||
if (deBlocks && deBlocks->isDeadEnd(succBlock))
|
||||
continue;
|
||||
|
||||
// Otherwise, add the successor to our SuccessorBlocksThatMustBeVisited
|
||||
@@ -483,7 +483,7 @@ void State::performDataflow(DeadEndBlocks &deBlocks) {
|
||||
}
|
||||
}
|
||||
|
||||
void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
|
||||
void State::checkDataflowEndState(DeadEndBlocks *deBlocks) {
|
||||
if (!successorBlocksThatMustBeVisited.empty()) {
|
||||
// If we are asked to store any leaking blocks, put them in the leaking
|
||||
// blocks array.
|
||||
@@ -518,7 +518,7 @@ void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
|
||||
// be a use-before-def or a use-after-free.
|
||||
for (auto pair : blocksWithNonConsumingUses.getRange()) {
|
||||
auto *block = pair.first;
|
||||
if (deBlocks.isDeadEnd(block)) {
|
||||
if (deBlocks && deBlocks->isDeadEnd(block)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -601,7 +601,7 @@ LinearLifetimeChecker::Error LinearLifetimeChecker::checkValueImpl(
|
||||
for (auto *use : nonConsumingUses) {
|
||||
auto *useParent = use->getUser()->getParent();
|
||||
if (useParent == value->getParentBlock() ||
|
||||
deadEndBlocks.isDeadEnd(useParent)) {
|
||||
(deadEndBlocks && deadEndBlocks->isDeadEnd(useParent))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -2551,11 +2551,11 @@ public:
|
||||
bool success = useKind == AddressUseKind::NonEscaping;
|
||||
|
||||
require(!success || checkScopedAddressUses(
|
||||
scopedAddress, &scopedAddressLiveness, &DEBlocks),
|
||||
scopedAddress, &scopedAddressLiveness, DEBlocks.get()),
|
||||
"Ill formed store_borrow scope");
|
||||
|
||||
require(!success || !hasOtherStoreBorrowsInLifetime(
|
||||
SI, &scopedAddressLiveness, &DEBlocks),
|
||||
SI, &scopedAddressLiveness, DEBlocks.get()),
|
||||
"A store_borrow cannot be nested within another "
|
||||
"store_borrow to its destination");
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ static bool fixupReferenceCounts(
|
||||
auto *stackLoc = builder.createAllocStack(loc, v->getType().getObjectType());
|
||||
builder.createCopyAddr(loc, v, stackLoc, IsNotTake, IsInitialization);
|
||||
|
||||
LinearLifetimeChecker checker(deadEndBlocks);
|
||||
LinearLifetimeChecker checker(&deadEndBlocks);
|
||||
bool consumedInLoop = checker.completeConsumingUseSet(
|
||||
pai, applySite.getCalleeOperand(),
|
||||
[&](SILBasicBlock::iterator insertPt) {
|
||||
@@ -173,7 +173,7 @@ static bool fixupReferenceCounts(
|
||||
// just cares about the block the value is in. In a forthcoming commit, I
|
||||
// am going to change this to use a different API on the linear lifetime
|
||||
// checker that makes this clearer.
|
||||
LinearLifetimeChecker checker(deadEndBlocks);
|
||||
LinearLifetimeChecker checker(&deadEndBlocks);
|
||||
bool consumedInLoop = checker.completeConsumingUseSet(
|
||||
pai, applySite.getCalleeOperand(),
|
||||
[&](SILBasicBlock::iterator insertPt) {
|
||||
@@ -220,7 +220,7 @@ static bool fixupReferenceCounts(
|
||||
// just cares about the block the value is in. In a forthcoming commit, I
|
||||
// am going to change this to use a different API on the linear lifetime
|
||||
// checker that makes this clearer.
|
||||
LinearLifetimeChecker checker(deadEndBlocks);
|
||||
LinearLifetimeChecker checker(&deadEndBlocks);
|
||||
checker.completeConsumingUseSet(
|
||||
pai, applySite.getCalleeOperand(),
|
||||
[&](SILBasicBlock::iterator insertPt) {
|
||||
@@ -257,7 +257,7 @@ static bool fixupReferenceCounts(
|
||||
// just cares about the block the value is in. In a forthcoming commit, I
|
||||
// am going to change this to use a different API on the linear lifetime
|
||||
// checker that makes this clearer.
|
||||
LinearLifetimeChecker checker(deadEndBlocks);
|
||||
LinearLifetimeChecker checker(&deadEndBlocks);
|
||||
checker.completeConsumingUseSet(
|
||||
pai, applySite.getCalleeOperand(),
|
||||
[&](SILBasicBlock::iterator insertPt) {
|
||||
|
||||
@@ -1197,7 +1197,7 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis(
|
||||
// Then perform the linear lifetime check. If we succeed, continue. We have
|
||||
// no further work to do.
|
||||
auto *loadOperand = &load->getAllOperands()[0];
|
||||
LinearLifetimeChecker checker(deadEndBlocks);
|
||||
LinearLifetimeChecker checker(&deadEndBlocks);
|
||||
bool consumedInLoop = checker.completeConsumingUseSet(
|
||||
phi, loadOperand, [&](SILBasicBlock::iterator iter) {
|
||||
SILBuilderWithScope builder(iter);
|
||||
@@ -1279,7 +1279,7 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues(
|
||||
// Then perform the linear lifetime check. If we succeed, continue. We have
|
||||
// no further work to do.
|
||||
auto *loadOperand = &load->getAllOperands()[0];
|
||||
LinearLifetimeChecker checker(deadEndBlocks);
|
||||
LinearLifetimeChecker checker(&deadEndBlocks);
|
||||
bool consumedInLoop = checker.completeConsumingUseSet(
|
||||
cvi, loadOperand, [&](SILBasicBlock::iterator iter) {
|
||||
SILBuilderWithScope builder(iter);
|
||||
|
||||
@@ -765,7 +765,7 @@ bool SemanticARCOptVisitor::tryPerformOwnedCopyValueOptimization(
|
||||
// Ok, we have an owned value. If we do not have any non-destroying consuming
|
||||
// uses, see if all of our uses (ignoring destroying uses) are within our
|
||||
// parent owned value's lifetime.
|
||||
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
|
||||
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
|
||||
if (!checker.validateLifetime(originalValue, parentLifetimeEndingUses,
|
||||
allCopyUses))
|
||||
return false;
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
SmallVector<Operand *, 8> endScopeUses;
|
||||
transform(access->getEndAccesses(), std::back_inserter(endScopeUses),
|
||||
[](EndAccessInst *eai) { return &eai->getAllOperands()[0]; });
|
||||
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
|
||||
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
|
||||
if (!checker.validateLifetime(access, endScopeUses,
|
||||
liveRange.getAllConsumingUses())) {
|
||||
// If we fail the linear lifetime check, then just recur:
|
||||
@@ -138,7 +138,7 @@ public:
|
||||
// Ok, we have some writes. See if any of them are within our live
|
||||
// range. If any are, we definitely can not promote to load_borrow.
|
||||
SmallVector<BeginAccessInst *, 16> foundBeginAccess;
|
||||
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
|
||||
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
|
||||
SILValue introducerValue = liveRange.getIntroducer().value;
|
||||
if (!checker.usesNotContainedWithinLifetime(introducerValue,
|
||||
liveRange.getDestroyingUses(),
|
||||
@@ -244,7 +244,7 @@ public:
|
||||
value.visitLocalScopeEndingUses(
|
||||
[&](Operand *use) { endScopeInsts.push_back(use); return true; });
|
||||
|
||||
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
|
||||
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
|
||||
|
||||
// Returns true on success. So we invert.
|
||||
bool foundError = !checker.validateLifetime(
|
||||
@@ -291,7 +291,7 @@ public:
|
||||
|
||||
// Then make sure that all of our load [copy] uses are within the
|
||||
// destroy_addr.
|
||||
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
|
||||
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
|
||||
// Returns true on success. So we invert.
|
||||
bool foundError = !checker.validateLifetime(
|
||||
stack, addrDestroyingOperands /*consuming users*/,
|
||||
|
||||
@@ -56,7 +56,7 @@ bool SemanticARCOptVisitor::visitUncheckedOwnershipConversionInst(
|
||||
|
||||
// Ok, now we need to perform our lifetime check.
|
||||
SmallVector<Operand *, 8> consumingUses(op->getConsumingUses());
|
||||
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
|
||||
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
|
||||
if (!checker.validateLifetime(op, consumingUses, newUses))
|
||||
return false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user