blob: eb6e37147008da39679f6348a59e68cdd41f3005 [file] [log] [blame]
//===--- LoopRegionAnalysis.cpp -------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-loop-region-analysis"
#include "swift/SILAnalysis/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;
llvm::SmallVector<LoopRegion::SuccessorID, 8> UniqueSuccList;
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() &&
"Can not 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()->isSuccessor(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 &SuccIter : BB->getSuccessors()) {
unsigned SuccRPOIndex = *PI->getRPONumber(SuccIter.getBB());
auto *SuccRegion = createRegion(SuccIter.getBB(), SuccRPOIndex);
BBRegion->addSucc(SuccRegion);
SuccRegion->addPred(BBRegion);
DEBUG(llvm::dbgs() << " Succ: ";
SuccIter.getBB()->printAsOperand(llvm::dbgs());
llvm::dbgs() << " RPONum: " << SuccRPOIndex << "\n");
}
}
void LoopRegionFunctionInfo::markIrreducibleLoopPredecessorsOfNonLoopHeader(
BlockTy *NonHeaderBB, RegionTy *NonHeaderBBRegion,
PostOrderFunctionInfo *PI) {
for (BlockTy *Pred : NonHeaderBB->getPreds()) {
// 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() << "Visting 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");
// We are abusing the fact that a block can only be a local successor.
PredRegion->removeLocalSucc(SubLoopHeaderRegion->getID());
propagateLivenessDownNonLocalSuccessorEdges(PredRegion);
}
SubLoopHeaderRegion->Preds.clear();
return SubLoopHeaderRegion;
}
static void
getExitingRegions(LoopRegionFunctionInfo *LRFI,
SILLoop *Loop,
LoopRegion *LRegion,
llvm::SmallVectorImpl<LoopRegion *> &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);
}
// 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.
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) {
llvm::SmallVector<RegionTy *, 8> ExitingRegions;
getExitingRegions(this, Loop, LRegion, ExitingRegions);
DEBUG(llvm::dbgs() << " Visiting Exit Blocks...\n");
for (auto *ExitingRegion : ExitingRegions) {
DEBUG(llvm::dbgs() << " Exiting Region: "
<< ExitingRegion->getID() << "\n");
for (auto SuccID : ExitingRegion->getSuccs()) {
DEBUG(llvm::dbgs() << " Succ: " << SuccID.ID
<< ". IsNonLocal: "
<< (SuccID.IsNonLocal ? "true" : "false") << "\n");
auto *SuccRegion = getRegion(SuccID.ID);
if (!LRegion->containsSubregion(SuccRegion)) {
DEBUG(llvm::dbgs() << " Is not a subregion, replacing.\n");
SuccRegion->replacePred(ExitingRegion->ID, LRegion->ID);
if (ExitingRegion->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);
ExitingRegion->replaceSucc(SuccID,
LoopRegion::SuccessorID(Index, true));
propagateLivenessDownNonLocalSuccessorEdges(ExitingRegion);
continue;
}
// If the rpo number of the successor is less than the RPO number of the
// BB, then we know that it is not a backedge.
if (SuccRegion->getRPONumber() > ExitingRegion->getRPONumber()) {
DEBUG(llvm::dbgs() << " Is a subregion, but not a "
"backedge, not removing.\n");
continue;
}
DEBUG(llvm::dbgs() << " Is a subregion and a backedge, "
"removing.\n");
auto Iter =
std::remove(SuccRegion->Preds.begin(), SuccRegion->Preds.end(),
ExitingRegion->getID());
SuccRegion->Preds.erase(Iter);
}
}
}
/// 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 descendents of Parent. If there is a non-local
/// successor edge path that points to a dead edge in Parent, mark the
/// descendent 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;
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;
// Finally if the non-local successor edge points to a parent successor
// that is not dead continue.
if (R->Succs[SuccID->ID].hasValue())
continue;
// Ok, we found a target! Mark is as dead and make sure that we visit
// the subregion's children.
ShouldVisit = true;
// This is safe to do since when erasing in a BlotSetVector, we do not
// invalidate the iterators.
Subregion->Succs.erase(*SuccID);
}
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";
}
}
//===----------------------------------------------------------------------===//
// 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::const_succ_iterator SuccIter;
alledge_iterator(LoopRegionWrapper *w,
swift::LoopRegion::subregion_iterator subregioniter,
LoopRegion::const_succ_iterator succiter)
: Wrapper(w), SubregionIter(subregioniter), SuccIter(succiter) {
// Prime the successor iterator so that we skip over any initial dead
// successor edges.
//
// TODO: Refactor this to use a FilterRange.
for (auto SuccEnd = Wrapper->Region->succ_end();
!SuccIter->hasValue() && SuccIter != SuccEnd; ++SuccIter) {
}
}
bool isSubregion() const {
return SubregionIter != Wrapper->Region->subregion_end();
}
bool isNonLocalEdge() const {
if (isSubregion())
return false;
return SuccIter->getValue().IsNonLocal;
}
Optional<unsigned> getSuccIndex() const {
if (isSubregion())
return None;
// Since we have a bidirectional iterator, this will perform increments
// until we get to SuccIter. This is the behavior we want so that we ensure
// that we skip over any dead successor edges. We are just performing
// graphing, so performance is not a concern.
return std::distance(Wrapper->Region->succ_begin(), SuccIter);
}
LoopRegionWrapper *operator*() const {
if (SubregionIter != Wrapper->Region->subregion_end()) {
return &Wrapper->FuncInfo.Data[*SubregionIter];
}
// 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 (SubregionIter != Wrapper->Region->subregion_end()) {
SubregionIter++;
} else {
// Make sure that we skip past any dead successors.
auto End = Wrapper->Region->succ_end();
do {
SuccIter++;
} while (!SuccIter->hasValue() && SuccIter != End);
}
return *this;
}
alledge_iterator operator++(int) {
if (SubregionIter != Wrapper->Region->subregion_end()) {
return alledge_iterator{Wrapper, SubregionIter++, SuccIter};
}
auto NewIter = *this;
++NewIter;
return NewIter;
}
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;
return true;
}
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());
}
alledge_iterator LoopRegionWrapper::end() {
return alledge_iterator(this, Region->subregion_end(), Region->succ_end());
}
namespace llvm {
template <> struct GraphTraits<LoopRegionWrapper> {
using NodeType = LoopRegionWrapper;
using ChildIteratorType = alledge_iterator;
static NodeType *getEntryNode(NodeType *BB) { return BB; }
static ChildIteratorType child_begin(NodeType *N) { return N->begin(); }
static ChildIteratorType child_end(NodeType *N) { return N->end(); }
};
template <>
struct GraphTraits<LoopRegionFunctionInfoGrapherWrapper *>
: public GraphTraits<LoopRegionWrapper> {
using GraphType = LoopRegionFunctionInfoGrapherWrapper;
static NodeType *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";
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";
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);
}
};
} // 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);
}