1 //===- MaximalStaticExpansion.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 // This pass fully expand the memory accesses of a Scop to get rid of
12 //===----------------------------------------------------------------------===//
14 #include "polly/MaximalStaticExpansion.h"
15 #include "polly/DependenceInfo.h"
16 #include "polly/LinkAllPasses.h"
17 #include "polly/ScopInfo.h"
18 #include "polly/ScopPass.h"
19 #include "polly/Support/ISLTools.h"
20 #include "llvm/ADT/SmallPtrSet.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
23 #include "llvm/InitializePasses.h"
24 #include "isl/isl-noexceptions.h"
25 #include "isl/union_map.h"
32 using namespace polly
;
34 #define DEBUG_TYPE "polly-mse"
38 class MaximalStaticExpanderWrapperPass final
: public ScopPass
{
42 explicit MaximalStaticExpanderWrapperPass() : ScopPass(ID
) {}
44 ~MaximalStaticExpanderWrapperPass() override
= default;
46 /// Expand the accesses of the SCoP.
48 /// @param S The SCoP that must be expanded.
49 bool runOnScop(Scop
&S
) override
;
53 /// @param OS The stream where to print.
54 /// @param S The SCop that must be printed.
55 void printScop(raw_ostream
&OS
, Scop
&S
) const override
;
57 /// Register all analyses and transformations required.
58 void getAnalysisUsage(AnalysisUsage
&AU
) const override
;
62 /// Whether a dimension of a set is bounded (lower and upper) by a constant,
63 /// i.e. there are two constants Min and Max, such that every value x of the
64 /// chosen dimensions is Min <= x <= Max.
65 static bool isDimBoundedByConstant(isl::set Set
, unsigned dim
) {
66 auto ParamDims
= unsignedFromIslSize(Set
.dim(isl::dim::param
));
67 Set
= Set
.project_out(isl::dim::param
, 0, ParamDims
);
68 Set
= Set
.project_out(isl::dim::set
, 0, dim
);
69 auto SetDims
= unsignedFromIslSize(Set
.tuple_dim());
71 Set
= Set
.project_out(isl::dim::set
, 1, SetDims
- 1);
72 return bool(Set
.is_bounded());
76 class MaximalStaticExpansionImpl
{
77 OptimizationRemarkEmitter
&ORE
;
79 isl::union_map
&Dependences
;
82 void emitRemark(StringRef Msg
, Instruction
*Inst
) {
83 ORE
.emit(OptimizationRemarkAnalysis(DEBUG_TYPE
, "ExpansionRejection", Inst
)
87 /// Filter the dependences to have only one related to current memory access.
89 /// @param S The SCop in which the memory access appears in.
90 /// @param MapDependences The dependences to filter.
91 /// @param MA The memory access that need to be expanded.
92 isl::union_map
filterDependences(const isl::union_map
&Dependences
,
94 auto SAI
= MA
->getLatestScopArrayInfo();
96 auto AccessDomainSet
= MA
->getAccessRelation().domain();
97 auto AccessDomainId
= AccessDomainSet
.get_tuple_id();
99 isl::union_map MapDependences
= isl::union_map::empty(S
.getIslCtx());
101 for (isl::map Map
: Dependences
.get_map_list()) {
102 // Filter out Statement to Statement dependences.
103 if (!Map
.can_curry())
106 // Intersect with the relevant SAI.
107 auto TmpMapDomainId
=
108 Map
.get_space().domain().unwrap().range().get_tuple_id(isl::dim::set
);
110 ScopArrayInfo
*UserSAI
=
111 static_cast<ScopArrayInfo
*>(TmpMapDomainId
.get_user());
116 // Get the correct S1[] -> S2[] dependence.
117 auto NewMap
= Map
.factor_domain();
118 auto NewMapDomainId
= NewMap
.domain().get_tuple_id();
120 if (AccessDomainId
.get() != NewMapDomainId
.get())
123 // Add the corresponding map to MapDependences.
124 MapDependences
= MapDependences
.unite(NewMap
);
127 return MapDependences
;
130 /// Return true if the SAI in parameter is expandable.
132 /// @param SAI the SAI that need to be checked.
133 /// @param Writes A set that will contains all the write accesses.
134 /// @param Reads A set that will contains all the read accesses.
135 /// @param S The SCop in which the SAI is in.
136 /// @param Dependences The RAW dependences of the SCop.
137 bool isExpandable(const ScopArrayInfo
*SAI
,
138 SmallPtrSetImpl
<MemoryAccess
*> &Writes
,
139 SmallPtrSetImpl
<MemoryAccess
*> &Reads
, Scop
&S
) {
140 if (SAI
->isValueKind()) {
141 Writes
.insert(S
.getValueDef(SAI
));
142 for (auto MA
: S
.getValueUses(SAI
))
145 } else if (SAI
->isPHIKind()) {
146 auto Read
= S
.getPHIRead(SAI
);
148 auto StmtDomain
= isl::union_set(Read
->getStatement()->getDomain());
150 auto Writes
= S
.getPHIIncomings(SAI
);
152 // Get the domain where all the writes are writing to.
153 auto WriteDomain
= isl::union_set::empty(S
.getIslCtx());
155 for (auto Write
: Writes
) {
156 auto MapDeps
= filterDependences(Dependences
, Write
);
157 for (isl::map Map
: MapDeps
.get_map_list())
158 WriteDomain
= WriteDomain
.unite(Map
.range());
161 // For now, read from original scalar is not possible.
162 if (!StmtDomain
.is_equal(WriteDomain
)) {
163 emitRemark(SAI
->getName() + " read from its original value.",
164 Read
->getAccessInstruction());
169 } else if (SAI
->isExitPHIKind()) {
170 // For now, we are not able to expand ExitPhi.
171 emitRemark(SAI
->getName() + " is a ExitPhi node.",
172 S
.getEnteringBlock()->getFirstNonPHI());
176 int NumberWrites
= 0;
177 for (ScopStmt
&Stmt
: S
) {
178 auto StmtReads
= isl::union_map::empty(S
.getIslCtx());
179 auto StmtWrites
= isl::union_map::empty(S
.getIslCtx());
181 for (MemoryAccess
*MA
: Stmt
) {
182 // Check if the current MemoryAccess involved the current SAI.
183 if (SAI
!= MA
->getLatestScopArrayInfo())
186 // For now, we are not able to expand array where read come after write
187 // (to the same location) in a same statement.
188 auto AccRel
= isl::union_map(MA
->getAccessRelation());
190 // Reject load after store to same location.
191 if (!StmtWrites
.is_disjoint(AccRel
)) {
192 emitRemark(SAI
->getName() + " has read after write to the same "
193 "element in same statement. The "
194 "dependences found during analysis may "
195 "be wrong because Polly is not able to "
196 "handle such case for now.",
197 MA
->getAccessInstruction());
201 StmtReads
= StmtReads
.unite(AccRel
);
203 StmtWrites
= StmtWrites
.unite(AccRel
);
206 // For now, we are not able to expand MayWrite.
207 if (MA
->isMayWrite()) {
208 emitRemark(SAI
->getName() + " has a maywrite access.",
209 MA
->getAccessInstruction());
213 // For now, we are not able to expand SAI with more than one write.
214 if (MA
->isMustWrite()) {
217 if (NumberWrites
> 1) {
218 emitRemark(SAI
->getName() + " has more than 1 write access.",
219 MA
->getAccessInstruction());
224 // Check if it is possible to expand this read.
226 // Get the domain of the current ScopStmt.
227 auto StmtDomain
= Stmt
.getDomain();
229 // Get the domain of the future Read access.
230 auto ReadDomainSet
= MA
->getAccessRelation().domain();
231 auto ReadDomain
= isl::union_set(ReadDomainSet
);
233 // Get the dependences relevant for this MA
234 auto MapDependences
= filterDependences(Dependences
.reverse(), MA
);
235 unsigned NumberElementMap
= isl_union_map_n_map(MapDependences
.get());
237 if (NumberElementMap
== 0) {
238 emitRemark("The expansion of " + SAI
->getName() +
239 " would lead to a read from the original array.",
240 MA
->getAccessInstruction());
244 auto DepsDomain
= MapDependences
.domain();
246 // If there are multiple maps in the Deps, we cannot handle this case
248 if (NumberElementMap
!= 1) {
249 emitRemark(SAI
->getName() +
250 " has too many dependences to be handle for now.",
251 MA
->getAccessInstruction());
255 auto DepsDomainSet
= isl::set(DepsDomain
);
257 // For now, read from the original array is not possible.
258 if (!StmtDomain
.is_subset(DepsDomainSet
)) {
259 emitRemark("The expansion of " + SAI
->getName() +
260 " would lead to a read from the original array.",
261 MA
->getAccessInstruction());
270 // No need to expand SAI with no write.
271 if (NumberWrites
== 0) {
272 emitRemark(SAI
->getName() + " has 0 write access.",
273 S
.getEnteringBlock()->getFirstNonPHI());
280 /// Expand the MemoryAccess according to Dependences and already expanded
283 /// @param The SCop in which the memory access appears in.
284 /// @param The memory access that need to be expanded.
285 /// @param Dependences The RAW dependences of the SCop.
286 /// @param ExpandedSAI The expanded SAI created during write expansion.
287 /// @param Reverse if true, the Dependences union_map is reversed before
289 void mapAccess(SmallPtrSetImpl
<MemoryAccess
*> &Accesses
,
290 const isl::union_map
&Dependences
, ScopArrayInfo
*ExpandedSAI
,
292 for (auto MA
: Accesses
) {
293 // Get the current AM.
294 auto CurrentAccessMap
= MA
->getAccessRelation();
296 // Get RAW dependences for the current WA.
297 auto DomainSet
= MA
->getAccessRelation().domain();
298 auto Domain
= isl::union_set(DomainSet
);
300 // Get the dependences relevant for this MA.
301 isl::union_map MapDependences
=
302 filterDependences(Reverse
? Dependences
.reverse() : Dependences
, MA
);
304 // If no dependences, no need to modify anything.
305 if (MapDependences
.is_empty())
308 assert(isl_union_map_n_map(MapDependences
.get()) == 1 &&
309 "There are more than one RAW dependencies in the union map.");
310 auto NewAccessMap
= isl::map::from_union_map(MapDependences
);
312 auto Id
= ExpandedSAI
->getBasePtrId();
314 // Replace the out tuple id with the one of the access array.
315 NewAccessMap
= NewAccessMap
.set_tuple_id(isl::dim::out
, Id
);
317 // Set the new access relation.
318 MA
->setNewAccessRelation(NewAccessMap
);
322 /// Expand the MemoryAccess according to its domain.
324 /// @param S The SCop in which the memory access appears in.
325 /// @param MA The memory access that need to be expanded.
326 ScopArrayInfo
*expandAccess(MemoryAccess
*MA
) {
327 // Get the current AM.
328 auto CurrentAccessMap
= MA
->getAccessRelation();
330 unsigned in_dimensions
=
331 unsignedFromIslSize(CurrentAccessMap
.domain_tuple_dim());
333 // Get domain from the current AM.
334 auto Domain
= CurrentAccessMap
.domain();
336 // Create a new AM from the domain.
337 auto NewAccessMap
= isl::map::from_domain(Domain
);
339 // Add dimensions to the new AM according to the current in_dim.
340 NewAccessMap
= NewAccessMap
.add_dims(isl::dim::out
, in_dimensions
);
342 // Create the string representing the name of the new SAI.
343 // One new SAI for each statement so that each write go to a different
345 auto CurrentStmtDomain
= MA
->getStatement()->getDomain();
346 auto CurrentStmtName
= CurrentStmtDomain
.get_tuple_name();
347 auto CurrentOutId
= CurrentAccessMap
.get_tuple_id(isl::dim::out
);
348 std::string CurrentOutIdString
=
349 MA
->getScopArrayInfo()->getName() + "_" + CurrentStmtName
+ "_expanded";
351 // Set the tuple id for the out dimension.
352 NewAccessMap
= NewAccessMap
.set_tuple_id(isl::dim::out
, CurrentOutId
);
354 // Create the size vector.
355 std::vector
<unsigned> Sizes
;
356 for (unsigned i
= 0; i
< in_dimensions
; i
++) {
357 assert(isDimBoundedByConstant(CurrentStmtDomain
, i
) &&
358 "Domain boundary are not constant.");
359 auto UpperBound
= getConstant(CurrentStmtDomain
.dim_max(i
), true, false);
360 assert(!UpperBound
.is_null() && UpperBound
.is_pos() &&
361 !UpperBound
.is_nan() &&
362 "The upper bound is not a positive integer.");
363 assert(UpperBound
.le(isl::val(CurrentAccessMap
.ctx(),
364 std::numeric_limits
<int>::max() - 1)) &&
365 "The upper bound overflow a int.");
366 Sizes
.push_back(UpperBound
.get_num_si() + 1);
369 // Get the ElementType of the current SAI.
370 auto ElementType
= MA
->getLatestScopArrayInfo()->getElementType();
372 // Create (or get if already existing) the new expanded SAI.
374 S
.createScopArrayInfo(ElementType
, CurrentOutIdString
, Sizes
);
375 ExpandedSAI
->setIsOnHeap(true);
377 // Get the out Id of the expanded Array.
378 auto NewOutId
= ExpandedSAI
->getBasePtrId();
380 // Set the out id of the new AM to the new SAI id.
381 NewAccessMap
= NewAccessMap
.set_tuple_id(isl::dim::out
, NewOutId
);
383 // Add constraints to linked output with input id.
384 auto SpaceMap
= NewAccessMap
.get_space();
385 auto ConstraintBasicMap
= isl::basic_map::equal(
386 SpaceMap
, unsignedFromIslSize(SpaceMap
.dim(isl::dim::in
)));
387 NewAccessMap
= isl::map(ConstraintBasicMap
);
389 // Set the new access relation map.
390 MA
->setNewAccessRelation(NewAccessMap
);
395 /// Expand PHI memory accesses.
397 /// @param The SCop in which the memory access appears in.
398 /// @param The ScopArrayInfo representing the PHI accesses to expand.
399 /// @param Dependences The RAW dependences of the SCop.
400 void expandPhi(Scop
&S
, const ScopArrayInfo
*SAI
,
401 const isl::union_map
&Dependences
) {
402 SmallPtrSet
<MemoryAccess
*, 4> Writes
;
403 for (auto MA
: S
.getPHIIncomings(SAI
))
405 auto Read
= S
.getPHIRead(SAI
);
406 auto ExpandedSAI
= expandAccess(Read
);
408 mapAccess(Writes
, Dependences
, ExpandedSAI
, false);
412 MaximalStaticExpansionImpl(Scop
&S
, isl::union_map
&Dependences
,
413 OptimizationRemarkEmitter
&ORE
)
414 : ORE(ORE
), S(S
), Dependences(Dependences
) {}
416 /// Expand the accesses of the SCoP
418 /// @param S The SCoP that must be expanded
419 /// @param D The dependencies information of SCoP
421 SmallVector
<ScopArrayInfo
*, 4> CurrentSAI(S
.arrays().begin(),
423 for (auto SAI
: CurrentSAI
) {
424 SmallPtrSet
<MemoryAccess
*, 4> AllWrites
;
425 SmallPtrSet
<MemoryAccess
*, 4> AllReads
;
426 if (!isExpandable(SAI
, AllWrites
, AllReads
, S
))
429 if (SAI
->isValueKind() || SAI
->isArrayKind()) {
430 assert(AllWrites
.size() == 1 || SAI
->isValueKind());
432 auto TheWrite
= *(AllWrites
.begin());
433 ScopArrayInfo
*ExpandedArray
= expandAccess(TheWrite
);
435 mapAccess(AllReads
, Dependences
, ExpandedArray
, true);
436 } else if (SAI
->isPHIKind()) {
437 expandPhi(S
, SAI
, Dependences
);
442 /// Dump the internal information about a performed MSE to @p OS.
443 void print(llvm::raw_ostream
&OS
) {
444 OS
<< "After arrays {\n";
446 for (auto &Array
: S
.arrays())
451 OS
<< "After accesses {\n";
452 for (auto &Stmt
: S
) {
453 OS
.indent(4) << Stmt
.getBaseName() << "{\n";
454 for (auto *MA
: Stmt
)
456 OS
.indent(4) << "}\n";
462 static std::unique_ptr
<MaximalStaticExpansionImpl
>
463 runMaximalStaticExpansion(Scop
&S
, OptimizationRemarkEmitter
&ORE
,
464 const Dependences
&D
) {
465 auto Dependences
= D
.getDependences(Dependences::TYPE_RAW
);
467 std::unique_ptr
<MaximalStaticExpansionImpl
> Impl
=
468 std::make_unique
<MaximalStaticExpansionImpl
>(S
, Dependences
, ORE
);
474 static PreservedAnalyses
runMSEUsingNPM(Scop
&S
, ScopAnalysisManager
&SAM
,
475 ScopStandardAnalysisResults
&SAR
,
477 OptimizationRemarkEmitter
ORE(&S
.getFunction());
479 auto &DI
= SAM
.getResult
<DependenceAnalysis
>(S
, SAR
);
480 auto &D
= DI
.getDependences(Dependences::AL_Reference
);
482 std::unique_ptr
<MaximalStaticExpansionImpl
> Impl
=
483 runMaximalStaticExpansion(S
, ORE
, D
);
486 *OS
<< "Printing analysis 'Polly - Maximal static expansion of SCoP' for "
488 << S
.getName() << "' in function '" << S
.getFunction().getName()
492 *OS
<< "MSE result:\n";
497 return PreservedAnalyses::all();
503 MaximalStaticExpansionPass::run(Scop
&S
, ScopAnalysisManager
&SAM
,
504 ScopStandardAnalysisResults
&SAR
,
506 return runMSEUsingNPM(S
, SAM
, SAR
, nullptr);
510 MaximalStaticExpansionPrinterPass::run(Scop
&S
, ScopAnalysisManager
&SAM
,
511 ScopStandardAnalysisResults
&SAR
,
513 return runMSEUsingNPM(S
, SAM
, SAR
, &OS
);
516 char MaximalStaticExpanderWrapperPass::ID
= 0;
518 bool MaximalStaticExpanderWrapperPass::runOnScop(Scop
&S
) {
519 // Get the ORE from OptimizationRemarkEmitterWrapperPass.
520 OptimizationRemarkEmitter
*ORE
=
521 &getAnalysis
<OptimizationRemarkEmitterWrapperPass
>().getORE();
523 // Get the RAW Dependences.
524 auto &DI
= getAnalysis
<DependenceInfo
>();
525 auto &D
= DI
.getDependences(Dependences::AL_Reference
);
527 std::unique_ptr
<MaximalStaticExpansionImpl
> Impl
=
528 runMaximalStaticExpansion(S
, *ORE
, D
);
533 void MaximalStaticExpanderWrapperPass::printScop(raw_ostream
&OS
,
538 void MaximalStaticExpanderWrapperPass::getAnalysisUsage(
539 AnalysisUsage
&AU
) const {
540 ScopPass::getAnalysisUsage(AU
);
541 AU
.addRequired
<DependenceInfo
>();
542 AU
.addRequired
<OptimizationRemarkEmitterWrapperPass
>();
545 Pass
*polly::createMaximalStaticExpansionPass() {
546 return new MaximalStaticExpanderWrapperPass();
549 INITIALIZE_PASS_BEGIN(MaximalStaticExpanderWrapperPass
, "polly-mse",
550 "Polly - Maximal static expansion of SCoP", false, false);
551 INITIALIZE_PASS_DEPENDENCY(DependenceInfo
);
552 INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass
);
553 INITIALIZE_PASS_END(MaximalStaticExpanderWrapperPass
, "polly-mse",
554 "Polly - Maximal static expansion of SCoP", false, false)