Skip to content

Commit d0ac007

Browse files
[NFC][ScopBuilder] Move buildSchedule and its callees to ScopBuilder 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
1 parent 9c7f426 commit d0ac007

File tree

6 files changed

+327
-320
lines changed

6 files changed

+327
-320
lines changed

polly/include/polly/ScopBuilder.h

+56
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,62 @@ class ScopBuilder {
584584
/// have been hoisted as loop invariant.
585585
void canonicalizeDynamicBasePtrs();
586586

587+
/// Construct the schedule of this SCoP.
588+
void buildSchedule();
589+
590+
/// A loop stack element to keep track of per-loop information during
591+
/// schedule construction.
592+
using LoopStackElementTy = struct LoopStackElement {
593+
// The loop for which we keep information.
594+
Loop *L;
595+
596+
// The (possibly incomplete) schedule for this loop.
597+
isl::schedule Schedule;
598+
599+
// The number of basic blocks in the current loop, for which a schedule has
600+
// already been constructed.
601+
unsigned NumBlocksProcessed;
602+
603+
LoopStackElement(Loop *L, isl::schedule S, unsigned NumBlocksProcessed)
604+
: L(L), Schedule(S), NumBlocksProcessed(NumBlocksProcessed) {}
605+
};
606+
607+
/// The loop stack used for schedule construction.
608+
///
609+
/// The loop stack keeps track of schedule information for a set of nested
610+
/// loops as well as an (optional) 'nullptr' loop that models the outermost
611+
/// schedule dimension. The loops in a loop stack always have a parent-child
612+
/// relation where the loop at position n is the parent of the loop at
613+
/// position n + 1.
614+
using LoopStackTy = SmallVector<LoopStackElementTy, 4>;
615+
616+
/// Construct schedule information for a given Region and add the
617+
/// derived information to @p LoopStack.
618+
///
619+
/// Given a Region we derive schedule information for all RegionNodes
620+
/// contained in this region ensuring that the assigned execution times
621+
/// correctly model the existing control flow relations.
622+
///
623+
/// @param R The region which to process.
624+
/// @param LoopStack A stack of loops that are currently under
625+
/// construction.
626+
void buildSchedule(Region *R, LoopStackTy &LoopStack);
627+
628+
/// Build Schedule for the region node @p RN and add the derived
629+
/// information to @p LoopStack.
630+
///
631+
/// In case @p RN is a BasicBlock or a non-affine Region, we construct the
632+
/// schedule for this @p RN and also finalize loop schedules in case the
633+
/// current @p RN completes the loop.
634+
///
635+
/// In case @p RN is a not-non-affine Region, we delegate the construction to
636+
/// buildSchedule(Region *R, ...).
637+
///
638+
/// @param RN The RegionNode region traversed.
639+
/// @param LoopStack A stack of loops that are currently under
640+
/// construction.
641+
void buildSchedule(RegionNode *RN, LoopStackTy &LoopStack);
642+
587643
public:
588644
explicit ScopBuilder(Region *R, AssumptionCache &AC, AliasAnalysis &AA,
589645
const DataLayout &DL, DominatorTree &DT, LoopInfo &LI,

polly/include/polly/ScopInfo.h

-60
Original file line numberDiff line numberDiff line change
@@ -2109,66 +2109,6 @@ class Scop {
21092109
/// have a corresponding domain in the domain map (or it is empty).
21102110
void removeStmtNotInDomainMap();
21112111

2112-
/// Construct the schedule of this SCoP.
2113-
///
2114-
/// @param LI The LoopInfo for the current function.
2115-
void buildSchedule(LoopInfo &LI);
2116-
2117-
/// A loop stack element to keep track of per-loop information during
2118-
/// schedule construction.
2119-
using LoopStackElementTy = struct LoopStackElement {
2120-
// The loop for which we keep information.
2121-
Loop *L;
2122-
2123-
// The (possibly incomplete) schedule for this loop.
2124-
isl::schedule Schedule;
2125-
2126-
// The number of basic blocks in the current loop, for which a schedule has
2127-
// already been constructed.
2128-
unsigned NumBlocksProcessed;
2129-
2130-
LoopStackElement(Loop *L, isl::schedule S, unsigned NumBlocksProcessed)
2131-
: L(L), Schedule(S), NumBlocksProcessed(NumBlocksProcessed) {}
2132-
};
2133-
2134-
/// The loop stack used for schedule construction.
2135-
///
2136-
/// The loop stack keeps track of schedule information for a set of nested
2137-
/// loops as well as an (optional) 'nullptr' loop that models the outermost
2138-
/// schedule dimension. The loops in a loop stack always have a parent-child
2139-
/// relation where the loop at position n is the parent of the loop at
2140-
/// position n + 1.
2141-
using LoopStackTy = SmallVector<LoopStackElementTy, 4>;
2142-
2143-
/// Construct schedule information for a given Region and add the
2144-
/// derived information to @p LoopStack.
2145-
///
2146-
/// Given a Region we derive schedule information for all RegionNodes
2147-
/// contained in this region ensuring that the assigned execution times
2148-
/// correctly model the existing control flow relations.
2149-
///
2150-
/// @param R The region which to process.
2151-
/// @param LoopStack A stack of loops that are currently under
2152-
/// construction.
2153-
/// @param LI The LoopInfo for the current function.
2154-
void buildSchedule(Region *R, LoopStackTy &LoopStack, LoopInfo &LI);
2155-
2156-
/// Build Schedule for the region node @p RN and add the derived
2157-
/// information to @p LoopStack.
2158-
///
2159-
/// In case @p RN is a BasicBlock or a non-affine Region, we construct the
2160-
/// schedule for this @p RN and also finalize loop schedules in case the
2161-
/// current @p RN completes the loop.
2162-
///
2163-
/// In case @p RN is a not-non-affine Region, we delegate the construction to
2164-
/// buildSchedule(Region *R, ...).
2165-
///
2166-
/// @param RN The RegionNode region traversed.
2167-
/// @param LoopStack A stack of loops that are currently under
2168-
/// construction.
2169-
/// @param LI The LoopInfo for the current function.
2170-
void buildSchedule(RegionNode *RN, LoopStackTy &LoopStack, LoopInfo &LI);
2171-
21722112
/// Collect all memory access relations of a given type.
21732113
///
21742114
/// @param Predicate A predicate function that returns true if an access is

polly/include/polly/Support/ScopHelper.h

+22
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Region;
2727
class Pass;
2828
class DominatorTree;
2929
class RegionInfo;
30+
class RegionNode;
3031
} // namespace llvm
3132

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

383+
/// Get the smallest loop that contains @p S but is not in @p S.
384+
llvm::Loop *getLoopSurroundingScop(Scop &S, llvm::LoopInfo &LI);
385+
386+
/// Get the number of blocks in @p L.
387+
///
388+
/// The number of blocks in a loop are the number of basic blocks actually
389+
/// belonging to the loop, as well as all single basic blocks that the loop
390+
/// exits to and which terminate in an unreachable instruction. We do not
391+
/// allow such basic blocks in the exit of a scop, hence they belong to the
392+
/// scop and represent run-time conditions which we want to model and
393+
/// subsequently speculate away.
394+
///
395+
/// @see getRegionNodeLoop for additional details.
396+
unsigned getNumBlocksInLoop(llvm::Loop *L);
397+
398+
/// Get the number of blocks in @p RN.
399+
unsigned getNumBlocksInRegionNode(llvm::RegionNode *RN);
400+
401+
/// Return the smallest loop surrounding @p RN.
402+
llvm::Loop *getRegionNodeLoop(llvm::RegionNode *RN, llvm::LoopInfo &LI);
403+
382404
/// Check if @p LInst can be hoisted in @p R.
383405
///
384406
/// @param LInst The load to check.

polly/lib/Analysis/ScopBuilder.cpp

+175-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "polly/Support/VirtualInstruction.h"
2525
#include "llvm/ADT/ArrayRef.h"
2626
#include "llvm/ADT/EquivalenceClasses.h"
27+
#include "llvm/ADT/PostOrderIterator.h"
2728
#include "llvm/ADT/Statistic.h"
2829
#include "llvm/Analysis/AliasAnalysis.h"
2930
#include "llvm/Analysis/Loads.h"
@@ -222,6 +223,179 @@ void ScopBuilder::buildScalarDependences(ScopStmt *UserStmt,
222223
ensureValueRead(Op.get(), UserStmt);
223224
}
224225

226+
// Create a sequence of two schedules. Either argument may be null and is
227+
// interpreted as the empty schedule. Can also return null if both schedules are
228+
// empty.
229+
static isl::schedule combineInSequence(isl::schedule Prev, isl::schedule Succ) {
230+
if (!Prev)
231+
return Succ;
232+
if (!Succ)
233+
return Prev;
234+
235+
return Prev.sequence(Succ);
236+
}
237+
238+
// Create an isl_multi_union_aff that defines an identity mapping from the
239+
// elements of USet to their N-th dimension.
240+
//
241+
// # Example:
242+
//
243+
// Domain: { A[i,j]; B[i,j,k] }
244+
// N: 1
245+
//
246+
// Resulting Mapping: { {A[i,j] -> [(j)]; B[i,j,k] -> [(j)] }
247+
//
248+
// @param USet A union set describing the elements for which to generate a
249+
// mapping.
250+
// @param N The dimension to map to.
251+
// @returns A mapping from USet to its N-th dimension.
252+
static isl::multi_union_pw_aff mapToDimension(isl::union_set USet, int N) {
253+
assert(N >= 0);
254+
assert(USet);
255+
assert(!USet.is_empty());
256+
257+
auto Result = isl::union_pw_multi_aff::empty(USet.get_space());
258+
259+
for (isl::set S : USet.get_set_list()) {
260+
int Dim = S.dim(isl::dim::set);
261+
auto PMA = isl::pw_multi_aff::project_out_map(S.get_space(), isl::dim::set,
262+
N, Dim - N);
263+
if (N > 1)
264+
PMA = PMA.drop_dims(isl::dim::out, 0, N - 1);
265+
266+
Result = Result.add_pw_multi_aff(PMA);
267+
}
268+
269+
return isl::multi_union_pw_aff(isl::union_pw_multi_aff(Result));
270+
}
271+
272+
void ScopBuilder::buildSchedule() {
273+
Loop *L = getLoopSurroundingScop(*scop, LI);
274+
LoopStackTy LoopStack({LoopStackElementTy(L, nullptr, 0)});
275+
buildSchedule(scop->getRegion().getNode(), LoopStack);
276+
assert(LoopStack.size() == 1 && LoopStack.back().L == L);
277+
scop->setScheduleTree(LoopStack[0].Schedule);
278+
}
279+
280+
/// To generate a schedule for the elements in a Region we traverse the Region
281+
/// in reverse-post-order and add the contained RegionNodes in traversal order
282+
/// to the schedule of the loop that is currently at the top of the LoopStack.
283+
/// For loop-free codes, this results in a correct sequential ordering.
284+
///
285+
/// Example:
286+
/// bb1(0)
287+
/// / \.
288+
/// bb2(1) bb3(2)
289+
/// \ / \.
290+
/// bb4(3) bb5(4)
291+
/// \ /
292+
/// bb6(5)
293+
///
294+
/// Including loops requires additional processing. Whenever a loop header is
295+
/// encountered, the corresponding loop is added to the @p LoopStack. Starting
296+
/// from an empty schedule, we first process all RegionNodes that are within
297+
/// this loop and complete the sequential schedule at this loop-level before
298+
/// processing about any other nodes. To implement this
299+
/// loop-nodes-first-processing, the reverse post-order traversal is
300+
/// insufficient. Hence, we additionally check if the traversal yields
301+
/// sub-regions or blocks that are outside the last loop on the @p LoopStack.
302+
/// These region-nodes are then queue and only traverse after the all nodes
303+
/// within the current loop have been processed.
304+
void ScopBuilder::buildSchedule(Region *R, LoopStackTy &LoopStack) {
305+
Loop *OuterScopLoop = getLoopSurroundingScop(*scop, LI);
306+
307+
ReversePostOrderTraversal<Region *> RTraversal(R);
308+
std::deque<RegionNode *> WorkList(RTraversal.begin(), RTraversal.end());
309+
std::deque<RegionNode *> DelayList;
310+
bool LastRNWaiting = false;
311+
312+
// Iterate over the region @p R in reverse post-order but queue
313+
// sub-regions/blocks iff they are not part of the last encountered but not
314+
// completely traversed loop. The variable LastRNWaiting is a flag to indicate
315+
// that we queued the last sub-region/block from the reverse post-order
316+
// iterator. If it is set we have to explore the next sub-region/block from
317+
// the iterator (if any) to guarantee progress. If it is not set we first try
318+
// the next queued sub-region/blocks.
319+
while (!WorkList.empty() || !DelayList.empty()) {
320+
RegionNode *RN;
321+
322+
if ((LastRNWaiting && !WorkList.empty()) || DelayList.empty()) {
323+
RN = WorkList.front();
324+
WorkList.pop_front();
325+
LastRNWaiting = false;
326+
} else {
327+
RN = DelayList.front();
328+
DelayList.pop_front();
329+
}
330+
331+
Loop *L = getRegionNodeLoop(RN, LI);
332+
if (!scop->contains(L))
333+
L = OuterScopLoop;
334+
335+
Loop *LastLoop = LoopStack.back().L;
336+
if (LastLoop != L) {
337+
if (LastLoop && !LastLoop->contains(L)) {
338+
LastRNWaiting = true;
339+
DelayList.push_back(RN);
340+
continue;
341+
}
342+
LoopStack.push_back({L, nullptr, 0});
343+
}
344+
buildSchedule(RN, LoopStack);
345+
}
346+
}
347+
348+
void ScopBuilder::buildSchedule(RegionNode *RN, LoopStackTy &LoopStack) {
349+
if (RN->isSubRegion()) {
350+
auto *LocalRegion = RN->getNodeAs<Region>();
351+
if (!scop->isNonAffineSubRegion(LocalRegion)) {
352+
buildSchedule(LocalRegion, LoopStack);
353+
return;
354+
}
355+
}
356+
357+
assert(LoopStack.rbegin() != LoopStack.rend());
358+
auto LoopData = LoopStack.rbegin();
359+
LoopData->NumBlocksProcessed += getNumBlocksInRegionNode(RN);
360+
361+
for (auto *Stmt : scop->getStmtListFor(RN)) {
362+
isl::union_set UDomain{Stmt->getDomain()};
363+
auto StmtSchedule = isl::schedule::from_domain(UDomain);
364+
LoopData->Schedule = combineInSequence(LoopData->Schedule, StmtSchedule);
365+
}
366+
367+
// Check if we just processed the last node in this loop. If we did, finalize
368+
// the loop by:
369+
//
370+
// - adding new schedule dimensions
371+
// - folding the resulting schedule into the parent loop schedule
372+
// - dropping the loop schedule from the LoopStack.
373+
//
374+
// Then continue to check surrounding loops, which might also have been
375+
// completed by this node.
376+
size_t Dimension = LoopStack.size();
377+
while (LoopData->L &&
378+
LoopData->NumBlocksProcessed == getNumBlocksInLoop(LoopData->L)) {
379+
isl::schedule Schedule = LoopData->Schedule;
380+
auto NumBlocksProcessed = LoopData->NumBlocksProcessed;
381+
382+
assert(std::next(LoopData) != LoopStack.rend());
383+
++LoopData;
384+
--Dimension;
385+
386+
if (Schedule) {
387+
isl::union_set Domain = Schedule.get_domain();
388+
isl::multi_union_pw_aff MUPA = mapToDimension(Domain, Dimension);
389+
Schedule = Schedule.insert_partial_schedule(MUPA);
390+
LoopData->Schedule = combineInSequence(LoopData->Schedule, Schedule);
391+
}
392+
393+
LoopData->NumBlocksProcessed += NumBlocksProcessed;
394+
}
395+
// Now pop all loops processed up there from the LoopStack
396+
LoopStack.erase(LoopStack.begin() + Dimension, LoopStack.end());
397+
}
398+
225399
void ScopBuilder::buildEscapingDependences(Instruction *Inst) {
226400
// Check for uses of this instruction outside the scop. Because we do not
227401
// iterate over such instructions and therefore did not "ensure" the existence
@@ -2554,7 +2728,7 @@ void ScopBuilder::buildScop(Region &R, AssumptionCache &AC) {
25542728
return;
25552729
}
25562730

2557-
scop->buildSchedule(LI);
2731+
buildSchedule();
25582732

25592733
finalizeAccesses();
25602734

0 commit comments

Comments
 (0)