Files
swift-mirror/lib/SILOptimizer/Analysis/LoopRegionAnalysis.cpp
Michael Gottesman 38ec08f45f [gardening] Standardize SILBasicBlock successor/predecessor methods that deal with blocks rather than the full successor data structure to have the suffix 'Block'.
This was already done for getSuccessorBlocks() to distinguish getting successor
blocks from getting the full list of SILSuccessors via getSuccessors(). This
commit just makes all of the successor/predecessor code follow that naming
convention.

Some examples:

getSingleSuccessor() => getSingleSuccessorBlock().
isSuccessor() => isSuccessorBlock().
getPreds() => getPredecessorBlocks().

Really, IMO, we should consider renaming SILSuccessor to a more verbose name so
that it is clear that it is more of an internal detail of SILBasicBlock's
implementation rather than something that one should consider as apart of one's
mental model of the IR when one really wants to be thinking about predecessor
and successor blocks. But that is not what this commit is trying to change, it
is just trying to eliminate a bit of technical debt by making the naming
conventions here consistent.
2016-11-27 12:32:51 -08:00

1274 lines
45 KiB
C++

//===--- LoopRegionAnalysis.cpp -------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 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
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-loop-region-analysis"
#include "swift/SILOptimizer/Analysis/LoopRegionAnalysis.h"
#include "swift/Basic/Range.h"
#include "llvm/Support/DOTGraphTraits.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/GraphWriter.h"
using namespace swift;
//===----------------------------------------------------------------------===//
// LoopRegion
//===----------------------------------------------------------------------===//
LoopRegion::~LoopRegion() {
// Blocks do not have subregion data, so everything should just clean up via
// RAII.
if (isBlock())
return;
// Otherwise, we need to cleanup subregion data.
getSubregionData().~SubregionData();
}
LoopRegion::BlockTy *LoopRegion::getBlock() const {
return Ptr.get<BlockTy *>();
}
LoopRegion::LoopTy *LoopRegion::getLoop() const {
return Ptr.get<LoopTy *>();
}
LoopRegion::FunctionTy *LoopRegion::getFunction() const {
return Ptr.get<FunctionTy *>();
}
void LoopRegion::dump() const {
print(llvm::outs());
llvm::outs() << "\n";
}
void LoopRegion::dumpName() const {
printName(llvm::outs());
llvm::outs() << "\n";
}
void LoopRegion::printName(llvm::raw_ostream &os) const {
if (isBlock()) {
os << "BB" << getID();
return;
}
if (isLoop()) {
os << "Loop" << getID();
return;
}
assert(isFunction());
os << "Function" << getID();
return;
}
void LoopRegion::print(llvm::raw_ostream &os, bool isShort) const {
os << "(region id:" << ID;
if (isShort) {
os << ")";
return;
}
os << " kind:";
if (isBlock()) {
os << "bb ";
} else if (isLoop()) {
os << "loop";
} else if (isFunction()) {
os << "func";
} else {
llvm_unreachable("Unknown region type");
}
os << " ucfh:" << (IsUnknownControlFlowEdgeHead? "true " : "false")
<< " ucft:" << (IsUnknownControlFlowEdgeTail? "true " : "false");
}
llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &os, LoopRegion &LR) {
LR.print(os);
return os;
}
LoopRegion::SuccRange LoopRegion::getSuccs() const {
auto Range = InnerSuccRange(Succs.begin(), Succs.end());
return SuccRange(Range, SuccessorID::ToLiveSucc());
}
LoopRegion::LocalSuccRange LoopRegion::getLocalSuccs() const {
auto Range = InnerSuccRange(Succs.begin(), Succs.end());
return LocalSuccRange(Range, SuccessorID::ToLiveLocalSucc());
}
LoopRegion::NonLocalSuccRange LoopRegion::getNonLocalSuccs() const {
auto Range = InnerSuccRange(Succs.begin(), Succs.end());
return NonLocalSuccRange(Range, SuccessorID::ToLiveNonLocalSucc());
}
/// Replace OldSuccID by NewSuccID, just deleting OldSuccID if what NewSuccID
/// is already in the list.
void LoopRegion::replaceSucc(SuccessorID OldSucc, SuccessorID NewSucc) {
DEBUG(llvm::dbgs() << " Replacing " << OldSucc << " with "
<< NewSucc << "\n");
Succs.replace(OldSucc, NewSucc);
}
llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &os,
LoopRegion::SuccessorID &S) {
return os << "(succ id:" << S.ID
<< " nonlocal:" << (S.IsNonLocal ? "true" : "false") << ")";
}
//===----------------------------------------------------------------------===//
// LoopRegionFunctionInfo
//===----------------------------------------------------------------------===//
LoopRegionFunctionInfo::LoopRegionFunctionInfo(FunctionTy *F,
PostOrderFunctionInfo *PI,
LoopInfoTy *LI)
: F(F), Allocator(), BBToIDMap(), LoopToIDMap(),
#ifndef NDEBUG
IDToRegionMap(PI->size()), AllBBRegionsCreated(false) {
#else
IDToRegionMap(PI->size()) {
#endif
if (F->isExternalDeclaration())
return;
DEBUG(llvm::dbgs() << "**** LOOP REGION FUNCTION INFO ****\n");
DEBUG(llvm::dbgs() << "Analyzing function: " << F->getName() << "\n");
initializeBlockRegions(PI, LI);
initializeLoopRegions(LI);
initializeFunctionRegion(LI->getTopLevelLoops());
#ifndef NDEBUG
verify();
#endif
}
LoopRegionFunctionInfo::~LoopRegionFunctionInfo() {
for (auto *R : IDToRegionMap) {
R->~RegionTy();
}
IDToRegionMap.clear();
}
void LoopRegionFunctionInfo::verify() {
#ifndef NDEBUG
llvm::SmallVector<unsigned, 8> UniquePredList;
for (auto *R : IDToRegionMap) {
// Make sure that our region has a pred list without duplicates. We do not
// care if the predecessor list is sorted, just that it is unique.
{
UniquePredList.clear();
std::copy(R->Preds.begin(), R->Preds.end(), std::back_inserter(UniquePredList));
std::sort(UniquePredList.begin(), UniquePredList.end());
auto End = UniquePredList.end();
assert(End == std::unique(UniquePredList.begin(), UniquePredList.end()) &&
"Expected UniquePredList to not have any duplicate elements");
}
// If this node does not have a parent, it should have no non-local
// successors.
if (!R->ParentID.hasValue()) {
auto NLSuccs = R->getNonLocalSuccs();
assert(NLSuccs.begin() == NLSuccs.end() &&
"Cannot have non local "
"successors without a parent node");
continue;
}
// If this node /does/ have a parent, make sure that all non-local
// successors point to a successor in the parent and match whether or not it
// is dead.
auto *ParentRegion = getRegion(*R->ParentID);
unsigned NumParentSuccs = ParentRegion->succ_size();
for (LoopRegion::SuccessorID ID : R->getSuccs()) {
// Skip local successors. We are not verifying anything here.
if (!ID.IsNonLocal)
continue;
assert(ID.ID < NumParentSuccs && "Non local successor pointing off the "
"parent node successor list?!");
// Since we are not dead, make sure our parent is not dead.
assert(ParentRegion->Succs[ID.ID].hasValue() &&
"non-local successor edge sources should have the same liveness "
"properties as non-local successor edge targets");
// Make sure that we can look up the local region corresponding to this
// region's successor.
auto *OtherR = getRegionForNonLocalSuccessor(R, ID.ID);
(void)OtherR;
// If R and OtherR are blocks, then OtherR should be a successor of the
// real block.
if (R->isBlock() && OtherR->isBlock())
assert(R->getBlock()->isSuccessorBlock(OtherR->getBlock()) &&
"Expected either R was not a block or OtherR was a CFG level "
"successor of R.");
}
}
#endif
}
//===---
// Region Creation Functions
//
LoopRegionFunctionInfo::RegionTy *
LoopRegionFunctionInfo::getRegion(BlockTy *BB) const {
assert(AllBBRegionsCreated && "All BB Regions have not been created yet?!");
// Check if we have allocated a region for this BB. If so, just return it.
auto Iter = BBToIDMap.find(BB);
if (Iter != BBToIDMap.end()) {
return IDToRegionMap[Iter->second];
}
llvm_unreachable("Unknown BB?!");
}
LoopRegionFunctionInfo::RegionTy *
LoopRegionFunctionInfo::getRegion(FunctionTy *F) const {
if (FunctionRegionID.hasValue()) {
return IDToRegionMap[FunctionRegionID.getValue()];
}
auto &Self = const_cast<LoopRegionFunctionInfo &>(*this);
unsigned Idx = IDToRegionMap.size();
unsigned NumBytes = sizeof(RegionTy) + sizeof(LoopRegion::SubregionData);
void *Memory = Self.Allocator.Allocate(NumBytes, alignof(RegionTy));
auto *R = new (Memory) RegionTy(F, Idx);
new ((void *)&R[1]) LoopRegion::SubregionData();
Self.IDToRegionMap.push_back(R);
Self.FunctionRegionID = Idx;
return R;
}
LoopRegionFunctionInfo::RegionTy *
LoopRegionFunctionInfo::getRegion(LoopTy *Loop) const {
auto Iter = LoopToIDMap.find(Loop);
if (Iter != LoopToIDMap.end()) {
return IDToRegionMap[Iter->second];
}
auto &Self = const_cast<LoopRegionFunctionInfo &>(*this);
unsigned Idx = IDToRegionMap.size();
unsigned NumBytes = sizeof(RegionTy) + sizeof(LoopRegion::SubregionData);
void *Memory = Self.Allocator.Allocate(NumBytes, alignof(RegionTy));
auto *R = new (Memory) RegionTy(Loop, Idx);
new ((void *)&R[1]) LoopRegion::SubregionData();
Self.IDToRegionMap.push_back(R);
Self.LoopToIDMap[Loop] = Idx;
return R;
}
LoopRegionFunctionInfo::RegionTy *
LoopRegionFunctionInfo::createRegion(BlockTy *BB, unsigned RPONum) {
assert(!AllBBRegionsCreated && "All BB Regions have been created?!");
// Check if we have allocated a region for this BB. If so, just return it.
auto Iter = BBToIDMap.find(BB);
if (Iter != BBToIDMap.end()) {
return IDToRegionMap[Iter->second];
}
unsigned Idx = RPONum;
auto *R = new (Allocator) RegionTy(BB, RPONum);
IDToRegionMap[RPONum] = R;
BBToIDMap[BB] = Idx;
return R;
}
//===---
// Block Region Initialization
//
void LoopRegionFunctionInfo::initializeBlockRegionSuccessors(
BlockTy *BB, RegionTy *BBRegion, PostOrderFunctionInfo *PI) {
for (auto *SuccBB : BB->getSuccessorBlocks()) {
unsigned SuccRPOIndex = *PI->getRPONumber(SuccBB);
auto *SuccRegion = createRegion(SuccBB, SuccRPOIndex);
BBRegion->addSucc(SuccRegion);
SuccRegion->addPred(BBRegion);
DEBUG(llvm::dbgs() << " Succ: ";
SuccBB->printAsOperand(llvm::dbgs());
llvm::dbgs() << " RPONum: " << SuccRPOIndex << "\n");
}
}
void LoopRegionFunctionInfo::markIrreducibleLoopPredecessorsOfNonLoopHeader(
BlockTy *NonHeaderBB, RegionTy *NonHeaderBBRegion,
PostOrderFunctionInfo *PI) {
for (BlockTy *Pred : NonHeaderBB->getPredecessorBlocks()) {
// If we do not have an RPO number for a predecessor, it is because the
// predecessor is unreachable and a pass did not clean up after
// itself. Just ignore it, it will be cleaned up by simplify-cfg.
auto PredRPONumber = PI->getRPONumber(Pred);
if (!PredRPONumber || *PredRPONumber < NonHeaderBBRegion->getRPONumber())
continue;
auto *PredRegion = createRegion(Pred, *PredRPONumber);
DEBUG(llvm::dbgs() << " Backedge: ";
Pred->printAsOperand(llvm::dbgs());
llvm::dbgs() << " " << PredRegion->getID() << "\n");
// We mark the head/tail as unknown control flow regions since in CFGs like
// the following:
//
// /------\
// v |
// BB0 -> BB1 -> BB2 -> BB4
// | |^
// | v|
// \----------> BB3
//
// We need to ensure that the edge from BB0 -> BB3 does not propagate any
// dataflow state into the irreducible loop even if the back edge we see is
// from BB3 -> BB2.
NonHeaderBBRegion->IsUnknownControlFlowEdgeHead = true;
NonHeaderBBRegion->IsUnknownControlFlowEdgeTail = true;
PredRegion->IsUnknownControlFlowEdgeHead = true;
PredRegion->IsUnknownControlFlowEdgeTail = true;
}
DEBUG(llvm::dbgs() << "\n");
}
void
LoopRegionFunctionInfo::
markMultipleLoopLatchLoopBackEdges(RegionTy *LoopHeaderRegion, LoopTy *Loop,
PostOrderFunctionInfo *PI) {
llvm::SmallVector<BlockTy *, 4> Latches;
Loop->getLoopLatches(Latches);
assert(Latches.size() > 1 &&
"Assumed that this loop had multiple loop latches");
LoopHeaderRegion->IsUnknownControlFlowEdgeHead = true;
for (auto *LatchBB : Latches) {
auto *LatchBBRegion = createRegion(LatchBB, *PI->getRPONumber(LatchBB));
LatchBBRegion->IsUnknownControlFlowEdgeTail = true;
}
}
void LoopRegionFunctionInfo::initializeBlockRegions(PostOrderFunctionInfo *PI,
LoopInfoTy *LI) {
DEBUG(llvm::dbgs() << "Visiting BB Regions:\n");
// Initialize regions for each BB and associate RPO numbers with each BB.
//
// We use the RPO number of a BB as its Index in our data structures.
for (auto P : PI->getEnumeratedReversePostOrder()) {
BlockTy *BB = P.first;
unsigned RPOIndex = P.second;
auto *BBRegion = createRegion(BB, RPOIndex);
assert(BBRegion && "Create region fail to create a BB?");
assert(*PI->getRPONumber(BB) == RPOIndex &&
"Enumerated Reverse Post Order out of sync with RPO number");
DEBUG(llvm::dbgs() << "Visiting BB: "; BB->printAsOperand(llvm::dbgs());
llvm::dbgs() << " RPO: " << RPOIndex << "\n");
// Wire up this BB as an "initial predecessor" of all of its successors
// and make each of its successors a successor for the region.
initializeBlockRegionSuccessors(BB, BBRegion, PI);
// Then try to lookup the innermost loop that BB belongs to. If we get back
// a nullptr, then we know that the BB belongs to the function region and is
// also not a loop header.
auto *Loop = LI->getLoopFor(BB);
auto *ParentRegion = Loop? getRegion(Loop) : getRegion(F);
// Add BBRegion to the ParentRegion.
ParentRegion->addBlockSubregion(BBRegion);
// Determine if this basic block is part of an irreducible loop by looking
// at all blocks that are:
//
// 1. Not in any loop identified by loop info.
// 2. Or are part of an identified loop but are not a loop header.
//
// In such a case, check if any predecessors have a smaller PO number. If so
// then we know that both this BB and the predecessor are boundaries of a
// loop that is not understood by SILLoopInfo. Mark them as unknown control
// flow boundaries. Then add the BB as a subregion to its parent region.
DEBUG(llvm::dbgs() << "Checking Preds for Back Edges\n");
if (!Loop || !LI->isLoopHeader(BB)) {
markIrreducibleLoopPredecessorsOfNonLoopHeader(BB, BBRegion, PI);
continue;
}
assert(Loop && "Should have a non-null Loop at this point");
assert(ParentRegion && "Expected a non-null LoopRegion");
// Now we know that the block is in a loop and is a loop header. Check if
// this loop has multiple latches. If so, mark the latch head/tail blocks as
// head/tail unknown cfg boundaries.
//
// We rely on Loop canonicalization to separate such loops into separate
// nested loops.
ParentRegion->getSubregionData().RPONumOfHeaderBlock = RPOIndex;
// If we have one loop latch, continue.
if (Loop->getLoopLatch()) {
DEBUG(llvm::dbgs() << "\n");
continue;
}
// Otherwise, mark each of the loop latches as irreducible control flow edge
// tails so we are conservative around them.
markMultipleLoopLatchLoopBackEdges(BBRegion, Loop, PI);
DEBUG(llvm::dbgs() << "\n");
}
#ifndef NDEBUG
AllBBRegionsCreated = true;
#endif
}
//===---
// Loop and Function Region Initialization
//
void LoopRegionFunctionInfo::initializeLoopRegions(LoopInfoTy *LI) {
// Now visit the loop nest in a DFS.
llvm::SmallVector<LoopTy *, 16> LoopWorklist(LI->begin(), LI->end());
llvm::SmallPtrSet<LoopTy *, 16> VisitedLoops;
while (LoopWorklist.size()) {
LoopTy *L = LoopWorklist.back();
auto *LRegion = getRegion(L);
// If we have not visited this loop yet and it has subloops, add its
// subloops to the worklist and continue.
auto SubLoops = L->getSubLoopRange();
if (VisitedLoops.insert(L).second && SubLoops.begin() != SubLoops.end()) {
for (auto *SubLoop : SubLoops) {
LoopWorklist.push_back(SubLoop);
}
continue;
}
LoopWorklist.pop_back();
// Otherwise, process this loop. We have already visited all of its
// potential children loops.
initializeLoopFunctionRegion(LRegion, SubLoops);
}
}
/// For each predecessor of the header of the loop:
///
/// 1. If the predecessor is not in the loop, make the predecessor a predecessor
/// of the loop instead of the header.
///
/// 2. If the predecessor *is* in the loop it must be the tail of a backedge. We
/// remove these back edges and instead represent them as unknown control flow
/// edges. This is because we rely on loop canonicalization to canonicalize
/// multiple backedge loops into separate loops.
LoopRegionFunctionInfo::RegionTy *
LoopRegionFunctionInfo::
rewriteLoopHeaderPredecessors(LoopTy *SubLoop, RegionTy *SubLoopRegion) {
auto *SubLoopHeaderRegion = getRegion(SubLoop->getHeader());
assert(SubLoopHeaderRegion->isBlock() && "A header must always be a block");
DEBUG(llvm::dbgs()
<< " Header: " << SubLoopHeaderRegion->getID() << "\n"
<< " Rewiring Header Predecessors to be Loop Preds.\n");
if (SubLoopHeaderRegion->IsUnknownControlFlowEdgeHead)
SubLoopRegion->IsUnknownControlFlowEdgeHead = true;
for (unsigned PredID : SubLoopHeaderRegion->Preds) {
auto *PredRegion = getRegion(PredID);
DEBUG(llvm::dbgs() << " " << PredRegion->getID() << "\n");
if (!SubLoopRegion->containsSubregion(PredRegion)) {
DEBUG(llvm::dbgs() << " Not in loop... Replacing...\n");
// This is always a local edge since non-local edges can only have loops
// as heads. Since the head of our edge is SubLoopHeaderRegion, this must
// be local.
PredRegion->replaceSucc(LoopRegion::SuccessorID(SubLoopHeaderRegion->ID,
/*IsNonLocal*/ false),
LoopRegion::SuccessorID(SubLoopRegion->ID,
/*IsNonLocal*/ false));
propagateLivenessDownNonLocalSuccessorEdges(PredRegion);
SubLoopRegion->addPred(PredRegion);
continue;
}
DEBUG(llvm::dbgs() << " Is in loop... Erasing...\n");
// Ok, we have a predecessor inside the loop. This must be a backedge.
//
// We are abusing the fact that a block can only be a local successor.
PredRegion->removeLocalSucc(SubLoopHeaderRegion->getID());
propagateLivenessDownNonLocalSuccessorEdges(PredRegion);
SubLoopRegion->getSubregionData().addBackedgeSubregion(PredRegion->getID());
}
SubLoopHeaderRegion->Preds.clear();
return SubLoopHeaderRegion;
}
static void getExitingRegions(LoopRegionFunctionInfo *LRFI, SILLoop *Loop,
LoopRegion *LRegion,
llvm::SmallVectorImpl<unsigned> &ExitingRegions) {
llvm::SmallVector<SILBasicBlock *, 8> ExitingBlocks;
Loop->getExitingBlocks(ExitingBlocks);
// Determine the outer most region that contains the exiting block that is not
// this subloop's region. That is the *true* exiting region.
for (auto *BB : ExitingBlocks) {
auto *Region = LRFI->getRegion(BB);
unsigned RegionParentID = *Region->getParentID();
while (RegionParentID != LRegion->getID()) {
Region = LRFI->getRegion(RegionParentID);
RegionParentID = *Region->getParentID();
}
ExitingRegions.push_back(Region->getID());
}
// We can have a loop subregion that has multiple exiting edges from the
// current loop. We do not want to visit that loop subregion multiple
// times. So we unique the exiting region list.
//
// In order to make sure we have a deterministic ordering when we visiting
// exiting subregions, we need to sort our exiting regions by ID, not pointer
// value.
sortUnique(ExitingRegions);
}
/// For each exiting block:
///
/// 1. Make any successors outside of the loop successors of the loop instead
/// of the exiting block.
///
/// 2. Any successors that are inside the loop but are not back edges are
/// left alone.
///
/// 3. Any successors that are inside the loop but are the head of a backedge
/// have the edge removed. This is computed by the successor having a greater
/// post order number than the exit.
///
/// *NOTE* We have to be careful here since exiting blocks may include BBs
/// that have been subsumed into a subloop already. Also to determine back
/// edges, we need to compare post order numbers potentially of loop headers,
/// not the loops themselves.
void
LoopRegionFunctionInfo::
rewriteLoopExitingBlockSuccessors(LoopTy *Loop, RegionTy *LRegion) {
// Begin by using loop info and loop region info to find all of the exiting
// regions.
//
// We do this by looking up the exiting blocks and finding the outermost
// region which the block is a subregion of. Since we initialize our data
// structure by processing the loop nest bottom up, this should always give us
// the correct region for the level of the loop we are processing.
auto &ExitingSubregions = LRegion->getSubregionData().ExitingSubregions;
getExitingRegions(this, Loop, LRegion, ExitingSubregions);
// Then for each exiting region ER of the Loop L...
DEBUG(llvm::dbgs() << " Visiting Exit Blocks...\n");
for (unsigned ExitingSubregionID : ExitingSubregions) {
auto *ExitingSubregion = getRegion(ExitingSubregionID);
DEBUG(llvm::dbgs() << " Exiting Region: "
<< ExitingSubregion->getID() << "\n");
bool HasBackedge = false;
// For each successor region S of ER...
for (auto SuccID : ExitingSubregion->getSuccs()) {
DEBUG(llvm::dbgs() << " Succ: " << SuccID.ID
<< ". IsNonLocal: "
<< (SuccID.IsNonLocal ? "true" : "false") << "\n");
// If S is not contained in L, then:
//
// 1. The successor/predecessor edge in between S and ER with a new
// successor/predecessor edge in between S and L.
// 2. ER is given a non-local successor edge that points at the successor
// index in L that points at S. This will enable us to recover the
// original edge if we need to.
//
// Then we continue.
auto *SuccRegion = getRegion(SuccID.ID);
if (!LRegion->containsSubregion(SuccRegion)) {
DEBUG(llvm::dbgs() << " Is not a subregion, replacing.\n");
SuccRegion->replacePred(ExitingSubregion->ID, LRegion->ID);
if (ExitingSubregion->IsUnknownControlFlowEdgeTail)
LRegion->IsUnknownControlFlowEdgeTail = true;
// If the successor region is already in this LRegion this returns that
// regions index. Otherwise it returns a new index.
unsigned Index = LRegion->addSucc(SuccRegion);
ExitingSubregion->replaceSucc(SuccID,
LoopRegion::SuccessorID(Index, true));
propagateLivenessDownNonLocalSuccessorEdges(ExitingSubregion);
continue;
}
// Otherwise, we know S is in L. If the RPO number of S is less than the
// RPO number of ER, then we know that the edge in between them is not a
// backedge and thus we do not want to clip the edge.
if (SuccRegion->getRPONumber() > ExitingSubregion->getRPONumber()) {
DEBUG(llvm::dbgs() << " Is a subregion, but not a "
"backedge, not removing.\n");
continue;
}
// If the edge from ER to S is a back edge, we want to clip it and add
// exiting subregion to
DEBUG(llvm::dbgs() << " Is a subregion and a backedge, "
"removing.\n");
HasBackedge = true;
auto Iter =
std::remove(SuccRegion->Preds.begin(), SuccRegion->Preds.end(),
ExitingSubregion->getID());
SuccRegion->Preds.erase(Iter);
}
// If we found a backedge, add ER's ID to LRegion's Backedge list.
if (!HasBackedge) {
continue;
}
LRegion->getSubregionData().addBackedgeSubregion(ExitingSubregion->getID());
}
}
/// The high level algorithm is that via the loop info we already know for
/// each subloop:
///
/// 1. predecessors.
/// 2. header.
/// 3. exiting blocks.
/// 4. successor blocks.
///
/// This means that we can simply fix up those BB regions.
///
/// *NOTE* We do not touch the successors/predecessors of the current loop. We
/// leave those connections in place for our parent loop to fix up. In the case
/// we are the function level loop, these will of course be empty anyways.
void
LoopRegionFunctionInfo::
initializeLoopFunctionRegion(RegionTy *ParentRegion,
iterator_range<LoopInfoTy::iterator> SubLoops) {
DEBUG(llvm::dbgs() << "Initializing Loop Region " << ParentRegion->getID()
<< "\n");
// For each subloop...
for (auto *SubLoop : SubLoops) {
// Grab the region associated with the subloop...
auto *SubLoopRegion = getRegion(SubLoop);
DEBUG(llvm::dbgs() << " Visiting Subloop: " << SubLoopRegion->getID()
<< "\n");
// First rewrite predecessors of the loop header to point at the loop.
auto *SubLoopHeaderRegion = rewriteLoopHeaderPredecessors(SubLoop,
SubLoopRegion);
// Then rewrite successors of all exits.
rewriteLoopExitingBlockSuccessors(SubLoop, SubLoopRegion);
// Add the subloop region to the subregion data.
ParentRegion->addLoopSubregion(SubLoopRegion, SubLoopHeaderRegion);
}
// Now that we have finished processing this loop, sort its subregions so that
// they are now in RPO order. This works because each BB's ID is its RPO
// number and we represent loops by the RPO number of their preheader (with a
// flag in the first bit to say to look in the subloop array for the *real* ID
// of the loop).
//
// TODO: Is this necessary? We visit BBs in RPO order. This means that we
// should always add BBs in RPO order to subregion lists, no? For now I am
// going to sort just to be careful while bringing this up.
ParentRegion->getSubregionData().sortSubregions();
}
void LoopRegionFunctionInfo::
initializeFunctionRegion(iterator_range<LoopInfoTy::iterator> SubLoops) {
// We have already processed all of our subloops, so everything there has
// already been properly setup.
initializeLoopFunctionRegion(getRegion(F), SubLoops);
}
/// Recursively visit all the descendants of Parent. If there is a non-local
/// successor edge path that points to a dead edge in Parent, mark the
/// descendant non-local successor edge as dead.
void LoopRegionFunctionInfo::
propagateLivenessDownNonLocalSuccessorEdges(LoopRegion *Parent) {
llvm::SmallVector<LoopRegion *, 4> Worklist;
Worklist.push_back(Parent);
while (Worklist.size()) {
LoopRegion *R = Worklist.pop_back_val();
for (unsigned SubregionID : R->getSubregions()) {
LoopRegion *Subregion = getRegion(SubregionID);
bool ShouldVisit = false;
// Make sure we can identify when the subregion has at least one dead
// non-local edge and no remaining live edges. In such a case, we need to
// remove the subregion from the exiting subregion array of R after the
// loop.
bool HasDeadNonLocalEdge = false;
bool HasNoLiveLocalEdges = true;
for (auto &SuccID : Subregion->Succs) {
// If the successor is already dead, skip it. We should have visited all
// its children when we marked it as dead.
if (!SuccID)
continue;
// We do not care about local IDs, we only process non-local IDs.
if (!SuccID->IsNonLocal)
continue;
// If the non-local successor edge points to a parent successor that is
// not dead continue.
if (R->Succs[SuccID->ID].hasValue()) {
HasNoLiveLocalEdges = false;
continue;
}
// Ok, we found a target! Mark it as dead and make sure that we visit
// the subregion's children if it is not a block.
HasDeadNonLocalEdge = true;
ShouldVisit = true;
// This is safe to do since when erasing in a BlotSetVector, we do not
// invalidate the iterators.
Subregion->Succs.erase(*SuccID);
}
// Remove Subregion from R's exiting subregion array if Subregion no
// longer has /any/ non-local successors.
if (HasDeadNonLocalEdge && HasNoLiveLocalEdges) {
auto &ExitingSubregions = R->getSubregionData().ExitingSubregions;
auto Iter =
std::remove(ExitingSubregions.begin(), ExitingSubregions.end(),
Subregion->getID());
ExitingSubregions.erase(Iter);
}
if (ShouldVisit)
Worklist.push_back(Subregion);
}
}
}
LoopRegionFunctionInfo::RegionTy *
LoopRegionFunctionInfo::
getRegionForNonLocalSuccessor(const LoopRegion *Child, unsigned SuccID) const {
const LoopRegion *Iter = Child;
LoopRegion::SuccessorID Succ = {0, 0};
do {
Iter = getRegion(*Iter->getParentID());
Succ = Iter->Succs[SuccID].getValue();
SuccID = Succ.ID;
} while (Succ.IsNonLocal);
return getRegion(SuccID);
}
void LoopRegionFunctionInfo::dump() const {
print(llvm::outs());
llvm::outs() << "\n";
}
void LoopRegionFunctionInfo::print(raw_ostream &os) const {
// Print out the information for each loop.
std::vector<std::pair<LoopRegion *, LoopRegion *>> RegionWorklist;
// Initialize the worklist with the function level region.
RegionWorklist.push_back({nullptr, getRegion(F)});
// Vectors that we use to sort our local successor/predecessor indices to make
// our output deterministic.
llvm::SmallVector<unsigned, 4> SortedPreds;
llvm::SmallVector<unsigned, 4> SortedSuccs;
// Then go to town...
while (RegionWorklist.size()) {
LoopRegion *Parent, *R;
std::tie(Parent, R) = RegionWorklist.back();
RegionWorklist.pop_back();
os << *R;
// If we have a parent, print out its id. This is not strictly necessary,
// but it makes it easier to read the output from a dump.
if (Parent)
os << " parent:" << Parent->getID();
os << "\n";
// Print predecessors.
SortedPreds.clear();
for (unsigned Pred : R->Preds)
SortedPreds.push_back(Pred);
std::sort(SortedPreds.begin(), SortedPreds.end());
os << " (preds";
for (unsigned SID : SortedPreds) {
os << "\n ";
LoopRegion *PredRegion = getRegion(SID);
PredRegion->print(os, true);
}
os << ")\n";
os << " (succs";
// To make our output deterministic, we sort local successor indices.
SortedSuccs.clear();
auto LSuccRange = R->getLocalSuccs();
std::copy(LSuccRange.begin(), LSuccRange.end(),
std::back_inserter(SortedSuccs));
std::sort(SortedSuccs.begin(), SortedSuccs.end());
for (unsigned SID : SortedSuccs) {
os << "\n ";
LoopRegion *SuccRegion = getRegion(SID);
SuccRegion->print(os, true);
}
os << ")\n";
os << " (subregs";
if (!R->isBlock()) {
// Go through the subregions.
for (unsigned SID : R->getSubregions()) {
os << "\n ";
LoopRegion *Subregion = getRegion(SID);
Subregion->print(os, true);
RegionWorklist.push_back({R, Subregion});
}
}
os << ")\n";
SortedSuccs.clear();
auto NonLSuccs = R->getNonLocalSuccs();
std::copy(NonLSuccs.begin(), NonLSuccs.end(),
std::back_inserter(SortedSuccs));
std::sort(SortedSuccs.begin(), SortedSuccs.end());
os << " (non-local-succs";
for (unsigned I : SortedSuccs) {
os << "\n (parentindex:" << I << ")";
}
os << ")\n";
os << " (exiting-subregs";
if (!R->isBlock()) {
llvm::SmallVector<unsigned, 4> ExitingSubregions;
auto ExitingSubRegs = R->getExitingSubregions();
std::copy(ExitingSubRegs.begin(), ExitingSubRegs.end(),
std::back_inserter(ExitingSubregions));
std::sort(ExitingSubregions.begin(), ExitingSubregions.begin());
for (unsigned SubregionID : ExitingSubregions) {
os << "\n ";
LoopRegion *Subregion = getRegion(SubregionID);
Subregion->print(os, true);
}
}
os << ")\n";
os << " (backedge-regs";
if (!R->isBlock()) {
auto BackedgeIDs = R->getBackedgeRegions();
if (!BackedgeIDs.empty()) {
for (unsigned BackedgeID : BackedgeIDs) {
os << "\n ";
LoopRegion *BackedgeRegion = getRegion(BackedgeID);
BackedgeRegion->print(os, true);
}
}
}
os << "))\n";
}
}
//===----------------------------------------------------------------------===//
// Graphing Support
//===----------------------------------------------------------------------===//
#ifndef NDEBUG
/// A lot of the code below is unfortunate and due to the inflexibility of
/// GraphUtils. But you gotta do what you gotta do.
namespace {
struct LoopRegionWrapper;
struct LoopRegionFunctionInfoGrapherWrapper {
LoopRegionFunctionInfo *FuncInfo;
std::vector<LoopRegionWrapper> Data;
};
struct alledge_iterator;
struct LoopRegionWrapper {
LoopRegionFunctionInfoGrapherWrapper &FuncInfo;
LoopRegion *Region;
LoopRegionWrapper *getParent() const {
unsigned ParentIndex = *Region->getParentID();
return &FuncInfo.Data[ParentIndex];
}
alledge_iterator begin();
alledge_iterator end();
};
/// An iterator on Regions that first iterates over subregions and then over
/// successors.
struct alledge_iterator
: std::iterator<std::forward_iterator_tag, LoopRegionWrapper> {
LoopRegionWrapper *Wrapper;
LoopRegion::subregion_iterator SubregionIter;
LoopRegion::backedge_iterator BackedgeIter;
using SuccIterTy =
OptionalTransformIterator<LoopRegion::const_succ_iterator,
LoopRegion::SuccessorID::ToLiveSucc>;
SuccIterTy SuccIter;
alledge_iterator(LoopRegionWrapper *w,
swift::LoopRegion::subregion_iterator subregioniter,
LoopRegion::const_succ_iterator succiter,
LoopRegion::backedge_iterator backedgeiter)
: Wrapper(w), SubregionIter(subregioniter),
BackedgeIter(backedgeiter),
SuccIter(succiter, w->Region->succ_end(),
LoopRegion::SuccessorID::ToLiveSucc()) {}
// This is not efficient, but this is graphing code...
SuccIterTy getSuccEnd() const {
return SuccIterTy(Wrapper->Region->succ_end(), Wrapper->Region->succ_end(),
LoopRegion::SuccessorID::ToLiveSucc());
}
bool isSubregion() const {
return SubregionIter != Wrapper->Region->subregion_end();
}
bool isNonLocalEdge() const {
// If we are not a subregion...
if (isSubregion())
return false;
// Then if succ iterator is not succ end, we are a non local edge if that
// value is Non Local.
return getSuccEnd() != SuccIter && (*SuccIter).IsNonLocal;
}
bool isBackEdge() const {
if (isSubregion())
return false;
// If our successor iterator has reached the end of the successor list.
return getSuccEnd() == SuccIter;
}
LoopRegionWrapper *operator*() const {
// If we have a subregion... return the wrapper for it.
if (isSubregion()) {
return &Wrapper->FuncInfo.Data[*SubregionIter];
}
// If we have a backedge... return the wrapper for that.
if (isBackEdge()) {
return &Wrapper->FuncInfo.Data[*BackedgeIter];
}
// If we have a non-local id, just return the parent region's data.
if ((*SuccIter).IsNonLocal)
return &Wrapper->FuncInfo.Data[*(Wrapper->Region->getParentID())];
// Otherwise return the data associated with this successor.
return &Wrapper->FuncInfo.Data[(*SuccIter).ID];
}
alledge_iterator &operator++() {
// If we are still a subregion, increment the SubregionIter and return.
if (isSubregion()) {
++SubregionIter;
return *this;
}
// Make sure that we skip past any dead successors. If after skipping dead
// successors, if our SuccIter is not end, then return. We have a true
// successor.
if (SuccIter != getSuccEnd()) {
++SuccIter;
return *this;
}
// Ok, now we know that we have a back edge region. Increment the backedge
// region.
++BackedgeIter;
return *this;
}
alledge_iterator operator++(int) {
alledge_iterator copy = *this;
++copy;
return copy;
}
bool operator==(alledge_iterator rhs) {
if (Wrapper->Region != rhs.Wrapper->Region)
return false;
if (SubregionIter != rhs.SubregionIter)
return false;
if (SuccIter != rhs.SuccIter)
return false;
if (BackedgeIter.hasValue() != rhs.BackedgeIter.hasValue())
return false;
return BackedgeIter == rhs.BackedgeIter;
}
bool operator!=(alledge_iterator rhs) { return !(*this == rhs); }
};
} // end anonymous namespace
alledge_iterator LoopRegionWrapper::begin() {
return alledge_iterator(this, Region->subregion_begin(), Region->succ_begin(),
Region->backedge_begin());
}
alledge_iterator LoopRegionWrapper::end() {
return alledge_iterator(this, Region->subregion_end(), Region->succ_end(),
Region->backedge_end());
}
namespace llvm {
template <> struct GraphTraits<LoopRegionWrapper> {
using NodeType = LoopRegionWrapper;
using ChildIteratorType = alledge_iterator;
typedef LoopRegionWrapper *NodeRef;
static NodeRef getEntryNode(NodeRef BB) { return BB; }
static ChildIteratorType child_begin(NodeRef N) { return N->begin(); }
static ChildIteratorType child_end(NodeRef N) { return N->end(); }
};
template <>
struct GraphTraits<LoopRegionFunctionInfoGrapherWrapper *>
: public GraphTraits<LoopRegionWrapper> {
using GraphType = LoopRegionFunctionInfoGrapherWrapper;
typedef LoopRegionWrapper *NodeRef;
static NodeRef getEntryNode(GraphType *F) { return &F->Data[0]; }
using nodes_iterator = std::vector<LoopRegionWrapper>::iterator;
static nodes_iterator nodes_begin(GraphType *F) { return F->Data.begin(); }
static nodes_iterator nodes_end(GraphType *F) { return F->Data.end(); }
static unsigned size(GraphType *F) { return F->Data.size(); }
};
} // end namespace llvm
static llvm::cl::opt<unsigned>
MaxColumns("view-loop-regions-max-columns", llvm::cl::init(80),
llvm::cl::desc("Maximum width of a printed node"));
namespace {
enum class LongLineBehavior { None, Truncate, Wrap };
} // end anonymous namespace
static llvm::cl::opt<LongLineBehavior> LLBehavior(
"view-loop-regions-long-line-behavior",
llvm::cl::init(LongLineBehavior::Truncate),
llvm::cl::desc("Behavior when line width is greater than the "
"value provided my -view-loop-regions-max-columns "
"option"),
llvm::cl::values(
clEnumValN(LongLineBehavior::None, "none", "Print everything"),
clEnumValN(LongLineBehavior::Truncate, "truncate",
"Truncate long lines"),
clEnumValN(LongLineBehavior::Wrap, "wrap", "Wrap long lines"),
clEnumValEnd));
static llvm::cl::opt<bool> RemoveUseListComments(
"view-loop-regions-remove-use-list-comments", llvm::cl::init(false),
llvm::cl::desc("Should use list comments be removed"));
namespace llvm {
template <>
struct DOTGraphTraits<LoopRegionFunctionInfoGrapherWrapper *>
: public DefaultDOTGraphTraits {
DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
static std::string
getGraphName(const LoopRegionFunctionInfoGrapherWrapper *F) {
return "Loop Regions for '" + F->FuncInfo->getFunction()->getName().str() +
"' function";
}
static std::string
getSimpleNodeLabel(LoopRegionWrapper *Node,
const LoopRegionFunctionInfoGrapherWrapper *F) {
std::string OutStr;
raw_string_ostream OSS(OutStr);
Node->Region->printName(OSS);
return OSS.str();
}
static std::string
getCompleteNodeLabel(LoopRegionWrapper *Node,
const LoopRegionFunctionInfoGrapherWrapper *F) {
std::string Tmp0;
raw_string_ostream OSS(Tmp0);
Node->Region->printName(OSS);
if (Node->Region->isUnknownControlFlowEdgeHead())
OSS << " Unknown CF Head";
if (Node->Region->isUnknownControlFlowEdgeTail())
OSS << " Unknown CF Tail";
if (!Node->Region->isBlock())
return OSS.str();
OSS << "\n";
std::string Tmp1;
raw_string_ostream OSS2(Tmp1);
OSS2 << *Node->Region->getBlock();
std::string OutStr = OSS2.str();
if (OutStr[0] == '\n')
OutStr.erase(OutStr.begin());
// Process string output to make it nicer...
unsigned ColNum = 0;
unsigned LastSpace = 0;
for (unsigned i = 0; i != OutStr.length(); ++i) {
if (OutStr[i] == '\n') { // Left justify
OutStr[i] = '\\';
OutStr.insert(OutStr.begin() + i + 1, 'l');
ColNum = 0;
LastSpace = 0;
} else if (RemoveUseListComments && OutStr[i] == '/' &&
i != (OutStr.size() - 1) && OutStr[i + 1] == '/') {
unsigned Idx = OutStr.find('\n', i + 1); // Find end of line
OutStr.erase(OutStr.begin() + i, OutStr.begin() + Idx);
--i;
} else if (ColNum == MaxColumns) { // Handle long lines.
if (LLBehavior == LongLineBehavior::Wrap) {
if (!LastSpace)
LastSpace = i;
OutStr.insert(LastSpace, "\\l...");
ColNum = i - LastSpace;
LastSpace = 0;
i += 3; // The loop will advance 'i' again.
} else if (LLBehavior == LongLineBehavior::Truncate) {
unsigned Idx = OutStr.find('\n', i + 1); // Find end of line
OutStr.erase(OutStr.begin() + i, OutStr.begin() + Idx);
--i;
}
// Else keep trying to find a space.
} else
++ColNum;
if (OutStr[i] == ' ')
LastSpace = i;
}
return OSS.str() + OutStr;
}
std::string getNodeLabel(LoopRegionWrapper *Node,
const LoopRegionFunctionInfoGrapherWrapper *Graph) {
if (isSimple())
return getSimpleNodeLabel(Node, Graph);
else
return getCompleteNodeLabel(Node, Graph);
}
static bool hasEdgeDestLabels() { return true; }
static unsigned numEdgeDestLabels(LoopRegionWrapper *Node) {
return std::distance(Node->begin(), Node->end());
}
static std::string getEdgeDestLabel(LoopRegionWrapper *Node, unsigned i) {
alledge_iterator Iter = Node->begin();
std::advance(Iter, i);
return getEdgeSourceLabel(Node, Iter);
}
static std::string getEdgeSourceLabel(LoopRegionWrapper *Node,
alledge_iterator I) {
if (I.isNonLocalEdge())
return "NLSuc";
if (I.isSubregion())
return "Sub";
if (I.isBackEdge())
return "B";
return "LSuc";
}
/// If you want to override the dot attributes printed for a particular
/// edge, override this method.
static std::string
getEdgeAttributes(const LoopRegionWrapper *Node, alledge_iterator EI,
const LoopRegionFunctionInfoGrapherWrapper *Graph) {
if (EI.isSubregion())
return "color=red";
if (EI.isNonLocalEdge())
return "color=blue";
if (EI.isBackEdge())
return "color=darkgreen";
return "";
}
static bool edgeTargetsEdgeSource(const LoopRegionWrapper *Node,
alledge_iterator I) {
return I.isNonLocalEdge();
}
/// If edgeTargetsEdgeSource returns true, this method is called to determine
/// which outgoing edge of Node is the target of this edge.
///
/// Currently this can only happen given a non-local edge in which case we
/// want the non-local edge to point at its parent's successor edge.
static alledge_iterator getEdgeTarget(LoopRegionWrapper *Node,
alledge_iterator I) {
assert(I.isNonLocalEdge() && "This should only be called given a non "
"local edge");
auto *ParentRegion = Node->getParent();
auto SuccIter = ParentRegion->Region->succ_begin();
std::advance(SuccIter, (*I.SuccIter).ID);
return alledge_iterator(ParentRegion, ParentRegion->Region->subregion_end(),
SuccIter, ParentRegion->Region->backedge_begin());
}
};
} // end llvm namespace
static llvm::cl::opt<std::string> TargetFunction(
"view-loop-regions-only-for-function", llvm::cl::init(""),
llvm::cl::desc("Only print out the loop regions for this function"));
#endif
void LoopRegionFunctionInfo::viewLoopRegions() const {
// This is a no-op when asserts are disabled.
#ifndef NDEBUG
// If we have a target function, only print that function out.
if (!TargetFunction.empty() && !(F->getName().str() == TargetFunction))
return;
LoopRegionFunctionInfoGrapherWrapper Wrapper;
Wrapper.FuncInfo = const_cast<LoopRegionFunctionInfo *>(this);
for (auto *R : IDToRegionMap) {
Wrapper.Data.push_back({Wrapper, R});
}
llvm::ViewGraph(&Wrapper, "loop region function info" + F->getName().str());
#endif
}
//===----------------------------------------------------------------------===//
// LoopRegionAnalysis
//===----------------------------------------------------------------------===//
void LoopRegionAnalysis::initialize(SILPassManager *PM) {
SLA = PM->getAnalysis<SILLoopAnalysis>();
POA = PM->getAnalysis<PostOrderAnalysis>();
}
//===----------------------------------------------------------------------===//
// Main Entry Point
//===----------------------------------------------------------------------===//
SILAnalysis *swift::createLoopRegionAnalysis(SILModule *M) {
return new LoopRegionAnalysis(M);
}