[polly] deb00cf - [Polly][NewPM] Port Simplify to the new pass manager
Pengxuan Zheng via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 20 19:18:22 PDT 2020
Author: Pengxuan Zheng
Date: 2020-09-20T19:18:01-07:00
New Revision: deb00cf0b5abd45e160f1e0ff3e3dcce97b071e5
URL: https://github.com/llvm/llvm-project/commit/deb00cf0b5abd45e160f1e0ff3e3dcce97b071e5
DIFF: https://github.com/llvm/llvm-project/commit/deb00cf0b5abd45e160f1e0ff3e3dcce97b071e5.diff
LOG: [Polly][NewPM] Port Simplify to the new pass manager
Reviewed By: Meinersbur
Differential Revision: https://reviews.llvm.org/D87328
Added:
Modified:
polly/include/polly/Simplify.h
polly/lib/Support/PollyPasses.def
polly/lib/Support/RegisterPasses.cpp
polly/lib/Transform/Simplify.cpp
polly/test/Simplify/dead_access_load.ll
polly/test/Simplify/dead_access_phi.ll
polly/test/Simplify/dead_access_value.ll
polly/test/Simplify/dead_instruction.ll
polly/test/Simplify/notdead_region_exitphi.ll
polly/test/Simplify/notdead_region_innerphi.ll
polly/test/Simplify/notredundant_region_middle.ll
polly/test/Simplify/notredundant_synthesizable_unknownit.ll
polly/test/Simplify/overwritten.ll
polly/test/Simplify/overwritten_3store.ll
polly/test/Simplify/overwritten_loadbetween.ll
polly/test/Simplify/pass_existence.ll
polly/test/Simplify/phi_in_regionstmt.ll
polly/test/Simplify/redundant.ll
polly/test/Simplify/redundant_differentindex.ll
polly/test/Simplify/redundant_storebetween.ll
Removed:
################################################################################
diff --git a/polly/include/polly/Simplify.h b/polly/include/polly/Simplify.h
index fb6a0152cdc0..aa8c8a2cd2e3 100644
--- a/polly/include/polly/Simplify.h
+++ b/polly/include/polly/Simplify.h
@@ -13,13 +13,109 @@
#ifndef POLLY_TRANSFORM_SIMPLIFY_H
#define POLLY_TRANSFORM_SIMPLIFY_H
+#include "polly/ScopPass.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/PassManager.h"
namespace llvm {
class PassRegistry;
class Pass;
} // namespace llvm
+namespace {
+class SimplifyVisitor {
+private:
+ /// The invocation id (if there are multiple instances in the pass manager's
+ /// pipeline) to determine which statistics to update.
+ int CallNo;
+
+ /// The last/current SCoP that is/has been processed.
+ Scop *S;
+
+ /// Number of statements with empty domains removed from the SCoP.
+ int EmptyDomainsRemoved = 0;
+
+ /// Number of writes that are overwritten anyway.
+ int OverwritesRemoved = 0;
+
+ /// Number of combined writes.
+ int WritesCoalesced = 0;
+
+ /// Number of redundant writes removed from this SCoP.
+ int RedundantWritesRemoved = 0;
+
+ /// Number of writes with empty access domain removed.
+ int EmptyPartialAccessesRemoved = 0;
+
+ /// Number of unused accesses removed from this SCoP.
+ int DeadAccessesRemoved = 0;
+
+ /// Number of unused instructions removed from this SCoP.
+ int DeadInstructionsRemoved = 0;
+
+ /// Number of unnecessary statements removed from the SCoP.
+ int StmtsRemoved = 0;
+
+ /// Return whether at least one simplification has been applied.
+ bool isModified() const;
+
+ /// Remove statements that are never executed due to their domains being
+ /// empty.
+ ///
+ /// In contrast to Scop::simplifySCoP, this removes based on the SCoP's
+ /// effective domain, i.e. including the SCoP's context as used by some other
+ /// simplification methods in this pass. This is necessary because the
+ /// analysis on empty domains is unreliable, e.g. remove a scalar value
+ /// definition MemoryAccesses, but not its use.
+ void removeEmptyDomainStmts();
+
+ /// Remove writes that are overwritten unconditionally later in the same
+ /// statement.
+ ///
+ /// There must be no read of the same value between the write (that is to be
+ /// removed) and the overwrite.
+ void removeOverwrites();
+
+ /// Combine writes that write the same value if possible.
+ ///
+ /// This function is able to combine:
+ /// - Partial writes with disjoint domain.
+ /// - Writes that write to the same array element.
+ ///
+ /// In all cases, both writes must write the same values.
+ void coalesceWrites();
+
+ /// Remove writes that just write the same value already stored in the
+ /// element.
+ void removeRedundantWrites();
+
+ /// Remove statements without side effects.
+ void removeUnnecessaryStmts();
+
+ /// Remove accesses that have an empty domain.
+ void removeEmptyPartialAccesses();
+
+ /// Mark all reachable instructions and access, and sweep those that are not
+ /// reachable.
+ void markAndSweep(LoopInfo *LI);
+
+ /// Print simplification statistics to @p OS.
+ void printStatistics(llvm::raw_ostream &OS, int Indent = 0) const;
+
+ /// Print the current state of all MemoryAccesses to @p OS.
+ void printAccesses(llvm::raw_ostream &OS, int Indent = 0) const;
+
+public:
+ explicit SimplifyVisitor(int CallNo = 0) : CallNo(CallNo) {}
+
+ bool visit(Scop &S, LoopInfo *LI);
+
+ void printScop(raw_ostream &OS, Scop &S) const;
+
+ void releaseMemory();
+};
+} // namespace
+
namespace polly {
class MemoryAccess;
@@ -50,10 +146,29 @@ llvm::SmallVector<MemoryAccess *, 32> getAccessesInOrder(ScopStmt &Stmt);
///
/// @return The Simplify pass.
llvm::Pass *createSimplifyPass(int CallNo = 0);
+
+struct SimplifyPass : public PassInfoMixin<SimplifyPass> {
+ SimplifyPass(int CallNo = 0) : Imp(CallNo) {}
+
+ llvm::PreservedAnalyses run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &AR, SPMUpdater &U);
+
+ SimplifyVisitor Imp;
+};
+
+struct SimplifyPrinterPass : public PassInfoMixin<SimplifyPrinterPass> {
+ SimplifyPrinterPass(raw_ostream &OS, int CallNo = 0) : OS(OS), Imp(CallNo) {}
+
+ PreservedAnalyses run(Scop &S, ScopAnalysisManager &,
+ ScopStandardAnalysisResults &, SPMUpdater &);
+
+ raw_ostream &OS;
+ SimplifyVisitor Imp;
+};
} // namespace polly
namespace llvm {
-void initializeSimplifyPass(llvm::PassRegistry &);
+void initializeSimplifyLegacyPassPass(llvm::PassRegistry &);
} // namespace llvm
#endif /* POLLY_TRANSFORM_SIMPLIFY_H */
diff --git a/polly/lib/Support/PollyPasses.def b/polly/lib/Support/PollyPasses.def
index b07f928639c1..2c0592af28cc 100644
--- a/polly/lib/Support/PollyPasses.def
+++ b/polly/lib/Support/PollyPasses.def
@@ -28,4 +28,6 @@ SCOP_PASS("polly-import-jscop", JSONImportPass())
SCOP_PASS("print<polly-ast>", IslAstPrinterPass(outs()))
SCOP_PASS("print<polly-dependences>", DependenceInfoPrinterPass(outs()))
SCOP_PASS("polly-codegen", CodeGenerationPass())
+SCOP_PASS("polly-simplify", SimplifyPass())
+SCOP_PASS("print<polly-simplify>", SimplifyPrinterPass(outs()))
#undef SCOP_PASS
diff --git a/polly/lib/Support/RegisterPasses.cpp b/polly/lib/Support/RegisterPasses.cpp
index 4ceca070b37f..ad6edb5807cc 100644
--- a/polly/lib/Support/RegisterPasses.cpp
+++ b/polly/lib/Support/RegisterPasses.cpp
@@ -284,7 +284,7 @@ void initializePollyPasses(PassRegistry &Registry) {
initializeFlattenSchedulePass(Registry);
initializeForwardOpTreePass(Registry);
initializeDeLICMPass(Registry);
- initializeSimplifyPass(Registry);
+ initializeSimplifyLegacyPassPass(Registry);
initializeDumpModulePass(Registry);
initializePruneUnprofitablePass(Registry);
}
diff --git a/polly/lib/Transform/Simplify.cpp b/polly/lib/Transform/Simplify.cpp
index d699aa4f4990..bad17ce92c45 100644
--- a/polly/lib/Transform/Simplify.cpp
+++ b/polly/lib/Transform/Simplify.cpp
@@ -117,538 +117,573 @@ static isl::union_map underapproximatedAddMap(isl::union_map UMap,
return UResult;
}
-class Simplify : public ScopPass {
-private:
- /// The invocation id (if there are multiple instances in the pass manager's
- /// pipeline) to determine which statistics to update.
- int CallNo;
-
- /// The last/current SCoP that is/has been processed.
- Scop *S;
+/// Return whether at least one simplification has been applied.
+bool SimplifyVisitor::isModified() const {
+ return EmptyDomainsRemoved > 0 || OverwritesRemoved > 0 ||
+ WritesCoalesced > 0 || RedundantWritesRemoved > 0 ||
+ EmptyPartialAccessesRemoved > 0 || DeadAccessesRemoved > 0 ||
+ DeadInstructionsRemoved > 0 || StmtsRemoved > 0;
+}
- /// Number of statements with empty domains removed from the SCoP.
- int EmptyDomainsRemoved = 0;
+/// Remove statements that are never executed due to their domains being
+/// empty.
+///
+/// In contrast to Scop::simplifySCoP, this removes based on the SCoP's
+/// effective domain, i.e. including the SCoP's context as used by some other
+/// simplification methods in this pass. This is necessary because the
+/// analysis on empty domains is unreliable, e.g. remove a scalar value
+/// definition MemoryAccesses, but not its use.
+void SimplifyVisitor::removeEmptyDomainStmts() {
+ size_t NumStmtsBefore = S->getSize();
+
+ S->removeStmts([](ScopStmt &Stmt) -> bool {
+ auto EffectiveDomain =
+ Stmt.getDomain().intersect_params(Stmt.getParent()->getContext());
+ return EffectiveDomain.is_empty();
+ });
+
+ assert(NumStmtsBefore >= S->getSize());
+ EmptyDomainsRemoved = NumStmtsBefore - S->getSize();
+ LLVM_DEBUG(dbgs() << "Removed " << EmptyDomainsRemoved << " (of "
+ << NumStmtsBefore << ") statements with empty domains \n");
+ TotalEmptyDomainsRemoved[CallNo] += EmptyDomainsRemoved;
+}
- /// Number of writes that are overwritten anyway.
- int OverwritesRemoved = 0;
+/// Remove writes that are overwritten unconditionally later in the same
+/// statement.
+///
+/// There must be no read of the same value between the write (that is to be
+/// removed) and the overwrite.
+void SimplifyVisitor::removeOverwrites() {
+ for (auto &Stmt : *S) {
+ isl::set Domain = Stmt.getDomain();
+ isl::union_map WillBeOverwritten =
+ isl::union_map::empty(S->getParamSpace());
+
+ SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
+
+ // Iterate in reverse order, so the overwrite comes before the write that
+ // is to be removed.
+ for (auto *MA : reverse(Accesses)) {
+
+ // In region statements, the explicit accesses can be in blocks that are
+ // can be executed in any order. We therefore process only the implicit
+ // writes and stop after that.
+ if (Stmt.isRegionStmt() && isExplicitAccess(MA))
+ break;
+
+ auto AccRel = MA->getAccessRelation();
+ AccRel = AccRel.intersect_domain(Domain);
+ AccRel = AccRel.intersect_params(S->getContext());
+
+ // If a value is read in-between, do not consider it as overwritten.
+ if (MA->isRead()) {
+ // Invalidate all overwrites for the array it accesses to avoid too
+ // complex isl sets.
+ isl::map AccRelUniv = isl::map::universe(AccRel.get_space());
+ WillBeOverwritten = WillBeOverwritten.subtract(AccRelUniv);
+ continue;
+ }
- /// Number of combined writes.
- int WritesCoalesced = 0;
+ // If all of a write's elements are overwritten, remove it.
+ isl::union_map AccRelUnion = AccRel;
+ if (AccRelUnion.is_subset(WillBeOverwritten)) {
+ LLVM_DEBUG(dbgs() << "Removing " << MA
+ << " which will be overwritten anyway\n");
- /// Number of redundant writes removed from this SCoP.
- int RedundantWritesRemoved = 0;
+ Stmt.removeSingleMemoryAccess(MA);
+ OverwritesRemoved++;
+ TotalOverwritesRemoved[CallNo]++;
+ }
- /// Number of writes with empty access domain removed.
- int EmptyPartialAccessesRemoved = 0;
+ // Unconditional writes overwrite other values.
+ if (MA->isMustWrite()) {
+ // Avoid too complex isl sets. If necessary, throw away some of the
+ // knowledge.
+ WillBeOverwritten = underapproximatedAddMap(WillBeOverwritten, AccRel);
+ }
+ }
+ }
+}
- /// Number of unused accesses removed from this SCoP.
- int DeadAccessesRemoved = 0;
+/// Combine writes that write the same value if possible.
+///
+/// This function is able to combine:
+/// - Partial writes with disjoint domain.
+/// - Writes that write to the same array element.
+///
+/// In all cases, both writes must write the same values.
+void SimplifyVisitor::coalesceWrites() {
+ for (auto &Stmt : *S) {
+ isl::set Domain = Stmt.getDomain().intersect_params(S->getContext());
+
+ // We let isl do the lookup for the same-value condition. For this, we
+ // wrap llvm::Value into an isl::set such that isl can do the lookup in
+ // its hashtable implementation. llvm::Values are only compared within a
+ // ScopStmt, so the map can be local to this scope. TODO: Refactor with
+ // ZoneAlgorithm::makeValueSet()
+ SmallDenseMap<Value *, isl::set> ValueSets;
+ auto makeValueSet = [&ValueSets, this](Value *V) -> isl::set {
+ assert(V);
+ isl::set &Result = ValueSets[V];
+ if (Result.is_null()) {
+ isl::ctx Ctx = S->getIslCtx();
+ std::string Name = getIslCompatibleName(
+ "Val", V, ValueSets.size() - 1, std::string(), UseInstructionNames);
+ isl::id Id = isl::id::alloc(Ctx, Name, V);
+ Result = isl::set::universe(
+ isl::space(Ctx, 0, 0).set_tuple_id(isl::dim::set, Id));
+ }
+ return Result;
+ };
+
+ // List of all eligible (for coalescing) writes of the future.
+ // { [Domain[] -> Element[]] -> [Value[] -> MemoryAccess[]] }
+ isl::union_map FutureWrites = isl::union_map::empty(S->getParamSpace());
+
+ // Iterate over accesses from the last to the first.
+ SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
+ for (MemoryAccess *MA : reverse(Accesses)) {
+ // In region statements, the explicit accesses can be in blocks that can
+ // be executed in any order. We therefore process only the implicit
+ // writes and stop after that.
+ if (Stmt.isRegionStmt() && isExplicitAccess(MA))
+ break;
+
+ // { Domain[] -> Element[] }
+ isl::map AccRel = MA->getLatestAccessRelation().intersect_domain(Domain);
+
+ // { [Domain[] -> Element[]] }
+ isl::set AccRelWrapped = AccRel.wrap();
+
+ // { Value[] }
+ isl::set ValSet;
+
+ if (MA->isMustWrite() && (MA->isOriginalScalarKind() ||
+ isa<StoreInst>(MA->getAccessInstruction()))) {
+ // Normally, tryGetValueStored() should be used to determine which
+ // element is written, but it can return nullptr; For PHI accesses,
+ // getAccessValue() returns the PHI instead of the PHI's incoming
+ // value. In this case, where we only compare values of a single
+ // statement, this is fine, because within a statement, a PHI in a
+ // successor block has always the same value as the incoming write. We
+ // still preferably use the incoming value directly so we also catch
+ // direct uses of that.
+ Value *StoredVal = MA->tryGetValueStored();
+ if (!StoredVal)
+ StoredVal = MA->getAccessValue();
+ ValSet = makeValueSet(StoredVal);
+
+ // { Domain[] }
+ isl::set AccDomain = AccRel.domain();
+
+ // Parts of the statement's domain that is not written by this access.
+ isl::set UndefDomain = Domain.subtract(AccDomain);
+
+ // { Element[] }
+ isl::set ElementUniverse =
+ isl::set::universe(AccRel.get_space().range());
- /// Number of unused instructions removed from this SCoP.
- int DeadInstructionsRemoved = 0;
+ // { Domain[] -> Element[] }
+ isl::map UndefAnything =
+ isl::map::from_domain_and_range(UndefDomain, ElementUniverse);
+
+ // We are looking a compatible write access. The other write can
+ // access these elements...
+ isl::map AllowedAccesses = AccRel.unite(UndefAnything);
+
+ // ... and must write the same value.
+ // { [Domain[] -> Element[]] -> Value[] }
+ isl::map Filter =
+ isl::map::from_domain_and_range(AllowedAccesses.wrap(), ValSet);
+
+ // Lookup future write that fulfills these conditions.
+ // { [[Domain[] -> Element[]] -> Value[]] -> MemoryAccess[] }
+ isl::union_map Filtered =
+ FutureWrites.uncurry().intersect_domain(Filter.wrap());
+
+ // Iterate through the candidates.
+ for (isl::map Map : Filtered.get_map_list()) {
+ MemoryAccess *OtherMA = (MemoryAccess *)Map.get_space()
+ .get_tuple_id(isl::dim::out)
+ .get_user();
+
+ isl::map OtherAccRel =
+ OtherMA->getLatestAccessRelation().intersect_domain(Domain);
+
+ // The filter only guaranteed that some of OtherMA's accessed
+ // elements are allowed. Verify that it only accesses allowed
+ // elements. Otherwise, continue with the next candidate.
+ if (!OtherAccRel.is_subset(AllowedAccesses).is_true())
+ continue;
+
+ // The combined access relation.
+ // { Domain[] -> Element[] }
+ isl::map NewAccRel = AccRel.unite(OtherAccRel);
+ simplify(NewAccRel);
- /// Number of unnecessary statements removed from the SCoP.
- int StmtsRemoved = 0;
+ // Carry out the coalescing.
+ Stmt.removeSingleMemoryAccess(MA);
+ OtherMA->setNewAccessRelation(NewAccRel);
- /// Return whether at least one simplification has been applied.
- bool isModified() const {
- return EmptyDomainsRemoved > 0 || OverwritesRemoved > 0 ||
- WritesCoalesced > 0 || RedundantWritesRemoved > 0 ||
- EmptyPartialAccessesRemoved > 0 || DeadAccessesRemoved > 0 ||
- DeadInstructionsRemoved > 0 || StmtsRemoved > 0;
- }
+ // We removed MA, OtherMA takes its role.
+ MA = OtherMA;
- /// Remove statements that are never executed due to their domains being
- /// empty.
- ///
- /// In contrast to Scop::simplifySCoP, this removes based on the SCoP's
- /// effective domain, i.e. including the SCoP's context as used by some other
- /// simplification methods in this pass. This is necessary because the
- /// analysis on empty domains is unreliable, e.g. remove a scalar value
- /// definition MemoryAccesses, but not its use.
- void removeEmptyDomainStmts() {
- size_t NumStmtsBefore = S->getSize();
-
- S->removeStmts([](ScopStmt &Stmt) -> bool {
- auto EffectiveDomain =
- Stmt.getDomain().intersect_params(Stmt.getParent()->getContext());
- return EffectiveDomain.is_empty();
- });
-
- assert(NumStmtsBefore >= S->getSize());
- EmptyDomainsRemoved = NumStmtsBefore - S->getSize();
- LLVM_DEBUG(dbgs() << "Removed " << EmptyDomainsRemoved << " (of "
- << NumStmtsBefore
- << ") statements with empty domains \n");
- TotalEmptyDomainsRemoved[CallNo] += EmptyDomainsRemoved;
- }
+ TotalWritesCoalesced[CallNo]++;
+ WritesCoalesced++;
- /// Remove writes that are overwritten unconditionally later in the same
- /// statement.
- ///
- /// There must be no read of the same value between the write (that is to be
- /// removed) and the overwrite.
- void removeOverwrites() {
- for (auto &Stmt : *S) {
- isl::set Domain = Stmt.getDomain();
- isl::union_map WillBeOverwritten =
- isl::union_map::empty(S->getParamSpace());
-
- SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
-
- // Iterate in reverse order, so the overwrite comes before the write that
- // is to be removed.
- for (auto *MA : reverse(Accesses)) {
-
- // In region statements, the explicit accesses can be in blocks that are
- // can be executed in any order. We therefore process only the implicit
- // writes and stop after that.
- if (Stmt.isRegionStmt() && isExplicitAccess(MA))
+ // Don't look for more candidates.
break;
-
- auto AccRel = MA->getAccessRelation();
- AccRel = AccRel.intersect_domain(Domain);
- AccRel = AccRel.intersect_params(S->getContext());
-
- // If a value is read in-between, do not consider it as overwritten.
- if (MA->isRead()) {
- // Invalidate all overwrites for the array it accesses to avoid too
- // complex isl sets.
- isl::map AccRelUniv = isl::map::universe(AccRel.get_space());
- WillBeOverwritten = WillBeOverwritten.subtract(AccRelUniv);
- continue;
}
+ }
- // If all of a write's elements are overwritten, remove it.
- isl::union_map AccRelUnion = AccRel;
- if (AccRelUnion.is_subset(WillBeOverwritten)) {
- LLVM_DEBUG(dbgs() << "Removing " << MA
- << " which will be overwritten anyway\n");
+ // Two writes cannot be coalesced if there is another access (to some of
+ // the written elements) between them. Remove all visited write accesses
+ // from the list of eligible writes. Don't just remove the accessed
+ // elements, but any MemoryAccess that touches any of the invalidated
+ // elements.
+ SmallPtrSet<MemoryAccess *, 2> TouchedAccesses;
+ for (isl::map Map :
+ FutureWrites.intersect_domain(AccRelWrapped).get_map_list()) {
+ MemoryAccess *MA = (MemoryAccess *)Map.get_space()
+ .range()
+ .unwrap()
+ .get_tuple_id(isl::dim::out)
+ .get_user();
+ TouchedAccesses.insert(MA);
+ }
+ isl::union_map NewFutureWrites =
+ isl::union_map::empty(FutureWrites.get_space());
+ for (isl::map FutureWrite : FutureWrites.get_map_list()) {
+ MemoryAccess *MA = (MemoryAccess *)FutureWrite.get_space()
+ .range()
+ .unwrap()
+ .get_tuple_id(isl::dim::out)
+ .get_user();
+ if (!TouchedAccesses.count(MA))
+ NewFutureWrites = NewFutureWrites.add_map(FutureWrite);
+ }
+ FutureWrites = NewFutureWrites;
- Stmt.removeSingleMemoryAccess(MA);
- OverwritesRemoved++;
- TotalOverwritesRemoved[CallNo]++;
- }
+ if (MA->isMustWrite() && !ValSet.is_null()) {
+ // { MemoryAccess[] }
+ auto AccSet =
+ isl::set::universe(isl::space(S->getIslCtx(), 0, 0)
+ .set_tuple_id(isl::dim::set, MA->getId()));
- // Unconditional writes overwrite other values.
- if (MA->isMustWrite()) {
- // Avoid too complex isl sets. If necessary, throw away some of the
- // knowledge.
- WillBeOverwritten =
- underapproximatedAddMap(WillBeOverwritten, AccRel);
- }
+ // { Val[] -> MemoryAccess[] }
+ isl::map ValAccSet = isl::map::from_domain_and_range(ValSet, AccSet);
+
+ // { [Domain[] -> Element[]] -> [Value[] -> MemoryAccess[]] }
+ isl::map AccRelValAcc =
+ isl::map::from_domain_and_range(AccRelWrapped, ValAccSet.wrap());
+ FutureWrites = FutureWrites.add_map(AccRelValAcc);
}
}
}
+}
- /// Combine writes that write the same value if possible.
- ///
- /// This function is able to combine:
- /// - Partial writes with disjoint domain.
- /// - Writes that write to the same array element.
- ///
- /// In all cases, both writes must write the same values.
- void coalesceWrites() {
- for (auto &Stmt : *S) {
- isl::set Domain = Stmt.getDomain().intersect_params(S->getContext());
-
- // We let isl do the lookup for the same-value condition. For this, we
- // wrap llvm::Value into an isl::set such that isl can do the lookup in
- // its hashtable implementation. llvm::Values are only compared within a
- // ScopStmt, so the map can be local to this scope. TODO: Refactor with
- // ZoneAlgorithm::makeValueSet()
- SmallDenseMap<Value *, isl::set> ValueSets;
- auto makeValueSet = [&ValueSets, this](Value *V) -> isl::set {
- assert(V);
- isl::set &Result = ValueSets[V];
- if (Result.is_null()) {
- isl::ctx Ctx = S->getIslCtx();
- std::string Name =
- getIslCompatibleName("Val", V, ValueSets.size() - 1,
- std::string(), UseInstructionNames);
- isl::id Id = isl::id::alloc(Ctx, Name, V);
- Result = isl::set::universe(
- isl::space(Ctx, 0, 0).set_tuple_id(isl::dim::set, Id));
- }
- return Result;
- };
-
- // List of all eligible (for coalescing) writes of the future.
- // { [Domain[] -> Element[]] -> [Value[] -> MemoryAccess[]] }
- isl::union_map FutureWrites = isl::union_map::empty(S->getParamSpace());
-
- // Iterate over accesses from the last to the first.
- SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
- for (MemoryAccess *MA : reverse(Accesses)) {
- // In region statements, the explicit accesses can be in blocks that can
- // be executed in any order. We therefore process only the implicit
- // writes and stop after that.
- if (Stmt.isRegionStmt() && isExplicitAccess(MA))
- break;
-
- // { Domain[] -> Element[] }
- isl::map AccRel =
- MA->getLatestAccessRelation().intersect_domain(Domain);
-
- // { [Domain[] -> Element[]] }
- isl::set AccRelWrapped = AccRel.wrap();
-
- // { Value[] }
- isl::set ValSet;
-
- if (MA->isMustWrite() && (MA->isOriginalScalarKind() ||
- isa<StoreInst>(MA->getAccessInstruction()))) {
- // Normally, tryGetValueStored() should be used to determine which
- // element is written, but it can return nullptr; For PHI accesses,
- // getAccessValue() returns the PHI instead of the PHI's incoming
- // value. In this case, where we only compare values of a single
- // statement, this is fine, because within a statement, a PHI in a
- // successor block has always the same value as the incoming write. We
- // still preferably use the incoming value directly so we also catch
- // direct uses of that.
- Value *StoredVal = MA->tryGetValueStored();
- if (!StoredVal)
- StoredVal = MA->getAccessValue();
- ValSet = makeValueSet(StoredVal);
-
- // { Domain[] }
- isl::set AccDomain = AccRel.domain();
-
- // Parts of the statement's domain that is not written by this access.
- isl::set UndefDomain = Domain.subtract(AccDomain);
-
- // { Element[] }
- isl::set ElementUniverse =
- isl::set::universe(AccRel.get_space().range());
+/// Remove writes that just write the same value already stored in the
+/// element.
+void SimplifyVisitor::removeRedundantWrites() {
+ for (auto &Stmt : *S) {
+ SmallDenseMap<Value *, isl::set> ValueSets;
+ auto makeValueSet = [&ValueSets, this](Value *V) -> isl::set {
+ assert(V);
+ isl::set &Result = ValueSets[V];
+ if (Result.is_null()) {
+ isl_ctx *Ctx = S->getIslCtx().get();
+ std::string Name = getIslCompatibleName(
+ "Val", V, ValueSets.size() - 1, std::string(), UseInstructionNames);
+ isl::id Id = isl::manage(isl_id_alloc(Ctx, Name.c_str(), V));
+ Result = isl::set::universe(
+ isl::space(Ctx, 0, 0).set_tuple_id(isl::dim::set, Id));
+ }
+ return Result;
+ };
+
+ isl::set Domain = Stmt.getDomain();
+ Domain = Domain.intersect_params(S->getContext());
+
+ // List of element reads that still have the same value while iterating
+ // through the MemoryAccesses.
+ // { [Domain[] -> Element[]] -> Val[] }
+ isl::union_map Known = isl::union_map::empty(S->getParamSpace());
+
+ SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
+ for (MemoryAccess *MA : Accesses) {
+ // Is the memory access in a defined order relative to the other
+ // accesses? In region statements, only the first and the last accesses
+ // have defined order. Execution of those in the middle may depend on
+ // runtime conditions an therefore cannot be modified.
+ bool IsOrdered =
+ Stmt.isBlockStmt() || MA->isOriginalScalarKind() ||
+ (!S->getBoxedLoops().size() && MA->getAccessInstruction() &&
+ Stmt.getEntryBlock() == MA->getAccessInstruction()->getParent());
+
+ isl::map AccRel = MA->getAccessRelation();
+ AccRel = AccRel.intersect_domain(Domain);
+ isl::set AccRelWrapped = AccRel.wrap();
+
+ // Determine whether a write is redundant (stores only values that are
+ // already present in the written array elements) and remove it if this
+ // is the case.
+ if (IsOrdered && MA->isMustWrite() &&
+ (isa<StoreInst>(MA->getAccessInstruction()) ||
+ MA->isOriginalScalarKind())) {
+ Value *StoredVal = MA->tryGetValueStored();
+ if (!StoredVal)
+ StoredVal = MA->getAccessValue();
+
+ if (StoredVal) {
+ // Lookup in the set of known values.
+ isl::map AccRelStoredVal = isl::map::from_domain_and_range(
+ AccRelWrapped, makeValueSet(StoredVal));
+ if (isl::union_map(AccRelStoredVal).is_subset(Known)) {
+ LLVM_DEBUG(dbgs() << "Cleanup of " << MA << ":\n");
+ LLVM_DEBUG(dbgs() << " Scalar: " << *StoredVal << "\n");
+ LLVM_DEBUG(dbgs() << " AccRel: " << AccRel << "\n");
- // { Domain[] -> Element[] }
- isl::map UndefAnything =
- isl::map::from_domain_and_range(UndefDomain, ElementUniverse);
-
- // We are looking a compatible write access. The other write can
- // access these elements...
- isl::map AllowedAccesses = AccRel.unite(UndefAnything);
-
- // ... and must write the same value.
- // { [Domain[] -> Element[]] -> Value[] }
- isl::map Filter =
- isl::map::from_domain_and_range(AllowedAccesses.wrap(), ValSet);
-
- // Lookup future write that fulfills these conditions.
- // { [[Domain[] -> Element[]] -> Value[]] -> MemoryAccess[] }
- isl::union_map Filtered =
- FutureWrites.uncurry().intersect_domain(Filter.wrap());
-
- // Iterate through the candidates.
- for (isl::map Map : Filtered.get_map_list()) {
- MemoryAccess *OtherMA = (MemoryAccess *)Map.get_space()
- .get_tuple_id(isl::dim::out)
- .get_user();
-
- isl::map OtherAccRel =
- OtherMA->getLatestAccessRelation().intersect_domain(Domain);
-
- // The filter only guaranteed that some of OtherMA's accessed
- // elements are allowed. Verify that it only accesses allowed
- // elements. Otherwise, continue with the next candidate.
- if (!OtherAccRel.is_subset(AllowedAccesses).is_true())
- continue;
-
- // The combined access relation.
- // { Domain[] -> Element[] }
- isl::map NewAccRel = AccRel.unite(OtherAccRel);
- simplify(NewAccRel);
-
- // Carry out the coalescing.
Stmt.removeSingleMemoryAccess(MA);
- OtherMA->setNewAccessRelation(NewAccRel);
-
- // We removed MA, OtherMA takes its role.
- MA = OtherMA;
-
- TotalWritesCoalesced[CallNo]++;
- WritesCoalesced++;
- // Don't look for more candidates.
- break;
+ RedundantWritesRemoved++;
+ TotalRedundantWritesRemoved[CallNo]++;
}
}
+ }
- // Two writes cannot be coalesced if there is another access (to some of
- // the written elements) between them. Remove all visited write accesses
- // from the list of eligible writes. Don't just remove the accessed
- // elements, but any MemoryAccess that touches any of the invalidated
- // elements.
- SmallPtrSet<MemoryAccess *, 2> TouchedAccesses;
- for (isl::map Map :
- FutureWrites.intersect_domain(AccRelWrapped).get_map_list()) {
- MemoryAccess *MA = (MemoryAccess *)Map.get_space()
- .range()
- .unwrap()
- .get_tuple_id(isl::dim::out)
- .get_user();
- TouchedAccesses.insert(MA);
- }
- isl::union_map NewFutureWrites =
- isl::union_map::empty(FutureWrites.get_space());
- for (isl::map FutureWrite : FutureWrites.get_map_list()) {
- MemoryAccess *MA = (MemoryAccess *)FutureWrite.get_space()
- .range()
- .unwrap()
- .get_tuple_id(isl::dim::out)
- .get_user();
- if (!TouchedAccesses.count(MA))
- NewFutureWrites = NewFutureWrites.add_map(FutureWrite);
- }
- FutureWrites = NewFutureWrites;
-
- if (MA->isMustWrite() && !ValSet.is_null()) {
- // { MemoryAccess[] }
- auto AccSet =
- isl::set::universe(isl::space(S->getIslCtx(), 0, 0)
- .set_tuple_id(isl::dim::set, MA->getId()));
-
- // { Val[] -> MemoryAccess[] }
- isl::map ValAccSet = isl::map::from_domain_and_range(ValSet, AccSet);
+ // Update the know values set.
+ if (MA->isRead()) {
+ // Loaded values are the currently known values of the array element
+ // it was loaded from.
+ Value *LoadedVal = MA->getAccessValue();
+ if (LoadedVal && IsOrdered) {
+ isl::map AccRelVal = isl::map::from_domain_and_range(
+ AccRelWrapped, makeValueSet(LoadedVal));
- // { [Domain[] -> Element[]] -> [Value[] -> MemoryAccess[]] }
- isl::map AccRelValAcc =
- isl::map::from_domain_and_range(AccRelWrapped, ValAccSet.wrap());
- FutureWrites = FutureWrites.add_map(AccRelValAcc);
+ Known = Known.add_map(AccRelVal);
}
+ } else if (MA->isWrite()) {
+ // Remove (possibly) overwritten values from the known elements set.
+ // We remove all elements of the accessed array to avoid too complex
+ // isl sets.
+ isl::set AccRelUniv = isl::set::universe(AccRelWrapped.get_space());
+ Known = Known.subtract_domain(AccRelUniv);
+
+ // At this point, we could add the written value of must-writes.
+ // However, writing same values is already handled by
+ // coalesceWrites().
}
}
}
+}
- /// Remove writes that just write the same value already stored in the
- /// element.
- void removeRedundantWrites() {
- for (auto &Stmt : *S) {
- SmallDenseMap<Value *, isl::set> ValueSets;
- auto makeValueSet = [&ValueSets, this](Value *V) -> isl::set {
- assert(V);
- isl::set &Result = ValueSets[V];
- if (Result.is_null()) {
- isl_ctx *Ctx = S->getIslCtx().get();
- std::string Name =
- getIslCompatibleName("Val", V, ValueSets.size() - 1,
- std::string(), UseInstructionNames);
- isl::id Id = isl::manage(isl_id_alloc(Ctx, Name.c_str(), V));
- Result = isl::set::universe(
- isl::space(Ctx, 0, 0).set_tuple_id(isl::dim::set, Id));
- }
- return Result;
- };
-
- isl::set Domain = Stmt.getDomain();
- Domain = Domain.intersect_params(S->getContext());
-
- // List of element reads that still have the same value while iterating
- // through the MemoryAccesses.
- // { [Domain[] -> Element[]] -> Val[] }
- isl::union_map Known = isl::union_map::empty(S->getParamSpace());
-
- SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
- for (MemoryAccess *MA : Accesses) {
- // Is the memory access in a defined order relative to the other
- // accesses? In region statements, only the first and the last accesses
- // have defined order. Execution of those in the middle may depend on
- // runtime conditions an therefore cannot be modified.
- bool IsOrdered =
- Stmt.isBlockStmt() || MA->isOriginalScalarKind() ||
- (!S->getBoxedLoops().size() && MA->getAccessInstruction() &&
- Stmt.getEntryBlock() == MA->getAccessInstruction()->getParent());
-
- isl::map AccRel = MA->getAccessRelation();
- AccRel = AccRel.intersect_domain(Domain);
- isl::set AccRelWrapped = AccRel.wrap();
-
- // Determine whether a write is redundant (stores only values that are
- // already present in the written array elements) and remove it if this
- // is the case.
- if (IsOrdered && MA->isMustWrite() &&
- (isa<StoreInst>(MA->getAccessInstruction()) ||
- MA->isOriginalScalarKind())) {
- Value *StoredVal = MA->tryGetValueStored();
- if (!StoredVal)
- StoredVal = MA->getAccessValue();
-
- if (StoredVal) {
- // Lookup in the set of known values.
- isl::map AccRelStoredVal = isl::map::from_domain_and_range(
- AccRelWrapped, makeValueSet(StoredVal));
- if (isl::union_map(AccRelStoredVal).is_subset(Known)) {
- LLVM_DEBUG(dbgs() << "Cleanup of " << MA << ":\n");
- LLVM_DEBUG(dbgs() << " Scalar: " << *StoredVal << "\n");
- LLVM_DEBUG(dbgs() << " AccRel: " << AccRel << "\n");
-
- Stmt.removeSingleMemoryAccess(MA);
-
- RedundantWritesRemoved++;
- TotalRedundantWritesRemoved[CallNo]++;
- }
- }
- }
+/// Remove statements without side effects.
+void SimplifyVisitor::removeUnnecessaryStmts() {
+ auto NumStmtsBefore = S->getSize();
+ S->simplifySCoP(true);
+ assert(NumStmtsBefore >= S->getSize());
+ StmtsRemoved = NumStmtsBefore - S->getSize();
+ LLVM_DEBUG(dbgs() << "Removed " << StmtsRemoved << " (of " << NumStmtsBefore
+ << ") statements\n");
+ TotalStmtsRemoved[CallNo] += StmtsRemoved;
+}
- // Update the know values set.
- if (MA->isRead()) {
- // Loaded values are the currently known values of the array element
- // it was loaded from.
- Value *LoadedVal = MA->getAccessValue();
- if (LoadedVal && IsOrdered) {
- isl::map AccRelVal = isl::map::from_domain_and_range(
- AccRelWrapped, makeValueSet(LoadedVal));
+/// Remove accesses that have an empty domain.
+void SimplifyVisitor::removeEmptyPartialAccesses() {
+ for (ScopStmt &Stmt : *S) {
+ // Defer the actual removal to not invalidate iterators.
+ SmallVector<MemoryAccess *, 8> DeferredRemove;
- Known = Known.add_map(AccRelVal);
- }
- } else if (MA->isWrite()) {
- // Remove (possibly) overwritten values from the known elements set.
- // We remove all elements of the accessed array to avoid too complex
- // isl sets.
- isl::set AccRelUniv = isl::set::universe(AccRelWrapped.get_space());
- Known = Known.subtract_domain(AccRelUniv);
-
- // At this point, we could add the written value of must-writes.
- // However, writing same values is already handled by
- // coalesceWrites().
- }
- }
+ for (MemoryAccess *MA : Stmt) {
+ if (!MA->isWrite())
+ continue;
+
+ isl::map AccRel = MA->getAccessRelation();
+ if (!AccRel.is_empty().is_true())
+ continue;
+
+ LLVM_DEBUG(
+ dbgs() << "Removing " << MA
+ << " because it's a partial access that never occurs\n");
+ DeferredRemove.push_back(MA);
+ }
+
+ for (MemoryAccess *MA : DeferredRemove) {
+ Stmt.removeSingleMemoryAccess(MA);
+ EmptyPartialAccessesRemoved++;
+ TotalEmptyPartialAccessesRemoved[CallNo]++;
}
}
+}
- /// Remove statements without side effects.
- void removeUnnecessaryStmts() {
- auto NumStmtsBefore = S->getSize();
- S->simplifySCoP(true);
- assert(NumStmtsBefore >= S->getSize());
- StmtsRemoved = NumStmtsBefore - S->getSize();
- LLVM_DEBUG(dbgs() << "Removed " << StmtsRemoved << " (of " << NumStmtsBefore
- << ") statements\n");
- TotalStmtsRemoved[CallNo] += StmtsRemoved;
+/// Mark all reachable instructions and access, and sweep those that are not
+/// reachable.
+void SimplifyVisitor::markAndSweep(LoopInfo *LI) {
+ DenseSet<MemoryAccess *> UsedMA;
+ DenseSet<VirtualInstruction> UsedInsts;
+
+ // Get all reachable instructions and accesses.
+ markReachable(S, LI, UsedInsts, UsedMA);
+
+ // Remove all non-reachable accesses.
+ // We need get all MemoryAccesses first, in order to not invalidate the
+ // iterators when removing them.
+ SmallVector<MemoryAccess *, 64> AllMAs;
+ for (ScopStmt &Stmt : *S)
+ AllMAs.append(Stmt.begin(), Stmt.end());
+
+ for (MemoryAccess *MA : AllMAs) {
+ if (UsedMA.count(MA))
+ continue;
+ LLVM_DEBUG(dbgs() << "Removing " << MA
+ << " because its value is not used\n");
+ ScopStmt *Stmt = MA->getStatement();
+ Stmt->removeSingleMemoryAccess(MA);
+
+ DeadAccessesRemoved++;
+ TotalDeadAccessesRemoved[CallNo]++;
}
- /// Remove accesses that have an empty domain.
- void removeEmptyPartialAccesses() {
- for (ScopStmt &Stmt : *S) {
- // Defer the actual removal to not invalidate iterators.
- SmallVector<MemoryAccess *, 8> DeferredRemove;
+ // Remove all non-reachable instructions.
+ for (ScopStmt &Stmt : *S) {
+ // Note that for region statements, we can only remove the non-terminator
+ // instructions of the entry block. All other instructions are not in the
+ // instructions list, but implicitly always part of the statement.
+
+ SmallVector<Instruction *, 32> AllInsts(Stmt.insts_begin(),
+ Stmt.insts_end());
+ SmallVector<Instruction *, 32> RemainInsts;
+
+ for (Instruction *Inst : AllInsts) {
+ auto It = UsedInsts.find({&Stmt, Inst});
+ if (It == UsedInsts.end()) {
+ LLVM_DEBUG(dbgs() << "Removing "; Inst->print(dbgs());
+ dbgs() << " because it is not used\n");
+ DeadInstructionsRemoved++;
+ TotalDeadInstructionsRemoved[CallNo]++;
+ continue;
+ }
- for (MemoryAccess *MA : Stmt) {
- if (!MA->isWrite())
- continue;
+ RemainInsts.push_back(Inst);
- isl::map AccRel = MA->getAccessRelation();
- if (!AccRel.is_empty().is_true())
- continue;
+ // If instructions appear multiple times, keep only the first.
+ UsedInsts.erase(It);
+ }
- LLVM_DEBUG(
- dbgs() << "Removing " << MA
- << " because it's a partial access that never occurs\n");
- DeferredRemove.push_back(MA);
- }
+ // Set the new instruction list to be only those we did not remove.
+ Stmt.setInstructions(RemainInsts);
+ }
+}
- for (MemoryAccess *MA : DeferredRemove) {
- Stmt.removeSingleMemoryAccess(MA);
- EmptyPartialAccessesRemoved++;
- TotalEmptyPartialAccessesRemoved[CallNo]++;
- }
- }
+/// Print simplification statistics to @p OS.
+void SimplifyVisitor::printStatistics(llvm::raw_ostream &OS, int Indent) const {
+ OS.indent(Indent) << "Statistics {\n";
+ OS.indent(Indent + 4) << "Empty domains removed: " << EmptyDomainsRemoved
+ << '\n';
+ OS.indent(Indent + 4) << "Overwrites removed: " << OverwritesRemoved << '\n';
+ OS.indent(Indent + 4) << "Partial writes coalesced: " << WritesCoalesced
+ << "\n";
+ OS.indent(Indent + 4) << "Redundant writes removed: "
+ << RedundantWritesRemoved << "\n";
+ OS.indent(Indent + 4) << "Accesses with empty domains removed: "
+ << EmptyPartialAccessesRemoved << "\n";
+ OS.indent(Indent + 4) << "Dead accesses removed: " << DeadAccessesRemoved
+ << '\n';
+ OS.indent(Indent + 4) << "Dead instructions removed: "
+ << DeadInstructionsRemoved << '\n';
+ OS.indent(Indent + 4) << "Stmts removed: " << StmtsRemoved << "\n";
+ OS.indent(Indent) << "}\n";
+}
+
+/// Print the current state of all MemoryAccesses to @p OS.
+void SimplifyVisitor::printAccesses(llvm::raw_ostream &OS, int Indent) const {
+ OS.indent(Indent) << "After accesses {\n";
+ for (auto &Stmt : *S) {
+ OS.indent(Indent + 4) << Stmt.getBaseName() << "\n";
+ for (auto *MA : Stmt)
+ MA->print(OS);
}
+ OS.indent(Indent) << "}\n";
+}
- /// Mark all reachable instructions and access, and sweep those that are not
- /// reachable.
- void markAndSweep(LoopInfo *LI) {
- DenseSet<MemoryAccess *> UsedMA;
- DenseSet<VirtualInstruction> UsedInsts;
+bool SimplifyVisitor::visit(Scop &S, LoopInfo *LI) {
+ // Reset statistics of last processed SCoP.
+ releaseMemory();
+ assert(!isModified());
- // Get all reachable instructions and accesses.
- markReachable(S, LI, UsedInsts, UsedMA);
+ // Prepare processing of this SCoP.
+ this->S = &S;
+ ScopsProcessed[CallNo]++;
- // Remove all non-reachable accesses.
- // We need get all MemoryAccesses first, in order to not invalidate the
- // iterators when removing them.
- SmallVector<MemoryAccess *, 64> AllMAs;
- for (ScopStmt &Stmt : *S)
- AllMAs.append(Stmt.begin(), Stmt.end());
+ LLVM_DEBUG(dbgs() << "Removing statements that are never executed...\n");
+ removeEmptyDomainStmts();
- for (MemoryAccess *MA : AllMAs) {
- if (UsedMA.count(MA))
- continue;
- LLVM_DEBUG(dbgs() << "Removing " << MA
- << " because its value is not used\n");
- ScopStmt *Stmt = MA->getStatement();
- Stmt->removeSingleMemoryAccess(MA);
+ LLVM_DEBUG(dbgs() << "Removing partial writes that never happen...\n");
+ removeEmptyPartialAccesses();
- DeadAccessesRemoved++;
- TotalDeadAccessesRemoved[CallNo]++;
- }
+ LLVM_DEBUG(dbgs() << "Removing overwrites...\n");
+ removeOverwrites();
- // Remove all non-reachable instructions.
- for (ScopStmt &Stmt : *S) {
- // Note that for region statements, we can only remove the non-terminator
- // instructions of the entry block. All other instructions are not in the
- // instructions list, but implicitly always part of the statement.
-
- SmallVector<Instruction *, 32> AllInsts(Stmt.insts_begin(),
- Stmt.insts_end());
- SmallVector<Instruction *, 32> RemainInsts;
-
- for (Instruction *Inst : AllInsts) {
- auto It = UsedInsts.find({&Stmt, Inst});
- if (It == UsedInsts.end()) {
- LLVM_DEBUG(dbgs() << "Removing "; Inst->print(dbgs());
- dbgs() << " because it is not used\n");
- DeadInstructionsRemoved++;
- TotalDeadInstructionsRemoved[CallNo]++;
- continue;
- }
+ LLVM_DEBUG(dbgs() << "Coalesce partial writes...\n");
+ coalesceWrites();
- RemainInsts.push_back(Inst);
+ LLVM_DEBUG(dbgs() << "Removing redundant writes...\n");
+ removeRedundantWrites();
- // If instructions appear multiple times, keep only the first.
- UsedInsts.erase(It);
- }
+ LLVM_DEBUG(dbgs() << "Cleanup unused accesses...\n");
+ markAndSweep(LI);
- // Set the new instruction list to be only those we did not remove.
- Stmt.setInstructions(RemainInsts);
- }
- }
+ LLVM_DEBUG(dbgs() << "Removing statements without side effects...\n");
+ removeUnnecessaryStmts();
- /// Print simplification statistics to @p OS.
- void printStatistics(llvm::raw_ostream &OS, int Indent = 0) const {
- OS.indent(Indent) << "Statistics {\n";
- OS.indent(Indent + 4) << "Empty domains removed: " << EmptyDomainsRemoved
- << '\n';
- OS.indent(Indent + 4) << "Overwrites removed: " << OverwritesRemoved
- << '\n';
- OS.indent(Indent + 4) << "Partial writes coalesced: " << WritesCoalesced
- << "\n";
- OS.indent(Indent + 4) << "Redundant writes removed: "
- << RedundantWritesRemoved << "\n";
- OS.indent(Indent + 4) << "Accesses with empty domains removed: "
- << EmptyPartialAccessesRemoved << "\n";
- OS.indent(Indent + 4) << "Dead accesses removed: " << DeadAccessesRemoved
- << '\n';
- OS.indent(Indent + 4) << "Dead instructions removed: "
- << DeadInstructionsRemoved << '\n';
- OS.indent(Indent + 4) << "Stmts removed: " << StmtsRemoved << "\n";
- OS.indent(Indent) << "}\n";
- }
+ if (isModified())
+ ScopsModified[CallNo]++;
+ LLVM_DEBUG(dbgs() << "\nFinal Scop:\n");
+ LLVM_DEBUG(dbgs() << S);
- /// Print the current state of all MemoryAccesses to @p OS.
- void printAccesses(llvm::raw_ostream &OS, int Indent = 0) const {
- OS.indent(Indent) << "After accesses {\n";
- for (auto &Stmt : *S) {
- OS.indent(Indent + 4) << Stmt.getBaseName() << "\n";
- for (auto *MA : Stmt)
- MA->print(OS);
- }
- OS.indent(Indent) << "}\n";
+ auto ScopStats = S.getStatistics();
+ NumValueWrites[CallNo] += ScopStats.NumValueWrites;
+ NumValueWritesInLoops[CallNo] += ScopStats.NumValueWritesInLoops;
+ NumPHIWrites[CallNo] += ScopStats.NumPHIWrites;
+ NumPHIWritesInLoops[CallNo] += ScopStats.NumPHIWritesInLoops;
+ NumSingletonWrites[CallNo] += ScopStats.NumSingletonWrites;
+ NumSingletonWritesInLoops[CallNo] += ScopStats.NumSingletonWritesInLoops;
+
+ return false;
+}
+
+void SimplifyVisitor::printScop(raw_ostream &OS, Scop &S) const {
+ assert(&S == this->S &&
+ "Can only print analysis for the last processed SCoP");
+ printStatistics(OS);
+
+ if (!isModified()) {
+ OS << "SCoP could not be simplified\n";
+ return;
}
+ printAccesses(OS);
+}
+
+void SimplifyVisitor::releaseMemory() {
+ S = nullptr;
+
+ EmptyDomainsRemoved = 0;
+ OverwritesRemoved = 0;
+ WritesCoalesced = 0;
+ RedundantWritesRemoved = 0;
+ EmptyPartialAccessesRemoved = 0;
+ DeadAccessesRemoved = 0;
+ DeadInstructionsRemoved = 0;
+ StmtsRemoved = 0;
+}
+class SimplifyLegacyPass : public ScopPass {
public:
static char ID;
- explicit Simplify(int CallNo = 0) : ScopPass(ID), CallNo(CallNo) {}
+ SimplifyVisitor Imp;
+
+ explicit SimplifyLegacyPass(int CallNo = 0) : ScopPass(ID), Imp(CallNo) {}
virtual void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequiredTransitive<ScopInfoRegionPass>();
@@ -657,82 +692,41 @@ class Simplify : public ScopPass {
}
virtual bool runOnScop(Scop &S) override {
- // Reset statistics of last processed SCoP.
- releaseMemory();
- assert(!isModified());
-
- // Prepare processing of this SCoP.
- this->S = &S;
- ScopsProcessed[CallNo]++;
-
- LLVM_DEBUG(dbgs() << "Removing statements that are never executed...\n");
- removeEmptyDomainStmts();
-
- LLVM_DEBUG(dbgs() << "Removing partial writes that never happen...\n");
- removeEmptyPartialAccesses();
-
- LLVM_DEBUG(dbgs() << "Removing overwrites...\n");
- removeOverwrites();
-
- LLVM_DEBUG(dbgs() << "Coalesce partial writes...\n");
- coalesceWrites();
-
- LLVM_DEBUG(dbgs() << "Removing redundant writes...\n");
- removeRedundantWrites();
-
- LLVM_DEBUG(dbgs() << "Cleanup unused accesses...\n");
- LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
- markAndSweep(LI);
-
- LLVM_DEBUG(dbgs() << "Removing statements without side effects...\n");
- removeUnnecessaryStmts();
-
- if (isModified())
- ScopsModified[CallNo]++;
- LLVM_DEBUG(dbgs() << "\nFinal Scop:\n");
- LLVM_DEBUG(dbgs() << S);
-
- auto ScopStats = S.getStatistics();
- NumValueWrites[CallNo] += ScopStats.NumValueWrites;
- NumValueWritesInLoops[CallNo] += ScopStats.NumValueWritesInLoops;
- NumPHIWrites[CallNo] += ScopStats.NumPHIWrites;
- NumPHIWritesInLoops[CallNo] += ScopStats.NumPHIWritesInLoops;
- NumSingletonWrites[CallNo] += ScopStats.NumSingletonWrites;
- NumSingletonWritesInLoops[CallNo] += ScopStats.NumSingletonWritesInLoops;
-
- return false;
+ return Imp.visit(S, &getAnalysis<LoopInfoWrapperPass>().getLoopInfo());
}
virtual void printScop(raw_ostream &OS, Scop &S) const override {
- assert(&S == this->S &&
- "Can only print analysis for the last processed SCoP");
- printStatistics(OS);
-
- if (!isModified()) {
- OS << "SCoP could not be simplified\n";
- return;
- }
- printAccesses(OS);
+ Imp.printScop(OS, S);
}
- virtual void releaseMemory() override {
- S = nullptr;
-
- EmptyDomainsRemoved = 0;
- OverwritesRemoved = 0;
- WritesCoalesced = 0;
- RedundantWritesRemoved = 0;
- EmptyPartialAccessesRemoved = 0;
- DeadAccessesRemoved = 0;
- DeadInstructionsRemoved = 0;
- StmtsRemoved = 0;
- }
+ virtual void releaseMemory() override { Imp.releaseMemory(); }
};
-char Simplify::ID;
+char SimplifyLegacyPass::ID;
} // anonymous namespace
namespace polly {
+llvm::PreservedAnalyses SimplifyPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR,
+ SPMUpdater &U) {
+ if (!Imp.visit(S, &SAR.LI))
+ return llvm::PreservedAnalyses::all();
+
+ return llvm::PreservedAnalyses::none();
+}
+
+llvm::PreservedAnalyses
+SimplifyPrinterPass::run(Scop &S, ScopAnalysisManager &SAM,
+ ScopStandardAnalysisResults &SAR, SPMUpdater &U) {
+ bool Changed = Imp.visit(S, &SAR.LI);
+ Imp.printScop(OS, S);
+
+ if (!Changed)
+ return llvm::PreservedAnalyses::all();
+
+ return llvm::PreservedAnalyses::none();
+}
+
SmallVector<MemoryAccess *, 32> getAccessesInOrder(ScopStmt &Stmt) {
SmallVector<MemoryAccess *, 32> Accesses;
@@ -753,10 +747,12 @@ SmallVector<MemoryAccess *, 32> getAccessesInOrder(ScopStmt &Stmt) {
}
} // namespace polly
-Pass *polly::createSimplifyPass(int CallNo) { return new Simplify(CallNo); }
+Pass *polly::createSimplifyPass(int CallNo) {
+ return new SimplifyLegacyPass(CallNo);
+}
-INITIALIZE_PASS_BEGIN(Simplify, "polly-simplify", "Polly - Simplify", false,
- false)
+INITIALIZE_PASS_BEGIN(SimplifyLegacyPass, "polly-simplify", "Polly - Simplify",
+ false, false)
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
-INITIALIZE_PASS_END(Simplify, "polly-simplify", "Polly - Simplify", false,
- false)
+INITIALIZE_PASS_END(SimplifyLegacyPass, "polly-simplify", "Polly - Simplify",
+ false, false)
diff --git a/polly/test/Simplify/dead_access_load.ll b/polly/test/Simplify/dead_access_load.ll
index e8c501ffd01b..4739e44bade1 100644
--- a/polly/test/Simplify/dead_access_load.ll
+++ b/polly/test/Simplify/dead_access_load.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-stmt-granularity=bb -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly -polly-stmt-granularity=bb "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Remove a dead load-instruction
; (an load whose result is not used anywhere)
diff --git a/polly/test/Simplify/dead_access_phi.ll b/polly/test/Simplify/dead_access_phi.ll
index edd56d864d5e..10fd3179ffde 100644
--- a/polly/test/Simplify/dead_access_phi.ll
+++ b/polly/test/Simplify/dead_access_phi.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-stmt-granularity=bb -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly -polly-stmt-granularity=bb "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Remove a dead PHI write/read pair
; (accesses that are effectively not used)
diff --git a/polly/test/Simplify/dead_access_value.ll b/polly/test/Simplify/dead_access_value.ll
index 1cb707d93d06..b95c3843f5b1 100644
--- a/polly/test/Simplify/dead_access_value.ll
+++ b/polly/test/Simplify/dead_access_value.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-stmt-granularity=bb -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly -polly-stmt-granularity=bb "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Remove a dead value write/read pair
; (accesses that are effectively not used)
diff --git a/polly/test/Simplify/dead_instruction.ll b/polly/test/Simplify/dead_instruction.ll
index 31bdaa2e2d99..4989daaa6b07 100644
--- a/polly/test/Simplify/dead_instruction.ll
+++ b/polly/test/Simplify/dead_instruction.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-stmt-granularity=bb -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly -polly-stmt-granularity=bb "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Remove a dead instruction
; (an instruction whose result is not used anywhere)
diff --git a/polly/test/Simplify/notdead_region_exitphi.ll b/polly/test/Simplify/notdead_region_exitphi.ll
index 77c34ed8f8eb..d56549006aea 100644
--- a/polly/test/Simplify/notdead_region_exitphi.ll
+++ b/polly/test/Simplify/notdead_region_exitphi.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Do not remove dependencies of a phi node in a region's exit block.
;
diff --git a/polly/test/Simplify/notdead_region_innerphi.ll b/polly/test/Simplify/notdead_region_innerphi.ll
index 5749186f4ce5..d340fc793dc3 100644
--- a/polly/test/Simplify/notdead_region_innerphi.ll
+++ b/polly/test/Simplify/notdead_region_innerphi.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Do not remove dependencies of a phi node within a region statement (%phi).
;
diff --git a/polly/test/Simplify/notredundant_region_middle.ll b/polly/test/Simplify/notredundant_region_middle.ll
index e42e60b94806..46dac96ec920 100644
--- a/polly/test/Simplify/notredundant_region_middle.ll
+++ b/polly/test/Simplify/notredundant_region_middle.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Do not remove redundant stores in the middle of region statements.
; The store in region_true could be removed, but in practice we do try to
diff --git a/polly/test/Simplify/notredundant_synthesizable_unknownit.ll b/polly/test/Simplify/notredundant_synthesizable_unknownit.ll
index c1dba95563ac..6a01a1c70bc4 100644
--- a/polly/test/Simplify/notredundant_synthesizable_unknownit.ll
+++ b/polly/test/Simplify/notredundant_synthesizable_unknownit.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-stmt-granularity=bb -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly -polly-stmt-granularity=bb "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Do not remove the scalar value write of %i.trunc in inner.for.
; It is used by body.
diff --git a/polly/test/Simplify/overwritten.ll b/polly/test/Simplify/overwritten.ll
index 09d48e73863f..8e82d3348eeb 100644
--- a/polly/test/Simplify/overwritten.ll
+++ b/polly/test/Simplify/overwritten.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-stmt-granularity=bb -polly-simplify -analyze < %s | FileCheck -match-full-lines %s
+; RUN: opt %loadPolly -polly-stmt-granularity=bb "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck -match-full-lines %s
;
; Remove a store that is overwritten by another store in the same statement.
;
diff --git a/polly/test/Simplify/overwritten_3store.ll b/polly/test/Simplify/overwritten_3store.ll
index 17353c65b6c4..c1a146be1c05 100644
--- a/polly/test/Simplify/overwritten_3store.ll
+++ b/polly/test/Simplify/overwritten_3store.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-stmt-granularity=bb -polly-simplify -analyze < %s | FileCheck -match-full-lines %s
+; RUN: opt %loadPolly -polly-stmt-granularity=bb "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck -match-full-lines %s
;
; Remove a store that is overwritten by another store in the same statement.
; Check that even multiple stores are removed.
diff --git a/polly/test/Simplify/overwritten_loadbetween.ll b/polly/test/Simplify/overwritten_loadbetween.ll
index c06263eb36ac..eb74910e2e98 100644
--- a/polly/test/Simplify/overwritten_loadbetween.ll
+++ b/polly/test/Simplify/overwritten_loadbetween.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-simplify -analyze < %s | FileCheck -match-full-lines %s
+; RUN: opt %loadPolly "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck -match-full-lines %s
;
; Do not remove overwrites when the value is read before.
;
diff --git a/polly/test/Simplify/pass_existence.ll b/polly/test/Simplify/pass_existence.ll
index 833504d33522..c9b108b2390f 100644
--- a/polly/test/Simplify/pass_existence.ll
+++ b/polly/test/Simplify/pass_existence.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-simplify -analyze < %s | FileCheck %s
+; RUN: opt %loadPolly -disable-output "-passes=scop(print<polly-simplify>)" < %s -aa-pipeline=basic-aa < %s | FileCheck %s
;
; Simple test for the existence of the Simplify pass.
;
diff --git a/polly/test/Simplify/phi_in_regionstmt.ll b/polly/test/Simplify/phi_in_regionstmt.ll
index 9ed4bc2b1e65..a64fae144443 100644
--- a/polly/test/Simplify/phi_in_regionstmt.ll
+++ b/polly/test/Simplify/phi_in_regionstmt.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; The PHINode %cond91.sink.sink.us.sink.6 is in the middle of a region
; statement.
diff --git a/polly/test/Simplify/redundant.ll b/polly/test/Simplify/redundant.ll
index b3d8647ac091..4c199b171d3e 100644
--- a/polly/test/Simplify/redundant.ll
+++ b/polly/test/Simplify/redundant.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Remove redundant store (a store that writes the same value already
; at the destination)
diff --git a/polly/test/Simplify/redundant_
diff erentindex.ll b/polly/test/Simplify/redundant_
diff erentindex.ll
index 3a5e07334cbe..e82621310c26 100644
--- a/polly/test/Simplify/redundant_
diff erentindex.ll
+++ b/polly/test/Simplify/redundant_
diff erentindex.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; A store that has a
diff erent index than the load it is storing is
; not redundant.
diff --git a/polly/test/Simplify/redundant_storebetween.ll b/polly/test/Simplify/redundant_storebetween.ll
index 5e1befcb1a4a..df141402b22d 100644
--- a/polly/test/Simplify/redundant_storebetween.ll
+++ b/polly/test/Simplify/redundant_storebetween.ll
@@ -1,4 +1,5 @@
; RUN: opt %loadPolly -polly-simplify -analyze < %s | FileCheck %s -match-full-lines
+; RUN: opt %loadPolly "-passes=scop(print<polly-simplify>)" -disable-output -aa-pipeline=basic-aa < %s | FileCheck %s -match-full-lines
;
; Don't remove store where there is another store to the same target
; in-between them.
More information about the llvm-commits
mailing list