blob: 46834b54cf3c8f40f913e18e0cc353cf8fff5ba3 [file] [log] [blame]
//===--- ValueLifetime.cpp - ValueLifetimeAnalysis ------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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/SILOptimizer/Utils/ValueLifetime.h"
#include "swift/Basic/STLExtras.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
using namespace swift;
void ValueLifetimeAnalysis::propagateLiveness() {
bool defIsInstruction = defValue.is<SILInstruction *>();
assert(liveBlocks.empty() && "frontier computed twice");
assert(
(!defIsInstruction || !userSet.count(defValue.get<SILInstruction *>())) &&
"definition cannot be its own use");
// Compute the def block only if we have a SILInstruction. If we have a
// SILArgument, this will be nullptr.
auto *defBB = getDefValueParentBlock();
SmallVector<SILBasicBlock *, 64> worklist;
int numUsersBeforeDef = 0;
// Find the initial set of blocks where the value is live, because
// it is used in those blocks.
for (SILInstruction *user : userSet) {
SILBasicBlock *userBlock = user->getParent();
if (liveBlocks.insert(userBlock))
worklist.push_back(userBlock);
// A user in the defBB could potentially be located before the defValue. If
// we had a SILArgument, defBB will be nullptr, so we should always have
// numUsersBeforeDef is 0. We assert this at the end of the loop.
if (defIsInstruction && userBlock == defBB)
++numUsersBeforeDef;
}
assert((defValue.is<SILInstruction *>() || (numUsersBeforeDef == 0)) &&
"Non SILInstruction defValue with users before the def?!");
// Don't count any users in the defBB which are actually located _after_ the
// defValue.
if (defIsInstruction) {
auto instIter = defValue.get<SILInstruction *>()->getIterator();
while (numUsersBeforeDef > 0 && ++instIter != defBB->end()) {
if (userSet.count(&*instIter))
--numUsersBeforeDef;
}
}
// Initialize the hasUsersBeforeDef field.
hasUsersBeforeDef = numUsersBeforeDef > 0;
assert(defIsInstruction || !hasUsersBeforeDef);
// Now propagate liveness backwards until we hit the block that defines the
// value.
while (!worklist.empty()) {
auto *bb = worklist.pop_back_val();
// Don't go beyond the definition.
if (bb == defBB && !hasUsersBeforeDef)
continue;
for (auto *predBB : bb->getPredecessorBlocks()) {
// If it's already in the set, then we've already queued and/or
// processed the predecessors.
if (liveBlocks.insert(predBB))
worklist.push_back(predBB);
}
}
}
SILInstruction *ValueLifetimeAnalysis::findLastUserInBlock(SILBasicBlock *bb) {
// Walk backwards in bb looking for last use of the value.
for (auto &inst : llvm::reverse(*bb)) {
assert(defValue.dyn_cast<SILInstruction *>() != &inst &&
"Found def before finding use!");
if (userSet.count(&inst))
return &inst;
}
llvm_unreachable("Expected to find use of value in block!");
}
bool ValueLifetimeAnalysis::computeFrontier(FrontierImpl &frontier, Mode mode,
DeadEndBlocks *deBlocks) {
assert(!isAliveAtBeginOfBlock(getFunction()->getEntryBlock()) &&
"Can't compute frontier for def which does not dominate all uses");
bool noCriticalEdges = true;
// Exit-blocks from the lifetime region. The value is live at the end of
// a predecessor block but not in the frontier block itself.
llvm::SmallSetVector<SILBasicBlock *, 16> frontierBlocks;
// Blocks where the value is live at the end of the block and which have
// a frontier block as successor.
llvm::SmallSetVector<SILBasicBlock *, 16> liveOutBlocks;
/// The lifetime ends if we have a live block and a not-live successor.
for (SILBasicBlock *bb : liveBlocks) {
if (deBlocks && deBlocks->isDeadEnd(bb))
continue;
bool liveInSucc = false;
bool deadInSucc = false;
bool usedAndRedefinedInSucc = false;
for (const SILSuccessor &succ : bb->getSuccessors()) {
if (isAliveAtBeginOfBlock(succ)) {
liveInSucc = true;
if (succ == getDefValueParentBlock()) {
// Here, the basic block bb uses the value but also redefines the
// value inside bb. The new value could be used by the successors
// of succ and therefore could be live at the end of succ as well.
//
// This should never happen if we have a SILArgument since the
// SILArgument can not have any uses before it in a block.
assert(defValue.is<SILInstruction *>() &&
"SILArguments dominate all instructions in their defining "
"blocks");
usedAndRedefinedInSucc = true;
}
} else if (!deBlocks || !deBlocks->isDeadEnd(succ)) {
deadInSucc = true;
}
}
if (usedAndRedefinedInSucc) {
// Here, the basic block bb uses the value and later redefines the value.
// Therefore, this value's lifetime ends after its last use preceding the
// re-definition of the value.
//
// We know that we can not have a SILArgument here since the SILArgument
// dominates all instructions in the same block.
auto ii = defValue.get<SILInstruction *>()->getReverseIterator();
for (; ii != bb->rend(); ++ii) {
if (userSet.count(&*ii)) {
frontier.push_back(&*std::next(ii));
break;
}
}
assert(ii != bb->rend() &&
"There must be a user in bb before definition");
}
if (!liveInSucc) {
// The value is not live in any of the successor blocks. This means the
// block contains a last use of the value. The next instruction after
// the last use is part of the frontier.
SILInstruction *lastUser = findLastUserInBlock(bb);
if (!isa<TermInst>(lastUser)) {
frontier.push_back(&*std::next(lastUser->getIterator()));
continue;
}
// In case the last user is a TermInst there is no further instruction in
// the block which can be the frontier. Instead we add all successor
// blocks to the frontier (see below).
// If the TermInst exits the function (e.g. 'return' or 'throw'), there
// are no successors and we have to bail.
if (!deadInSucc) {
assert(cast<TermInst>(lastUser)->isFunctionExiting() &&
"The final using TermInst must have successors");
assert(mode != AllowToModifyCFG &&
"Cannot bail if the mode is AllowToModifyCFG");
return false;
}
}
if (deadInSucc) {
if (mode == UsersMustPostDomDef)
return false;
// The value is not live in some of the successor blocks.
liveOutBlocks.insert(bb);
for (const SILSuccessor &succ : bb->getSuccessors()) {
if (!isAliveAtBeginOfBlock(succ)) {
// It's an "exit" edge from the lifetime region.
frontierBlocks.insert(succ);
}
}
}
}
// Handle "exit" edges from the lifetime region.
llvm::SmallPtrSet<SILBasicBlock *, 16> unhandledFrontierBlocks;
for (SILBasicBlock *frontierBB : frontierBlocks) {
assert(mode != UsersMustPostDomDef);
bool needSplit = false;
// If the value is live only in part of the predecessor blocks we have to
// split those predecessor edges.
for (SILBasicBlock *Pred : frontierBB->getPredecessorBlocks()) {
if (!liveOutBlocks.count(Pred)) {
needSplit = true;
break;
}
}
if (needSplit) {
// We need to split the critical edge to create a frontier instruction.
unhandledFrontierBlocks.insert(frontierBB);
} else {
// The first instruction of the exit-block is part of the frontier.
frontier.push_back(&*frontierBB->begin());
}
}
if (unhandledFrontierBlocks.size() == 0) {
return true;
}
// Split critical edges from the lifetime region to not yet handled frontier
// blocks.
for (SILBasicBlock *frontierPred : liveOutBlocks) {
assert(mode != UsersMustPostDomDef);
auto *term = frontierPred->getTerminator();
// Cache the successor blocks because splitting critical edges invalidates
// the successor list iterator of T.
llvm::SmallVector<SILBasicBlock *, 4> succBlocks;
for (const SILSuccessor &succ : term->getSuccessors())
succBlocks.push_back(succ);
for (unsigned i = 0, e = succBlocks.size(); i != e; ++i) {
if (unhandledFrontierBlocks.count(succBlocks[i])) {
assert((isCriticalEdge(term, i) || userSet.count(term)) &&
"actually not a critical edge?");
noCriticalEdges = false;
if (mode != AllowToModifyCFG) {
// If the CFG need not be modified, just record the critical edge and
// continue.
this->criticalEdges.push_back({term, i});
continue;
}
SILBasicBlock *newBlock = splitEdge(term, i);
// The single terminator instruction is part of the frontier.
frontier.push_back(&*newBlock->begin());
}
}
}
return noCriticalEdges;
}
bool ValueLifetimeAnalysis::isWithinLifetime(SILInstruction *inst) {
SILBasicBlock *bb = inst->getParent();
// Check if the value is not live anywhere in inst's block.
if (!liveBlocks.count(bb))
return false;
for (const SILSuccessor &succ : bb->getSuccessors()) {
// If the value is live at the beginning of any successor block it is also
// live at the end of bb and therefore inst is definitely in the lifetime
// region (Note that we don't check in upward direction against the value's
// definition).
if (isAliveAtBeginOfBlock(succ))
return true;
}
// The value is live in the block but not at the end of the block. Check if
// inst is located before (or at) the last use.
for (auto ii = bb->rbegin(); ii != bb->rend(); ++ii) {
if (userSet.count(&*ii)) {
return true;
}
if (inst == &*ii)
return false;
}
llvm_unreachable("Expected to find use of value in block!");
}
// Searches \p bb backwards from the instruction before \p frontierInst
// to the beginning of the list and returns true if we find a dealloc_ref
// /before/ we find \p defValue (the instruction that defines our target value).
static bool
blockContainsDeallocRef(SILBasicBlock *bb,
PointerUnion<SILInstruction *, SILArgument *> defValue,
SILInstruction *frontierInst) {
SILBasicBlock::reverse_iterator End = bb->rend();
SILBasicBlock::reverse_iterator iter = frontierInst->getReverseIterator();
for (++iter; iter != End; ++iter) {
SILInstruction *inst = &*iter;
if (isa<DeallocRefInst>(inst))
return true;
// We know that inst is not a nullptr, so if we have a SILArgument, this
// will always fail as we want.
if (inst == defValue.dyn_cast<SILInstruction *>())
return false;
}
return false;
}
bool ValueLifetimeAnalysis::containsDeallocRef(const FrontierImpl &frontier) {
SmallPtrSet<SILBasicBlock *, 8> frontierBlocks;
// Search in live blocks where the value is not alive until the end of the
// block, i.e. the live range is terminated by a frontier instruction.
for (SILInstruction *frontierInst : frontier) {
SILBasicBlock *bb = frontierInst->getParent();
if (blockContainsDeallocRef(bb, defValue, frontierInst))
return true;
frontierBlocks.insert(bb);
}
// Search in all other live blocks where the value is alive until the end of
// the block.
for (SILBasicBlock *bb : liveBlocks) {
if (frontierBlocks.count(bb) == 0) {
if (blockContainsDeallocRef(bb, defValue, bb->getTerminator()))
return true;
}
}
return false;
}
void ValueLifetimeAnalysis::dump() const {
llvm::errs() << "lifetime of def: ";
if (auto *ii = defValue.dyn_cast<SILInstruction *>()) {
llvm::errs() << *ii;
} else {
llvm::errs() << *defValue.get<SILArgument *>();
}
for (SILInstruction *use : userSet) {
llvm::errs() << " use: " << *use;
}
llvm::errs() << " live blocks:";
for (SILBasicBlock *bb : liveBlocks) {
llvm::errs() << ' ' << bb->getDebugID();
}
llvm::errs() << '\n';
}
void swift::endLifetimeAtFrontier(
SILValue valueOrStackLoc,
const ValueLifetimeAnalysis::FrontierImpl &frontier,
SILBuilderContext &builderCtxt, InstModCallbacks callbacks) {
for (SILInstruction *endPoint : frontier) {
SILBuilderWithScope builder(endPoint, builderCtxt);
SILLocation loc = RegularLocation(endPoint->getLoc().getSourceLoc());
emitDestroyOperation(builder, loc, valueOrStackLoc, callbacks);
if (isa<AllocStackInst>(valueOrStackLoc)) {
builder.createDeallocStack(loc, valueOrStackLoc);
}
}
}