mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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) {
|
||||
|
||||
@@ -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?!");
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user