Skip to content

Commit ecad424

Browse files
committed
Split PrunedLiveness::findBoundariesInBlock into fast paths
This allows dynamic dispatch into the fast paths. It's also easier to understand the fast paths when written as separate loops. This way, we can used MultiDefPrunedLiveness to compute ownership liveness, but still benefit from the fact that the common case (owned non-phi value) is much simpler and faster to compute. This approach is necessary because we use stack-based data structures within the liveness result, so we need to statically instantiate something that can handle all cases.
1 parent eee184a commit ecad424

File tree

1 file changed

+72
-18
lines changed

1 file changed

+72
-18
lines changed

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -442,32 +442,66 @@ template class PrunedLiveRange<MultiDefPrunedLiveness>;
442442
// SSAPrunedLiveness
443443
//===----------------------------------------------------------------------===//
444444

445-
void SSAPrunedLiveness::findBoundariesInBlock(
446-
SILBasicBlock *block, bool isLiveOut,
447-
PrunedLivenessBoundary &boundary) const {
448-
assert(isInitialized());
449-
450-
// For SSA, a live-out block cannot have a boundary.
451-
if (isLiveOut)
452-
return;
453-
454-
bool isDefBlockState = isDefBlock(block);
445+
/// Given live-within (non-live-out) \p block, find the last user.
446+
void findBoundaryInNonDefBlock(SILBasicBlock *block,
447+
PrunedLivenessBoundary &boundary,
448+
const PrunedLiveness &liveness) {
455449
for (SILInstruction &inst : llvm::reverse(*block)) {
456-
if (isDefBlockState && isDef(&inst)) {
450+
if (liveness.isInterestingUser(&inst)) {
451+
boundary.lastUsers.push_back(&inst);
452+
return;
453+
}
454+
}
455+
llvm_unreachable("live-within block must contain an interesting use");
456+
}
457+
458+
/// Given a live-within \p block that contains an SSA definition, and knowledge
459+
/// that all live uses are dominated by that single definition, find either the
460+
/// last user or a dead def.
461+
///
462+
/// A live range with a single definition cannot have any uses above that
463+
/// definition in the same block. This even holds for unreachable self-loops.
464+
void findBoundaryInSSADefBlock(SILNode *ssaDef,
465+
PrunedLivenessBoundary &boundary,
466+
const PrunedLiveness &liveness) {
467+
// defInst is null for argument defs.
468+
SILInstruction *defInst = dyn_cast<SILInstruction>(ssaDef);
469+
for (SILInstruction &inst : llvm::reverse(*ssaDef->getParentBlock())) {
470+
if (&inst == defInst) {
457471
boundary.deadDefs.push_back(cast<SILNode>(&inst));
458472
return;
459473
}
460-
if (isInterestingUser(&inst)) {
474+
if (liveness.isInterestingUser(&inst)) {
461475
boundary.lastUsers.push_back(&inst);
462476
return;
463477
}
464478
}
465-
auto *deadArg = dyn_cast<SILArgument>(def);
466-
assert(deadArg && deadArg->getParent() == block
479+
auto *deadArg = dyn_cast<SILArgument>(ssaDef);
480+
assert(deadArg
467481
&& "findBoundariesInBlock must be called on a live block");
468482
boundary.deadDefs.push_back(deadArg);
469483
}
470484

485+
void SSAPrunedLiveness::findBoundariesInBlock(
486+
SILBasicBlock *block, bool isLiveOut,
487+
PrunedLivenessBoundary &boundary) const {
488+
assert(isInitialized());
489+
490+
// For SSA, a live-out block cannot have a boundary.
491+
if (isLiveOut)
492+
return;
493+
494+
// Handle live-within block
495+
if (!isDefBlock(block)) {
496+
findBoundaryInNonDefBlock(block, boundary, *this);
497+
return;
498+
}
499+
// Find either the last user or a dead def
500+
auto *defInst = def->getDefiningInstruction();
501+
SILNode *defNode = defInst ? cast<SILNode>(defInst) : cast<SILArgument>(def);
502+
findBoundaryInSSADefBlock(defNode, boundary, *this);
503+
}
504+
471505
//===----------------------------------------------------------------------===//
472506
// MultiDefPrunedLiveness
473507
//===----------------------------------------------------------------------===//
@@ -476,25 +510,45 @@ void MultiDefPrunedLiveness::findBoundariesInBlock(
476510
SILBasicBlock *block, bool isLiveOut,
477511
PrunedLivenessBoundary &boundary) const {
478512
assert(isInitialized());
479-
unsigned prevCount = boundary.deadDefs.size() + boundary.lastUsers.size();
480513

514+
if (!isDefBlock(block)) {
515+
// A live-out block with no defs cannot have a boundary.
516+
if (!isLiveOut) {
517+
findBoundaryInNonDefBlock(block, boundary, *this);
518+
}
519+
return;
520+
}
521+
// Handle def blocks...
522+
//
523+
// First, check for an SSA live range
524+
if (++defs.begin() == defs.end()) {
525+
// For SSA, a live-out block cannot have a boundary.
526+
if (!isLiveOut) {
527+
findBoundaryInSSADefBlock(*defs.begin(), boundary, *this);
528+
}
529+
return;
530+
}
531+
// Handle a live-out or live-within block with potentially multiple defs
532+
unsigned prevCount = boundary.deadDefs.size() + boundary.lastUsers.size();
481533
bool isLive = isLiveOut;
482-
bool isDefBlockState = isDefBlock(block);
483534
for (auto &inst : llvm::reverse(*block)) {
484535
// Check if the instruction is a def before checking whether it is a
485536
// use. The same instruction can be both a dead def and boundary use.
486-
if (isDefBlockState && isDef(&inst)) {
537+
if (isDef(&inst)) {
487538
if (!isLive) {
488539
boundary.deadDefs.push_back(cast<SILNode>(&inst));
489540
}
490541
isLive = false;
491542
}
543+
// Note: the same instruction could potentially be both a dead def and last
544+
// user. The liveness boundary supports this, although it won't happen in
545+
// any context where we care about inserting code on the boundary.
492546
if (!isLive && isInterestingUser(&inst)) {
493547
boundary.lastUsers.push_back(&inst);
494548
isLive = true;
495549
}
496550
}
497-
if (!isLive && isDefBlockState) {
551+
if (!isLive) {
498552
for (SILArgument *deadArg : block->getArguments()) {
499553
if (defs.contains(deadArg)) {
500554
boundary.deadDefs.push_back(deadArg);

0 commit comments

Comments
 (0)