1 //===------ ManualOptimizer.cpp -------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Handle pragma/metadata-directed transformations.
11 //===----------------------------------------------------------------------===//
13 #include "polly/ManualOptimizer.h"
14 #include "polly/DependenceInfo.h"
15 #include "polly/Options.h"
16 #include "polly/ScheduleTreeTransform.h"
17 #include "polly/Support/ScopHelper.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Analysis/LoopInfo.h"
20 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
21 #include "llvm/IR/Metadata.h"
22 #include "llvm/Transforms/Utils/LoopUtils.h"
25 #include "polly/Support/PollyDebug.h"
26 #define DEBUG_TYPE "polly-opt-manual"
28 using namespace polly
;
33 static cl::opt
<bool> IgnoreDepcheck(
34 "polly-pragma-ignore-depcheck",
35 cl::desc("Skip the dependency check for pragma-based transformations"),
36 cl::cat(PollyCategory
));
38 /// Same as llvm::hasUnrollTransformation(), but takes a LoopID as argument
39 /// instead of a Loop.
40 static TransformationMode
hasUnrollTransformation(MDNode
*LoopID
) {
41 if (getBooleanLoopAttribute(LoopID
, "llvm.loop.unroll.disable"))
42 return TM_SuppressedByUser
;
44 std::optional
<int> Count
=
45 getOptionalIntLoopAttribute(LoopID
, "llvm.loop.unroll.count");
47 return *Count
== 1 ? TM_SuppressedByUser
: TM_ForcedByUser
;
49 if (getBooleanLoopAttribute(LoopID
, "llvm.loop.unroll.enable"))
50 return TM_ForcedByUser
;
52 if (getBooleanLoopAttribute(LoopID
, "llvm.loop.unroll.full"))
53 return TM_ForcedByUser
;
55 if (hasDisableAllTransformsHint(LoopID
))
58 return TM_Unspecified
;
61 // Return the first DebugLoc in the list.
62 static DebugLoc
findFirstDebugLoc(MDNode
*MD
) {
64 for (const MDOperand
&X
: drop_begin(MD
->operands(), 1)) {
65 Metadata
*A
= X
.get();
66 if (!isa
<DILocation
>(A
))
68 return cast
<DILocation
>(A
);
75 static DebugLoc
findTransformationDebugLoc(MDNode
*LoopMD
, StringRef Name
) {
76 // First find dedicated transformation location
77 // (such as the location of #pragma clang loop)
78 MDNode
*MD
= findOptionMDForLoopID(LoopMD
, Name
);
79 if (DebugLoc K
= findFirstDebugLoc(MD
))
82 // Otherwise, fall back to the location of the loop itself
83 return findFirstDebugLoc(LoopMD
);
86 /// Apply full or partial unrolling.
87 static isl::schedule
applyLoopUnroll(MDNode
*LoopMD
,
88 isl::schedule_node BandToUnroll
) {
89 TransformationMode UnrollMode
= ::hasUnrollTransformation(LoopMD
);
90 if (UnrollMode
& TM_Disable
)
93 assert(!BandToUnroll
.is_null());
94 // TODO: Isl's codegen also supports unrolling by isl_ast_build via
95 // isl_schedule_node_band_set_ast_build_options({ unroll[x] }) which would be
96 // more efficient because the content duplication is delayed. However, the
97 // unrolled loop could be input of another loop transformation which expects
98 // the explicit schedule nodes. That is, we would need this explicit expansion
99 // anyway and using the ISL codegen option is a compile-time optimization.
101 getOptionalIntLoopAttribute(LoopMD
, "llvm.loop.unroll.count").value_or(0);
102 bool Full
= getBooleanLoopAttribute(LoopMD
, "llvm.loop.unroll.full");
103 assert((!Full
|| !(Factor
> 0)) &&
104 "Cannot unroll fully and partially at the same time");
107 return applyFullUnroll(BandToUnroll
);
110 return applyPartialUnroll(BandToUnroll
, Factor
);
112 // For heuristic unrolling, fall back to LLVM's LoopUnroll pass.
116 static isl::schedule
applyLoopFission(MDNode
*LoopMD
,
117 isl::schedule_node BandToFission
) {
118 // TODO: Make it possible to selectively fission substatements.
119 // TODO: Apply followup loop properties.
120 // TODO: Instead of fission every statement, find the maximum set that does
121 // not cause a dependency violation.
122 return applyMaxFission(BandToFission
);
125 // Return the properties from a LoopID. Scalar properties are ignored.
126 static auto getLoopMDProps(MDNode
*LoopMD
) {
129 drop_begin(LoopMD
->operands(), 1),
130 [](const MDOperand
&MDOp
) { return isa
<MDNode
>(MDOp
.get()); }),
131 [](const MDOperand
&MDOp
) { return cast
<MDNode
>(MDOp
.get()); });
134 /// Recursively visit all nodes in a schedule, loop for loop-transformations
135 /// metadata and apply the first encountered.
136 class SearchTransformVisitor final
137 : public RecursiveScheduleTreeVisitor
<SearchTransformVisitor
> {
139 using BaseTy
= RecursiveScheduleTreeVisitor
<SearchTransformVisitor
>;
140 BaseTy
&getBase() { return *this; }
141 const BaseTy
&getBase() const { return *this; }
144 const Dependences
*D
;
145 OptimizationRemarkEmitter
*ORE
;
147 // Set after a transformation is applied. Recursive search must be aborted
148 // once this happens to ensure that any new followup transformation is
149 // transformed in innermost-first order.
150 isl::schedule Result
;
152 /// Check wether a schedule after a transformation is legal. Return the old
153 /// schedule without the transformation.
155 checkDependencyViolation(llvm::MDNode
*LoopMD
, llvm::Value
*CodeRegion
,
156 const isl::schedule_node
&OrigBand
,
157 StringRef DebugLocAttr
, StringRef TransPrefix
,
158 StringRef RemarkName
, StringRef TransformationName
) {
159 if (D
->isValidSchedule(*S
, Result
))
162 LLVMContext
&Ctx
= LoopMD
->getContext();
163 POLLY_DEBUG(dbgs() << "Dependency violation detected\n");
165 DebugLoc TransformLoc
= findTransformationDebugLoc(LoopMD
, DebugLocAttr
);
167 if (IgnoreDepcheck
) {
168 POLLY_DEBUG(dbgs() << "Still accepting transformation due to "
169 "-polly-pragma-ignore-depcheck\n");
172 OptimizationRemark(DEBUG_TYPE
, RemarkName
, TransformLoc
, CodeRegion
)
173 << (Twine("Could not verify dependencies for ") +
175 "; still applying because of -polly-pragma-ignore-depcheck")
181 POLLY_DEBUG(dbgs() << "Rolling back transformation\n");
184 ORE
->emit(DiagnosticInfoOptimizationFailure(DEBUG_TYPE
, RemarkName
,
185 TransformLoc
, CodeRegion
)
186 << (Twine("not applying ") + TransformationName
+
187 ": cannot ensure semantic equivalence due to possible "
188 "dependency violations")
192 // If illegal, revert and remove the transformation to not risk re-trying
195 makePostTransformationMetadata(Ctx
, LoopMD
, {TransPrefix
}, {});
196 BandAttr
*Attr
= getBandAttr(OrigBand
);
197 Attr
->Metadata
= NewLoopMD
;
199 // Roll back old schedule.
200 return OrigBand
.get_schedule();
204 SearchTransformVisitor(polly::Scop
*S
, const Dependences
*D
,
205 OptimizationRemarkEmitter
*ORE
)
206 : S(S
), D(D
), ORE(ORE
) {}
208 static isl::schedule
applyOneTransformation(polly::Scop
*S
,
209 const Dependences
*D
,
210 OptimizationRemarkEmitter
*ORE
,
211 const isl::schedule
&Sched
) {
212 SearchTransformVisitor
Transformer(S
, D
, ORE
);
213 Transformer
.visit(Sched
);
214 return Transformer
.Result
;
217 void visitBand(isl::schedule_node_band Band
) {
218 // Transform inner loops first (depth-first search).
219 getBase().visitBand(Band
);
220 if (!Result
.is_null())
223 // Since it is (currently) not possible to have a BandAttr marker that is
224 // specific to each loop in a band, we only support single-loop bands.
225 if (isl_schedule_node_band_n_member(Band
.get()) != 1)
228 BandAttr
*Attr
= getBandAttr(Band
);
230 // Band has no attribute.
234 // CodeRegion used but ORE to determine code hotness.
235 // TODO: Works only for original loop; for transformed loops, should track
236 // where the loop's body code comes from.
237 Loop
*Loop
= Attr
->OriginalLoop
;
238 Value
*CodeRegion
= nullptr;
240 CodeRegion
= Loop
->getHeader();
242 MDNode
*LoopMD
= Attr
->Metadata
;
246 // Iterate over loop properties to find the first transformation.
247 // FIXME: If there are more than one transformation in the LoopMD (making
248 // the order of transformations ambiguous), all others are silently ignored.
249 for (MDNode
*MD
: getLoopMDProps(LoopMD
)) {
250 auto *NameMD
= dyn_cast
<MDString
>(MD
->getOperand(0).get());
253 StringRef AttrName
= NameMD
->getString();
255 // Honor transformation order; transform the first transformation in the
257 if (AttrName
== "llvm.loop.unroll.enable" ||
258 AttrName
== "llvm.loop.unroll.count" ||
259 AttrName
== "llvm.loop.unroll.full") {
260 Result
= applyLoopUnroll(LoopMD
, Band
);
261 if (!Result
.is_null())
263 } else if (AttrName
== "llvm.loop.distribute.enable") {
264 Result
= applyLoopFission(LoopMD
, Band
);
265 if (!Result
.is_null())
266 Result
= checkDependencyViolation(
267 LoopMD
, CodeRegion
, Band
, "llvm.loop.distribute.loc",
268 "llvm.loop.distribute.", "FailedRequestedFission",
269 "loop fission/distribution");
270 if (!Result
.is_null())
274 // not a loop transformation; look for next property
278 void visitNode(isl::schedule_node Other
) {
279 if (!Result
.is_null())
281 getBase().visitNode(Other
);
288 polly::applyManualTransformations(Scop
*S
, isl::schedule Sched
,
289 const Dependences
&D
,
290 OptimizationRemarkEmitter
*ORE
) {
291 // Search the loop nest for transformations until fixpoint.
293 isl::schedule Result
=
294 SearchTransformVisitor::applyOneTransformation(S
, &D
, ORE
, Sched
);
295 if (Result
.is_null()) {
296 // No (more) transformation has been found.
300 // Use transformed schedule and look for more transformations.