mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
* spelling: accessible Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: accessories Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: allocated Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: amortizes Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: are Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: arguments Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: cacheable Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: check Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: clazz Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: compatible Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: compilation Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: completely Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: construct Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: conversion Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: declarations Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: derivation Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: deserialization Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: destroyed Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: determined Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: different Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: doesn't Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: equality Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: equivalent Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: formation Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: forwards Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: global Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: guaranteed Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: have Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: identify Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: inaccessible Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: indeterminate Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: indices Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: inefficient Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: inheritance Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: instantaneous Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: instruction Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: intentionally Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: interior Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: intrinsic Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: introducing Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: irrelevant Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: message Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: multi Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: necessarily Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: object Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: one Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: optimization Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: otherwise Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: overridden Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: parameter Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: pattern Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: pipeline Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: possibility Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: postdominance Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: providing Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: reached Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: recognized Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: refrigerator Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: remaining Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: resilient Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: retrieve Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: scavenge Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: scheduled Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: separately Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: serializable Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: signature Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: simplicity Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: specifically Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: substituted Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: substitution Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: subtypes Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: supplement Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: syntax Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: the Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: there Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: these Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: this Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: though Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: through Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: transitively Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: transpose Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: trivial Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: value Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: verification Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: visibility Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: weird Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: whole Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> Co-authored-by: Josh Soref <jsoref@users.noreply.github.com>
303 lines
11 KiB
C++
303 lines
11 KiB
C++
//===--- PrunedLiveness.cpp - Compute liveness from selected uses ---------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SIL/PrunedLiveness.h"
|
|
#include "swift/SIL/BasicBlockDatastructures.h"
|
|
#include "swift/SIL/BasicBlockUtils.h"
|
|
#include "swift/SIL/OwnershipUtils.h"
|
|
|
|
using namespace swift;
|
|
|
|
/// Mark blocks live during a reverse CFG traversal from one specific block
|
|
/// containing a user.
|
|
void PrunedLiveBlocks::computeUseBlockLiveness(SILBasicBlock *userBB) {
|
|
// If we are visiting this block, then it is not already LiveOut. Mark it
|
|
// LiveWithin to indicate a liveness boundary within the block.
|
|
markBlockLive(userBB, LiveWithin);
|
|
|
|
SmallVector<SILBasicBlock *, 8> predBBWorklist({userBB});
|
|
while (!predBBWorklist.empty()) {
|
|
SILBasicBlock *bb = predBBWorklist.pop_back_val();
|
|
|
|
// The popped `bb` is live; now mark all its predecessors LiveOut.
|
|
//
|
|
// Traversal terminates at any previously visited block, including the
|
|
// blocks initialized as definition blocks.
|
|
for (auto *predBB : bb->getPredecessorBlocks()) {
|
|
switch (getBlockLiveness(predBB)) {
|
|
case Dead:
|
|
predBBWorklist.push_back(predBB);
|
|
LLVM_FALLTHROUGH;
|
|
case LiveWithin:
|
|
markBlockLive(predBB, LiveOut);
|
|
break;
|
|
case LiveOut:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Update the current def's liveness based on one specific use instruction.
|
|
///
|
|
/// Return the updated liveness of the \p use block (LiveOut or LiveWithin).
|
|
///
|
|
/// Terminators are not live out of the block.
|
|
PrunedLiveBlocks::IsLive PrunedLiveBlocks::updateForUse(SILInstruction *user) {
|
|
SWIFT_ASSERT_ONLY(seenUse = true);
|
|
|
|
auto *bb = user->getParent();
|
|
switch (getBlockLiveness(bb)) {
|
|
case LiveOut:
|
|
return LiveOut;
|
|
case LiveWithin:
|
|
return LiveWithin;
|
|
case Dead: {
|
|
// This use block has not yet been marked live. Mark it and its predecessor
|
|
// blocks live.
|
|
computeUseBlockLiveness(bb);
|
|
return getBlockLiveness(bb);
|
|
}
|
|
}
|
|
llvm_unreachable("covered switch");
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MARK: PrunedLiveness
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void PrunedLiveness::updateForUse(SILInstruction *user, bool lifetimeEnding) {
|
|
auto useBlockLive = liveBlocks.updateForUse(user);
|
|
// Record all uses of blocks on the liveness boundary. For blocks marked
|
|
// LiveWithin, the boundary is considered to be the last use in the block.
|
|
//
|
|
// FIXME: Why is nonLifetimeEndingUsesInLiveOut inside PrunedLiveness, and
|
|
// what does it mean? Blocks may transition to LiveOut later. Or they may
|
|
// already be LiveOut from a previous use. After computing liveness, clients
|
|
// should check uses that are in PrunedLivenessBoundary.
|
|
if (!lifetimeEnding && useBlockLive == PrunedLiveBlocks::LiveOut) {
|
|
if (nonLifetimeEndingUsesInLiveOut)
|
|
nonLifetimeEndingUsesInLiveOut->insert(user);
|
|
return;
|
|
}
|
|
// Note that a user may use the current value from multiple operands. If any
|
|
// of the uses are non-lifetime-ending, then we must consider the user
|
|
// itself non-lifetime-ending; it cannot be a final destroy point because
|
|
// the value of the non-lifetime-ending operand must be kept alive until the
|
|
// end of the user. Consider a call that takes the same value using
|
|
// different conventions:
|
|
//
|
|
// apply %f(%val, %val) : $(@guaranteed, @owned) -> ()
|
|
//
|
|
// This call is not considered the end of %val's lifetime. The @owned
|
|
// argument must be copied.
|
|
auto iterAndSuccess = users.insert({user, lifetimeEnding});
|
|
if (!iterAndSuccess.second)
|
|
iterAndSuccess.first->second &= lifetimeEnding;
|
|
}
|
|
|
|
bool PrunedLiveness::updateForBorrowingOperand(Operand *op) {
|
|
assert(op->getOperandOwnership() == OperandOwnership::Borrow);
|
|
|
|
// A nested borrow scope is considered a use-point at each scope ending
|
|
// instruction.
|
|
//
|
|
// TODO: Handle reborrowed copies by considering the extended borrow
|
|
// scope. Temporarily bail-out on reborrows because we can't handle uses
|
|
// that aren't dominated by currentDef.
|
|
if (!BorrowingOperand(op).visitScopeEndingUses([this](Operand *end) {
|
|
if (end->getOperandOwnership() == OperandOwnership::Reborrow) {
|
|
return false;
|
|
}
|
|
updateForUse(end->getUser(), /*lifetimeEnding*/ false);
|
|
return true;
|
|
})) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void PrunedLiveness::extendAcrossLiveness(PrunedLiveness &otherLivesness) {
|
|
// update this liveness for all the interesting users in otherLivesness.
|
|
for (std::pair<SILInstruction *, bool> userAndEnd : otherLivesness.users) {
|
|
updateForUse(userAndEnd.first, userAndEnd.second);
|
|
}
|
|
}
|
|
|
|
bool PrunedLiveness::isWithinBoundary(SILInstruction *inst) const {
|
|
SILBasicBlock *block = inst->getParent();
|
|
switch (getBlockLiveness(block)) {
|
|
case PrunedLiveBlocks::Dead:
|
|
return false;
|
|
case PrunedLiveBlocks::LiveWithin:
|
|
break;
|
|
case PrunedLiveBlocks::LiveOut:
|
|
return true;
|
|
}
|
|
// The boundary is within this block. This instruction is before the boundary
|
|
// iff any interesting uses occur after it.
|
|
for (SILInstruction &inst :
|
|
make_range(std::next(inst->getIterator()), block->end())) {
|
|
switch (isInterestingUser(&inst)) {
|
|
case PrunedLiveness::NonUser:
|
|
break;
|
|
case PrunedLiveness::NonLifetimeEndingUse:
|
|
case PrunedLiveness::LifetimeEndingUse:
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PrunedLiveness::areUsesWithinBoundary(ArrayRef<Operand *> uses,
|
|
DeadEndBlocks *deadEndBlocks) const {
|
|
auto checkDeadEnd = [deadEndBlocks](SILInstruction *inst) {
|
|
return deadEndBlocks && deadEndBlocks->isDeadEnd(inst->getParent());
|
|
};
|
|
|
|
for (auto *use : uses) {
|
|
auto *user = use->getUser();
|
|
if (!isWithinBoundary(user) && !checkDeadEnd(user))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PrunedLiveness::areUsesOutsideBoundary(
|
|
ArrayRef<Operand *> uses, DeadEndBlocks *deadEndBlocks) const {
|
|
auto checkDeadEnd = [deadEndBlocks](SILInstruction *inst) {
|
|
return deadEndBlocks && deadEndBlocks->isDeadEnd(inst->getParent());
|
|
};
|
|
|
|
for (auto *use : uses) {
|
|
auto *user = use->getUser();
|
|
if (isWithinBoundary(user) || checkDeadEnd(user))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// An SSA def meets all the criteria for pruned liveness--def dominates all uses
|
|
// with no holes in the liverange. The lifetime-ending uses are also
|
|
// recorded--destroy_value or end_borrow. However destroy_values may not
|
|
// jointly-post dominate if dead-end blocks are present.
|
|
void PrunedLiveness::computeSSALiveness(SILValue def) {
|
|
initializeDefBlock(def->getParentBlock());
|
|
for (Operand *use : def->getUses()) {
|
|
updateForUse(use->getUser(), use->isLifetimeEnding());
|
|
}
|
|
}
|
|
|
|
void PrunedLivenessBoundary::visitInsertionPoints(
|
|
llvm::function_ref<void(SILBasicBlock::iterator insertPt)> visitor,
|
|
DeadEndBlocks *deBlocks) {
|
|
for (SILInstruction *user : lastUsers) {
|
|
if (!isa<TermInst>(user)) {
|
|
visitor(std::next(user->getIterator()));
|
|
continue;
|
|
}
|
|
auto *predBB = user->getParent();
|
|
for (SILBasicBlock *succ : predBB->getSuccessors()) {
|
|
if (deBlocks && deBlocks->isDeadEnd(succ))
|
|
continue;
|
|
|
|
assert(succ->getSinglePredecessorBlock() == predBB);
|
|
visitor(succ->begin());
|
|
}
|
|
}
|
|
for (SILBasicBlock *edge : boundaryEdges) {
|
|
if (deBlocks && deBlocks->isDeadEnd(edge))
|
|
continue;
|
|
|
|
visitor(edge->begin());
|
|
}
|
|
}
|
|
|
|
// Use \p liveness to find the last use in \p bb and add it to \p
|
|
// boundary.lastUsers.
|
|
static void findLastUserInBlock(SILBasicBlock *bb,
|
|
PrunedLivenessBoundary &boundary,
|
|
const PrunedLiveness &liveness) {
|
|
for (auto instIter = bb->rbegin(), endIter = bb->rend(); instIter != endIter;
|
|
++instIter) {
|
|
auto *inst = &*instIter;
|
|
if (liveness.isInterestingUser(inst) == PrunedLiveness::NonUser)
|
|
continue;
|
|
|
|
boundary.lastUsers.push_back(inst);
|
|
return;
|
|
}
|
|
llvm_unreachable("No user in LiveWithin block");
|
|
}
|
|
|
|
void PrunedLivenessBoundary::compute(const PrunedLiveness &liveness) {
|
|
for (SILBasicBlock *bb : liveness.getDiscoveredBlocks()) {
|
|
// Process each block that has not been visited and is not LiveOut.
|
|
switch (liveness.getBlockLiveness(bb)) {
|
|
case PrunedLiveBlocks::LiveOut:
|
|
for (SILBasicBlock *succBB : bb->getSuccessors()) {
|
|
if (liveness.getBlockLiveness(succBB) == PrunedLiveBlocks::Dead) {
|
|
boundaryEdges.push_back(succBB);
|
|
}
|
|
}
|
|
break;
|
|
case PrunedLiveBlocks::LiveWithin: {
|
|
// The liveness boundary is inside this block. Insert a final destroy
|
|
// inside the block if it doesn't already have one.
|
|
findLastUserInBlock(bb, *this, liveness);
|
|
break;
|
|
}
|
|
case PrunedLiveBlocks::Dead:
|
|
llvm_unreachable("All discovered blocks must be live");
|
|
}
|
|
}
|
|
}
|
|
|
|
void PrunedLivenessBoundary::compute(const PrunedLiveness &liveness,
|
|
ArrayRef<SILBasicBlock *> postDomBlocks) {
|
|
if (postDomBlocks.empty())
|
|
return; // all paths must be dead-ends or infinite loops
|
|
|
|
BasicBlockWorklist blockWorklist(postDomBlocks[0]->getParent());
|
|
|
|
// Visit each post-dominating block as the starting point for a
|
|
// backward CFG traversal.
|
|
for (auto *bb : postDomBlocks) {
|
|
blockWorklist.push(bb);
|
|
}
|
|
while (auto *bb = blockWorklist.pop()) {
|
|
// Process each block that has not been visited and is not LiveOut.
|
|
switch (liveness.getBlockLiveness(bb)) {
|
|
case PrunedLiveBlocks::LiveOut:
|
|
// A lifetimeEndBlock may be determined to be LiveOut after analyzing the
|
|
// extended liveness. It is irrelevant for finding the boundary.
|
|
break;
|
|
case PrunedLiveBlocks::LiveWithin: {
|
|
// The liveness boundary is inside this block. Insert a final destroy
|
|
// inside the block if it doesn't already have one.
|
|
findLastUserInBlock(bb, *this, liveness);
|
|
break;
|
|
}
|
|
case PrunedLiveBlocks::Dead:
|
|
// Continue searching upward to find the pruned liveness boundary.
|
|
for (auto *predBB : bb->getPredecessorBlocks()) {
|
|
if (liveness.getBlockLiveness(predBB) == PrunedLiveBlocks::LiveOut) {
|
|
boundaryEdges.push_back(bb);
|
|
} else {
|
|
blockWorklist.pushIfNotVisited(predBB);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|