forked from OSchip/llvm-project
[DeLICM] Add Knowledge class. NFC.
The Knowledge class remembers the state of data at any timepoint of a SCoP's execution. Currently, it tracks whether an array element is unused or is occupied by some value, and the writes to it. A future addition will be to also remember which value it contains. Objects are used to determine whether two Knowledge contain conflicting information, i.e. two states cannot be true a the same time. This commit was extracted from the DeLICM algorithm at https://reviews.llvm.org/D24716. llvm-svn: 295197
This commit is contained in:
parent
fbe32f59c2
commit
e23e94a08d
|
@ -18,6 +18,8 @@
|
||||||
#ifndef POLLY_DELICM_H
|
#ifndef POLLY_DELICM_H
|
||||||
#define POLLY_DELICM_H
|
#define POLLY_DELICM_H
|
||||||
|
|
||||||
|
#include "polly/Support/GICHelper.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
class PassRegistry;
|
class PassRegistry;
|
||||||
class Pass;
|
class Pass;
|
||||||
|
@ -26,6 +28,17 @@ class Pass;
|
||||||
namespace polly {
|
namespace polly {
|
||||||
/// Create a new DeLICM pass instance.
|
/// Create a new DeLICM pass instance.
|
||||||
llvm::Pass *createDeLICMPass();
|
llvm::Pass *createDeLICMPass();
|
||||||
|
|
||||||
|
/// Determine whether two lifetimes are conflicting.
|
||||||
|
///
|
||||||
|
/// Used by unittesting.
|
||||||
|
bool isConflicting(IslPtr<isl_union_set> ExistingOccupied,
|
||||||
|
IslPtr<isl_union_set> ExistingUnused,
|
||||||
|
IslPtr<isl_union_set> ExistingWrites,
|
||||||
|
IslPtr<isl_union_set> ProposedOccupied,
|
||||||
|
IslPtr<isl_union_set> ProposedUnused,
|
||||||
|
IslPtr<isl_union_set> ProposedWrites,
|
||||||
|
llvm::raw_ostream *OS = nullptr, unsigned Indent = 0);
|
||||||
} // namespace polly
|
} // namespace polly
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
|
@ -13,11 +13,106 @@
|
||||||
// Namely, remove register/scalar dependencies by mapping them back to array
|
// Namely, remove register/scalar dependencies by mapping them back to array
|
||||||
// elements.
|
// elements.
|
||||||
//
|
//
|
||||||
|
// The algorithms here work on the scatter space - the image space of the
|
||||||
|
// schedule returned by Scop::getSchedule(). We call an element in that space a
|
||||||
|
// "timepoint". Timepoints are lexicographically ordered such that we can
|
||||||
|
// defined ranges in the scatter space. We use two flavors of such ranges:
|
||||||
|
// Timepoint sets and zones. A timepoint set is simply a subset of the scatter
|
||||||
|
// space and is directly stored as isl_set.
|
||||||
|
//
|
||||||
|
// Zones are used to describe the space between timepoints as open sets, i.e.
|
||||||
|
// they do not contain the extrema. Using isl rational sets to express these
|
||||||
|
// would be overkill. We also cannot store them as the integer timepoints they
|
||||||
|
// contain; the (nonempty) zone between 1 and 2 would be empty and
|
||||||
|
// indistinguishable from e.g. the zone between 3 and 4. Also, we cannot store
|
||||||
|
// the integer set including the extrema; the set ]1,2[ + ]3,4[ could be
|
||||||
|
// coalesced to ]1,3[, although we defined the range [2,3] to be not in the set.
|
||||||
|
// Instead, we store the "half-open" integer extrema, including the lower bound,
|
||||||
|
// but excluding the upper bound. Examples:
|
||||||
|
//
|
||||||
|
// * The set { [i] : 1 <= i <= 3 } represents the zone ]0,3[ (which contains the
|
||||||
|
// integer points 1 and 2, but not 0 or 3)
|
||||||
|
//
|
||||||
|
// * { [1] } represents the zone ]0,1[
|
||||||
|
//
|
||||||
|
// * { [i] : i = 1 or i = 3 } represents the zone ]0,1[ + ]2,3[
|
||||||
|
//
|
||||||
|
// Therefore, an integer i in the set represents the zone ]i-1,i[, i.e. strictly
|
||||||
|
// speaking the integer points never belong to the zone. However, depending an
|
||||||
|
// the interpretation, one might want to include them. Part of the
|
||||||
|
// interpretation may not be known when the zone is constructed.
|
||||||
|
//
|
||||||
|
// Reads are assumed to always take place before writes, hence we can think of
|
||||||
|
// reads taking place at the beginning of a timepoint and writes at the end.
|
||||||
|
//
|
||||||
|
// Let's assume that the zone represents the lifetime of a variable. That is,
|
||||||
|
// the zone begins with a write that defines the value during its lifetime and
|
||||||
|
// ends with the last read of that value. In the following we consider whether a
|
||||||
|
// read/write at the beginning/ending of the lifetime zone should be within the
|
||||||
|
// zone or outside of it.
|
||||||
|
//
|
||||||
|
// * A read at the timepoint that starts the live-range loads the previous
|
||||||
|
// value. Hence, exclude the timepoint starting the zone.
|
||||||
|
//
|
||||||
|
// * A write at the timepoint that starts the live-range is not defined whether
|
||||||
|
// it occurs before or after the write that starts the lifetime. We do not
|
||||||
|
// allow this situation to occur. Hence, we include the timepoint starting the
|
||||||
|
// zone to determine whether they are conflicting.
|
||||||
|
//
|
||||||
|
// * A read at the timepoint that ends the live-range reads the same variable.
|
||||||
|
// We include the timepoint at the end of the zone to include that read into
|
||||||
|
// the live-range. Doing otherwise would mean that the two reads access
|
||||||
|
// different values, which would mean that the value they read are both alive
|
||||||
|
// at the same time but occupy the same variable.
|
||||||
|
//
|
||||||
|
// * A write at the timepoint that ends the live-range starts a new live-range.
|
||||||
|
// It must not be included in the live-range of the previous definition.
|
||||||
|
//
|
||||||
|
// All combinations of reads and writes at the endpoints are possible, but most
|
||||||
|
// of the time only the write->read (for instance, a live-range from definition
|
||||||
|
// to last use) and read->write (for instance, an unused range from last use to
|
||||||
|
// overwrite) and combinations are interesting (half-open ranges). write->write
|
||||||
|
// zones might be useful as well in some context to represent
|
||||||
|
// output-dependencies.
|
||||||
|
//
|
||||||
|
// @see convertZoneToTimepoints
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// The code makes use of maps and sets in many different spaces. To not loose
|
||||||
|
// track in which space a set or map is expected to be in, variables holding an
|
||||||
|
// isl reference are usually annotated in the comments. They roughly follow isl
|
||||||
|
// syntax for spaces, but only the tuples, not the dimensions. The tuples have a
|
||||||
|
// meaning as follows:
|
||||||
|
//
|
||||||
|
// * Space[] - An unspecified tuple. Used for function parameters such that the
|
||||||
|
// function caller can use it for anything they like.
|
||||||
|
//
|
||||||
|
// * Domain[] - A statement instance as returned by ScopStmt::getDomain()
|
||||||
|
// isl_id_get_name: Stmt_<NameOfBasicBlock>
|
||||||
|
// isl_id_get_user: Pointer to ScopStmt
|
||||||
|
//
|
||||||
|
// * Element[] - An array element as in the range part of
|
||||||
|
// MemoryAccess::getAccessRelation()
|
||||||
|
// isl_id_get_name: MemRef_<NameOfArrayVariable>
|
||||||
|
// isl_id_get_user: Pointer to ScopArrayInfo
|
||||||
|
//
|
||||||
|
// * Scatter[] - Scatter space or space of timepoints
|
||||||
|
// Has no tuple id
|
||||||
|
//
|
||||||
|
// * Zone[] - Range between timepoints as described above
|
||||||
|
// Has no tuple id
|
||||||
|
//
|
||||||
|
// An annotation "{ Domain[] -> Scatter[] }" therefore means: A map from a
|
||||||
|
// statement instance to a timepoint, aka a schedule. There is only one scatter
|
||||||
|
// space, but most of the time multiple statements are processed in one set.
|
||||||
|
// This is why most of the time isl_union_map has to be used.
|
||||||
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "polly/DeLICM.h"
|
#include "polly/DeLICM.h"
|
||||||
#include "polly/ScopInfo.h"
|
#include "polly/ScopInfo.h"
|
||||||
#include "polly/ScopPass.h"
|
#include "polly/ScopPass.h"
|
||||||
|
#include "polly/Support/ISLTools.h"
|
||||||
#define DEBUG_TYPE "polly-delicm"
|
#define DEBUG_TYPE "polly-delicm"
|
||||||
|
|
||||||
using namespace polly;
|
using namespace polly;
|
||||||
|
@ -25,6 +120,259 @@ using namespace llvm;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/// Represent the knowledge of the contents of any array elements in any zone or
|
||||||
|
/// the knowledge we would add when mapping a scalar to an array element.
|
||||||
|
///
|
||||||
|
/// Every array element at every zone unit has one of two states:
|
||||||
|
///
|
||||||
|
/// - Unused: Not occupied by any value so a transformation can change it to
|
||||||
|
/// other values.
|
||||||
|
///
|
||||||
|
/// - Occupied: The element contains a value that is still needed.
|
||||||
|
///
|
||||||
|
/// The union of Unused and Unknown zones forms the universe, the set of all
|
||||||
|
/// elements at every timepoint. The universe can easily be derived from the
|
||||||
|
/// array elements that are accessed someway. Arrays that are never accessed
|
||||||
|
/// also never play a role in any computation and can hence be ignored. With a
|
||||||
|
/// given universe, only one of the sets needs to stored implicitly. Computing
|
||||||
|
/// the complement is also an expensive operation, hence this class has been
|
||||||
|
/// designed that only one of sets is needed while the other is assumed to be
|
||||||
|
/// implicit. It can still be given, but is mostly ignored.
|
||||||
|
///
|
||||||
|
/// There are two use cases for the Knowledge class:
|
||||||
|
///
|
||||||
|
/// 1) To represent the knowledge of the current state of ScopInfo. The unused
|
||||||
|
/// state means that an element is currently unused: there is no read of it
|
||||||
|
/// before the next overwrite. Also called 'Existing'.
|
||||||
|
///
|
||||||
|
/// 2) To represent the requirements for mapping a scalar to array elements. The
|
||||||
|
/// unused state means that there is no change/requirement. Also called
|
||||||
|
/// 'Proposed'.
|
||||||
|
///
|
||||||
|
/// In addition to these states at unit zones, Knowledge needs to know when
|
||||||
|
/// values are written. This is because written values may have no lifetime (one
|
||||||
|
/// reason is that the value is never read). Such writes would therefore never
|
||||||
|
/// conflict, but overwrite values that might still be required. Another source
|
||||||
|
/// of problems are multiple writes to the same element at the same timepoint,
|
||||||
|
/// because their order is undefined.
|
||||||
|
class Knowledge {
|
||||||
|
private:
|
||||||
|
/// { [Element[] -> Zone[]] }
|
||||||
|
/// Set of array elements and when they are alive.
|
||||||
|
/// Can contain a nullptr; in this case the set is implicitly defined as the
|
||||||
|
/// complement of #Unused.
|
||||||
|
///
|
||||||
|
/// The set of alive array elements is represented as zone, as the set of live
|
||||||
|
/// values can differ depending on how the elements are interpreted.
|
||||||
|
/// Assuming a value X is written at timestep [0] and read at timestep [1]
|
||||||
|
/// without being used at any later point, then the value is alive in the
|
||||||
|
/// interval ]0,1[. This interval cannot be represented by an integer set, as
|
||||||
|
/// it does not contain any integer point. Zones allow us to represent this
|
||||||
|
/// interval and can be converted to sets of timepoints when needed (e.g., in
|
||||||
|
/// isConflicting when comparing to the write sets).
|
||||||
|
/// @see convertZoneToTimepoints and this file's comment for more details.
|
||||||
|
IslPtr<isl_union_set> Occupied;
|
||||||
|
|
||||||
|
/// { [Element[] -> Zone[]] }
|
||||||
|
/// Set of array elements when they are not alive, i.e. their memory can be
|
||||||
|
/// used for other purposed. Can contain a nullptr; in this case the set is
|
||||||
|
/// implicitly defined as the complement of #Occupied.
|
||||||
|
IslPtr<isl_union_set> Unused;
|
||||||
|
|
||||||
|
/// { [Element[] -> Scatter[]] }
|
||||||
|
/// The write actions currently in the scop or that would be added when
|
||||||
|
/// mapping a scalar.
|
||||||
|
IslPtr<isl_union_set> Written;
|
||||||
|
|
||||||
|
/// Check whether this Knowledge object is well-formed.
|
||||||
|
void checkConsistency() const {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// Default-initialized object
|
||||||
|
if (!Occupied && !Unused && !Written)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(Occupied || Unused);
|
||||||
|
assert(Written);
|
||||||
|
|
||||||
|
// If not all fields are defined, we cannot derived the universe.
|
||||||
|
if (!Occupied || !Unused)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(isl_union_set_is_disjoint(Occupied.keep(), Unused.keep()) ==
|
||||||
|
isl_bool_true);
|
||||||
|
auto Universe = give(isl_union_set_union(Occupied.copy(), Unused.copy()));
|
||||||
|
assert(isl_union_set_is_subset(Written.keep(), Universe.keep()) ==
|
||||||
|
isl_bool_true);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Initialize a nullptr-Knowledge. This is only provided for convenience; do
|
||||||
|
/// not use such an object.
|
||||||
|
Knowledge() {}
|
||||||
|
|
||||||
|
/// Create a new object with the given members.
|
||||||
|
Knowledge(IslPtr<isl_union_set> Occupied, IslPtr<isl_union_set> Unused,
|
||||||
|
IslPtr<isl_union_set> Written)
|
||||||
|
: Occupied(std::move(Occupied)), Unused(std::move(Unused)),
|
||||||
|
Written(std::move(Written)) {
|
||||||
|
checkConsistency();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alternative constructor taking isl_sets instead isl_union_sets.
|
||||||
|
Knowledge(IslPtr<isl_set> Occupied, IslPtr<isl_set> Unused,
|
||||||
|
IslPtr<isl_set> Written)
|
||||||
|
: Knowledge(give(isl_union_set_from_set(Occupied.take())),
|
||||||
|
give(isl_union_set_from_set(Unused.take())),
|
||||||
|
give(isl_union_set_from_set(Written.take()))) {}
|
||||||
|
|
||||||
|
/// Return whether this object was not default-constructed.
|
||||||
|
bool isUsable() const { return (Occupied || Unused) && Written; }
|
||||||
|
|
||||||
|
/// Print the content of this object to @p OS.
|
||||||
|
void print(llvm::raw_ostream &OS, unsigned Indent = 0) const {
|
||||||
|
if (isUsable()) {
|
||||||
|
if (Occupied)
|
||||||
|
OS.indent(Indent) << "Occupied: " << Occupied << "\n";
|
||||||
|
else
|
||||||
|
OS.indent(Indent) << "Occupied: <Everything else not in Unused>\n";
|
||||||
|
if (Unused)
|
||||||
|
OS.indent(Indent) << "Unused: " << Unused << "\n";
|
||||||
|
else
|
||||||
|
OS.indent(Indent) << "Unused: <Everything else not in Occupied>\n";
|
||||||
|
OS.indent(Indent) << "Written : " << Written << '\n';
|
||||||
|
} else {
|
||||||
|
OS.indent(Indent) << "Invalid knowledge\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Combine two knowledges, this and @p That.
|
||||||
|
void learnFrom(Knowledge That) {
|
||||||
|
assert(!isConflicting(*this, That));
|
||||||
|
assert(Unused && That.Occupied);
|
||||||
|
assert(
|
||||||
|
!That.Unused &&
|
||||||
|
"This function is only prepared to learn occupied elements from That");
|
||||||
|
assert(!Occupied && "This function does not implement "
|
||||||
|
"`this->Occupied = "
|
||||||
|
"give(isl_union_set_union(this->Occupied.take(), "
|
||||||
|
"That.Occupied.copy()));`");
|
||||||
|
|
||||||
|
Unused = give(isl_union_set_subtract(Unused.take(), That.Occupied.copy()));
|
||||||
|
Written = give(isl_union_set_union(Written.take(), That.Written.take()));
|
||||||
|
|
||||||
|
checkConsistency();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine whether two Knowledges conflict with each other.
|
||||||
|
///
|
||||||
|
/// In theory @p Existing and @p Proposed are symmetric, but the
|
||||||
|
/// implementation is constrained by the implicit interpretation. That is, @p
|
||||||
|
/// Existing must have #Unused defined (use case 1) and @p Proposed must have
|
||||||
|
/// #Occupied defined (use case 1).
|
||||||
|
///
|
||||||
|
/// A conflict is defined as non-preserved semantics when they are merged. For
|
||||||
|
/// instance, when for the same array and zone they assume different
|
||||||
|
/// llvm::Values.
|
||||||
|
///
|
||||||
|
/// @param Existing One of the knowledges with #Unused defined.
|
||||||
|
/// @param Proposed One of the knowledges with #Occupied defined.
|
||||||
|
/// @param OS Dump the conflict reason to this output stream; use
|
||||||
|
/// nullptr to not output anything.
|
||||||
|
/// @param Indent Indention for the conflict reason.
|
||||||
|
///
|
||||||
|
/// @return True, iff the two knowledges are conflicting.
|
||||||
|
static bool isConflicting(const Knowledge &Existing,
|
||||||
|
const Knowledge &Proposed,
|
||||||
|
llvm::raw_ostream *OS = nullptr,
|
||||||
|
unsigned Indent = 0) {
|
||||||
|
assert(Existing.Unused);
|
||||||
|
assert(Proposed.Occupied);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (Existing.Occupied && Proposed.Unused) {
|
||||||
|
auto ExistingUniverse = give(isl_union_set_union(Existing.Occupied.copy(),
|
||||||
|
Existing.Unused.copy()));
|
||||||
|
auto ProposedUniverse = give(isl_union_set_union(Proposed.Occupied.copy(),
|
||||||
|
Proposed.Unused.copy()));
|
||||||
|
assert(isl_union_set_is_equal(ExistingUniverse.keep(),
|
||||||
|
ProposedUniverse.keep()) == isl_bool_true &&
|
||||||
|
"Both inputs' Knowledges must be over the same universe");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Are the new lifetimes required for Proposed unused in Existing?
|
||||||
|
if (isl_union_set_is_subset(Proposed.Occupied.keep(),
|
||||||
|
Existing.Unused.keep()) != isl_bool_true) {
|
||||||
|
if (OS) {
|
||||||
|
auto ConflictingLifetimes = give(isl_union_set_subtract(
|
||||||
|
Proposed.Occupied.copy(), Existing.Unused.copy()));
|
||||||
|
OS->indent(Indent) << "Proposed lifetimes are not unused in existing\n";
|
||||||
|
OS->indent(Indent) << "Conflicting lifetimes: " << ConflictingLifetimes
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the writes in Existing only overwrite unused values in Proposed?
|
||||||
|
// We convert here the set of lifetimes to actual timepoints. A lifetime is
|
||||||
|
// in conflict with a set of write timepoints, if either a live timepoint is
|
||||||
|
// clearly within the lifetime or if a write happens at the beginning of the
|
||||||
|
// lifetime (where it would conflict with the value that actually writes the
|
||||||
|
// value alive). There is no conflict at the end of a lifetime, as the alive
|
||||||
|
// value will always be read, before it is overwritten again. The last
|
||||||
|
// property holds in Polly for all scalar values and we expect all users of
|
||||||
|
// Knowledge to check this property also for accesses to MemoryKind::Array.
|
||||||
|
auto ProposedFixedDefs =
|
||||||
|
convertZoneToTimepoints(Proposed.Occupied, true, false);
|
||||||
|
if (isl_union_set_is_disjoint(Existing.Written.keep(),
|
||||||
|
ProposedFixedDefs.keep()) != isl_bool_true) {
|
||||||
|
if (OS) {
|
||||||
|
auto ConflictingWrites = give(isl_union_set_intersect(
|
||||||
|
Existing.Written.copy(), ProposedFixedDefs.copy()));
|
||||||
|
OS->indent(Indent) << "Proposed writes into range used by existing\n";
|
||||||
|
OS->indent(Indent) << "Conflicting writes: " << ConflictingWrites
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the new writes in Proposed only overwrite unused values in Existing?
|
||||||
|
auto ExistingAvailableDefs =
|
||||||
|
convertZoneToTimepoints(Existing.Unused, true, false);
|
||||||
|
if (isl_union_set_is_subset(Proposed.Written.keep(),
|
||||||
|
ExistingAvailableDefs.keep()) !=
|
||||||
|
isl_bool_true) {
|
||||||
|
if (OS) {
|
||||||
|
auto ConflictingWrites = give(isl_union_set_subtract(
|
||||||
|
Proposed.Written.copy(), ExistingAvailableDefs.copy()));
|
||||||
|
OS->indent(Indent)
|
||||||
|
<< "Proposed a lifetime where there is an Existing write into it\n";
|
||||||
|
OS->indent(Indent) << "Conflicting writes: " << ConflictingWrites
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does Proposed write at the same time as Existing already does (order of
|
||||||
|
// writes is undefined)?
|
||||||
|
if (isl_union_set_is_disjoint(Existing.Written.keep(),
|
||||||
|
Proposed.Written.keep()) != isl_bool_true) {
|
||||||
|
if (OS) {
|
||||||
|
auto ConflictingWrites = give(isl_union_set_intersect(
|
||||||
|
Existing.Written.copy(), Proposed.Written.copy()));
|
||||||
|
OS->indent(Indent) << "Proposed writes at the same time as an already "
|
||||||
|
"Existing write\n";
|
||||||
|
OS->indent(Indent) << "Conflicting writes: " << ConflictingWrites
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class DeLICM : public ScopPass {
|
class DeLICM : public ScopPass {
|
||||||
private:
|
private:
|
||||||
DeLICM(const DeLICM &) = delete;
|
DeLICM(const DeLICM &) = delete;
|
||||||
|
@ -68,3 +416,18 @@ INITIALIZE_PASS_BEGIN(DeLICM, "polly-delicm", "Polly - DeLICM/DePRE", false,
|
||||||
INITIALIZE_PASS_DEPENDENCY(ScopInfoWrapperPass)
|
INITIALIZE_PASS_DEPENDENCY(ScopInfoWrapperPass)
|
||||||
INITIALIZE_PASS_END(DeLICM, "polly-delicm", "Polly - DeLICM/DePRE", false,
|
INITIALIZE_PASS_END(DeLICM, "polly-delicm", "Polly - DeLICM/DePRE", false,
|
||||||
false)
|
false)
|
||||||
|
|
||||||
|
bool polly::isConflicting(IslPtr<isl_union_set> ExistingOccupied,
|
||||||
|
IslPtr<isl_union_set> ExistingUnused,
|
||||||
|
IslPtr<isl_union_set> ExistingWrites,
|
||||||
|
IslPtr<isl_union_set> ProposedOccupied,
|
||||||
|
IslPtr<isl_union_set> ProposedUnused,
|
||||||
|
IslPtr<isl_union_set> ProposedWrites,
|
||||||
|
llvm::raw_ostream *OS, unsigned Indent) {
|
||||||
|
Knowledge Existing(std::move(ExistingOccupied), std::move(ExistingUnused),
|
||||||
|
std::move(ExistingWrites));
|
||||||
|
Knowledge Proposed(std::move(ProposedOccupied), std::move(ProposedUnused),
|
||||||
|
std::move(ProposedWrites));
|
||||||
|
|
||||||
|
return Knowledge::isConflicting(Existing, Proposed, OS, Indent);
|
||||||
|
}
|
||||||
|
|
|
@ -16,8 +16,9 @@ function(add_polly_unittest test_name)
|
||||||
|
|
||||||
set_property(TARGET ${test_name} PROPERTY FOLDER "Polly")
|
set_property(TARGET ${test_name} PROPERTY FOLDER "Polly")
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(${test_name} Polly LLVMCore LLVMSupport LLVMDemangle)
|
target_link_libraries(${test_name} Polly LLVMCore LLVMSupport LLVMDemangle LLVMipo)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
add_subdirectory(Isl)
|
add_subdirectory(Isl)
|
||||||
add_subdirectory(Flatten)
|
add_subdirectory(Flatten)
|
||||||
|
add_subdirectory(DeLICM)
|
||||||
|
|
Loading…
Reference in New Issue