Skip to content

Commit 715d3f7

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 715d3f7

File tree

1 file changed

+73
-17
lines changed

1 file changed

+73
-17
lines changed

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -442,32 +442,68 @@ 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;
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) {
449+
assert(liveness.getBlockLiveness(block) == PrunedLiveBlocks::LiveWithin);
453450

454-
bool isDefBlockState = isDefBlock(block);
455451
for (SILInstruction &inst : llvm::reverse(*block)) {
456-
if (isDefBlockState && isDef(&inst)) {
452+
if (liveness.isInterestingUser(&inst)) {
453+
boundary.lastUsers.push_back(&inst);
454+
return;
455+
}
456+
}
457+
llvm_unreachable("live-within block must contain an interesting use");
458+
}
459+
460+
/// Given a live-within \p block that contains an SSA definition, and knowledge
461+
/// that all live uses are dominated by that single definition, find either the
462+
/// last user or a dead def.
463+
///
464+
/// A live range with a single definition cannot have any uses above that
465+
/// definition in the same block. This even holds for unreachable self-loops.
466+
void findBoundaryInSSADefBlock(SILNode *ssaDef,
467+
PrunedLivenessBoundary &boundary,
468+
const PrunedLiveness &liveness) {
469+
// defInst is null for argument defs.
470+
SILInstruction *defInst = dyn_cast<SILInstruction>(ssaDef);
471+
for (SILInstruction &inst : llvm::reverse(*ssaDef->getParentBlock())) {
472+
if (&inst == defInst) {
457473
boundary.deadDefs.push_back(cast<SILNode>(&inst));
458474
return;
459475
}
460-
if (isInterestingUser(&inst)) {
476+
if (liveness.isInterestingUser(&inst)) {
461477
boundary.lastUsers.push_back(&inst);
462478
return;
463479
}
464480
}
465-
auto *deadArg = dyn_cast<SILArgument>(def);
466-
assert(deadArg && deadArg->getParent() == block
481+
auto *deadArg = dyn_cast<SILArgument>(ssaDef);
482+
assert(deadArg
467483
&& "findBoundariesInBlock must be called on a live block");
468484
boundary.deadDefs.push_back(deadArg);
469485
}
470486

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

516+
if (!isDefBlock(block)) {
517+
// A live-out block with no defs cannot have a boundary.
518+
if (!isLiveOut) {
519+
findBoundaryInNonDefBlock(block, boundary, *this);
520+
}
521+
return;
522+
}
523+
// Handle def blocks...
524+
//
525+
// First, check for an SSA live range
526+
if (++defs.begin() == defs.end()) {
527+
// For SSA, a live-out block cannot have a boundary.
528+
if (!isLiveOut) {
529+
findBoundaryInSSADefBlock(*defs.begin(), boundary, *this);
530+
}
531+
return;
532+
}
533+
// Handle a live-out or live-within block with potentially multiple defs
534+
unsigned prevCount = boundary.deadDefs.size() + boundary.lastUsers.size();
481535
bool isLive = isLiveOut;
482-
bool isDefBlockState = isDefBlock(block);
483536
for (auto &inst : llvm::reverse(*block)) {
484537
// Check if the instruction is a def before checking whether it is a
485538
// use. The same instruction can be both a dead def and boundary use.
486-
if (isDefBlockState && isDef(&inst)) {
539+
if (isDef(&inst)) {
487540
if (!isLive) {
488541
boundary.deadDefs.push_back(cast<SILNode>(&inst));
489542
}
490543
isLive = false;
491544
}
545+
// Note: the same instruction could potentially be both a dead def and last
546+
// user. The liveness boundary supports this, although it won't happen in
547+
// any context where we care about inserting code on the boundary.
492548
if (!isLive && isInterestingUser(&inst)) {
493549
boundary.lastUsers.push_back(&inst);
494550
isLive = true;
495551
}
496552
}
497-
if (!isLive && isDefBlockState) {
553+
if (!isLive) {
498554
for (SILArgument *deadArg : block->getArguments()) {
499555
if (defs.contains(deadArg)) {
500556
boundary.deadDefs.push_back(deadArg);

0 commit comments

Comments
 (0)