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 #define DEBUG_TYPE "polly-opt-manual"
27 using namespace polly
;
32 static cl::opt
<bool> IgnoreDepcheck(
33 "polly-pragma-ignore-depcheck",
34 cl::desc("Skip the dependency check for pragma-based transformations"),
35 cl::cat(PollyCategory
));
37 /// Same as llvm::hasUnrollTransformation(), but takes a LoopID as argument
38 /// instead of a Loop.
39 static TransformationMode
hasUnrollTransformation(MDNode
*LoopID
) {
40 if (getBooleanLoopAttribute(LoopID
, "llvm.loop.unroll.disable"))
41 return TM_SuppressedByUser
;
43 std::optional
<int> Count
=
44 getOptionalIntLoopAttribute(LoopID
, "llvm.loop.unroll.count");
46 return *Count
== 1 ? TM_SuppressedByUser
: TM_ForcedByUser
;
48 if (getBooleanLoopAttribute(LoopID
, "llvm.loop.unroll.enable"))
49 return TM_ForcedByUser
;
51 if (getBooleanLoopAttribute(LoopID
, "llvm.loop.unroll.full"))
52 return TM_ForcedByUser
;
54 if (hasDisableAllTransformsHint(LoopID
))
57 return TM_Unspecified
;
60 // Return the first DebugLoc in the list.
61 static DebugLoc
findFirstDebugLoc(MDNode
*MD
) {
63 for (const MDOperand
&X
: drop_begin(MD
->operands(), 1)) {
64 Metadata
*A
= X
.get();
65 if (!isa
<DILocation
>(A
))
67 return cast
<DILocation
>(A
);
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
))
81 // Otherwise, fall back to the location of the loop itself
82 return findFirstDebugLoc(LoopMD
);
85 /// Apply full or partial unrolling.
86 static isl::schedule
applyLoopUnroll(MDNode
*LoopMD
,
87 isl::schedule_node BandToUnroll
) {
88 TransformationMode UnrollMode
= ::hasUnrollTransformation(LoopMD
);
89 if (UnrollMode
& TM_Disable
)
92 assert(!BandToUnroll
.is_null());
93 // TODO: Isl's codegen also supports unrolling by isl_ast_build via
94 // isl_schedule_node_band_set_ast_build_options({ unroll[x] }) which would be
95 // more efficient because the content duplication is delayed. However, the
96 // unrolled loop could be input of another loop transformation which expects
97 // the explicit schedule nodes. That is, we would need this explicit expansion
98 // anyway and using the ISL codegen option is a compile-time optimization.
100 getOptionalIntLoopAttribute(LoopMD
, "llvm.loop.unroll.count").value_or(0);
101 bool Full
= getBooleanLoopAttribute(LoopMD
, "llvm.loop.unroll.full");
102 assert((!Full
|| !(Factor
> 0)) &&
103 "Cannot unroll fully and partially at the same time");
106 return applyFullUnroll(BandToUnroll
);
109 return applyPartialUnroll(BandToUnroll
, Factor
);
111 // For heuristic unrolling, fall back to LLVM's LoopUnroll pass.
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
);
124 // Return the properties from a LoopID. Scalar properties are ignored.
125 static auto getLoopMDProps(MDNode
*LoopMD
) {
128 drop_begin(LoopMD
->operands(), 1),
129 [](const MDOperand
&MDOp
) { return isa
<MDNode
>(MDOp
.get()); }),
130 [](const MDOperand
&MDOp
) { return cast
<MDNode
>(MDOp
.get()); });
133 /// Recursively visit all nodes in a schedule, loop for loop-transformations
134 /// metadata and apply the first encountered.
135 class SearchTransformVisitor final
136 : public RecursiveScheduleTreeVisitor
<SearchTransformVisitor
> {
138 using BaseTy
= RecursiveScheduleTreeVisitor
<SearchTransformVisitor
>;
139 BaseTy
&getBase() { return *this; }
140 const BaseTy
&getBase() const { return *this; }
143 const Dependences
*D
;
144 OptimizationRemarkEmitter
*ORE
;
146 // Set after a transformation is applied. Recursive search must be aborted
147 // once this happens to ensure that any new followup transformation is
148 // transformed in innermost-first order.
149 isl::schedule Result
;
151 /// Check wether a schedule after a transformation is legal. Return the old
152 /// schedule without the transformation.
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
))
161 LLVMContext
&Ctx
= LoopMD
->getContext();
162 LLVM_DEBUG(dbgs() << "Dependency violation detected\n");
164 DebugLoc TransformLoc
= findTransformationDebugLoc(LoopMD
, DebugLocAttr
);
166 if (IgnoreDepcheck
) {
167 LLVM_DEBUG(dbgs() << "Still accepting transformation due to "
168 "-polly-pragma-ignore-depcheck\n");
171 OptimizationRemark(DEBUG_TYPE
, RemarkName
, TransformLoc
, CodeRegion
)
172 << (Twine("Could not verify dependencies for ") +
174 "; still applying because of -polly-pragma-ignore-depcheck")
180 LLVM_DEBUG(dbgs() << "Rolling back transformation\n");
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")
191 // If illegal, revert and remove the transformation to not risk re-trying
194 makePostTransformationMetadata(Ctx
, LoopMD
, {TransPrefix
}, {});
195 BandAttr
*Attr
= getBandAttr(OrigBand
);
196 Attr
->Metadata
= NewLoopMD
;
198 // Roll back old schedule.
199 return OrigBand
.get_schedule();
203 SearchTransformVisitor(polly::Scop
*S
, const Dependences
*D
,
204 OptimizationRemarkEmitter
*ORE
)
205 : S(S
), D(D
), ORE(ORE
) {}
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
);
212 Transformer
.visit(Sched
);
213 return Transformer
.Result
;
216 void visitBand(isl::schedule_node_band Band
) {
217 // Transform inner loops first (depth-first search).
218 getBase().visitBand(Band
);
219 if (!Result
.is_null())
222 // Since it is (currently) not possible to have a BandAttr marker that is
223 // specific to each loop in a band, we only support single-loop bands.
224 if (isl_schedule_node_band_n_member(Band
.get()) != 1)
227 BandAttr
*Attr
= getBandAttr(Band
);
229 // Band has no attribute.
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;
239 CodeRegion
= Loop
->getHeader();
241 MDNode
*LoopMD
= Attr
->Metadata
;
245 // Iterate over loop properties to find the first transformation.
246 // FIXME: If there are more than one transformation in the LoopMD (making
247 // the order of transformations ambiguous), all others are silently ignored.
248 for (MDNode
*MD
: getLoopMDProps(LoopMD
)) {
249 auto *NameMD
= dyn_cast
<MDString
>(MD
->getOperand(0).get());
252 StringRef AttrName
= NameMD
->getString();
254 // Honor transformation order; transform the first transformation in the
256 if (AttrName
== "llvm.loop.unroll.enable" ||
257 AttrName
== "llvm.loop.unroll.count" ||
258 AttrName
== "llvm.loop.unroll.full") {
259 Result
= applyLoopUnroll(LoopMD
, Band
);
260 if (!Result
.is_null())
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())
273 // not a loop transformation; look for next property
277 void visitNode(isl::schedule_node Other
) {
278 if (!Result
.is_null())
280 getBase().visitNode(Other
);
287 polly::applyManualTransformations(Scop
*S
, isl::schedule Sched
,
288 const Dependences
&D
,
289 OptimizationRemarkEmitter
*ORE
) {
290 // Search the loop nest for transformations until fixpoint.
292 isl::schedule Result
=
293 SearchTransformVisitor::applyOneTransformation(S
, &D
, ORE
, Sched
);
294 if (Result
.is_null()) {
295 // No (more) transformation has been found.
299 // Use transformed schedule and look for more transformations.