Skip to content

Commit

Permalink
[NFC][ScopBuilder] Move buildSchedule and its callees to ScopBuilder …
Browse files Browse the repository at this point in the history
…or ScopHelper

Scope of changes:
1. Moved buildSchedule functions to ScopBuilder.
2. Moved combineInSequence function to ScopBuilder.
3. Moved mapToDimension function to ScopBuilder.
4. Moved LoopStackTy to ScopBuilder.
5. Moved getLoopSurroundingScop to ScopHelper.
6. Moved getNumBlocksInLoop to ScopHelper.
7. Moved getNumBlocksInRegionNode to ScopHelper.
8. Moved getRegionNodeLoop to ScopHelper.

Differential Revision: https://reviews.llvm.org/D64223

llvm-svn: 366377
  • Loading branch information
DominikAdamski committed Jul 17, 2019
1 parent 9c7f426 commit d0ac007
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 320 deletions.
56 changes: 56 additions & 0 deletions polly/include/polly/ScopBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,62 @@ class ScopBuilder {
/// have been hoisted as loop invariant.
void canonicalizeDynamicBasePtrs();

/// Construct the schedule of this SCoP.
void buildSchedule();

/// A loop stack element to keep track of per-loop information during
/// schedule construction.
using LoopStackElementTy = struct LoopStackElement {
// The loop for which we keep information.
Loop *L;

// The (possibly incomplete) schedule for this loop.
isl::schedule Schedule;

// The number of basic blocks in the current loop, for which a schedule has
// already been constructed.
unsigned NumBlocksProcessed;

LoopStackElement(Loop *L, isl::schedule S, unsigned NumBlocksProcessed)
: L(L), Schedule(S), NumBlocksProcessed(NumBlocksProcessed) {}
};

/// The loop stack used for schedule construction.
///
/// The loop stack keeps track of schedule information for a set of nested
/// loops as well as an (optional) 'nullptr' loop that models the outermost
/// schedule dimension. The loops in a loop stack always have a parent-child
/// relation where the loop at position n is the parent of the loop at
/// position n + 1.
using LoopStackTy = SmallVector<LoopStackElementTy, 4>;

/// Construct schedule information for a given Region and add the
/// derived information to @p LoopStack.
///
/// Given a Region we derive schedule information for all RegionNodes
/// contained in this region ensuring that the assigned execution times
/// correctly model the existing control flow relations.
///
/// @param R The region which to process.
/// @param LoopStack A stack of loops that are currently under
/// construction.
void buildSchedule(Region *R, LoopStackTy &LoopStack);

/// Build Schedule for the region node @p RN and add the derived
/// information to @p LoopStack.
///
/// In case @p RN is a BasicBlock or a non-affine Region, we construct the
/// schedule for this @p RN and also finalize loop schedules in case the
/// current @p RN completes the loop.
///
/// In case @p RN is a not-non-affine Region, we delegate the construction to
/// buildSchedule(Region *R, ...).
///
/// @param RN The RegionNode region traversed.
/// @param LoopStack A stack of loops that are currently under
/// construction.
void buildSchedule(RegionNode *RN, LoopStackTy &LoopStack);

public:
explicit ScopBuilder(Region *R, AssumptionCache &AC, AliasAnalysis &AA,
const DataLayout &DL, DominatorTree &DT, LoopInfo &LI,
Expand Down
60 changes: 0 additions & 60 deletions polly/include/polly/ScopInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -2109,66 +2109,6 @@ class Scop {
/// have a corresponding domain in the domain map (or it is empty).
void removeStmtNotInDomainMap();

/// Construct the schedule of this SCoP.
///
/// @param LI The LoopInfo for the current function.
void buildSchedule(LoopInfo &LI);

/// A loop stack element to keep track of per-loop information during
/// schedule construction.
using LoopStackElementTy = struct LoopStackElement {
// The loop for which we keep information.
Loop *L;

// The (possibly incomplete) schedule for this loop.
isl::schedule Schedule;

// The number of basic blocks in the current loop, for which a schedule has
// already been constructed.
unsigned NumBlocksProcessed;

LoopStackElement(Loop *L, isl::schedule S, unsigned NumBlocksProcessed)
: L(L), Schedule(S), NumBlocksProcessed(NumBlocksProcessed) {}
};

/// The loop stack used for schedule construction.
///
/// The loop stack keeps track of schedule information for a set of nested
/// loops as well as an (optional) 'nullptr' loop that models the outermost
/// schedule dimension. The loops in a loop stack always have a parent-child
/// relation where the loop at position n is the parent of the loop at
/// position n + 1.
using LoopStackTy = SmallVector<LoopStackElementTy, 4>;

/// Construct schedule information for a given Region and add the
/// derived information to @p LoopStack.
///
/// Given a Region we derive schedule information for all RegionNodes
/// contained in this region ensuring that the assigned execution times
/// correctly model the existing control flow relations.
///
/// @param R The region which to process.
/// @param LoopStack A stack of loops that are currently under
/// construction.
/// @param LI The LoopInfo for the current function.
void buildSchedule(Region *R, LoopStackTy &LoopStack, LoopInfo &LI);

/// Build Schedule for the region node @p RN and add the derived
/// information to @p LoopStack.
///
/// In case @p RN is a BasicBlock or a non-affine Region, we construct the
/// schedule for this @p RN and also finalize loop schedules in case the
/// current @p RN completes the loop.
///
/// In case @p RN is a not-non-affine Region, we delegate the construction to
/// buildSchedule(Region *R, ...).
///
/// @param RN The RegionNode region traversed.
/// @param LoopStack A stack of loops that are currently under
/// construction.
/// @param LI The LoopInfo for the current function.
void buildSchedule(RegionNode *RN, LoopStackTy &LoopStack, LoopInfo &LI);

/// Collect all memory access relations of a given type.
///
/// @param Predicate A predicate function that returns true if an access is
Expand Down
22 changes: 22 additions & 0 deletions polly/include/polly/Support/ScopHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Region;
class Pass;
class DominatorTree;
class RegionInfo;
class RegionNode;
} // namespace llvm

namespace polly {
Expand Down Expand Up @@ -379,6 +380,27 @@ bool isErrorBlock(llvm::BasicBlock &BB, const llvm::Region &R,
/// @return The condition of @p TI and nullptr if none could be extracted.
llvm::Value *getConditionFromTerminator(llvm::Instruction *TI);

/// Get the smallest loop that contains @p S but is not in @p S.
llvm::Loop *getLoopSurroundingScop(Scop &S, llvm::LoopInfo &LI);

/// Get the number of blocks in @p L.
///
/// The number of blocks in a loop are the number of basic blocks actually
/// belonging to the loop, as well as all single basic blocks that the loop
/// exits to and which terminate in an unreachable instruction. We do not
/// allow such basic blocks in the exit of a scop, hence they belong to the
/// scop and represent run-time conditions which we want to model and
/// subsequently speculate away.
///
/// @see getRegionNodeLoop for additional details.
unsigned getNumBlocksInLoop(llvm::Loop *L);

/// Get the number of blocks in @p RN.
unsigned getNumBlocksInRegionNode(llvm::RegionNode *RN);

/// Return the smallest loop surrounding @p RN.
llvm::Loop *getRegionNodeLoop(llvm::RegionNode *RN, llvm::LoopInfo &LI);

/// Check if @p LInst can be hoisted in @p R.
///
/// @param LInst The load to check.
Expand Down
176 changes: 175 additions & 1 deletion polly/lib/Analysis/ScopBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "polly/Support/VirtualInstruction.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/EquivalenceClasses.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/Loads.h"
Expand Down Expand Up @@ -222,6 +223,179 @@ void ScopBuilder::buildScalarDependences(ScopStmt *UserStmt,
ensureValueRead(Op.get(), UserStmt);
}

// Create a sequence of two schedules. Either argument may be null and is
// interpreted as the empty schedule. Can also return null if both schedules are
// empty.
static isl::schedule combineInSequence(isl::schedule Prev, isl::schedule Succ) {
if (!Prev)
return Succ;
if (!Succ)
return Prev;

return Prev.sequence(Succ);
}

// Create an isl_multi_union_aff that defines an identity mapping from the
// elements of USet to their N-th dimension.
//
// # Example:
//
// Domain: { A[i,j]; B[i,j,k] }
// N: 1
//
// Resulting Mapping: { {A[i,j] -> [(j)]; B[i,j,k] -> [(j)] }
//
// @param USet A union set describing the elements for which to generate a
// mapping.
// @param N The dimension to map to.
// @returns A mapping from USet to its N-th dimension.
static isl::multi_union_pw_aff mapToDimension(isl::union_set USet, int N) {
assert(N >= 0);
assert(USet);
assert(!USet.is_empty());

auto Result = isl::union_pw_multi_aff::empty(USet.get_space());

for (isl::set S : USet.get_set_list()) {
int Dim = S.dim(isl::dim::set);
auto PMA = isl::pw_multi_aff::project_out_map(S.get_space(), isl::dim::set,
N, Dim - N);
if (N > 1)
PMA = PMA.drop_dims(isl::dim::out, 0, N - 1);

Result = Result.add_pw_multi_aff(PMA);
}

return isl::multi_union_pw_aff(isl::union_pw_multi_aff(Result));
}

void ScopBuilder::buildSchedule() {
Loop *L = getLoopSurroundingScop(*scop, LI);
LoopStackTy LoopStack({LoopStackElementTy(L, nullptr, 0)});
buildSchedule(scop->getRegion().getNode(), LoopStack);
assert(LoopStack.size() == 1 && LoopStack.back().L == L);
scop->setScheduleTree(LoopStack[0].Schedule);
}

/// To generate a schedule for the elements in a Region we traverse the Region
/// in reverse-post-order and add the contained RegionNodes in traversal order
/// to the schedule of the loop that is currently at the top of the LoopStack.
/// For loop-free codes, this results in a correct sequential ordering.
///
/// Example:
/// bb1(0)
/// / \.
/// bb2(1) bb3(2)
/// \ / \.
/// bb4(3) bb5(4)
/// \ /
/// bb6(5)
///
/// Including loops requires additional processing. Whenever a loop header is
/// encountered, the corresponding loop is added to the @p LoopStack. Starting
/// from an empty schedule, we first process all RegionNodes that are within
/// this loop and complete the sequential schedule at this loop-level before
/// processing about any other nodes. To implement this
/// loop-nodes-first-processing, the reverse post-order traversal is
/// insufficient. Hence, we additionally check if the traversal yields
/// sub-regions or blocks that are outside the last loop on the @p LoopStack.
/// These region-nodes are then queue and only traverse after the all nodes
/// within the current loop have been processed.
void ScopBuilder::buildSchedule(Region *R, LoopStackTy &LoopStack) {
Loop *OuterScopLoop = getLoopSurroundingScop(*scop, LI);

ReversePostOrderTraversal<Region *> RTraversal(R);
std::deque<RegionNode *> WorkList(RTraversal.begin(), RTraversal.end());
std::deque<RegionNode *> DelayList;
bool LastRNWaiting = false;

// Iterate over the region @p R in reverse post-order but queue
// sub-regions/blocks iff they are not part of the last encountered but not
// completely traversed loop. The variable LastRNWaiting is a flag to indicate
// that we queued the last sub-region/block from the reverse post-order
// iterator. If it is set we have to explore the next sub-region/block from
// the iterator (if any) to guarantee progress. If it is not set we first try
// the next queued sub-region/blocks.
while (!WorkList.empty() || !DelayList.empty()) {
RegionNode *RN;

if ((LastRNWaiting && !WorkList.empty()) || DelayList.empty()) {
RN = WorkList.front();
WorkList.pop_front();
LastRNWaiting = false;
} else {
RN = DelayList.front();
DelayList.pop_front();
}

Loop *L = getRegionNodeLoop(RN, LI);
if (!scop->contains(L))
L = OuterScopLoop;

Loop *LastLoop = LoopStack.back().L;
if (LastLoop != L) {
if (LastLoop && !LastLoop->contains(L)) {
LastRNWaiting = true;
DelayList.push_back(RN);
continue;
}
LoopStack.push_back({L, nullptr, 0});
}
buildSchedule(RN, LoopStack);
}
}

void ScopBuilder::buildSchedule(RegionNode *RN, LoopStackTy &LoopStack) {
if (RN->isSubRegion()) {
auto *LocalRegion = RN->getNodeAs<Region>();
if (!scop->isNonAffineSubRegion(LocalRegion)) {
buildSchedule(LocalRegion, LoopStack);
return;
}
}

assert(LoopStack.rbegin() != LoopStack.rend());
auto LoopData = LoopStack.rbegin();
LoopData->NumBlocksProcessed += getNumBlocksInRegionNode(RN);

for (auto *Stmt : scop->getStmtListFor(RN)) {
isl::union_set UDomain{Stmt->getDomain()};
auto StmtSchedule = isl::schedule::from_domain(UDomain);
LoopData->Schedule = combineInSequence(LoopData->Schedule, StmtSchedule);
}

// Check if we just processed the last node in this loop. If we did, finalize
// the loop by:
//
// - adding new schedule dimensions
// - folding the resulting schedule into the parent loop schedule
// - dropping the loop schedule from the LoopStack.
//
// Then continue to check surrounding loops, which might also have been
// completed by this node.
size_t Dimension = LoopStack.size();
while (LoopData->L &&
LoopData->NumBlocksProcessed == getNumBlocksInLoop(LoopData->L)) {
isl::schedule Schedule = LoopData->Schedule;
auto NumBlocksProcessed = LoopData->NumBlocksProcessed;

assert(std::next(LoopData) != LoopStack.rend());
++LoopData;
--Dimension;

if (Schedule) {
isl::union_set Domain = Schedule.get_domain();
isl::multi_union_pw_aff MUPA = mapToDimension(Domain, Dimension);
Schedule = Schedule.insert_partial_schedule(MUPA);
LoopData->Schedule = combineInSequence(LoopData->Schedule, Schedule);
}

LoopData->NumBlocksProcessed += NumBlocksProcessed;
}
// Now pop all loops processed up there from the LoopStack
LoopStack.erase(LoopStack.begin() + Dimension, LoopStack.end());
}

void ScopBuilder::buildEscapingDependences(Instruction *Inst) {
// Check for uses of this instruction outside the scop. Because we do not
// iterate over such instructions and therefore did not "ensure" the existence
Expand Down Expand Up @@ -2554,7 +2728,7 @@ void ScopBuilder::buildScop(Region &R, AssumptionCache &AC) {
return;
}

scop->buildSchedule(LI);
buildSchedule();

finalizeAccesses();

Expand Down
Loading

0 comments on commit d0ac007

Please sign in to comment.