Skip to content

Commit e470f92

Browse files
committed
[Polly] Implement user-directed loop distribution/fission.
This is a simple version without the possibility to define distribute points or followup-transformations. However, it is the first transformation that has to check whether the transformation is correct. It interprets the same metadata as the LoopDistribute pass. Re-apply after revert in c7bcd72 with fix: Take isBand out of #ifndef NDEBUG since it now is used unconditionally.
1 parent 83f3c61 commit e470f92

File tree

10 files changed

+493
-13
lines changed

10 files changed

+493
-13
lines changed

polly/include/polly/DependenceInfo.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ struct Dependences {
124124
/// dependences.
125125
bool isValidSchedule(Scop &S, const StatementToIslMapTy &NewSchedules) const;
126126

127+
/// Return true of the schedule @p NewSched is a schedule for @S that does not
128+
/// violate any dependences.
129+
bool isValidSchedule(Scop &S, isl::schedule NewSched) const;
130+
127131
/// Print the stored dependence information.
128132
void print(llvm::raw_ostream &OS) const;
129133

polly/include/polly/ManualOptimizer.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@
1515

1616
#include "isl/isl-noexceptions.h"
1717

18+
namespace llvm {
19+
class OptimizationRemarkEmitter;
20+
}
21+
1822
namespace polly {
1923
class Scop;
24+
struct Dependences;
2025

2126
/// Apply loop-transformation metadata.
2227
///
@@ -30,7 +35,9 @@ class Scop;
3035
/// @return The transformed schedule with all mark-nodes with loop
3136
/// transformations applied. Returns NULL in case of an error or @p
3237
/// Sched itself if no transformation has been applied.
33-
isl::schedule applyManualTransformations(Scop *S, isl::schedule Sched);
38+
isl::schedule applyManualTransformations(Scop *S, isl::schedule Sched,
39+
const Dependences &D,
40+
llvm::OptimizationRemarkEmitter *ORE);
3441
} // namespace polly
3542

3643
#endif /* POLLY_MANUALOPTIMIZER_H */

polly/include/polly/ScheduleTreeTransform.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ isl::schedule applyFullUnroll(isl::schedule_node BandToUnroll);
178178
/// Replace the AST band @p BandToUnroll by a partially unrolled equivalent.
179179
isl::schedule applyPartialUnroll(isl::schedule_node BandToUnroll, int Factor);
180180

181+
/// Loop-distribute the band @p BandToFission as much as possible.
182+
isl::schedule applyMaxFission(isl::schedule_node BandToFission);
183+
181184
/// Build the desired set of partial tile prefixes.
182185
///
183186
/// We build a set of partial tile prefixes, which are prefixes of the vector

polly/lib/Analysis/DependenceInfo.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,19 @@ void Dependences::calculateDependences(Scop &S) {
636636
LLVM_DEBUG(dump());
637637
}
638638

639+
bool Dependences::isValidSchedule(Scop &S, isl::schedule NewSched) const {
640+
// TODO: Also check permutable/coincident flags as well.
641+
642+
StatementToIslMapTy NewSchedules;
643+
for (auto NewMap : NewSched.get_map().get_map_list()) {
644+
auto Stmt = reinterpret_cast<ScopStmt *>(
645+
NewMap.get_tuple_id(isl::dim::in).get_user());
646+
NewSchedules[Stmt] = NewMap;
647+
}
648+
649+
return isValidSchedule(S, NewSchedules);
650+
}
651+
639652
bool Dependences::isValidSchedule(
640653
Scop &S, const StatementToIslMapTy &NewSchedule) const {
641654
if (LegalityCheckDisabled)

polly/lib/Transform/ManualOptimizer.cpp

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "polly/ManualOptimizer.h"
14+
#include "polly/DependenceInfo.h"
15+
#include "polly/Options.h"
1416
#include "polly/ScheduleTreeTransform.h"
1517
#include "polly/Support/ScopHelper.h"
1618
#include "llvm/ADT/Optional.h"
1719
#include "llvm/ADT/StringRef.h"
1820
#include "llvm/Analysis/LoopInfo.h"
21+
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
1922
#include "llvm/IR/Metadata.h"
2023
#include "llvm/Transforms/Utils/LoopUtils.h"
2124

@@ -25,6 +28,12 @@ using namespace polly;
2528
using namespace llvm;
2629

2730
namespace {
31+
32+
static cl::opt<bool> IgnoreDepcheck(
33+
"polly-pragma-ignore-depcheck",
34+
cl::desc("Skip the dependency check for pragma-based transformations"),
35+
cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory));
36+
2837
/// Same as llvm::hasUnrollTransformation(), but takes a LoopID as argument
2938
/// instead of a Loop.
3039
static TransformationMode hasUnrollTransformation(MDNode *LoopID) {
@@ -48,6 +57,31 @@ static TransformationMode hasUnrollTransformation(MDNode *LoopID) {
4857
return TM_Unspecified;
4958
}
5059

60+
// Return the first DebugLoc in the list.
61+
static DebugLoc findFirstDebugLoc(MDNode *MD) {
62+
if (MD) {
63+
for (const MDOperand &X : drop_begin(MD->operands(), 1)) {
64+
Metadata *A = X.get();
65+
if (!isa<DILocation>(A))
66+
continue;
67+
return cast<DILocation>(A);
68+
}
69+
}
70+
71+
return {};
72+
}
73+
74+
static DebugLoc findTransformationDebugLoc(MDNode *LoopMD, StringRef Name) {
75+
// First find dedicated transformation location
76+
// (such as the location of #pragma clang loop)
77+
MDNode *MD = findOptionMDForLoopID(LoopMD, Name);
78+
if (DebugLoc K = findFirstDebugLoc(MD))
79+
return K;
80+
81+
// Otherwise, fall back to the location of the loop itself
82+
return findFirstDebugLoc(LoopMD);
83+
}
84+
5185
/// Apply full or partial unrolling.
5286
static isl::schedule applyLoopUnroll(MDNode *LoopMD,
5387
isl::schedule_node BandToUnroll) {
@@ -78,6 +112,15 @@ static isl::schedule applyLoopUnroll(MDNode *LoopMD,
78112
return {};
79113
}
80114

115+
static isl::schedule applyLoopFission(MDNode *LoopMD,
116+
isl::schedule_node BandToFission) {
117+
// TODO: Make it possible to selectively fission substatements.
118+
// TODO: Apply followup loop properties.
119+
// TODO: Instead of fission every statement, find the maximum set that does
120+
// not cause a dependency violation.
121+
return applyMaxFission(BandToFission);
122+
}
123+
81124
// Return the properties from a LoopID. Scalar properties are ignored.
82125
static auto getLoopMDProps(MDNode *LoopMD) {
83126
return map_range(
@@ -96,14 +139,76 @@ class SearchTransformVisitor
96139
BaseTy &getBase() { return *this; }
97140
const BaseTy &getBase() const { return *this; }
98141

142+
polly::Scop *S;
143+
const Dependences *D;
144+
OptimizationRemarkEmitter *ORE;
145+
99146
// Set after a transformation is applied. Recursive search must be aborted
100147
// once this happens to ensure that any new followup transformation is
101148
// transformed in innermost-first order.
102149
isl::schedule Result;
103150

151+
/// Check wether a schedule after a transformation is legal. Return the old
152+
/// schedule without the transformation.
153+
isl::schedule
154+
checkDependencyViolation(llvm::MDNode *LoopMD, llvm::Value *CodeRegion,
155+
const isl::schedule_node &OrigBand,
156+
StringRef DebugLocAttr, StringRef TransPrefix,
157+
StringRef RemarkName, StringRef TransformationName) {
158+
if (D->isValidSchedule(*S, Result))
159+
return Result;
160+
161+
LLVMContext &Ctx = LoopMD->getContext();
162+
LLVM_DEBUG(dbgs() << "Dependency violation detected\n");
163+
164+
DebugLoc TransformLoc = findTransformationDebugLoc(LoopMD, DebugLocAttr);
165+
166+
if (IgnoreDepcheck) {
167+
LLVM_DEBUG(dbgs() << "Still accepting transformation due to "
168+
"-polly-pragma-ignore-depcheck\n");
169+
if (ORE) {
170+
ORE->emit(
171+
OptimizationRemark(DEBUG_TYPE, RemarkName, TransformLoc, CodeRegion)
172+
<< (Twine("Could not verify dependencies for ") +
173+
TransformationName +
174+
"; still applying because of -polly-pragma-ignore-depcheck")
175+
.str());
176+
}
177+
return Result;
178+
}
179+
180+
LLVM_DEBUG(dbgs() << "Rolling back transformation\n");
181+
182+
if (ORE) {
183+
ORE->emit(DiagnosticInfoOptimizationFailure(DEBUG_TYPE, RemarkName,
184+
TransformLoc, CodeRegion)
185+
<< (Twine("not applying ") + TransformationName +
186+
": cannot ensure semantic equivalence due to possible "
187+
"dependency violations")
188+
.str());
189+
}
190+
191+
// If illegal, revert and remove the transformation to not risk re-trying
192+
// indefintely.
193+
MDNode *NewLoopMD =
194+
makePostTransformationMetadata(Ctx, LoopMD, {TransPrefix}, {});
195+
BandAttr *Attr = getBandAttr(OrigBand);
196+
Attr->Metadata = NewLoopMD;
197+
198+
// Roll back old schedule.
199+
return OrigBand.get_schedule();
200+
}
201+
104202
public:
105-
static isl::schedule applyOneTransformation(const isl::schedule &Sched) {
106-
SearchTransformVisitor Transformer;
203+
SearchTransformVisitor(polly::Scop *S, const Dependences *D,
204+
OptimizationRemarkEmitter *ORE)
205+
: S(S), D(D), ORE(ORE) {}
206+
207+
static isl::schedule applyOneTransformation(polly::Scop *S,
208+
const Dependences *D,
209+
OptimizationRemarkEmitter *ORE,
210+
const isl::schedule &Sched) {
211+
SearchTransformVisitor Transformer(S, D, ORE);
107212
Transformer.visit(Sched);
108213
return Transformer.Result;
109214
}
@@ -125,6 +230,14 @@ class SearchTransformVisitor
125230
return;
126231
}
127232

233+
// CodeRegion used but ORE to determine code hotness.
234+
// TODO: Works only for original loop; for transformed loops, should track
235+
// where the loop's body code comes from.
236+
Loop *Loop = Attr->OriginalLoop;
237+
Value *CodeRegion = nullptr;
238+
if (Loop)
239+
CodeRegion = Loop->getHeader();
240+
128241
MDNode *LoopMD = Attr->Metadata;
129242
if (!LoopMD)
130243
return;
@@ -146,6 +259,15 @@ class SearchTransformVisitor
146259
Result = applyLoopUnroll(LoopMD, Band);
147260
if (!Result.is_null())
148261
return;
262+
} else if (AttrName == "llvm.loop.distribute.enable") {
263+
Result = applyLoopFission(LoopMD, Band);
264+
if (!Result.is_null())
265+
Result = checkDependencyViolation(
266+
LoopMD, CodeRegion, Band, "llvm.loop.distribute.loc",
267+
"llvm.loop.distribute.", "FailedRequestedFission",
268+
"loop fission/distribution");
269+
if (!Result.is_null())
270+
return;
149271
}
150272

151273
// not a loop transformation; look for next property
@@ -162,11 +284,14 @@ class SearchTransformVisitor
162284

163285
} // namespace
164286

165-
isl::schedule polly::applyManualTransformations(Scop *S, isl::schedule Sched) {
287+
isl::schedule
288+
polly::applyManualTransformations(Scop *S, isl::schedule Sched,
289+
const Dependences &D,
290+
OptimizationRemarkEmitter *ORE) {
166291
// Search the loop nest for transformations until fixpoint.
167292
while (true) {
168293
isl::schedule Result =
169-
SearchTransformVisitor::applyOneTransformation(Sched);
294+
SearchTransformVisitor::applyOneTransformation(S, &D, ORE, Sched);
170295
if (Result.is_null()) {
171296
// No (more) transformation has been found.
172297
break;

polly/lib/Transform/ScheduleOptimizer.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include "polly/Support/ISLOStream.h"
5656
#include "llvm/ADT/Sequence.h"
5757
#include "llvm/ADT/Statistic.h"
58+
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
5859
#include "llvm/InitializePasses.h"
5960
#include "llvm/Support/CommandLine.h"
6061
#include "isl/options.h"
@@ -668,7 +669,9 @@ static void walkScheduleTreeForStatistics(isl::schedule Schedule, int Version) {
668669
static bool runIslScheduleOptimizer(
669670
Scop &S,
670671
function_ref<const Dependences &(Dependences::AnalysisLevel)> GetDeps,
671-
TargetTransformInfo *TTI, isl::schedule &LastSchedule) {
672+
TargetTransformInfo *TTI, OptimizationRemarkEmitter *ORE,
673+
isl::schedule &LastSchedule) {
674+
672675
// Skip SCoPs in case they're already optimised by PPCGCodeGeneration
673676
if (S.isToBeSkipped())
674677
return false;
@@ -689,8 +692,8 @@ static bool runIslScheduleOptimizer(
689692

690693
bool HasUserTransformation = false;
691694
if (PragmaBasedOpts) {
692-
isl::schedule ManuallyTransformed =
693-
applyManualTransformations(&S, Schedule);
695+
isl::schedule ManuallyTransformed = applyManualTransformations(
696+
&S, Schedule, GetDeps(Dependences::AL_Statement), ORE);
694697
if (ManuallyTransformed.is_null()) {
695698
LLVM_DEBUG(dbgs() << "Error during manual optimization\n");
696699
return false;
@@ -849,7 +852,9 @@ static bool runIslScheduleOptimizer(
849852
walkScheduleTreeForStatistics(Schedule, 2);
850853
}
851854

852-
if (!ScheduleTreeOptimizer::isProfitableSchedule(S, Schedule))
855+
// Skip profitability check if user transformation(s) have been applied.
856+
if (!HasUserTransformation &&
857+
!ScheduleTreeOptimizer::isProfitableSchedule(S, Schedule))
853858
return false;
854859

855860
auto ScopStats = S.getStatistics();
@@ -878,9 +883,11 @@ bool IslScheduleOptimizerWrapperPass::runOnScop(Scop &S) {
878883
return getAnalysis<DependenceInfo>().getDependences(
879884
Dependences::AL_Statement);
880885
};
886+
OptimizationRemarkEmitter &ORE =
887+
getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE();
881888
TargetTransformInfo *TTI =
882889
&getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
883-
return runIslScheduleOptimizer(S, getDependences, TTI, LastSchedule);
890+
return runIslScheduleOptimizer(S, getDependences, TTI, &ORE, LastSchedule);
884891
}
885892

886893
static void runScheduleOptimizerPrinter(raw_ostream &OS,
@@ -915,8 +922,10 @@ void IslScheduleOptimizerWrapperPass::getAnalysisUsage(
915922
ScopPass::getAnalysisUsage(AU);
916923
AU.addRequired<DependenceInfo>();
917924
AU.addRequired<TargetTransformInfoWrapperPass>();
925+
AU.addRequired<OptimizationRemarkEmitterWrapperPass>();
918926

919927
AU.addPreserved<DependenceInfo>();
928+
AU.addPreserved<OptimizationRemarkEmitterWrapperPass>();
920929
}
921930

922931
} // namespace
@@ -930,6 +939,7 @@ INITIALIZE_PASS_BEGIN(IslScheduleOptimizerWrapperPass, "polly-opt-isl",
930939
INITIALIZE_PASS_DEPENDENCY(DependenceInfo);
931940
INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass);
932941
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass);
942+
INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass);
933943
INITIALIZE_PASS_END(IslScheduleOptimizerWrapperPass, "polly-opt-isl",
934944
"Polly - Optimize schedule of SCoP", false, false)
935945

@@ -941,9 +951,10 @@ runIslScheduleOptimizerUsingNPM(Scop &S, ScopAnalysisManager &SAM,
941951
auto GetDeps = [&Deps](Dependences::AnalysisLevel) -> const Dependences & {
942952
return Deps.getDependences(Dependences::AL_Statement);
943953
};
954+
OptimizationRemarkEmitter ORE(&S.getFunction());
944955
TargetTransformInfo *TTI = &SAR.TTI;
945956
isl::schedule LastSchedule;
946-
bool Modified = runIslScheduleOptimizer(S, GetDeps, TTI, LastSchedule);
957+
bool Modified = runIslScheduleOptimizer(S, GetDeps, TTI, &ORE, LastSchedule);
947958
if (OS) {
948959
*OS << "Printing analysis 'Polly - Optimize schedule of SCoP' for region: '"
949960
<< S.getName() << "' in function '" << S.getFunction().getName()

0 commit comments

Comments
 (0)