Files
clang-p2996/polly/lib/Transform/DeadCodeElimination.cpp
Tobias Grosser 3e618c33fe [DeadCodeElimination] Translate to C++ bindings
This pass is a small and self-contained example of a piece of code that was
written with the isl C interface. The diff of this change nicely shows how the
C++ bindings can improve the readability of the code by avoiding the long C
function names and by avoiding any need for memory management.

As you will see, no calls to isl_*_copy or isl_*_free are needed anymore.
Instead the C++ interface takes care of automatically managing the objects.
This may introduce internally additional copies, but due to the isl reference
counting, such copies are expected to be cheap. For performance critical
operations, we will later exploit move semantics to eliminate unnecessary
copies that have shown to be costly.

Below we give a set of examples that shows the benefit of the C++ interface vs.
the pure C interface.

Check properties
----------------

Before:

  if (isl_aff_is_zero(aff) ||  isl_aff_is_one(aff))
    return true;

After:

  if (Aff.is_zero() || Aff.is_one())
    return true;

Type conversion
---------------

Before:

  isl_union_pw_multi_aff *UPMA = isl_union_pw_multi_aff_from_union_map(umap);

After:

  isl::union_pw_multi_aff UPMA = UMap;

Type construction
-----------------

Before:

  auto *Empty = isl_union_map_empty(space);

After:

  auto Empty = isl::union_map::empty(Space);

Operations
----------

Before:

  set = isl_union_set_intersect(set, set2);

After:

  Set = Set.intersect(Set2);

The use of isl::boolean in return types also adds an increases the robustness
of Polly, as on conversion to true or false, we verify that no isl_bool_error
has been returned and assert in case an error was returned. Before this change
we would have just ignored the error and proceeded with (some) exection path.

Tags: #polly

Reviewed By: Meinersbur

Differential Revision: https://reviews.llvm.org/D30619

llvm-svn: 297466
2017-03-10 15:05:38 +00:00

183 lines
6.0 KiB
C++

//===- DeadCodeElimination.cpp - Eliminate dead iteration ----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// The polyhedral dead code elimination pass analyses a SCoP to eliminate
// statement instances that can be proven dead.
// As a consequence, the code generated for this SCoP may execute a statement
// less often. This means, a statement may be executed only in certain loop
// iterations or it may not even be part of the generated code at all.
//
// This code:
//
// for (i = 0; i < N; i++)
// arr[i] = 0;
// for (i = 0; i < N; i++)
// arr[i] = 10;
// for (i = 0; i < N; i++)
// arr[i] = i;
//
// is e.g. simplified to:
//
// for (i = 0; i < N; i++)
// arr[i] = i;
//
// The idea and the algorithm used was first implemented by Sven Verdoolaege in
// the 'ppcg' tool.
//
//===----------------------------------------------------------------------===//
#include "polly/DependenceInfo.h"
#include "polly/LinkAllPasses.h"
#include "polly/Options.h"
#include "polly/ScopInfo.h"
#include "llvm/Support/CommandLine.h"
#include "isl/flow.h"
#include "isl/map.h"
#include "isl/set.h"
#include "isl/union_map.h"
#include "isl/union_set.h"
#include "isl-noexceptions.h"
using namespace llvm;
using namespace polly;
namespace {
cl::opt<int> DCEPreciseSteps(
"polly-dce-precise-steps",
cl::desc("The number of precise steps between two approximating "
"iterations. (A value of -1 schedules another approximation stage "
"before the actual dead code elimination."),
cl::ZeroOrMore, cl::init(-1), cl::cat(PollyCategory));
class DeadCodeElim : public ScopPass {
public:
static char ID;
explicit DeadCodeElim() : ScopPass(ID) {}
/// Remove dead iterations from the schedule of @p S.
bool runOnScop(Scop &S) override;
/// Register all analyses and transformation required.
void getAnalysisUsage(AnalysisUsage &AU) const override;
private:
/// Return the set of live iterations.
///
/// The set of live iterations are all iterations that write to memory and for
/// which we can not prove that there will be a later write that _must_
/// overwrite the same memory location and is consequently the only one that
/// is visible after the execution of the SCoP.
///
isl::union_set getLiveOut(Scop &S);
bool eliminateDeadCode(Scop &S, int PreciseSteps);
};
} // namespace
char DeadCodeElim::ID = 0;
// To compute the live outs, we compute for the data-locations that are
// must-written to the last statement that touches these locations. On top of
// this we add all statements that perform may-write accesses.
//
// We could be more precise by removing may-write accesses for which we know
// that they are overwritten by a must-write after. However, at the moment the
// only may-writes we introduce access the full (unbounded) array, such that
// bounded write accesses can not overwrite all of the data-locations. As
// this means may-writes are in the current situation always live, there is
// no point in trying to remove them from the live-out set.
isl::union_set DeadCodeElim::getLiveOut(Scop &S) {
isl::union_map Schedule = isl::manage(S.getSchedule());
isl::union_map MustWrites = isl::manage(S.getMustWrites());
isl::union_map WriteIterations = MustWrites.reverse();
isl::union_map WriteTimes = WriteIterations.apply_range(Schedule);
isl::union_map LastWriteTimes = WriteTimes.lexmax();
isl::union_map LastWriteIterations =
LastWriteTimes.apply_range(Schedule.reverse());
isl::union_set Live = LastWriteIterations.range();
isl::union_map MayWrites = isl::manage(S.getMayWrites());
Live = Live.unite(MayWrites.domain());
return Live.coalesce();
}
/// Performs polyhedral dead iteration elimination by:
/// o Assuming that the last write to each location is live.
/// o Following each RAW dependency from a live iteration backwards and adding
/// that iteration to the live set.
///
/// To ensure the set of live iterations does not get too complex we always
/// combine a certain number of precise steps with one approximating step that
/// simplifies the life set with an affine hull.
bool DeadCodeElim::eliminateDeadCode(Scop &S, int PreciseSteps) {
DependenceInfo &DI = getAnalysis<DependenceInfo>();
const Dependences &D = DI.getDependences(Dependences::AL_Statement);
if (!D.hasValidDependences())
return false;
isl::union_set Live = getLiveOut(S);
isl::union_map Dep = isl::manage(
D.getDependences(Dependences::TYPE_RAW | Dependences::TYPE_RED));
Dep = Dep.reverse();
if (PreciseSteps == -1)
Live = Live.affine_hull();
isl::union_set OriginalDomain = isl::manage(S.getDomains());
int Steps = 0;
while (true) {
Steps++;
isl::union_set Extra = Live.apply(Dep);
if (Extra.is_subset(Live))
break;
Live = Live.unite(Extra);
if (Steps > PreciseSteps) {
Steps = 0;
Live = Live.affine_hull();
}
Live = Live.intersect(OriginalDomain);
}
Live = Live.coalesce();
bool Changed = S.restrictDomains(Live.copy());
// FIXME: We can probably avoid the recomputation of all dependences by
// updating them explicitly.
if (Changed)
DI.recomputeDependences(Dependences::AL_Statement);
return Changed;
}
bool DeadCodeElim::runOnScop(Scop &S) {
return eliminateDeadCode(S, DCEPreciseSteps);
}
void DeadCodeElim::getAnalysisUsage(AnalysisUsage &AU) const {
ScopPass::getAnalysisUsage(AU);
AU.addRequired<DependenceInfo>();
}
Pass *polly::createDeadCodeElimPass() { return new DeadCodeElim(); }
INITIALIZE_PASS_BEGIN(DeadCodeElim, "polly-dce",
"Polly - Remove dead iterations", false, false)
INITIALIZE_PASS_DEPENDENCY(DependenceInfo)
INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass)
INITIALIZE_PASS_END(DeadCodeElim, "polly-dce", "Polly - Remove dead iterations",
false, false)