#include "polly/Simplify.h"
#include "polly/ScopInfo.h"
#include "polly/ScopPass.h"
#include "polly/Support/GICHelper.h"
#include "polly/Support/ISLOStream.h"
#include "polly/Support/ISLTools.h"
#include "polly/Support/VirtualInstruction.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/Debug.h"
#include <optional>
#include "polly/Support/PollyDebug.h"
#define DEBUG_TYPE "polly-simplify"
using namespace llvm;
using namespace polly;
namespace {
#define TWO_STATISTICS(VARNAME, DESC) \
static llvm::Statistic VARNAME[2] = { \
{DEBUG_TYPE, #VARNAME "0", DESC " (first)"}, \
{DEBUG_TYPE, #VARNAME "1", DESC " (second)"}}
static unsigned const SimplifyMaxDisjuncts = 4;
TWO_STATISTICS(ScopsProcessed, "Number of SCoPs processed");
TWO_STATISTICS(ScopsModified, "Number of SCoPs simplified");
TWO_STATISTICS(TotalEmptyDomainsRemoved,
"Number of statement with empty domains removed in any SCoP");
TWO_STATISTICS(TotalOverwritesRemoved, "Number of removed overwritten writes");
TWO_STATISTICS(TotalWritesCoalesced, "Number of writes coalesced with another");
TWO_STATISTICS(TotalRedundantWritesRemoved,
"Number of writes of same value removed in any SCoP");
TWO_STATISTICS(TotalEmptyPartialAccessesRemoved,
"Number of empty partial accesses removed");
TWO_STATISTICS(TotalDeadAccessesRemoved, "Number of dead accesses removed");
TWO_STATISTICS(TotalDeadInstructionsRemoved,
"Number of unused instructions removed");
TWO_STATISTICS(TotalStmtsRemoved, "Number of statements removed in any SCoP");
TWO_STATISTICS(NumValueWrites, "Number of scalar value writes after Simplify");
TWO_STATISTICS(
NumValueWritesInLoops,
"Number of scalar value writes nested in affine loops after Simplify");
TWO_STATISTICS(NumPHIWrites,
"Number of scalar phi writes after the first simplification");
TWO_STATISTICS(
NumPHIWritesInLoops,
"Number of scalar phi writes nested in affine loops after Simplify");
TWO_STATISTICS(NumSingletonWrites, "Number of singleton writes after Simplify");
TWO_STATISTICS(
NumSingletonWritesInLoops,
"Number of singleton writes nested in affine loops after Simplify");
static bool isImplicitRead(MemoryAccess *MA) {
return MA->isRead() && MA->isOriginalScalarKind();
}
static bool isExplicitAccess(MemoryAccess *MA) {
return MA->isOriginalArrayKind();
}
static bool isImplicitWrite(MemoryAccess *MA) {
return MA->isWrite() && MA->isOriginalScalarKind();
}
static isl::union_map underapproximatedAddMap(isl::union_map UMap,
isl::map Map) {
if (UMap.is_null() || Map.is_null())
return {};
isl::map PrevMap = UMap.extract_map(Map.get_space());
if (unsignedFromIslSize(PrevMap.n_basic_map()) +
unsignedFromIslSize(Map.n_basic_map()) <=
SimplifyMaxDisjuncts)
return UMap.unite(Map);
isl::map Result = isl::map::empty(PrevMap.get_space());
for (isl::basic_map BMap : PrevMap.get_basic_map_list()) {
if (unsignedFromIslSize(Result.n_basic_map()) > SimplifyMaxDisjuncts)
break;
Result = Result.unite(BMap);
}
for (isl::basic_map BMap : Map.get_basic_map_list()) {
if (unsignedFromIslSize(Result.n_basic_map()) > SimplifyMaxDisjuncts)
break;
Result = Result.unite(BMap);
}
isl::union_map UResult =
UMap.subtract(isl::map::universe(PrevMap.get_space()));
UResult.unite(Result);
return UResult;
}
class SimplifyImpl final {
private:
int CallNo;
Scop *S = nullptr;
int EmptyDomainsRemoved = 0;
int OverwritesRemoved = 0;
int WritesCoalesced = 0;
int RedundantWritesRemoved = 0;
int EmptyPartialAccessesRemoved = 0;
int DeadAccessesRemoved = 0;
int DeadInstructionsRemoved = 0;
int StmtsRemoved = 0;
void removeEmptyDomainStmts();
void removeOverwrites();
void coalesceWrites();
void removeRedundantWrites();
void removeUnnecessaryStmts();
void removeEmptyPartialAccesses();
void markAndSweep(LoopInfo *LI);
void printStatistics(llvm::raw_ostream &OS, int Indent = 0) const;
void printAccesses(llvm::raw_ostream &OS, int Indent = 0) const;
public:
explicit SimplifyImpl(int CallNo = 0) : CallNo(CallNo) {}
void run(Scop &S, LoopInfo *LI);
void printScop(raw_ostream &OS, Scop &S) const;
bool isModified() const;
};
bool SimplifyImpl::isModified() const {
return EmptyDomainsRemoved > 0 || OverwritesRemoved > 0 ||
WritesCoalesced > 0 || RedundantWritesRemoved > 0 ||
EmptyPartialAccessesRemoved > 0 || DeadAccessesRemoved > 0 ||
DeadInstructionsRemoved > 0 || StmtsRemoved > 0;
}
void SimplifyImpl::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();
POLLY_DEBUG(dbgs() << "Removed " << EmptyDomainsRemoved << " (of "
<< NumStmtsBefore << ") statements with empty domains \n");
TotalEmptyDomainsRemoved[CallNo] += EmptyDomainsRemoved;
}
void SimplifyImpl::removeOverwrites() {
for (auto &Stmt : *S) {
isl::set Domain = Stmt.getDomain();
isl::union_map WillBeOverwritten = isl::union_map::empty(S->getIslCtx());
SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
for (auto *MA : reverse(Accesses)) {
if (Stmt.isRegionStmt() && isExplicitAccess(MA))
break;
auto AccRel = MA->getAccessRelation();
AccRel = AccRel.intersect_domain(Domain);
AccRel = AccRel.intersect_params(S->getContext());
if (MA->isRead()) {
isl::map AccRelUniv = isl::map::universe(AccRel.get_space());
WillBeOverwritten = WillBeOverwritten.subtract(AccRelUniv);
continue;
}
isl::union_map AccRelUnion = AccRel;
if (AccRelUnion.is_subset(WillBeOverwritten)) {
POLLY_DEBUG(dbgs() << "Removing " << MA
<< " which will be overwritten anyway\n");
Stmt.removeSingleMemoryAccess(MA);
OverwritesRemoved++;
TotalOverwritesRemoved[CallNo]++;
}
if (MA->isMustWrite()) {
WillBeOverwritten = underapproximatedAddMap(WillBeOverwritten, AccRel);
}
}
}
}
void SimplifyImpl::coalesceWrites() {
for (auto &Stmt : *S) {
isl::set Domain = Stmt.getDomain().intersect_params(S->getContext());
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;
};
isl::union_map FutureWrites = isl::union_map::empty(S->getIslCtx());
SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
for (MemoryAccess *MA : reverse(Accesses)) {
if (Stmt.isRegionStmt() && isExplicitAccess(MA))
break;
isl::map AccRel = MA->getLatestAccessRelation().intersect_domain(Domain);
isl::set AccRelWrapped = AccRel.wrap();
isl::set ValSet;
if (MA->isMustWrite() && (MA->isOriginalScalarKind() ||
isa<StoreInst>(MA->getAccessInstruction()))) {
Value *StoredVal = MA->tryGetValueStored();
if (!StoredVal)
StoredVal = MA->getAccessValue();
ValSet = makeValueSet(StoredVal);
isl::set AccDomain = AccRel.domain();
isl::set UndefDomain = Domain.subtract(AccDomain);
isl::set ElementUniverse =
isl::set::universe(AccRel.get_space().range());
isl::map UndefAnything =
isl::map::from_domain_and_range(UndefDomain, ElementUniverse);
isl::map AllowedAccesses = AccRel.unite(UndefAnything);
isl::map Filter =
isl::map::from_domain_and_range(AllowedAccesses.wrap(), ValSet);
isl::union_map Filtered =
FutureWrites.uncurry().intersect_domain(Filter.wrap());
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);
if (!OtherAccRel.is_subset(AllowedAccesses).is_true())
continue;
isl::map NewAccRel = AccRel.unite(OtherAccRel);
simplify(NewAccRel);
Stmt.removeSingleMemoryAccess(MA);
OtherMA->setNewAccessRelation(NewAccRel);
MA = OtherMA;
TotalWritesCoalesced[CallNo]++;
WritesCoalesced++;
break;
}
}
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.ctx());
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.unite(FutureWrite);
}
FutureWrites = NewFutureWrites;
if (MA->isMustWrite() && !ValSet.is_null()) {
auto AccSet =
isl::set::universe(isl::space(S->getIslCtx(), 0, 0)
.set_tuple_id(isl::dim::set, MA->getId()));
isl::map ValAccSet = isl::map::from_domain_and_range(ValSet, AccSet);
isl::map AccRelValAcc =
isl::map::from_domain_and_range(AccRelWrapped, ValAccSet.wrap());
FutureWrites = FutureWrites.unite(AccRelValAcc);
}
}
}
}
void SimplifyImpl::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());
isl::union_map Known = isl::union_map::empty(S->getIslCtx());
SmallVector<MemoryAccess *, 32> Accesses(getAccessesInOrder(Stmt));
for (MemoryAccess *MA : Accesses) {
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();
if (IsOrdered && MA->isMustWrite() &&
(isa<StoreInst>(MA->getAccessInstruction()) ||
MA->isOriginalScalarKind())) {
Value *StoredVal = MA->tryGetValueStored();
if (!StoredVal)
StoredVal = MA->getAccessValue();
if (StoredVal) {
isl::map AccRelStoredVal = isl::map::from_domain_and_range(
AccRelWrapped, makeValueSet(StoredVal));
if (isl::union_map(AccRelStoredVal).is_subset(Known)) {
POLLY_DEBUG(dbgs() << "Cleanup of " << MA << ":\n");
POLLY_DEBUG(dbgs() << " Scalar: " << *StoredVal << "\n");
POLLY_DEBUG(dbgs() << " AccRel: " << AccRel << "\n");
Stmt.removeSingleMemoryAccess(MA);
RedundantWritesRemoved++;
TotalRedundantWritesRemoved[CallNo]++;
}
}
}
if (MA->isRead()) {
Value *LoadedVal = MA->getAccessValue();
if (LoadedVal && IsOrdered) {
isl::map AccRelVal = isl::map::from_domain_and_range(
AccRelWrapped, makeValueSet(LoadedVal));
Known = Known.unite(AccRelVal);
}
} else if (MA->isWrite()) {
isl::set AccRelUniv = isl::set::universe(AccRelWrapped.get_space());
Known = Known.subtract_domain(AccRelUniv);
}
}
}
}
void SimplifyImpl::removeUnnecessaryStmts() {
auto NumStmtsBefore = S->getSize();
S->simplifySCoP(true);
assert(NumStmtsBefore >= S->getSize());
StmtsRemoved = NumStmtsBefore - S->getSize();
POLLY_DEBUG(dbgs() << "Removed " << StmtsRemoved << " (of " << NumStmtsBefore
<< ") statements\n");
TotalStmtsRemoved[CallNo] += StmtsRemoved;
}
void SimplifyImpl::removeEmptyPartialAccesses() {
for (ScopStmt &Stmt : *S) {
SmallVector<MemoryAccess *, 8> DeferredRemove;
for (MemoryAccess *MA : Stmt) {
if (!MA->isWrite())
continue;
isl::map AccRel = MA->getAccessRelation();
if (!AccRel.is_empty().is_true())
continue;
POLLY_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]++;
}
}
}
void SimplifyImpl::markAndSweep(LoopInfo *LI) {
DenseSet<MemoryAccess *> UsedMA;
DenseSet<VirtualInstruction> UsedInsts;
markReachable(S, LI, UsedInsts, UsedMA);
SmallVector<MemoryAccess *, 64> AllMAs;
for (ScopStmt &Stmt : *S)
AllMAs.append(Stmt.begin(), Stmt.end());
for (MemoryAccess *MA : AllMAs) {
if (UsedMA.count(MA))
continue;
POLLY_DEBUG(dbgs() << "Removing " << MA
<< " because its value is not used\n");
ScopStmt *Stmt = MA->getStatement();
Stmt->removeSingleMemoryAccess(MA);
DeadAccessesRemoved++;
TotalDeadAccessesRemoved[CallNo]++;
}
for (ScopStmt &Stmt : *S) {
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()) {
POLLY_DEBUG(dbgs() << "Removing "; Inst->print(dbgs());
dbgs() << " because it is not used\n");
DeadInstructionsRemoved++;
TotalDeadInstructionsRemoved[CallNo]++;
continue;
}
RemainInsts.push_back(Inst);
UsedInsts.erase(It);
}
Stmt.setInstructions(RemainInsts);
}
}
void SimplifyImpl::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";
}
void SimplifyImpl::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";
}
void SimplifyImpl::run(Scop &S, LoopInfo *LI) {
assert(!this->S);
assert(!isModified());
this->S = &S;
ScopsProcessed[CallNo]++;
POLLY_DEBUG(dbgs() << "Removing statements that are never executed...\n");
removeEmptyDomainStmts();
POLLY_DEBUG(dbgs() << "Removing partial writes that never happen...\n");
removeEmptyPartialAccesses();
POLLY_DEBUG(dbgs() << "Removing overwrites...\n");
removeOverwrites();
POLLY_DEBUG(dbgs() << "Coalesce partial writes...\n");
coalesceWrites();
POLLY_DEBUG(dbgs() << "Removing redundant writes...\n");
removeRedundantWrites();
POLLY_DEBUG(dbgs() << "Cleanup unused accesses...\n");
markAndSweep(LI);
POLLY_DEBUG(dbgs() << "Removing statements without side effects...\n");
removeUnnecessaryStmts();
if (isModified())
ScopsModified[CallNo]++;
POLLY_DEBUG(dbgs() << "\nFinal Scop:\n");
POLLY_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;
}
void SimplifyImpl::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);
}
class SimplifyWrapperPass final : public ScopPass {
public:
static char ID;
int CallNo;
std::optional<SimplifyImpl> Impl;
explicit SimplifyWrapperPass(int CallNo = 0) : ScopPass(ID), CallNo(CallNo) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequiredTransitive<ScopInfoRegionPass>();
AU.addRequired<LoopInfoWrapperPass>();
AU.setPreservesAll();
}
bool runOnScop(Scop &S) override {
LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
Impl.emplace(CallNo);
Impl->run(S, LI);
return false;
}
void printScop(raw_ostream &OS, Scop &S) const override {
if (Impl)
Impl->printScop(OS, S);
}
void releaseMemory() override { Impl.reset(); }
};
char SimplifyWrapperPass::ID;
static llvm::PreservedAnalyses
runSimplifyUsingNPM(Scop &S, ScopAnalysisManager &SAM,
ScopStandardAnalysisResults &SAR, SPMUpdater &U, int CallNo,
raw_ostream *OS) {
SimplifyImpl Impl(CallNo);
Impl.run(S, &SAR.LI);
if (OS) {
*OS << "Printing analysis 'Polly - Simplify' for region: '" << S.getName()
<< "' in function '" << S.getFunction().getName() << "':\n";
Impl.printScop(*OS, S);
}
if (!Impl.isModified())
return llvm::PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserveSet<AllAnalysesOn<Module>>();
PA.preserveSet<AllAnalysesOn<Function>>();
PA.preserveSet<AllAnalysesOn<Loop>>();
return PA;
}
}
llvm::PreservedAnalyses SimplifyPass::run(Scop &S, ScopAnalysisManager &SAM,
ScopStandardAnalysisResults &SAR,
SPMUpdater &U) {
return runSimplifyUsingNPM(S, SAM, SAR, U, CallNo, nullptr);
}
llvm::PreservedAnalyses
SimplifyPrinterPass::run(Scop &S, ScopAnalysisManager &SAM,
ScopStandardAnalysisResults &SAR, SPMUpdater &U) {
return runSimplifyUsingNPM(S, SAM, SAR, U, CallNo, &OS);
}
SmallVector<MemoryAccess *, 32> polly::getAccessesInOrder(ScopStmt &Stmt) {
SmallVector<MemoryAccess *, 32> Accesses;
for (MemoryAccess *MemAcc : Stmt)
if (isImplicitRead(MemAcc))
Accesses.push_back(MemAcc);
for (MemoryAccess *MemAcc : Stmt)
if (isExplicitAccess(MemAcc))
Accesses.push_back(MemAcc);
for (MemoryAccess *MemAcc : Stmt)
if (isImplicitWrite(MemAcc))
Accesses.push_back(MemAcc);
return Accesses;
}
Pass *polly::createSimplifyWrapperPass(int CallNo) {
return new SimplifyWrapperPass(CallNo);
}
INITIALIZE_PASS_BEGIN(SimplifyWrapperPass, "polly-simplify", "Polly - Simplify",
false, false)
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
INITIALIZE_PASS_END(SimplifyWrapperPass, "polly-simplify", "Polly - Simplify",
false, false)
namespace {
class SimplifyPrinterLegacyPass final : public ScopPass {
public:
static char ID;
SimplifyPrinterLegacyPass() : SimplifyPrinterLegacyPass(outs()) {}
explicit SimplifyPrinterLegacyPass(llvm::raw_ostream &OS)
: ScopPass(ID), OS(OS) {}
bool runOnScop(Scop &S) override {
SimplifyWrapperPass &P = getAnalysis<SimplifyWrapperPass>();
OS << "Printing analysis '" << P.getPassName() << "' for region: '"
<< S.getRegion().getNameStr() << "' in function '"
<< S.getFunction().getName() << "':\n";
P.printScop(OS, S);
return false;
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
ScopPass::getAnalysisUsage(AU);
AU.addRequired<SimplifyWrapperPass>();
AU.setPreservesAll();
}
private:
llvm::raw_ostream &OS;
};
char SimplifyPrinterLegacyPass::ID = 0;
}
Pass *polly::createSimplifyPrinterLegacyPass(raw_ostream &OS) {
return new SimplifyPrinterLegacyPass(OS);
}
INITIALIZE_PASS_BEGIN(SimplifyPrinterLegacyPass, "polly-print-simplify",
"Polly - Print Simplify actions", false, false)
INITIALIZE_PASS_DEPENDENCY(SimplifyWrapperPass)
INITIALIZE_PASS_END(SimplifyPrinterLegacyPass, "polly-print-simplify",
"Polly - Print Simplify actions", false, false)