FieldSensitivePrunedLiveness: Handle conditionality of try_apply defs.

A `try_apply` with indirect out arguments is only a def for those arguments on
the success path. Model this by sinking the def-ness of the instruction into the
success branch of the try_apply, and introducing a new `DeadToLiveEdge` mode for
block liveness which stops propagation of use-before-def conditions into the
block that introduced the def. Fixes rdar://118567869.
This commit is contained in:
Joe Groff
2024-03-19 22:46:26 -07:00
parent 8a65c9be94
commit 1166f2e0d8
4 changed files with 79 additions and 44 deletions

View File

@@ -493,17 +493,21 @@ public:
/// LiveWithin blocks have at least one use and/or def within the block, but
/// are not (yet) LiveOut.
///
/// DeadToLiveEdge blocks are not live within the block itself, but the value
/// becomes live on one or more of the edges out.
///
/// LiveOut blocks are live on at least one successor path. LiveOut blocks may
/// or may not contain defs or uses.
///
/// NOTE: The values below for Dead, LiveWithin, LiveOut were picked to ensure
/// that given a 2 bit representation of the value, a value is Dead if the
/// first bit is 0 and is LiveOut if the second bit is set.
enum IsLive {
Dead = 0,
LiveWithin = 1,
DeadToLiveEdge = 2,
LiveOut = 3,
};
static bool isDead(IsLive liveness) {
return liveness == Dead || liveness == DeadToLiveEdge;
}
/// A bit vector that stores information about liveness. This is composed
/// with SmallBitVector since it contains two bits per liveness so that it
@@ -524,37 +528,27 @@ public:
unsigned size() const { return bits.size() / 2; }
IsLive getLiveness(unsigned bitNo) const {
if (!bits[bitNo * 2])
return IsLive::Dead;
return bits[bitNo * 2 + 1] ? LiveOut : LiveWithin;
return IsLive((bits[bitNo * 2 + 1] << 1) | bits[bitNo * 2]);
}
/// Returns the liveness in \p resultingFoundLiveness. We only return the
/// bits for endBitNo - startBitNo.
void getLiveness(unsigned startBitNo, unsigned endBitNo,
SmallVectorImpl<IsLive> &resultingFoundLiveness) const {
unsigned actualStartBitNo = startBitNo * 2;
unsigned actualEndBitNo = endBitNo * 2;
for (unsigned i = actualStartBitNo, e = actualEndBitNo; i != e; i += 2) {
if (!bits[i]) {
resultingFoundLiveness.push_back(Dead);
continue;
}
resultingFoundLiveness.push_back(bits[i + 1] ? LiveOut : LiveWithin);
}
}
void setLiveness(unsigned startBitNo, unsigned endBitNo, IsLive isLive) {
for (unsigned i = startBitNo * 2, e = endBitNo * 2; i != e; i += 2) {
bits[i] = isLive & 1;
bits[i + 1] = isLive & 2;
for (unsigned i = startBitNo, e = endBitNo; i != e; ++i) {
resultingFoundLiveness.push_back(getLiveness(i));
}
}
void setLiveness(unsigned bitNo, IsLive isLive) {
setLiveness(bitNo, bitNo + 1, isLive);
bits[bitNo * 2] = isLive & 1;
bits[bitNo * 2 + 1] = bool(isLive & 2);
}
void setLiveness(unsigned startBitNo, unsigned endBitNo, IsLive isLive) {
for (unsigned i = startBitNo, e = endBitNo; i != e; ++i) {
setLiveness(i, isLive);
}
}
};
@@ -620,9 +614,10 @@ public:
}
void initializeDefBlock(SILBasicBlock *defBB, unsigned startBitNo,
unsigned endBitNo) {
unsigned endBitNo,
IsLive isLive = LiveWithin) {
assert(isInitialized());
markBlockLive(defBB, startBitNo, endBitNo, LiveWithin);
markBlockLive(defBB, startBitNo, endBitNo, isLive);
}
/// Update this liveness result for a single use.
@@ -632,7 +627,7 @@ public:
auto *block = user->getParent();
if (!isUserBeforeDef) {
auto liveness = getBlockLiveness(block, bitNo);
if (liveness != Dead)
if (!isDead(liveness))
return liveness;
}
computeScalarUseBlockLiveness(block, bitNo);
@@ -696,6 +691,7 @@ protected:
// If we are dead, always update to the new liveness.
switch (iterAndInserted.first->getSecond().getLiveness(bitNo)) {
case Dead:
case DeadToLiveEdge:
iterAndInserted.first->getSecond().setLiveness(bitNo, bitNo + 1,
isLive);
break;
@@ -935,10 +931,12 @@ public:
return UserBlockRange(getAllUsers(), op);
}
void initializeDefBlock(SILBasicBlock *defBB, TypeTreeLeafTypeRange span) {
void initializeDefBlock(SILBasicBlock *defBB, TypeTreeLeafTypeRange span,
FieldSensitivePrunedLiveBlocks::IsLive isLive
= FieldSensitivePrunedLiveBlocks::LiveWithin) {
assert(isInitialized());
liveBlocks.initializeDefBlock(defBB, span.startEltOffset,
span.endEltOffset);
span.endEltOffset, isLive);
}
/// For flexibility, \p lifetimeEnding is provided by the
@@ -1303,6 +1301,14 @@ public:
FieldSensitivePrunedLivenessBoundary &boundary) const;
};
static inline SILBasicBlock *getDefinedInBlock(SILNode *node) {
// try_apply defines the value only on the success edge.
if (auto ta = dyn_cast<TryApplyInst>(node)) {
return ta->getNormalBB();
}
return node->getParentBlock();
}
/// MultiDefPrunedLiveness is computed incrementally by calling updateForUse.
///
/// Defs should be initialized before calling updatingForUse on any def
@@ -1351,9 +1357,17 @@ public:
void initializeDef(SILNode *node, TypeTreeLeafTypeRange span) {
assert(Super::isInitialized());
defs.insert(node, span);
auto *block = node->getParentBlock();
defBlocks.insert(block, span);
initializeDefBlock(block, span);
auto defBlock = getDefinedInBlock(node);
defBlocks.insert(defBlock, span);
initializeDefBlock(defBlock, span);
if (auto ta = dyn_cast<TryApplyInst>(node)) {
// The value becomes live on the success edge.
// Mark the basic block the try_apply terminates as a dead-to-live
// edge.
initializeDefBlock(ta->getParent(), span,
FieldSensitivePrunedLiveBlocks::DeadToLiveEdge);
}
}
void initializeDef(SILInstruction *def, TypeTreeLeafTypeRange span) {

View File

@@ -579,6 +579,7 @@ void FieldSensitivePrunedLiveBlocks::computeScalarUseBlockLiveness(
case LiveWithin:
markBlockLive(predBlock, bitNo, LiveOut);
break;
case DeadToLiveEdge:
case LiveOut:
break;
}
@@ -615,6 +616,7 @@ void FieldSensitivePrunedLiveBlocks::updateForUse(
} else {
LLVM_FALLTHROUGH;
}
case DeadToLiveEdge:
case Dead: {
// This use block has not yet been marked live. Mark it and its
// predecessor blocks live.
@@ -634,6 +636,8 @@ FieldSensitivePrunedLiveBlocks::getStringRef(IsLive isLive) const {
return "Dead";
case LiveWithin:
return "LiveWithin";
case DeadToLiveEdge:
return "DeadToLiveEdge";
case LiveOut:
return "LiveOut";
}
@@ -853,6 +857,7 @@ bool FieldSensitivePrunedLiveRange<LivenessWithDefs>::isWithinBoundary(
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Visiting bit: " << bit << '\n');
bool isLive = false;
switch (pair.value()) {
case FieldSensitivePrunedLiveBlocks::DeadToLiveEdge:
case FieldSensitivePrunedLiveBlocks::Dead:
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Dead... continuing!\n");
// We are only not within the boundary if all of our bits are dead. We
@@ -942,6 +947,8 @@ static StringRef getStringRef(FieldSensitivePrunedLiveBlocks::IsLive isLive) {
return "Dead";
case FieldSensitivePrunedLiveBlocks::LiveWithin:
return "LiveWithin";
case FieldSensitivePrunedLiveBlocks::DeadToLiveEdge:
return "DeadToLiveEdge";
case FieldSensitivePrunedLiveBlocks::LiveOut:
return "LiveOut";
}
@@ -972,8 +979,8 @@ void FieldSensitivePrunedLiveRange<LivenessWithDefs>::computeBoundary(
switch (pair.value()) {
case FieldSensitivePrunedLiveBlocks::LiveOut:
for (SILBasicBlock *succBB : block->getSuccessors()) {
if (getBlockLiveness(succBB, index) ==
FieldSensitivePrunedLiveBlocks::Dead) {
if (FieldSensitivePrunedLiveBlocks::isDead(
getBlockLiveness(succBB, index))) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Marking succBB as boundary edge: bb"
<< succBB->getDebugID() << '\n');
boundary.getBoundaryEdgeBits(succBB).set(index);
@@ -989,6 +996,9 @@ void FieldSensitivePrunedLiveRange<LivenessWithDefs>::computeBoundary(
foundAnyNonDead = true;
break;
}
case FieldSensitivePrunedLiveBlocks::DeadToLiveEdge:
foundAnyNonDead = true;
LLVM_FALLTHROUGH;
case FieldSensitivePrunedLiveBlocks::Dead:
// We do not assert here like in the normal pruned liveness
// implementation since we can have dead on some bits and liveness along
@@ -1178,7 +1188,7 @@ void findBoundaryInSSADefBlock(SILNode *ssaDef, unsigned bitNo,
// defInst is null for argument defs.
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Searching using findBoundaryInSSADefBlock.\n");
SILInstruction *defInst = dyn_cast<SILInstruction>(ssaDef);
for (SILInstruction &inst : llvm::reverse(*ssaDef->getParentBlock())) {
for (SILInstruction &inst : llvm::reverse(*getDefinedInBlock(ssaDef))) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting: " << inst);
if (&inst == defInst) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found dead def: " << *defInst);
@@ -1192,9 +1202,21 @@ void findBoundaryInSSADefBlock(SILNode *ssaDef, unsigned bitNo,
}
}
auto *deadArg = cast<SILArgument>(ssaDef);
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found dead arg: " << *deadArg);
boundary.getDeadDefsBits(deadArg).set(bitNo);
if (auto *deadArg = dyn_cast<SILArgument>(ssaDef)) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found dead arg: " << *deadArg);
boundary.getDeadDefsBits(deadArg).set(bitNo);
return;
}
// If we searched the success branch of a try_apply and found no uses, then
// the try_apply itself is a dead def.
if (isa<TryApplyInst>(ssaDef)) {
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found dead try_apply: " << *ssaDef);
boundary.getDeadDefsBits(ssaDef).set(bitNo);
return;
}
llvm_unreachable("def not found?!");
}
//===----------------------------------------------------------------------===//

View File

@@ -2816,6 +2816,7 @@ bool GlobalLivenessChecker::testInstVectorLiveness(
for (auto isLive : isLiveArray) {
switch (isLive) {
case IsLive::Dead:
case IsLive::DeadToLiveEdge:
LLVM_DEBUG(llvm::dbgs() << " Dead block!\n");
// Ignore a dead block. Our error use could not be in such a block.
//

View File

@@ -3,11 +3,9 @@
// RUN: %target-codesign %t/a.out.fragile
// RUN: %target-run %t/a.out.fragile | %FileCheck %s
// FIXME: miscompiles cause extra deinits with library evolution enabled
// R/UN: %target-build-swift -enable-library-evolution -o %t/a.out.resilient %s
// R/UN: %target-codesign %t/a.out.resilient
// R/UN: %target-run %t/a.out.resilient | %FileCheck %s
// RUN: %target-build-swift -enable-library-evolution -o %t/a.out.resilient %s
// RUN: %target-codesign %t/a.out.resilient
// RUN: %target-run %t/a.out.resilient | %FileCheck %s
// REQUIRES: executable_test