mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
594 lines
20 KiB
C++
594 lines
20 KiB
C++
//===--- CFGOptUtils.cpp - SIL CFG edge utilities -------------------------===//
|
|
//
|
|
// 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/CFGOptUtils.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/STLExtras.h"
|
|
#include "swift/Demangling/ManglingMacros.h"
|
|
#include "swift/SIL/BasicBlockUtils.h"
|
|
#include "swift/SIL/Dominance.h"
|
|
#include "swift/SIL/LoopInfo.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/BasicBlockDatastructures.h"
|
|
#include "swift/SIL/OwnershipUtils.h"
|
|
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
|
|
#include "llvm/ADT/TinyPtrVector.h"
|
|
|
|
using namespace swift;
|
|
|
|
TermInst *swift::addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest,
|
|
SILValue val,
|
|
InstructionDeleter &deleter) {
|
|
SILBuilderWithScope builder(branch);
|
|
TermInst *newBr = nullptr;
|
|
|
|
if (auto *cbi = dyn_cast<CondBranchInst>(branch)) {
|
|
SmallVector<SILValue, 8> trueArgs;
|
|
SmallVector<SILValue, 8> falseArgs;
|
|
|
|
for (auto arg : cbi->getTrueArgs())
|
|
trueArgs.push_back(arg);
|
|
|
|
for (auto arg : cbi->getFalseArgs())
|
|
falseArgs.push_back(arg);
|
|
|
|
if (dest == cbi->getTrueBB()) {
|
|
trueArgs.push_back(val);
|
|
assert(trueArgs.size() == dest->getNumArguments());
|
|
}
|
|
if (dest == cbi->getFalseBB()) {
|
|
falseArgs.push_back(val);
|
|
assert(falseArgs.size() == dest->getNumArguments());
|
|
}
|
|
|
|
newBr = builder.createCondBranch(
|
|
cbi->getLoc(), cbi->getCondition(), cbi->getTrueBB(), trueArgs,
|
|
cbi->getFalseBB(), falseArgs, cbi->getTrueBBCount(),
|
|
cbi->getFalseBBCount());
|
|
deleter.getCallbacks().createdNewInst(newBr);
|
|
} else if (auto *bi = dyn_cast<BranchInst>(branch)) {
|
|
SmallVector<SILValue, 8> args;
|
|
|
|
for (auto arg : bi->getArgs())
|
|
args.push_back(arg);
|
|
|
|
args.push_back(val);
|
|
assert(args.size() == dest->getNumArguments());
|
|
newBr = builder.createBranch(bi->getLoc(), bi->getDestBB(), args);
|
|
deleter.getCallbacks().createdNewInst(newBr);
|
|
} else {
|
|
// At the moment we can only add arguments to br and cond_br.
|
|
llvm_unreachable("Can't add argument to terminator");
|
|
}
|
|
|
|
deleter.forceDelete(branch);
|
|
|
|
return newBr;
|
|
}
|
|
|
|
static void deleteTriviallyDeadOperandsOfDeadArgument(
|
|
MutableArrayRef<Operand> termOperands, unsigned deadArgIndex,
|
|
InstModCallbacks callbacks = InstModCallbacks()) {
|
|
Operand &op = termOperands[deadArgIndex];
|
|
auto *i = op.get()->getDefiningInstruction();
|
|
if (!i)
|
|
return;
|
|
op.set(SILUndef::get(op.get()));
|
|
eliminateDeadInstruction(i, callbacks);
|
|
}
|
|
|
|
// Our implementation assumes that our caller is attempting to remove a dead
|
|
// SILPhiArgument from a SILBasicBlock and has already RAUWed the argument.
|
|
TermInst *swift::deleteEdgeValue(TermInst *branch, SILBasicBlock *destBlock,
|
|
size_t argIndex, bool cleanupDeadPhiOps,
|
|
InstModCallbacks callbacks) {
|
|
if (auto *cbi = dyn_cast<CondBranchInst>(branch)) {
|
|
SmallVector<SILValue, 8> trueArgs;
|
|
SmallVector<SILValue, 8> falseArgs;
|
|
|
|
llvm::copy(cbi->getTrueArgs(), std::back_inserter(trueArgs));
|
|
llvm::copy(cbi->getFalseArgs(), std::back_inserter(falseArgs));
|
|
|
|
if (destBlock == cbi->getTrueBB()) {
|
|
if (cleanupDeadPhiOps) {
|
|
deleteTriviallyDeadOperandsOfDeadArgument(cbi->getTrueOperands(),
|
|
argIndex, callbacks);
|
|
}
|
|
trueArgs.erase(trueArgs.begin() + argIndex);
|
|
}
|
|
|
|
if (destBlock == cbi->getFalseBB()) {
|
|
if (cleanupDeadPhiOps) {
|
|
deleteTriviallyDeadOperandsOfDeadArgument(cbi->getFalseOperands(),
|
|
argIndex, callbacks);
|
|
}
|
|
falseArgs.erase(falseArgs.begin() + argIndex);
|
|
}
|
|
|
|
SILBuilderWithScope builder(cbi);
|
|
auto *result = builder.createCondBranch(
|
|
cbi->getLoc(), cbi->getCondition(), cbi->getTrueBB(), trueArgs,
|
|
cbi->getFalseBB(), falseArgs, cbi->getTrueBBCount(),
|
|
cbi->getFalseBBCount());
|
|
branch->eraseFromParent();
|
|
return result;
|
|
}
|
|
|
|
if (auto *bi = dyn_cast<BranchInst>(branch)) {
|
|
SmallVector<SILValue, 8> args;
|
|
llvm::copy(bi->getArgs(), std::back_inserter(args));
|
|
if (cleanupDeadPhiOps) {
|
|
deleteTriviallyDeadOperandsOfDeadArgument(bi->getAllOperands(), argIndex,
|
|
callbacks);
|
|
}
|
|
args.erase(args.begin() + argIndex);
|
|
auto *result = SILBuilderWithScope(bi).createBranch(bi->getLoc(),
|
|
bi->getDestBB(), args);
|
|
branch->eraseFromParent();
|
|
return result;
|
|
}
|
|
|
|
llvm_unreachable("unsupported terminator");
|
|
}
|
|
|
|
void swift::erasePhiArgument(SILBasicBlock *block, unsigned argIndex,
|
|
bool cleanupDeadPhiOps,
|
|
InstModCallbacks callbacks) {
|
|
SILArgument *arg = block->getArgument(argIndex);
|
|
assert(arg->isPhi() && "Only should be used on phi arguments");
|
|
if (auto *bfi = getBorrowedFromUser(arg)) {
|
|
bfi->replaceAllUsesWith(arg);
|
|
bfi->eraseFromParent();
|
|
}
|
|
block->eraseArgument(argIndex);
|
|
|
|
// Determine the set of predecessors in case any predecessor has
|
|
// two edges to this block (e.g. a conditional branch where both
|
|
// sides reach this block).
|
|
//
|
|
// NOTE: This needs to be a SmallSetVector since we need both uniqueness /and/
|
|
// insertion order. Otherwise non-determinism can result.
|
|
BasicBlockSetVector predBlocks(block->getParent());
|
|
|
|
for (auto *pred : block->getPredecessorBlocks())
|
|
predBlocks.insert(pred);
|
|
|
|
for (auto *pred : predBlocks)
|
|
deleteEdgeValue(pred->getTerminator(), block, argIndex, cleanupDeadPhiOps,
|
|
callbacks);
|
|
}
|
|
|
|
/// Changes the edge value between a branch and destination basic block
|
|
/// at the specified index. Changes all edges from \p branch to \p dest to carry
|
|
/// the value.
|
|
///
|
|
/// \param branch The branch to modify.
|
|
/// \param dest The destination of the edge.
|
|
/// \param idx The index of the argument to modify.
|
|
/// \param Val The new value to use.
|
|
/// \return The new branch. Deletes the old one.
|
|
/// Changes the edge value between a branch and destination basic block at the
|
|
/// specified index.
|
|
TermInst *swift::changeEdgeValue(TermInst *branch, SILBasicBlock *dest,
|
|
size_t idx, SILValue Val) {
|
|
SILBuilderWithScope builder(branch);
|
|
|
|
if (auto *cbi = dyn_cast<CondBranchInst>(branch)) {
|
|
SmallVector<SILValue, 8> trueArgs;
|
|
SmallVector<SILValue, 8> falseArgs;
|
|
|
|
OperandValueArrayRef oldTrueArgs = cbi->getTrueArgs();
|
|
bool branchOnTrue = cbi->getTrueBB() == dest;
|
|
assert((!branchOnTrue || idx < oldTrueArgs.size()) && "Not enough edges");
|
|
|
|
// Copy the edge values overwriting the edge at idx.
|
|
for (unsigned i = 0, e = oldTrueArgs.size(); i != e; ++i) {
|
|
if (branchOnTrue && idx == i)
|
|
trueArgs.push_back(Val);
|
|
else
|
|
trueArgs.push_back(oldTrueArgs[i]);
|
|
}
|
|
assert(trueArgs.size() == cbi->getTrueBB()->getNumArguments()
|
|
&& "Destination block's number of arguments must match");
|
|
|
|
OperandValueArrayRef oldFalseArgs = cbi->getFalseArgs();
|
|
bool branchOnFalse = cbi->getFalseBB() == dest;
|
|
assert((!branchOnFalse || idx < oldFalseArgs.size()) && "Not enough edges");
|
|
|
|
// Copy the edge values overwriting the edge at idx.
|
|
for (unsigned i = 0, e = oldFalseArgs.size(); i != e; ++i) {
|
|
if (branchOnFalse && idx == i)
|
|
falseArgs.push_back(Val);
|
|
else
|
|
falseArgs.push_back(oldFalseArgs[i]);
|
|
}
|
|
assert(falseArgs.size() == cbi->getFalseBB()->getNumArguments()
|
|
&& "Destination block's number of arguments must match");
|
|
|
|
cbi = builder.createCondBranch(
|
|
cbi->getLoc(), cbi->getCondition(), cbi->getTrueBB(), trueArgs,
|
|
cbi->getFalseBB(), falseArgs, cbi->getTrueBBCount(),
|
|
cbi->getFalseBBCount());
|
|
branch->dropAllReferences();
|
|
branch->eraseFromParent();
|
|
return cbi;
|
|
}
|
|
|
|
if (auto *bi = dyn_cast<BranchInst>(branch)) {
|
|
SmallVector<SILValue, 8> args;
|
|
|
|
assert(idx < bi->getNumArgs() && "Not enough edges");
|
|
OperandValueArrayRef oldArgs = bi->getArgs();
|
|
|
|
// Copy the edge values overwriting the edge at idx.
|
|
for (unsigned i = 0, e = oldArgs.size(); i != e; ++i) {
|
|
if (idx == i)
|
|
args.push_back(Val);
|
|
else
|
|
args.push_back(oldArgs[i]);
|
|
}
|
|
assert(args.size() == dest->getNumArguments());
|
|
|
|
bi = builder.createBranch(bi->getLoc(), bi->getDestBB(), args);
|
|
branch->dropAllReferences();
|
|
branch->eraseFromParent();
|
|
return bi;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled terminator leading to merge block");
|
|
}
|
|
|
|
/// Check if the edge from the terminator is critical.
|
|
bool swift::isCriticalEdge(TermInst *t, unsigned edgeIdx) {
|
|
assert(t->getSuccessors().size() > edgeIdx && "Not enough successors");
|
|
|
|
auto srcSuccs = t->getSuccessors();
|
|
|
|
if (srcSuccs.size() <= 1 &&
|
|
// Also consider non-branch instructions with a single successor for
|
|
// critical edges, for example: a switch_enum of a single-case enum.
|
|
(isa<BranchInst>(t) || isa<CondBranchInst>(t)))
|
|
return false;
|
|
|
|
SILBasicBlock *destBB = srcSuccs[edgeIdx];
|
|
assert(!destBB->pred_empty() && "There should be a predecessor");
|
|
if (destBB->getSinglePredecessorBlock())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
SILBasicBlock *swift::createSplitBranchTarget(SILBasicBlock *targetBlock,
|
|
SILBuilder &builder,
|
|
SILLocation loc) {
|
|
auto *function = targetBlock->getParent();
|
|
auto *edgeBB = function->createBasicBlockBefore(targetBlock);
|
|
SILBuilderWithScope(edgeBB, builder.getBuilderContext(),
|
|
builder.getCurrentDebugScope())
|
|
.createBranch(loc, targetBlock);
|
|
return edgeBB;
|
|
}
|
|
|
|
/// Splits the basic block at the iterator with an unconditional branch and
|
|
/// updates the dominator tree and loop info.
|
|
SILBasicBlock *swift::splitBasicBlockAndBranch(SILBuilder &builder,
|
|
SILInstruction *splitBeforeInst,
|
|
DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo) {
|
|
auto *origBB = splitBeforeInst->getParent();
|
|
auto *newBB = origBB->split(splitBeforeInst->getIterator());
|
|
builder.setInsertionPoint(origBB);
|
|
builder.createBranch(splitBeforeInst->getLoc(), newBB);
|
|
|
|
// Update the dominator tree.
|
|
if (domInfo) {
|
|
auto origBBDTNode = domInfo->getNode(origBB);
|
|
if (origBBDTNode) {
|
|
// Change the immediate dominators of the children of the block we
|
|
// splitted to the splitted block.
|
|
SmallVector<DominanceInfoNode *, 16> Adoptees(origBBDTNode->begin(),
|
|
origBBDTNode->end());
|
|
|
|
auto newBBDTNode = domInfo->addNewBlock(newBB, origBB);
|
|
for (auto *adoptee : Adoptees)
|
|
domInfo->changeImmediateDominator(adoptee, newBBDTNode);
|
|
}
|
|
}
|
|
|
|
// Update loop info.
|
|
if (loopInfo)
|
|
if (auto *origBBLoop = loopInfo->getLoopFor(origBB)) {
|
|
origBBLoop->addBasicBlockToLoop(newBB, loopInfo->getBase());
|
|
}
|
|
|
|
return newBB;
|
|
}
|
|
|
|
/// Split every edge between two basic blocks.
|
|
void swift::splitEdgesFromTo(SILBasicBlock *From, SILBasicBlock *To,
|
|
DominanceInfo *domInfo, SILLoopInfo *loopInfo) {
|
|
for (unsigned edgeIndex = 0, E = From->getSuccessors().size(); edgeIndex != E;
|
|
++edgeIndex) {
|
|
SILBasicBlock *succBB = From->getSuccessors()[edgeIndex];
|
|
if (succBB != To)
|
|
continue;
|
|
splitEdge(From->getTerminator(), edgeIndex, domInfo, loopInfo);
|
|
}
|
|
}
|
|
|
|
/// Splits the n-th critical edge from the terminator and updates dominance and
|
|
/// loop info if set.
|
|
/// Returns the newly created basic block on success or nullptr otherwise (if
|
|
/// the edge was not critical.
|
|
SILBasicBlock *swift::splitCriticalEdge(TermInst *t, unsigned edgeIdx,
|
|
DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo) {
|
|
if (!isCriticalEdge(t, edgeIdx))
|
|
return nullptr;
|
|
|
|
return splitEdge(t, edgeIdx, domInfo, loopInfo);
|
|
}
|
|
|
|
bool swift::splitCriticalEdgesFrom(SILBasicBlock *fromBB,
|
|
DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo) {
|
|
bool changed = false;
|
|
for (unsigned idx = 0, e = fromBB->getSuccessors().size(); idx != e; ++idx) {
|
|
auto *newBB =
|
|
splitCriticalEdge(fromBB->getTerminator(), idx, domInfo, loopInfo);
|
|
changed |= (newBB != nullptr);
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
bool swift::splitCriticalEdgesTo(SILBasicBlock *toBB, DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo) {
|
|
bool changed = false;
|
|
unsigned numPreds = std::distance(toBB->pred_begin(), toBB->pred_end());
|
|
|
|
for (unsigned idx = 0; idx != numPreds; ++idx) {
|
|
SILBasicBlock *fromBB = *std::next(toBB->pred_begin(), idx);
|
|
auto *newBB = splitIfCriticalEdge(fromBB, toBB);
|
|
changed |= (newBB != nullptr);
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
bool swift::hasCriticalEdges(SILFunction &f, bool onlyNonCondBr) {
|
|
for (SILBasicBlock &bb : f) {
|
|
// Only consider critical edges for terminators that don't support block
|
|
// arguments.
|
|
if (onlyNonCondBr && isa<CondBranchInst>(bb.getTerminator()))
|
|
continue;
|
|
|
|
if (isa<BranchInst>(bb.getTerminator()))
|
|
continue;
|
|
|
|
for (SILBasicBlock *succBB : bb.getSuccessorBlocks()) {
|
|
if (!isNonCriticalEdge(&bb, succBB))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Split all critical edges in the function updating the dominator tree and
|
|
/// loop information (if they are not set to null).
|
|
bool swift::splitAllCriticalEdges(SILFunction &f, DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo) {
|
|
bool changed = false;
|
|
|
|
for (SILBasicBlock &bb : f) {
|
|
if (isa<BranchInst>(bb.getTerminator()))
|
|
continue;
|
|
|
|
for (unsigned idx = 0, e = bb.getSuccessors().size(); idx != e; ++idx) {
|
|
auto *newBB =
|
|
splitCriticalEdge(bb.getTerminator(), idx, domInfo, loopInfo);
|
|
assert(!newBB
|
|
|| isa<CondBranchInst>(bb.getTerminator())
|
|
&& "Only cond_br may have a critical edge.");
|
|
changed |= (newBB != nullptr);
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
/// Merge the basic block with its successor if possible. If dominance
|
|
/// information or loop info is non null update it. Return true if block was
|
|
/// merged.
|
|
bool swift::mergeBasicBlockWithSuccessor(SILBasicBlock *bb,
|
|
DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo) {
|
|
auto *branch = dyn_cast<BranchInst>(bb->getTerminator());
|
|
if (!branch)
|
|
return false;
|
|
|
|
auto *succBB = branch->getDestBB();
|
|
if (bb == succBB || !succBB->getSinglePredecessorBlock())
|
|
return false;
|
|
|
|
if (domInfo)
|
|
if (auto *succBBNode = domInfo->getNode(succBB)) {
|
|
// Change the immediate dominator for children of the successor to be the
|
|
// current block.
|
|
auto *bbNode = domInfo->getNode(bb);
|
|
SmallVector<DominanceInfoNode *, 8> Children(succBBNode->begin(),
|
|
succBBNode->end());
|
|
for (auto *ChildNode : Children)
|
|
domInfo->changeImmediateDominator(ChildNode, bbNode);
|
|
|
|
domInfo->eraseNode(succBB);
|
|
}
|
|
|
|
if (loopInfo)
|
|
loopInfo->removeBlock(succBB);
|
|
|
|
mergeBasicBlockWithSingleSuccessor(bb, succBB);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool swift::mergeBasicBlocks(SILFunction *f) {
|
|
bool merged = false;
|
|
for (auto bbIter = f->begin(); bbIter != f->end();) {
|
|
if (mergeBasicBlockWithSuccessor(&*bbIter, /*domInfo*/ nullptr,
|
|
/*loopInfo*/ nullptr)) {
|
|
merged = true;
|
|
// Continue to merge the current block without advancing.
|
|
continue;
|
|
}
|
|
++bbIter;
|
|
}
|
|
return merged;
|
|
}
|
|
|
|
/// Splits the critical edges between from and to. This code assumes there is
|
|
/// only one edge between the two basic blocks.
|
|
SILBasicBlock *swift::splitIfCriticalEdge(SILBasicBlock *from,
|
|
SILBasicBlock *to,
|
|
DominanceInfo *domInfo,
|
|
SILLoopInfo *loopInfo) {
|
|
auto *t = from->getTerminator();
|
|
for (unsigned i = 0, e = t->getSuccessors().size(); i != e; ++i) {
|
|
if (t->getSuccessors()[i] == to)
|
|
return splitCriticalEdge(t, i, domInfo, loopInfo);
|
|
}
|
|
llvm_unreachable("Destination block not found");
|
|
}
|
|
|
|
bool swift::splitAllCondBrCriticalEdgesWithNonTrivialArgs(
|
|
SILFunction &fn, DominanceInfo *domInfo, SILLoopInfo *loopInfo) {
|
|
// Find our targets.
|
|
llvm::SmallVector<std::pair<SILBasicBlock *, unsigned>, 8> targets;
|
|
for (auto &block : fn) {
|
|
auto *cbi = dyn_cast<CondBranchInst>(block.getTerminator());
|
|
if (!cbi)
|
|
continue;
|
|
|
|
// See if our true index is a critical edge. If so, add block to the list
|
|
// and continue. If the false edge is also critical, we will handle it at
|
|
// the same time.
|
|
if (isCriticalEdge(cbi, CondBranchInst::TrueIdx)) {
|
|
targets.emplace_back(&block, CondBranchInst::TrueIdx);
|
|
}
|
|
|
|
if (!isCriticalEdge(cbi, CondBranchInst::FalseIdx)) {
|
|
continue;
|
|
}
|
|
|
|
targets.emplace_back(&block, CondBranchInst::FalseIdx);
|
|
}
|
|
|
|
if (targets.empty())
|
|
return false;
|
|
|
|
for (auto p : targets) {
|
|
SILBasicBlock *block = p.first;
|
|
unsigned index = p.second;
|
|
auto *result =
|
|
splitCriticalEdge(block->getTerminator(), index, domInfo, loopInfo);
|
|
(void)result;
|
|
assert(result);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool isSafeNonExitTerminator(TermInst *ti) {
|
|
switch (ti->getTermKind()) {
|
|
case TermKind::BranchInst:
|
|
case TermKind::CondBranchInst:
|
|
case TermKind::SwitchValueInst:
|
|
case TermKind::SwitchEnumInst:
|
|
case TermKind::SwitchEnumAddrInst:
|
|
case TermKind::DynamicMethodBranchInst:
|
|
case TermKind::CheckedCastBranchInst:
|
|
case TermKind::CheckedCastAddrBranchInst:
|
|
return true;
|
|
case TermKind::UnreachableInst:
|
|
case TermKind::ReturnInst:
|
|
case TermKind::ReturnBorrowInst:
|
|
case TermKind::ThrowInst:
|
|
case TermKind::ThrowAddrInst:
|
|
case TermKind::UnwindInst:
|
|
return false;
|
|
// yield is special because it can do arbitrary,
|
|
// potentially-process-terminating things.
|
|
case TermKind::YieldInst:
|
|
case TermKind::AwaitAsyncContinuationInst:
|
|
return false;
|
|
case TermKind::TryApplyInst:
|
|
return true;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled TermKind in switch.");
|
|
}
|
|
|
|
bool swift::isTrapNoReturnFunction(SILFunction *f) {
|
|
const char *fatalName = MANGLE_AS_STRING(
|
|
MANGLE_SYM(s18_fatalErrorMessageyys12StaticStringV_AcCSutF));
|
|
|
|
// We use ends_with here since if we specialize fatal error we will always
|
|
// prepend the specialization records to fatalName.
|
|
if (!f || !f->getName().ends_with(fatalName))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool swift::findAllNonFailureExitBBs(
|
|
SILFunction *f, llvm::TinyPtrVector<SILBasicBlock *> &bbs) {
|
|
for (SILBasicBlock &bb : *f) {
|
|
TermInst *ti = bb.getTerminator();
|
|
|
|
// If we know that this terminator is not an exit terminator, continue.
|
|
if (isSafeNonExitTerminator(ti))
|
|
continue;
|
|
|
|
// A return inst is always a non-failure exit bb.
|
|
if (ti->isFunctionExiting()) {
|
|
bbs.push_back(&bb);
|
|
continue;
|
|
}
|
|
|
|
// If we don't have an unreachable inst at this point, this is a terminator
|
|
// we don't understand. Be conservative and return false.
|
|
if (!isa<UnreachableInst>(ti))
|
|
return false;
|
|
|
|
// Ok, at this point we know we have a terminator. If it is the only
|
|
// instruction in our bb, it is a failure bb. continue...
|
|
if (ti == &*bb.begin())
|
|
continue;
|
|
|
|
// If the unreachable is preceded by a no-return apply inst, then it is a
|
|
// non-failure exit bb. Add it to our list and continue.
|
|
auto prevIter = std::prev(SILBasicBlock::iterator(ti));
|
|
if (auto *ai = dyn_cast<ApplyInst>(&*prevIter)) {
|
|
if (ai->isCalleeNoReturn() &&
|
|
!isTrapNoReturnFunction(ai->getReferencedFunctionOrNull())) {
|
|
bbs.push_back(&bb);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Otherwise, it must be a failure bb where we leak, continue.
|
|
continue;
|
|
}
|
|
|
|
// We understood all terminators, return true.
|
|
return true;
|
|
}
|