blob: c3682a33f01c29e93a9335f789d361b81d1382f0 [file] [log] [blame]
//===--- LoopRegionAnalysis.h -----------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
///
/// Purpose
/// =======
///
/// This is an analysis that is an implementation of interval analysis in order
/// to allow for dataflow to be done in a structured way on the loop tree. The
/// facilities it provides are:
///
/// 1. An abstraction called ``LoopRegion`` that abstracts over natural loops,
/// basic blocks, and the top level function. This is meant to provide a
/// function level pointer for summarizing information.
///
/// 2. For each Region, if it has subregions (i.e. is not a Block), an RPO
/// ordering of the Subregions are provided. Loops in this RPO ordering have the
/// RPO number of their header.
///
/// 3. Natural loops are defined by the loops that SILLoopInfo can find. Thus it
/// is useful to be able to know about loops that are not able to be found by
/// SILLoopInfo (i.e. irreducible loops). This information is computed by
/// finding all backedges and any backedge that is not recognized by SILLoopInfo
/// as a backedge has its head and tail region marked as unknown control flow
/// boundaries. This can be queries via the methods:
///
/// - LoopRegion::isUnknownControlFlowEdgeHead().
/// - LoopRegion::isUnknownControlFlowEdgeTail().
///
/// 4. Only loops with single back edges are supported by this analysis. If such
/// a loop is detected, we mark the head, tail of the edge as irreducible
/// control flow boundaries. To handle such loops, we rely on loop
/// canonicalization to transform the multiple backedge loops into multiple
/// loops with singular backedges or by merging the backedge blocks.
///
/// Algorithm
/// =========
///
/// The main consideration here is that we want to ensure that we only traverse
/// the CFG once and reuse the information that is already provided via the loop
/// info analysis and the post order analysis.
///
/// We begin by visiting each block in RPO order. During this RPO traversal, we:
///
/// 1. Create LoopRegions for each block and wire up each block's predecessor
/// and successor blocks as predecessors/successors of the block's region.
///
/// 2. Detect any backedges that are unknown to SILLoopInfo and mark the blocks
/// that form the head/tail of the backedge as unknown control flow boundaries.
///
/// 3. We lookup/create the region for the innermost loop that contains the
/// block and add the block's region as a subregion of that loop.
///
/// Then we perform a postorder DFS of the loop nest. The postorder provides the
/// inductive rule that all loops will be visited after all of their subloops
/// have been visited. If a Loop has no subloops (i.e. all subregions are
/// blocks), we do nothing. Otherwise, if the loop does have subloops, we visit
/// each subloop and do the following:
///
/// 1. The region data structure of the header of the subloop still is the
/// successor of its predecessor outside of the subloop in the various block
/// datastructures. Rewire those regions to consider the loop to be their
/// successor instead and make each of those regions on of the predecessors of
/// the subloop. Then clear the predecessor list of the subloop's header
/// region. The header should have no predecessor edges after this operation
/// completes.
///
/// 2. For each of the exiting regions of the subloop (that is the regions
/// inside the loop that leave the subloop):
///
/// a. Replace each predecessor edge in the exiting block's successors with an
/// edge pointing at the subloop instead of at the exiting block and
/// vis-a-versa.
///
/// b. Introduce a loop-escape edge from the exiting regions that associates
/// the specific successor number of the region with the loop successor edge
/// that we just created. By using this indirection, if the loop is an inner
/// loop and further acts as an exit from an outer loop, the region's
/// successor edge will be able to be used to traverse from the region ->
/// inner loop -> outer loop successor. (i).
///
/// After this is complete, the exiting blocks should have no successor edges
/// going outside the loop. For each of the removed edges additionally we
/// introduce an "exiting edge". The reason this is necessary is to make it
/// easier to handle exits through
///
/// 3. If the loop has multiple back edges, mark each of the backedges as
/// unknown control flow boundaries.
///
/// We complete by performing the same operation on the top level function.
///
/// After this is done, each level of the loop hierarchy should be able to be
/// treated from a dataflow perspective as its own separate function with only
/// one caller, the parent loop region (or the top level function), enabling
/// extremely aggressive operations (or even outlining).
///
/// (i). The reason why this is important is to be able to handle exits to
/// trapping code. Trapping code will always be associated with the outer most
/// loop since it is an exit from all of the loops. Yet in ARC we need to be
/// able to find that region from For certain optimizations like ARC, we need to
/// be able to ignore these successor regions. By tracing up the loop nest
/// hierarchy by these loop exits, we can find the unreachable regions in the
/// parent and ignore them. This also allows us to be more precise about
/// hoisting retains/releases and avoid hoisting into such regions.
///
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_LOOPNESTANALYSIS_H
#define SWIFT_SILOPTIMIZER_ANALYSIS_LOOPNESTANALYSIS_H
#include "swift/SILOptimizer/Analysis/Analysis.h"
#include "swift/Basic/BlotSetVector.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/STLExtras.h"
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
#include "swift/SILOptimizer/Analysis/LoopAnalysis.h"
#include "swift/SILOptimizer/PassManager/PassManager.h"
#include "swift/SIL/LoopInfo.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILFunction.h"
#include "llvm/Support/raw_ostream.h"
namespace swift {
/// A loop region is a data structure which represents one of a basic block,
/// loop, or function.
///
/// In the case of a loop, function, it contains an internal
/// data structure that represents the subregions of the loop/function. This
/// data is tail allocated so that the basic block case is not penalized by
/// storing this unnecessary information.
class LoopRegion {
// FIXME: This should use llvm::TrailingObjects for its tail allocations, but
// that requires restructuring the file a bit.
/// This is a data structure that is an unsigned integer with a top bit flag
/// that says whether it is an RPO ID for a BB Region or is an RPO ID of the
/// header BB of a Loop Region that used as a placeholder to ensure that the
/// Loop is visited in the proper RPO order. When the placeholder flag is hit,
/// the subloop array should be searched for the pair that has that flag as a
/// first element. The loop's flag will be the second element of the pair.
struct SubregionID {
static constexpr unsigned IDBitSize = (sizeof(unsigned) * CHAR_BIT) - 1;
static constexpr unsigned MaxID = (unsigned(1) << IDBitSize) - 1;
unsigned IsLoop : 1;
unsigned ID : IDBitSize;
SubregionID(unsigned id, bool isloop) {
IsLoop = unsigned(isloop);
ID = id;
}
bool operator<(const SubregionID &Other) const { return ID < Other.ID; }
};
/// These checks are just for performance.
static_assert(IsTriviallyCopyable<SubregionID>::value,
"Expected trivially copyable type");
struct SubregionData;
friend class LoopRegionFunctionInfo;
public:
/// We operate in terms of FunctionTy, LoopTy, and BlockTy so that this can
/// potentially be ported to LLVM in the future.
using FunctionTy = SILFunction;
using LoopTy = SILLoop;
using BlockTy = SILBasicBlock;
/// A tagged data structure that stores in its bottom bit whether or not the
/// successor edge is non-local (implying it goes through the parent region).
struct SuccessorID {
static constexpr unsigned NumTaggedBits = 2;
static constexpr unsigned IDBitSize =
(sizeof(unsigned) * CHAR_BIT) - NumTaggedBits;
static constexpr unsigned MaxID = (unsigned(1) << IDBitSize) - 1;
private:
unsigned IsDead : 1;
public:
unsigned IsNonLocal : 1;
unsigned ID : IDBitSize;
SuccessorID(unsigned rawValue)
: IsDead(rawValue & 1), IsNonLocal((rawValue & 2) >> 1),
ID(rawValue >> 2) {}
SuccessorID(unsigned id, bool isnonlocal)
: IsDead(unsigned(false)), IsNonLocal(unsigned(isnonlocal)), ID(id) {}
bool operator<(const SuccessorID &Other) const { return ID < Other.ID; }
bool operator==(const SuccessorID &Other) const {
return ID == Other.ID && IsNonLocal == Other.IsNonLocal &&
IsDead == Other.IsDead;
}
bool operator!=(const SuccessorID &Other) const {
return !(*this == Other);
}
unsigned asInt() const { return *reinterpret_cast<const unsigned *>(this); }
struct ToLiveSucc {
Optional<SuccessorID> operator()(Optional<SuccessorID> ID) const {
return ID;
}
};
struct ToLiveLocalSucc {
Optional<unsigned> operator()(Optional<SuccessorID> ID) const {
if (!ID)
return None;
if ((*ID).IsNonLocal)
return None;
return (*ID).ID;
}
};
struct ToLiveNonLocalSucc {
Optional<unsigned> operator()(Optional<SuccessorID> ID) const {
if (!ID)
return None;
if (!(*ID).IsNonLocal)
return None;
return (*ID).ID;
}
};
};
// These checks are just for performance.
static_assert(IsTriviallyCopyable<SuccessorID>::value,
"Expected trivially copyable type");
/// An iterator that knows how to iterate over the subregion indices of a
/// region.
class subregion_iterator :
public std::iterator<std::bidirectional_iterator_tag, unsigned> {
friend struct SubregionData;
llvm::SmallVectorImpl<SubregionID>::const_iterator InnerIter;
const llvm::SmallVectorImpl<std::pair<unsigned, unsigned>> *Subloops;
/// A flag that says that this iterator is an invalid iterator belonging to
/// a basic block. The iterator can only be compared against another invalid
/// iterator. Any other use causes an unreachable being hit.
///
/// The reason this is needed is because basic blocks cannot have
/// subregions, yet many graph algorithms want to be able to iterate over
/// the subregions of a region regardless of whether it is a basic block,
/// loop, or function. By allowing for invalid iterators for basic blocks,
/// we can allow for this.
unsigned IsInvalid : 1;
subregion_iterator(
llvm::SmallVectorImpl<SubregionID>::const_iterator iter,
const llvm::SmallVectorImpl<std::pair<unsigned, unsigned>> *subloops)
: InnerIter(iter), Subloops(subloops), IsInvalid(false) {}
public:
using value_type = unsigned;
using reference = unsigned;
using pointer = void;
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = int;
/// Construct a subregion_iterator suitable for use with a basic block. It
/// does not contain any data and can only be compared against another
/// invalid iterator (for which it will return true). Any other usage
/// results in an unreachable being hit.
subregion_iterator() : InnerIter(), Subloops(), IsInvalid(true) {}
/// Return the index of the current subregion index.
unsigned operator*() const {
if (IsInvalid)
llvm_unreachable("Invalid Iterator?!");
auto ID = *InnerIter;
if (!ID.IsLoop)
return ID.ID;
for (auto &p : *Subloops) {
if (p.first == ID.ID) {
return p.second;
}
}
llvm_unreachable("Out of sync subloops array?!");
}
subregion_iterator &operator++() {
if (IsInvalid)
llvm_unreachable("Invalid Iterator?!");
InnerIter++;
return *this;
}
subregion_iterator operator++(int) {
if (IsInvalid)
llvm_unreachable("Invalid Iterator?!");
return subregion_iterator(InnerIter++, Subloops);
}
subregion_iterator &operator--() {
if (IsInvalid)
llvm_unreachable("Invalid Iterator?!");
InnerIter--;
return *this;
}
subregion_iterator operator--(int) {
if (IsInvalid)
llvm_unreachable("Invalid Iterator?!");
return subregion_iterator(InnerIter--, Subloops);
}
bool operator==(subregion_iterator rhs) const {
if (IsInvalid) {
if (rhs.IsInvalid)
return true;
llvm_unreachable("Invalid Iterator?!");
}
return InnerIter == rhs.InnerIter;
}
bool operator!=(subregion_iterator rhs) const { return !(*this == rhs); }
};
using subregion_reverse_iterator = std::reverse_iterator<subregion_iterator>;
/// An iterator that knows how to iterate over the backedge indices of a
/// region.
class backedge_iterator
: public std::iterator<std::bidirectional_iterator_tag, unsigned> {
friend struct SubregionData;
using InnerIterTy = llvm::SmallVectorImpl<unsigned>::const_iterator;
llvm::Optional<InnerIterTy> InnerIter;
backedge_iterator(llvm::SmallVectorImpl<unsigned>::const_iterator iter)
: InnerIter(iter) {}
public:
using value_type = unsigned;
using reference = unsigned;
using pointer = void;
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = int;
/// Construct a backedge_iterator suitable for use with a basic block. It
/// does not contain any data and can only be compared against another
/// invalid iterator (for which it will return true). Any other usage
/// results in an unreachable being hit.
backedge_iterator() : InnerIter() {}
bool hasValue() const { return InnerIter.hasValue(); }
/// Return the index of the current backedge index.
unsigned operator*() const { return **InnerIter; }
backedge_iterator &operator++() {
++(*InnerIter);
return *this;
}
backedge_iterator operator++(int) {
backedge_iterator iter = *this;
++iter;
return iter;
}
backedge_iterator &operator--() {
--(*InnerIter);
return *this;
}
backedge_iterator operator--(int) {
backedge_iterator iter = *this;
--iter;
return iter;
}
bool operator==(backedge_iterator rhs) const {
if (InnerIter.hasValue() != rhs.InnerIter.hasValue())
llvm_unreachable("Comparing uncomparable iterators");
// Now we know that the two either both have values or both do not have
// values.
if (!InnerIter.hasValue())
return true;
return *InnerIter == *rhs.InnerIter;
}
bool operator!=(backedge_iterator rhs) const { return !(*this == rhs); }
};
using backedge_reverse_iterator = std::reverse_iterator<backedge_iterator>;
private:
/// A pointer to one of a Loop, Basic Block, or Function represented by this
/// region.
llvm::PointerUnion<FunctionTy *, LoopTy *, BlockTy *> Ptr;
/// The ID of this region.
unsigned ID;
/// The parent region of this ID if it exists.
llvm::Optional<unsigned> ParentID;
/// The IDs of the predecessor regions of this region.
llvm::SmallVector<unsigned, 4> Preds;
/// The IDs of the local and non-local successor regions of this region.
///
/// Let R1 be this region. A local successor of R1 is a region R2 for which
/// the inner most parent region that contains R2 is the same as the inner
/// most parent region that contains R1. A local successor is represented by
/// its real ID. A non-local successor is represented by the index of the
/// non-local successor in this region's parent's successor list.
///
/// Discussion: The reason that we have this separate representation is so
/// that if the non-local exit is to a block in a grand+-ancestor region of
/// this BB, we can represent each jump as individual non-local edges in
/// between loops.
///
/// *NOTE* This list is not sorted, but cannot have any duplicate
/// elements. We have a check in LoopRegionFunctionInfo::verify to make sure
/// that this stays true. The reason why this is necessary is that subregions
/// of a loop, may have a non-local successor edge pointed at this region's
/// successor edge. If we were to sort these edges, we would need to update
/// those subregion edges as well which is strictly not necessary.
SmallBlotSetVector<SuccessorID, 8> Succs;
/// True if this region the head of an edge that results from control flow
/// that we do not handle.
///
/// Currently this includes irreducible control flow and loops with multiple
/// backedges. We rely on loop canonicalization to handle the multiple
/// backedge case.
bool IsUnknownControlFlowEdgeHead;
/// True if this region the tail of an edge that results from control flow
/// that we do not handle.
///
/// Currently this includes irreducible control flow and loops with multiple
/// backedges. We rely on loop canonicalization to handle the multiple
/// backedge case.
bool IsUnknownControlFlowEdgeTail;
/// A tail allocated LoopSubregionData structure that is used to store state
/// about subregions of a loop.
///
/// We tail allocate this onto LoopRegions for loops/functions so that in the
/// case of having a Block, we do not have any memory size overhead of these
/// SmallVectors, but at the same time have reasonable memory performance. In
/// the case of loops this overhead is acceptable since we shouldn't have many
/// loops (compared to Blocks).
struct SubregionData {
/// The RPO number of the header block of this loop or function. This is
/// used as the RPO number for the whole region.
unsigned RPONumOfHeaderBlock;
/// The RPO number of the back edge blocks of this loop. We use a
/// SmallVector of size 1 since after loop canonicalization we will always
/// have exactly one back edge block. But we want to be correct even in a
/// non-canonicalized case implying that we need to be able to support
/// multiple backedge blocks.
llvm::SmallVector<unsigned, 1> BackedgeRegions;
/// A list of subregion IDs of this region sorted in RPO order.
///
/// This takes advantage of the fact that the ID of a basic block is the
/// block's RPO number.
///
/// This contains IDs that represent both basic blocks and loops that are
/// subregions of this region. What is key to notice is that a loop is
/// represented by the RPO number of its header. We use an auxiliary map to
/// map the preheader's RPO number to the loop's ID.
llvm::SmallVector<SubregionID, 16> Subregions;
/// A map from RPO number of a subregion loop's preheader to a subloop
/// regions id. This is necessary since we represent a loop in the
/// Subregions array by the RPO number of its header.
llvm::SmallVector<std::pair<unsigned, unsigned>, 2> Subloops;
/// A list of subregions with non-local successors. This is the actual ID
/// of the subregion since we do not care about any ordering.
llvm::SmallVector<unsigned, 2> ExitingSubregions;
subregion_iterator begin() const {
return subregion_iterator(Subregions.begin(), &Subloops);
}
subregion_iterator end() const {
return subregion_iterator(Subregions.end(), &Subloops);
}
subregion_reverse_iterator rbegin() const {
return subregion_reverse_iterator(end());
}
subregion_reverse_iterator rend() const {
return subregion_reverse_iterator(begin());
}
unsigned size() const { return Subregions.size(); }
bool empty() const { return Subregions.empty(); }
void addBlockSubregion(LoopRegion *R) {
assert(R->ID <= SubregionID::MaxID && "Unrepresentable ID");
Subregions.push_back(SubregionID(R->ID, false));
}
void addLoopSubregion(LoopRegion *L, LoopRegion *Header) {
assert(Header->ID <= SubregionID::MaxID && "Unrepresentable ID");
Subregions.push_back(SubregionID(Header->ID, true));
Subloops.push_back({Header->ID, L->ID});
}
void addBackedgeSubregion(unsigned ID) {
if (count(BackedgeRegions, ID))
return;
BackedgeRegions.push_back(ID);
}
backedge_iterator backedge_begin() const {
return backedge_iterator(BackedgeRegions.begin());
}
backedge_iterator backedge_end() const {
return backedge_iterator(BackedgeRegions.end());
}
backedge_reverse_iterator backedge_rbegin() const {
return backedge_reverse_iterator(backedge_begin());
}
backedge_reverse_iterator backedge_rend() const {
return backedge_reverse_iterator(backedge_end());
}
bool backedge_empty() const { return BackedgeRegions.empty(); }
unsigned backedge_size() const { return BackedgeRegions.size(); }
ArrayRef<unsigned> getBackedgeRegions() const { return BackedgeRegions; }
/// Once we finish processing a loop, we sort its subregions so that they
/// are guaranteed to be 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.
void sortSubregions() { std::sort(Subregions.begin(), Subregions.end()); }
};
public:
~LoopRegion();
/// These will assert if this is not a loop region. If this is a loop region,
/// the forward iterators will give the IDs of the subregions in RPO order.
/// The backward iterators will give the IDs of the subregions in PO order.
///
/// The reason why all of this work is being done for subregions is because we
/// are working around the following contradiction:
///
/// 1. There are not that many loops in a function so it makes sense to take
/// advantage of this to store more data that makes performing analysis easy
/// (such as the IDs of subregions of the loop in RPO order).
/// 2. There are a lot of BBs in a function so it makes sense to try to
/// minimize the amount of state stored for each BB.
/// 3. This is a data structure that attempts to generalize over both of them
/// without using dynamic dispatch.
///
/// We use tail allocation of the extra data for loops so we do not incur the
/// memory cost of large SmallVectors for BBs.
subregion_iterator subregion_begin() const {
if (isBlock())
return subregion_iterator();
return getSubregionData().begin();
}
/// This is the end equivalent of subregion_begin(). Please see comment on
/// subregion_begin().
subregion_iterator subregion_end() const {
if (isBlock())
return subregion_iterator();
return getSubregionData().end();
}
bool subregions_empty() const {
if (isBlock())
return true;
return getSubregionData().empty();
}
unsigned subregions_size() const {
if (isBlock())
return 0;
return getSubregionData().size();
}
subregion_reverse_iterator subregion_rbegin() const {
if (isBlock())
return subregion_reverse_iterator();
return getSubregionData().rbegin();
}
subregion_reverse_iterator subregion_rend() const {
if (isBlock())
return subregion_reverse_iterator();
return getSubregionData().rend();
}
llvm::iterator_range<subregion_iterator> getSubregions() const {
return {subregion_begin(), subregion_end()};
}
llvm::iterator_range<subregion_reverse_iterator>
getReverseSubregions() const {
return {subregion_rbegin(), subregion_rend()};
}
/// Returns true if \p R is an immediate subregion of this region.
bool containsSubregion(LoopRegion *R) {
auto End = subregion_end();
return std::find(subregion_begin(), End, R->getID()) != End;
}
/// Returns an ArrayRef containing IDs of the exiting subregions of this
/// region. The exit regions associated with the exiting subregions are the
/// end points of the non-local edges. This asserts if this is a region
/// representing a block.
ArrayRef<unsigned> getExitingSubregions() const {
return getSubregionData().ExitingSubregions;
}
bool isBackedgeRegion(unsigned ID) const {
if (isBlock())
return false;
return count(getSubregionData().getBackedgeRegions(), ID);
}
ArrayRef<unsigned> getBackedgeRegions() const {
if (isBlock())
return ArrayRef<unsigned>();
return getSubregionData().getBackedgeRegions();
}
Optional<unsigned> getBackedgeRegion() const {
if (isBlock())
return None;
auto bedge_begin = getSubregionData().backedge_begin();
auto bedge_end = getSubregionData().backedge_end();
if (bedge_begin == bedge_end)
return None;
return *bedge_begin;
}
using backedge_iterator = backedge_iterator;
backedge_iterator backedge_begin() const {
if (isBlock())
return backedge_iterator();
return getSubregionData().backedge_begin();
}
backedge_iterator backedge_end() const {
if (isBlock())
return backedge_iterator();
return getSubregionData().backedge_end();
}
bool backedge_empty() const {
if (isBlock())
return true;
return getSubregionData().backedge_empty();
}
unsigned backedge_size() const {
if (isBlock())
return 0;
return getSubregionData().backedge_size();
}
using pred_const_iterator = decltype(Preds)::const_iterator;
pred_const_iterator pred_begin() const { return Preds.begin(); }
pred_const_iterator pred_end() const { return Preds.end(); }
bool pred_empty() const { return Preds.empty(); }
unsigned pred_size() const { return Preds.size(); }
iterator_range<pred_const_iterator> getPreds() const {
return {pred_begin(), pred_end()};
}
using const_succ_iterator = decltype(Succs)::const_iterator;
const_succ_iterator succ_begin() const { return Succs.begin(); }
const_succ_iterator succ_end() const { return Succs.end(); }
bool succ_empty() const { return Succs.empty(); }
unsigned succ_size() const { return Succs.size(); }
private:
using InnerSuccRange = IteratorRange<decltype(Succs)::const_iterator>;
public:
using SuccRange =
OptionalTransformRange<InnerSuccRange, SuccessorID::ToLiveSucc>;
using LocalSuccRange =
OptionalTransformRange<InnerSuccRange, SuccessorID::ToLiveLocalSucc>;
using NonLocalSuccRange =
OptionalTransformRange<InnerSuccRange, SuccessorID::ToLiveNonLocalSucc>;
SuccRange getSuccs() const;
LocalSuccRange getLocalSuccs() const;
NonLocalSuccRange getNonLocalSuccs() const;
BlockTy *getBlock() const;
LoopTy *getLoop() const;
FunctionTy *getFunction() const;
bool isBlock() const { return Ptr.is<BlockTy *>(); }
bool isLoop() const { return Ptr.is<LoopTy *>(); }
bool isFunction() const { return Ptr.is<FunctionTy *>(); }
/// Is this the head of an edge that causes unknown control flow.
///
/// This means that dataflow that enters the region must not propagate any
/// information into this region from predecessors.
bool isUnknownControlFlowEdgeHead() const {
return IsUnknownControlFlowEdgeHead;
}
/// Is this the head of an edge that causes unknown control flow.
///
/// This means that dataflow state must not be propagated out of this region.
bool isUnknownControlFlowEdgeTail() const {
return IsUnknownControlFlowEdgeTail;
}
/// Returns the ID of this region in the region array.
///
/// For basic blocks this is the RPO number of the basic block. This is done
/// just as a convenient way to compress our region data structures.
unsigned getID() const { return ID; }
/// Return the ID of the parent region of this BB. Asserts if this is a
/// function region.
Optional<unsigned> getParentID() const { return ParentID; }
unsigned getRPONumber() const {
if (isBlock())
return getID();
return getSubregionData().RPONumOfHeaderBlock;
}
void dump() const;
void print(llvm::raw_ostream &os, bool insertSpaces = false) const;
void dumpName() const;
void printName(llvm::raw_ostream &os) const;
private:
LoopRegion(LoopTy *L, unsigned Idx)
: Ptr(L), ID(Idx), ParentID(), Preds(), Succs(),
IsUnknownControlFlowEdgeHead(false),
IsUnknownControlFlowEdgeTail(false) {}
LoopRegion(BlockTy *BB, unsigned Idx)
: Ptr(BB), ID(Idx), ParentID(), Preds(), Succs(),
IsUnknownControlFlowEdgeHead(false),
IsUnknownControlFlowEdgeTail(false) {}
LoopRegion(FunctionTy *F, unsigned Idx)
: Ptr(F), ID(Idx), ParentID(), Preds(), Succs(),
IsUnknownControlFlowEdgeHead(false),
IsUnknownControlFlowEdgeTail(false) {}
void setParent(LoopRegion *PR) {
assert(!isFunction() && "Functions cannot be subregions");
assert(!PR->isBlock() && "BB regions cannot be parents of a region");
ParentID = PR->getID();
}
void addPred(LoopRegion *LNR) {
assert(!isFunction() && "Functions cannot have predecessors");
if (count(getPreds(), LNR->getID()))
return;
Preds.push_back(LNR->ID);
}
unsigned addSucc(LoopRegion *Successor) {
assert(!isFunction() && "Functions cannot have successors");
return Succs.insert(SuccessorID(Successor->getID(), false)).first;
}
void replacePred(unsigned OldPredID, unsigned NewPredID) {
// Check if we already have NewPred in our list. If so, we just delete
// OldPredID.
if (count(Preds, NewPredID)) {
// If this becomes a performance issue due to copying/moving/etc (which it
// most likely will not), just use the marked dead model like successor
// does.
auto Iter = std::remove(Preds.begin(), Preds.end(), OldPredID);
Preds.erase(Iter);
return;
}
std::replace(Preds.begin(), Preds.end(), OldPredID, NewPredID);
}
/// Replace OldSuccID by NewSuccID, just deleting OldSuccID if what NewSuccID
/// is already in the list.
void replaceSucc(SuccessorID OldSucc, SuccessorID NewSucc);
/// Set the IsDead flag on all successors of this region that have an id \p
/// ID.
///
/// Due to the creation of up-edges during loop region construction, we can
/// never actually remove successors. Instead we mark successors as being dead
/// and ignore such values when iterating.
void removeLocalSucc(unsigned ID) { Succs.erase(SuccessorID(ID, false)); }
void addBlockSubregion(LoopRegion *R) {
assert(!isBlock() && "Blocks cannot have subregions");
assert(R->isBlock() && "Assumed R was a basic block");
R->setParent(this);
getSubregionData().addBlockSubregion(R);
}
void addLoopSubregion(LoopRegion *L, LoopRegion *Header) {
assert(!isBlock() && "Blocks cannot have subregions");
assert(L->isLoop() && "Assumed L was a loop");
assert(Header->isBlock() && "Assumed Header was a loop");
L->setParent(this);
getSubregionData().addLoopSubregion(L, Header);
}
SubregionData &getSubregionData() {
assert(!isBlock() && "BBs do not have subregion data");
return reinterpret_cast<SubregionData &>(*(this + 1));
}
const SubregionData &getSubregionData() const {
assert(!isBlock() && "BBs do not have subregion data");
return reinterpret_cast<const SubregionData &>(*(this + 1));
}
};
} // end swift namespace
namespace llvm {
raw_ostream &operator<<(raw_ostream &os, swift::LoopRegion &LR);
raw_ostream &operator<<(raw_ostream &os, swift::LoopRegion::SuccessorID &S);
template <> struct DenseMapInfo<swift::LoopRegion::SuccessorID> {
using Type = swift::LoopRegion::SuccessorID;
static_assert(sizeof(Type) == sizeof(unsigned),
"Expected SuccessorID to be the size of an unsigned!");
static inline Type getEmptyKey() {
return Type(DenseMapInfo<unsigned>::getEmptyKey());
}
static inline Type getTombstoneKey() {
return Type(DenseMapInfo<unsigned>::getTombstoneKey());
}
static unsigned getHashValue(const swift::LoopRegion::SuccessorID Val) {
return DenseMapInfo<unsigned>::getHashValue(Val.asInt());
}
static bool isEqual(const swift::LoopRegion::SuccessorID LHS,
const swift::LoopRegion::SuccessorID RHS) {
return LHS == RHS;
}
};
} // end llvm namespace
namespace swift {
class LoopRegionFunctionInfo {
using RegionTy = LoopRegion;
using BlockTy = SILBasicBlock;
using LoopInfoTy = SILLoopInfo;
using LoopTy = SILLoop;
using FunctionTy = SILFunction;
/// The function that this data structure contains loop regions for.
FunctionTy *F;
/// A bump ptr allocator that we allocate regions from.
llvm::BumpPtrAllocator Allocator;
/// The ID in the IDToRegionMap of the Region associated with \p F.
llvm::Optional<unsigned> FunctionRegionID;
/// A map from a BB to the ID in the IDToRegionMap of the Region associated
/// with the BB.
///
/// *NOTE* This ID is *also* the function level RPO number of the BB.
llvm::DenseMap<BlockTy *, unsigned> BBToIDMap;
/// A map from a Loop to the ID in the IDToRegionMap of the Region associated
/// with the loop.
llvm::DenseMap<LoopTy *, unsigned> LoopToIDMap;
/// A map from an unsigned integer ID to a region.
///
/// *WARNING* Before modifying the initialization of this field of the data
/// structure please read the comment below:
///
/// We assign IDs to BBs, Loops, and the top level Function, so that we can
/// abstract above the underlying type of any specific region. A key thing to
/// notice is that we always allocate regions associated with BBs before
/// regions associated with Loops or the top level function (1). The reason
/// that we do this is to ensure that each BBs ID is the RPO number of the BB
/// at the function level. This is done so we do not have to represent the RPO
/// numbers in a separate array. This means that when initializing this data
/// structure, we need to be careful about how we initialize regions in order
/// to preserve said property.
///
/// In order to avoid having to initialize loop/function regions after BB
/// regions (which would cause us to have to visit BBs twice), we instead
/// allocate the memory for all of the BBs up front and set the pointer entry
/// to be nullptr.
///
/// Then when we initialize BBRegions, we just assign the resulting Region's
/// pointer into the array using its RPO index rather than performing a
/// push_back. For regions associated with Loops, Functions we still perform a
/// push_back. This enables us to initialize regions for all types at the same
/// time while preserving said property.
///
/// (1) Currently there is no work done to ensure that the creation of the
/// function level region will have any ordering with respect to the creation
/// of any of the loop regions.
std::vector<RegionTy *> IDToRegionMap;
/// True if all BB regions have been created. Only in Debug Builds. Used to
/// fire asserts to make sure that:
///
/// RegionTy *createRegion(SILBasicBlock *, unsigned)
///
/// is only called in initializeBBRegions and everywhere else:
///
/// RegionTy *getRegion(SILBasicBlock *)
///
/// is called.
#ifndef NDEBUG
unsigned AllBBRegionsCreated : 1;
#endif
public:
LoopRegionFunctionInfo(FunctionTy *F, PostOrderFunctionInfo *POI,
LoopInfoTy *LI);
~LoopRegionFunctionInfo();
RegionTy *getRegion(unsigned RegionID) const {
return IDToRegionMap[RegionID];
}
RegionTy *getRegionForNonLocalSuccessor(const RegionTy *Child,
unsigned SuccID) const;
Optional<unsigned> getGrandparentID(const RegionTy *GrandChild) {
if (auto ParentID = GrandChild->getParentID()) {
return getRegion(*ParentID)->getParentID();
}
return None;
}
/// Look up the region associated with this block and return it. Asserts if
/// the block does not have a region associated with it.
///
/// Regions associated with blocks are only created by the function
/// createRegion(). FunctionTy and LoopTy have their RegionTys created via
/// getRegion() lazily. For more information read the documentation for
/// IDToRegionMap.
RegionTy *getRegion(BlockTy *BB) const;
/// Return the RegionTy associated with \p F. Creates the region if it does
/// not exist yet.
RegionTy *getRegion(FunctionTy *F) const;
/// Return the RegionTy associated with \p Loop. Creates the region if it does
/// not exist yet.
RegionTy *getRegion(LoopTy *Loop) const;
using iterator = decltype(IDToRegionMap)::iterator;
using const_iterator = decltype(IDToRegionMap)::const_iterator;
iterator begin() { return IDToRegionMap.begin(); }
iterator end() { return IDToRegionMap.end(); }
const_iterator begin() const { return IDToRegionMap.begin(); }
const_iterator end() const { return IDToRegionMap.end(); }
unsigned size() const { return IDToRegionMap.size(); }
bool empty() const { return IDToRegionMap.empty(); }
llvm::iterator_range<const_iterator> getRegions() const {
return {begin(), end()};
}
RegionTy *getTopLevelRegion() const { return getRegion(F); }
FunctionTy *getFunction() const { return F; }
void dump() const;
void print(llvm::raw_ostream &os) const;
void viewLoopRegions() const;
void verify();
private:
RegionTy *createRegion(BlockTy *BB, unsigned RPONum);
/// Initialize regions for all basic blocks.
///
/// This initializes all BBs to have their regular predecessors, successors in
/// the CFG ignoring BBs that are unreachable from the entry. During
/// initialization of loops, we modify these edges in the region graph by
/// removing and or substituting edges to loops for basic block edges. See
/// large comment at the top of the file.
void initializeBlockRegions(PostOrderFunctionInfo *PI, LoopInfoTy *LI);
/// Initialize regions for all loops.
///
/// This traverses the loop nest bottom up:
///
/// 1. Removing backedges of loops.
/// 2. Changing header predecessors to be loop predecessors.
/// 3. Changing exiting block successors to be loop successors.
///
/// For more information see large comment at the top of LoopRegionAnalysis.h.
void initializeLoopRegions(LoopInfoTy *LI);
/// Initialize the subregions of a function, treating the function as a top
/// level loop.
void
initializeFunctionRegion(iterator_range<LoopInfoTy::iterator> TopLevelLoops);
/// This is a refactored routine that handles common initialization of loops
/// and functions.
void
initializeLoopFunctionRegion(RegionTy *ParentRegion,
iterator_range<LoopInfoTy::iterator> SubLoops);
/// This helper routine rewrites all predecessors of the header of \p Loop to
/// be predecessors of \p Loop and vis-a-versa. Returns the region for the
/// SubLoopHeaderRegion
RegionTy *
rewriteLoopHeaderPredecessors(LoopTy *Loop, RegionTy *LoopRegion);
/// This helper routine rewrites all successors of loop region exiting blocks
/// to be successors of the loop. It also removes backedges and ignores
/// non-backedge edges in the loop.
void
rewriteLoopExitingBlockSuccessors(LoopTy *Loop, RegionTy *LoopRegion);
/// This helper routine for a given block, creates successor edges in between
/// the block's region and all of the regions associated with the block's
/// successor blocks.
void initializeBlockRegionSuccessors(BlockTy *BB, RegionTy *BBRegion,
PostOrderFunctionInfo *PI);
/// This helper method goes through the predecessors of the basic block \p
/// NonHeaderBB and determines if any of them are back edges. Since we know
/// that \p NonHeaderBB is a basic block that has not been identified by loop
/// info as a loop header, we know that these backedges are not known to us
/// via loop info. We treat them as irreducible control flow edges to be
/// conservative. In truth some of them may not be due to deficiencies in loop
/// info.
///
/// TODO: This needs a better name.
void
markIrreducibleLoopPredecessorsOfNonLoopHeader(BlockTy *NonHeaderBB,
RegionTy *NonHeaderBBRegion,
PostOrderFunctionInfo *PI);
/// This helper method takes in a loop that has multiple loop latches and
/// marks each of those loop latches as being unknown control flow tails. We
/// do not support loops with multiple loop latches and instead rely on loops
/// to be canonicalized to have one back edge. But we still need to be
/// conservatively correct.
///
/// TODO: This needs a better name.
void
markMultipleLoopLatchLoopBackEdges(RegionTy *LoopHeaderRegion,
LoopTy *L,
PostOrderFunctionInfo *PI);
/// 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 propagateLivenessDownNonLocalSuccessorEdges(LoopRegion *Parent);
};
class LoopRegionAnalysis : public FunctionAnalysisBase<LoopRegionFunctionInfo> {
SILLoopAnalysis *SLA;
PostOrderAnalysis *POA;
public:
LoopRegionAnalysis(SILModule *M)
: FunctionAnalysisBase<LoopRegionFunctionInfo>(
SILAnalysisKind::LoopRegion) {}
LoopRegionAnalysis(const LoopRegionAnalysis &) = delete;
LoopRegionAnalysis &operator=(const LoopRegionAnalysis &) = delete;
virtual ~LoopRegionAnalysis() {}
virtual void initialize(SILPassManager *PM) override;
static bool classof(const SILAnalysis *S) {
return S->getKind() == SILAnalysisKind::LoopRegion;
}
virtual std::unique_ptr<LoopRegionFunctionInfo>
newFunctionAnalysis(SILFunction *F) override {
return llvm::make_unique<LoopRegionFunctionInfo>(F, POA->get(F),
SLA->get(F));
}
virtual bool shouldInvalidate(SILAnalysis::InvalidationKind K) override {
return K & InvalidationKind::Branches;
}
};
} // end namespace swift
#endif