22#include "llvm/ADT/STLExtras.h"
23#include "llvm/ADT/SmallVector.h"
24#include "llvm/ADT/Statistic.h"
25#include "llvm/Analysis/LoopInfo.h"
26#include "llvm/Analysis/ValueTracking.h"
27#include "llvm/IR/Instruction.h"
28#include "llvm/IR/Instructions.h"
29#include "llvm/IR/Value.h"
30#include "llvm/Support/Casting.h"
31#include "llvm/Support/CommandLine.h"
32#include "llvm/Support/Compiler.h"
33#include "llvm/Support/Debug.h"
34#include "llvm/Support/ErrorHandling.h"
35#include "llvm/Support/raw_ostream.h"
42#define DEBUG_TYPE "polly-optree"
49 cl::desc(
"Analyze array contents for load forwarding"),
54 cl::desc(
"Replace PHIs by their incoming values"),
57static cl::opt<unsigned>
59 cl::desc(
"Maximum number of ISL operations to invest for known "
60 "analysis; 0=no limit"),
65 cl::desc(
"Polly - Print forward operand tree result"),
68STATISTIC(KnownAnalyzed,
"Number of successfully analyzed SCoPs");
70 "Analyses aborted because max_operations was reached");
72STATISTIC(TotalInstructionsCopied,
"Number of copied instructions");
74 "Number of forwarded loads because their value was known");
75STATISTIC(TotalReloads,
"Number of reloaded values");
76STATISTIC(TotalReadOnlyCopied,
"Number of copied read-only accesses");
77STATISTIC(TotalForwardedTrees,
"Number of forwarded operand trees");
79 "Number of statements with at least one forwarded tree");
81STATISTIC(ScopsModified,
"Number of SCoPs with at least one forwarded tree");
83STATISTIC(NumValueWrites,
"Number of scalar value writes after OpTree");
85 "Number of scalar value writes nested in affine loops after OpTree");
86STATISTIC(NumPHIWrites,
"Number of scalar phi writes after OpTree");
88 "Number of scalar phi writes nested in affine loops after OpTree");
89STATISTIC(NumSingletonWrites,
"Number of singleton writes after OpTree");
91 "Number of singleton writes nested in affine loops after OpTree");
100enum ForwardingDecision {
127 FD_CanForwardProfitably,
137struct ForwardingAction {
138 using KeyTy = std::pair<Value *, ScopStmt *>;
141 ForwardingDecision Decision = FD_Unknown;
149 std::function<bool()> Execute = []() ->
bool {
150 llvm_unreachable(
"unspecified how to forward");
155 SmallVector<KeyTy, 4> Depends;
159 static ForwardingAction notApplicable() {
160 ForwardingAction Result;
161 Result.Decision = FD_NotApplicable;
166 static ForwardingAction cannotForward() {
167 ForwardingAction Result;
168 Result.Decision = FD_CannotForward;
173 static ForwardingAction triviallyForwardable(
bool IsProfitable,
Value *Val) {
174 ForwardingAction Result;
176 IsProfitable ? FD_CanForwardProfitably : FD_CanForwardLeaf;
177 Result.Execute = [=]() {
178 POLLY_DEBUG(dbgs() <<
" trivially forwarded: " << *Val <<
"\n");
185 static ForwardingAction canForward(std::function<
bool()> Execute,
186 ArrayRef<KeyTy> Depends,
188 ForwardingAction Result;
190 IsProfitable ? FD_CanForwardProfitably : FD_CanForwardLeaf;
191 Result.Execute = std::move(Execute);
192 Result.Depends.append(Depends.begin(), Depends.end());
206 using MemoizationTy = DenseMap<ForwardingAction::KeyTy, ForwardingAction>;
209 IslMaxOperationsGuard &MaxOpGuard;
212 int NumInstructionsCopied = 0;
215 int NumKnownLoadsForwarded = 0;
221 int NumReadOnlyCopied = 0;
224 int NumForwardedTrees = 0;
227 int NumModifiedStmts = 0;
230 bool Modified =
false;
242 MemoizationTy ForwardingActions;
248 isl::union_map Known;
253 isl::union_map Translator;
264 isl::union_map findSameContentElements(isl::union_map ValInst) {
271 isl::union_map Schedule = getScatterFor(
Domain);
274 isl::union_map MustKnownCurried =
281 isl::union_map SchedValDomVal =
285 isl::union_map MustKnownInst = MustKnownCurried.
apply_range(SchedValDomVal);
288 isl::union_map MustKnownMap =
309 isl::map singleLocation(isl::union_map MustKnown,
isl::set Domain) {
323 ScopArrayInfo *SAI =
static_cast<ScopArrayInfo *
>(ArrayId.
get_user());
331 if (!
Domain.is_subset(MapDom).is_true())
346 ForwardOpTreeImpl(Scop *
S, LoopInfo *LI, IslMaxOperationsGuard &MaxOpGuard)
347 : ZoneAlgorithm(
"polly-optree",
S, LI), MaxOpGuard(MaxOpGuard) {}
352 bool computeKnownValues() {
353 isl::union_map MustKnown, KnownFromLoad, KnownFromInit;
356 collectCompatibleElts();
359 IslQuotaScope QuotaScope = MaxOpGuard.enter();
363 computeNormalizedPHIs();
364 Known = computeKnown(
true,
true);
370 if (Known.is_null() || Translator.is_null() || NormalizeMap.is_null()) {
375 POLLY_DEBUG(dbgs() <<
"Known analysis exceeded max_operations\n");
380 POLLY_DEBUG(dbgs() <<
"All known: " << Known <<
"\n");
385 void printStatistics(raw_ostream &OS,
int Indent = 0) {
386 OS.indent(Indent) <<
"Statistics {\n";
387 OS.indent(Indent + 4) <<
"Instructions copied: " << NumInstructionsCopied
389 OS.indent(Indent + 4) <<
"Known loads forwarded: " << NumKnownLoadsForwarded
391 OS.indent(Indent + 4) <<
"Reloads: " << NumReloads <<
'\n';
392 OS.indent(Indent + 4) <<
"Read-only accesses copied: " << NumReadOnlyCopied
394 OS.indent(Indent + 4) <<
"Operand trees forwarded: " << NumForwardedTrees
396 OS.indent(Indent + 4) <<
"Statements with forwarded operand trees: "
397 << NumModifiedStmts <<
'\n';
398 OS.indent(Indent) <<
"}\n";
401 void printStatements(raw_ostream &OS,
int Indent = 0)
const {
402 OS.indent(Indent) <<
"After statements {\n";
403 for (
auto &Stmt : *
S) {
404 OS.indent(Indent + 4) << Stmt.getBaseName() <<
"\n";
405 for (
auto *MA : Stmt)
408 OS.indent(Indent + 12);
409 Stmt.printInstructions(OS);
411 OS.indent(Indent) <<
"}\n";
422 MemoryAccess *makeReadArrayAccess(ScopStmt *Stmt, LoadInst *LI,
423 isl::map AccessRelation) {
425 ScopArrayInfo *SAI =
reinterpret_cast<ScopArrayInfo *
>(ArrayId.
get_user());
428 SmallVector<const SCEV *, 4> Sizes;
430 SmallVector<const SCEV *, 4> Subscripts;
434 Subscripts.push_back(
nullptr);
437 MemoryAccess *Access =
439 LI->getType(),
true, {}, Sizes, LI, MemoryKind::Array);
440 S->addAccessFunction(Access);
461 ForwardingAction forwardKnownLoad(ScopStmt *TargetStmt, Instruction *Inst,
462 ScopStmt *UseStmt, Loop *UseLoop,
463 ScopStmt *DefStmt, Loop *DefLoop) {
465 if (Known.is_null() || Translator.is_null() ||
466 MaxOpGuard.hasQuotaExceeded())
467 return ForwardingAction::notApplicable();
469 LoadInst *LI = dyn_cast<LoadInst>(Inst);
471 return ForwardingAction::notApplicable();
473 ForwardingDecision OpDecision =
474 forwardTree(TargetStmt, LI->getPointerOperand(), DefStmt, DefLoop);
475 switch (OpDecision) {
476 case FD_CanForwardProfitably:
477 case FD_CanForwardLeaf:
479 case FD_CannotForward:
480 return ForwardingAction::cannotForward();
482 llvm_unreachable(
"Shouldn't return this");
495 auto ExecAction = [
this, TargetStmt, LI, Access]() ->
bool {
498 dbgs() <<
" forwarded known load with preexisting MemoryAccess"
502 NumKnownLoadsForwarded++;
503 TotalKnownLoadsForwarded++;
506 return ForwardingAction::canForward(
507 ExecAction, {{LI->getPointerOperand(), DefStmt}},
true);
513 IslQuotaScope QuotaScope = MaxOpGuard.enter();
516 isl::map ExpectedVal = makeValInst(Inst, UseStmt, UseLoop);
517 assert(!isNormalized(ExpectedVal).is_false() &&
518 "LoadInsts are always normalized");
521 isl::map UseToTarget = getDefToTarget(UseStmt, TargetStmt);
524 isl::map TargetExpectedVal = ExpectedVal.
apply_domain(UseToTarget);
525 isl::union_map TranslatedExpectedVal =
526 isl::union_map(TargetExpectedVal).
apply_range(Translator);
529 isl::union_map Candidates = findSameContentElements(TranslatedExpectedVal);
531 isl::map SameVal = singleLocation(Candidates, getDomainFor(TargetStmt));
533 return ForwardingAction::notApplicable();
535 POLLY_DEBUG(dbgs() <<
" expected values where " << TargetExpectedVal
537 POLLY_DEBUG(dbgs() <<
" candidate elements where " << Candidates
555 isl::map LocalTranslator;
564 isl::space ValSpace = ValInstSpace.
unwrap().
range();
571 isl::map DefToTarget = getDefToTarget(DefStmt, TargetStmt);
575 POLLY_DEBUG(dbgs() <<
" local translator is " << LocalTranslator
579 return ForwardingAction::notApplicable();
582 auto ExecAction = [
this, TargetStmt, LI, SameVal,
583 LocalTranslator]() ->
bool {
585 MemoryAccess *Access = makeReadArrayAccess(TargetStmt, LI, SameVal);
586 POLLY_DEBUG(dbgs() <<
" forwarded known load with new MemoryAccess"
590 if (!LocalTranslator.
is_null())
591 Translator = Translator.unite(LocalTranslator);
593 NumKnownLoadsForwarded++;
594 TotalKnownLoadsForwarded++;
597 return ForwardingAction::canForward(
598 ExecAction, {{LI->getPointerOperand(), DefStmt}},
true);
614 ForwardingAction reloadKnownContent(ScopStmt *TargetStmt, Instruction *Inst,
615 ScopStmt *UseStmt, Loop *UseLoop,
616 ScopStmt *DefStmt, Loop *DefLoop) {
618 if (Known.is_null() || Translator.is_null() ||
619 MaxOpGuard.hasQuotaExceeded())
620 return ForwardingAction::notApplicable();
623 IslQuotaScope QuotaScope = MaxOpGuard.enter();
626 isl::union_map ExpectedVal = makeNormalizedValInst(Inst, UseStmt, UseLoop);
629 isl::map UseToTarget = getDefToTarget(UseStmt, TargetStmt);
632 isl::union_map TargetExpectedVal = ExpectedVal.
apply_domain(UseToTarget);
633 isl::union_map TranslatedExpectedVal =
637 isl::union_map Candidates = findSameContentElements(TranslatedExpectedVal);
639 isl::map SameVal = singleLocation(Candidates, getDomainFor(TargetStmt));
642 return ForwardingAction::notApplicable();
644 auto ExecAction = [
this, TargetStmt, Inst, SameVal]() {
650 POLLY_DEBUG(dbgs() <<
" forwarded known content of " << *Inst
651 <<
" which is " << SameVal <<
"\n");
657 return ForwardingAction::canForward(ExecAction, {},
true);
670 ForwardingAction forwardSpeculatable(ScopStmt *TargetStmt,
671 Instruction *UseInst, ScopStmt *DefStmt,
674 if (isa<PHINode>(UseInst))
675 return ForwardingAction::notApplicable();
689 if (mayHaveNonDefUseDependency(*UseInst))
690 return ForwardingAction::notApplicable();
692 SmallVector<ForwardingAction::KeyTy, 4> Depends;
693 Depends.reserve(UseInst->getNumOperands());
694 for (
Value *OpVal : UseInst->operand_values()) {
695 ForwardingDecision OpDecision =
696 forwardTree(TargetStmt, OpVal, DefStmt, DefLoop);
697 switch (OpDecision) {
698 case FD_CannotForward:
699 return ForwardingAction::cannotForward();
701 case FD_CanForwardLeaf:
702 case FD_CanForwardProfitably:
703 Depends.emplace_back(OpVal, DefStmt);
706 case FD_NotApplicable:
709 "forwardTree should never return FD_NotApplicable/FD_Unknown");
713 auto ExecAction = [
this, TargetStmt, UseInst]() {
719 POLLY_DEBUG(dbgs() <<
" forwarded speculable instruction: " << *UseInst
721 NumInstructionsCopied++;
722 TotalInstructionsCopied++;
725 return ForwardingAction::canForward(ExecAction, Depends,
true);
740 ForwardingAction forwardTreeImpl(ScopStmt *TargetStmt,
Value *UseVal,
741 ScopStmt *UseStmt, Loop *UseLoop) {
742 ScopStmt *DefStmt =
nullptr;
743 Loop *DefLoop =
nullptr;
746 isl::map DefToTarget;
754 return ForwardingAction::triviallyForwardable(
false, UseVal);
770 return ForwardingAction::triviallyForwardable(
false, UseVal);
773 dbgs() <<
" Synthesizable would not be synthesizable anymore: "
775 return ForwardingAction::cannotForward();
780 return ForwardingAction::triviallyForwardable(
false, UseVal);
783 auto ExecAction = [
this, TargetStmt, UseVal]() {
786 POLLY_DEBUG(dbgs() <<
" forwarded read-only value " << *UseVal
789 TotalReadOnlyCopied++;
800 return ForwardingAction::canForward(ExecAction, {},
false);
810 Instruction *Inst = cast<Instruction>(UseVal);
813 DefStmt =
S->getStmtFor(Inst);
815 return ForwardingAction::cannotForward();
818 DefLoop = LI->getLoopFor(Inst->getParent());
820 ForwardingAction SpeculativeResult =
821 forwardSpeculatable(TargetStmt, Inst, DefStmt, DefLoop);
822 if (SpeculativeResult.Decision != FD_NotApplicable)
823 return SpeculativeResult;
825 ForwardingAction KnownResult = forwardKnownLoad(
826 TargetStmt, Inst, UseStmt, UseLoop, DefStmt, DefLoop);
827 if (KnownResult.Decision != FD_NotApplicable)
830 ForwardingAction ReloadResult = reloadKnownContent(
831 TargetStmt, Inst, UseStmt, UseLoop, DefStmt, DefLoop);
832 if (ReloadResult.Decision != FD_NotApplicable)
837 POLLY_DEBUG(dbgs() <<
" Cannot forward instruction: " << *Inst
839 return ForwardingAction::cannotForward();
842 llvm_unreachable(
"Case unhandled");
859 ForwardingDecision forwardTree(ScopStmt *TargetStmt,
Value *UseVal,
860 ScopStmt *UseStmt, Loop *UseLoop) {
862 auto It = ForwardingActions.find({UseVal, UseStmt});
863 if (It != ForwardingActions.end())
864 return It->second.Decision;
867 ForwardingAction Action =
868 forwardTreeImpl(TargetStmt, UseVal, UseStmt, UseLoop);
869 ForwardingDecision Result = Action.Decision;
872 assert(!ForwardingActions.count({UseVal, UseStmt}) &&
873 "circular dependency?");
874 ForwardingActions.insert({{UseVal, UseStmt}, std::move(Action)});
885 void applyForwardingActions(ScopStmt *Stmt,
Value *UseVal, MemoryAccess *RA) {
887 decltype(std::declval<ForwardingAction>().Depends.begin());
888 using EdgeTy = std::pair<ForwardingAction *, ChildItTy>;
890 DenseSet<ForwardingAction::KeyTy> Visited;
891 SmallVector<EdgeTy, 32> Stack;
892 SmallVector<ForwardingAction *, 32> Ordered;
895 assert(ForwardingActions.count({UseVal, Stmt}));
896 ForwardingAction *RootAction = &ForwardingActions[{UseVal, Stmt}];
897 Stack.emplace_back(RootAction, RootAction->Depends.begin());
909 while (!Stack.empty()) {
910 EdgeTy &Top = Stack.back();
911 ForwardingAction *TopAction = Top.first;
912 ChildItTy &TopEdge = Top.second;
914 if (TopEdge == TopAction->Depends.end()) {
916 Ordered.push_back(TopAction);
920 ForwardingAction::KeyTy Key = *TopEdge;
925 auto VisitIt = Visited.insert(Key);
929 assert(ForwardingActions.count(Key) &&
930 "Must not insert new actions during execution phase");
931 ForwardingAction *ChildAction = &ForwardingActions[Key];
932 Stack.emplace_back(ChildAction, ChildAction->Depends.begin());
938 assert(Ordered.back() == RootAction);
939 if (RootAction->Execute())
942 for (
auto DepAction : reverse(Ordered)) {
943 assert(DepAction->Decision != FD_Unknown &&
944 DepAction->Decision != FD_CannotForward);
945 assert(DepAction != RootAction);
946 DepAction->Execute();
951 bool tryForwardTree(MemoryAccess *RA) {
953 POLLY_DEBUG(dbgs() <<
"Trying to forward operand tree " << RA <<
"...\n");
958 isl::map TargetToUse;
959 if (!Known.is_null()) {
965 ForwardingDecision Assessment =
969 bool Changed =
false;
970 if (Assessment == FD_CanForwardProfitably) {
975 ForwardingActions.clear();
980 Scop *getScop()
const {
return S; }
984 bool forwardOperandTrees() {
985 for (ScopStmt &Stmt : *
S) {
986 bool StmtModified =
false;
990 SmallVector<MemoryAccess *, 16> Accs(Stmt.
begin(), Stmt.
end());
992 for (MemoryAccess *RA : Accs) {
998 if (tryForwardTree(RA)) {
1000 StmtModified =
true;
1001 NumForwardedTrees++;
1002 TotalForwardedTrees++;
1008 TotalModifiedStmts++;
1021 void print(raw_ostream &OS,
int Indent = 0) {
1022 printStatistics(OS, Indent);
1026 OS <<
"ForwardOpTree executed, but did not modify anything\n";
1030 printStatements(OS, Indent);
1033 bool isModified()
const {
return Modified; }
1036static std::unique_ptr<ForwardOpTreeImpl> runForwardOpTreeImpl(
Scop &
S,
1038 std::unique_ptr<ForwardOpTreeImpl> Impl;
1041 Impl = std::make_unique<ForwardOpTreeImpl>(&
S, &LI, MaxOpGuard);
1045 Impl->computeKnownValues();
1048 POLLY_DEBUG(dbgs() <<
"Forwarding operand trees...\n");
1049 Impl->forwardOperandTrees();
1052 POLLY_DEBUG(dbgs() <<
"Not all operations completed because of "
1053 "max_operations exceeded\n");
1075 LoopInfo &LI = *
S.getLI();
1077 std::unique_ptr<ForwardOpTreeImpl> Impl = runForwardOpTreeImpl(
S, LI);
1079 outs() <<
"Printing analysis 'Polly - Forward operand tree' for region: '"
1080 <<
S.getName() <<
"' in function '" <<
S.getFunction().getName()
1083 assert(Impl->getScop() == &
S);
1085 Impl->print(outs());
1089 return Impl->isModified();
static cl::opt< bool > NormalizePHIs("polly-optree-normalize-phi", cl::desc("Replace PHIs by their incoming values"), cl::cat(PollyCategory), cl::init(false), cl::Hidden)
static cl::opt< bool > AnalyzeKnown("polly-optree-analyze-known", cl::desc("Analyze array contents for load forwarding"), cl::cat(PollyCategory), cl::init(true), cl::Hidden)
static cl::opt< unsigned > MaxOps("polly-optree-max-ops", cl::desc("Maximum number of ISL operations to invest for known " "analysis; 0=no limit"), cl::init(1000000), cl::cat(PollyCategory), cl::Hidden)
static cl::opt< bool > PollyPrintOptree("polly-print-optree", cl::desc("Polly - Print forward operand tree result"), cl::cat(PollyCategory))
STATISTIC(KnownAnalyzed, "Number of successfully analyzed SCoPs")
llvm::cl::OptionCategory PollyCategory
isl::map product(isl::map map2) const
isl::id get_tuple_id(isl::dim type) const
isl::map apply_range(isl::map map2) const
isl::map apply_domain(isl::map map2) const
static isl::map identity(isl::space space)
isl::space get_space() const
isl::space map_from_domain_and_range(isl::space range) const
isl::space unwrap() const
boolean is_wrapping() const
isl::union_map range_map() const
isl::union_map reverse() const
isl::union_map uncurry() const
isl::union_set domain() const
isl::map_list get_map_list() const
isl::union_map apply_domain(isl::union_map umap2) const
isl::union_map apply_range(isl::union_map umap2) const
isl::union_map range_product(isl::union_map umap2) const
boolean is_single_valued() const
isl::union_map domain_map() const
isl::union_map unwrap() const
Scoped limit of ISL operations.
bool hasQuotaExceeded() const
Return whether the current quota has exceeded.
bool isLatestScalarKind() const
Whether this access is an array to a scalar memory object, also considering changes by setNewAccessRe...
bool isRead() const
Is this a read memory access?
ScopStmt * getStatement() const
Get the statement that contains this memory access.
Value * getAccessValue() const
Return the access value of this memory access.
void setNewAccessRelation(isl::map NewAccessRelation)
Set the updated access relation read from JSCOP file.
const SCEV * getDimensionSize(unsigned Dim) const
Return the size of dimension dim as SCEV*.
Value * getBasePtr() const
Return the base pointer.
unsigned getNumberOfDimensions() const
Return the number of dimensions.
const ScopArrayInfo * getBasePtrOriginSAI() const
For indirect accesses return the origin SAI of the BP, else null.
void removeSingleMemoryAccess(MemoryAccess *MA, bool AfterHoisting=true)
Remove MA from this statement.
MemoryAccess * ensureValueRead(Value *V)
Check whether there is a value read access for V in this statement, and if not, create one.
MemoryAccess * lookupInputAccessOf(Value *Val) const
Return the input access of the value, or null if no such MemoryAccess exists.
void prependInstruction(Instruction *Inst)
Insert an instruction before all other instructions in this statement.
MemoryAccess * getArrayAccessOrNULLFor(const Instruction *Inst) const
Return the only array access for Inst, if existing.
void addAccess(MemoryAccess *Access, bool Prepend=false)
Add Access to this statement's list of accesses.
Loop * getSurroundingLoop() const
Return the closest innermost loop that contains this statement, but is not contained in it.
isl::space getDomainSpace() const
Get the space of the iteration domain.
static VirtualUse create(Scop *S, const Use &U, LoopInfo *LI, bool Virtual)
Get a VirtualUse for an llvm::Use.
UseKind getKind() const
Return the type of use.
Base class for algorithms based on zones, like DeLICM.
enum isl_error isl_ctx_last_error(isl_ctx *ctx)
@ Value
MemoryKind::Value: Models an llvm::Value.
isl::map makeIdentityMap(const isl::set &Set, bool RestrictDomain)
Construct an identity map for the given domain values.
void simplify(isl::set &Set)
Simplify a set inplace.
bool ModelReadOnlyScalars
Command line switch whether to model read-only accesses.
bool runForwardOpTree(Scop &S)
Pass that redirects scalar reads to array elements that are known to contain the same value.
isl::union_set convertZoneToTimepoints(isl::union_set Zone, bool InclStart, bool InclEnd)
Convert a zone (range between timepoints) to timepoints.
int NumValueWritesInLoops
int NumSingletonWritesInLoops
static TupleKindPtr Domain("Domain")