Skip to content

Commit d257f7c

Browse files
committed
[MLIR][FlatAffineConstraints] Remove duplicate divisions while merging local ids
This patch implements detecting duplicate local identifiers by extracting their division representation while merging local identifiers. For example, given the FACs A, B: ``` A: (x, y)[s0] : (exists d0 = [x / 4], d1 = [y / 4]: d0 <= s0, d1 <= s0, x + y >= 2) B: (x, y)[s0] : (exists d0 = [x / 4], d1 = [y / 4]: d0 <= s0, d1 <= s0, x + y >= 5) ``` The intersection of A and B without this patch would lead to the following FAC: ``` (x, y)[s0] : (exists d0 = [x / 4], d1 = [y / 4], d2 = [x / 4], d3 = [x / 4]: d0 <= s0, d1 <= s0, d2 <= s0, d3 <= s0, x + y >= 2, x + y >= 5) ``` after this patch, merging of local ids will detect that `d0 = d2` and `d1 = d3`, and the intersection of these two FACs will be (after removing duplicate constraints): ``` (x, y)[s0] : (exists d0 = [x / 4], d1 = [y / 4] : d0 <= s0, d1 <= s0, x + y >= 2, x + y >= 5) ``` This reduces the number of constraints by 2 (constraints) + 4 (2 constraints for each extra division) for this case. This is used to reduce the output size representation of operations like PresburgerSet::subtract, PresburgerSet::intersect which require merging local variables. Reviewed By: arjunp, bondhugula Differential Revision: https://reviews.llvm.org/D112867
1 parent cff427e commit d257f7c

File tree

3 files changed

+234
-15
lines changed

3 files changed

+234
-15
lines changed

mlir/include/mlir/Analysis/AffineStructures.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,16 @@ class FlatAffineConstraints {
441441
/// variables.
442442
void convertDimToLocal(unsigned dimStart, unsigned dimLimit);
443443

444-
/// Merge local ids of `this` and `other`. This is done by appending local ids
445-
/// of `other` to `this` and inserting local ids of `this` to `other` at start
446-
/// of its local ids. Number of dimension and symbol ids should match in
447-
/// `this` and `other`.
444+
/// Adds additional local ids to the sets such that they both have the union
445+
/// of the local ids in each set, without changing the set of points that
446+
/// lie in `this` and `other`. The ordering of the local ids in the
447+
/// sets may also be changed. After merging, if the `i^th` local variable in
448+
/// one set has a known division representation, then the `i^th` local
449+
/// variable in the other set either has the same division representation or
450+
/// no known division representation.
451+
///
452+
/// The number of dimensions and symbol ids in `this` and `other` should
453+
/// match.
448454
void mergeLocalIds(FlatAffineConstraints &other);
449455

450456
/// Removes all equalities and inequalities.
@@ -819,8 +825,8 @@ class FlatAffineValueConstraints : public FlatAffineConstraints {
819825
/// constraint systems are updated so that they have the union of all
820826
/// identifiers, with `this`'s original identifiers appearing first followed
821827
/// by any of `other`'s identifiers that didn't appear in `this`. Local
822-
/// identifiers of each system are by design separate/local and are placed
823-
/// one after other (`this`'s followed by `other`'s).
828+
/// identifiers in `other` that have the same division representation as local
829+
/// identifiers in `this` are merged into one.
824830
// E.g.: Input: `this` has (%i, %j) [%M, %N]
825831
// `other` has (%k, %j) [%P, %N, %M]
826832
// Output: both `this`, `other` have (%i, %j, %k) [%M, %N, %P]

mlir/lib/Analysis/AffineStructures.cpp

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -493,8 +493,8 @@ static bool LLVM_ATTRIBUTE_UNUSED areIdsUnique(
493493
/// dimension-wise and symbol-wise unique; both constraint systems are updated
494494
/// so that they have the union of all identifiers, with A's original
495495
/// identifiers appearing first followed by any of B's identifiers that didn't
496-
/// appear in A. Local identifiers of each system are by design separate/local
497-
/// and are placed one after other (A's followed by B's).
496+
/// appear in A. Local identifiers in B that have the same division
497+
/// representation as local identifiers in A are merged into one.
498498
// E.g.: Input: A has ((%i, %j) [%M, %N]) and B has (%k, %j) [%P, %N, %M])
499499
// Output: both A, B have (%i, %j, %k) [%M, %N, %P]
500500
static void mergeAndAlignIds(unsigned offset, FlatAffineValueConstraints *a,
@@ -1918,18 +1918,108 @@ void FlatAffineConstraints::removeRedundantConstraints() {
19181918
equalities.resizeVertically(pos);
19191919
}
19201920

1921-
/// Merge local ids of `this` and `other`. This is done by appending local ids
1922-
/// of `other` to `this` and inserting local ids of `this` to `other` at start
1923-
/// of its local ids. Number of dimension and symbol ids should match in
1924-
/// `this` and `other`.
1921+
/// Eliminate `pos2^th` local identifier, replacing its every instance with
1922+
/// `pos1^th` local identifier. This function is intended to be used to remove
1923+
/// redundancy when local variables at position `pos1` and `pos2` are restricted
1924+
/// to have the same value.
1925+
static void eliminateRedundantLocalId(FlatAffineConstraints &fac, unsigned pos1,
1926+
unsigned pos2) {
1927+
1928+
assert(pos1 < fac.getNumLocalIds() && "Invalid local id position");
1929+
assert(pos2 < fac.getNumLocalIds() && "Invalid local id position");
1930+
1931+
unsigned localOffset = fac.getNumDimAndSymbolIds();
1932+
pos1 += localOffset;
1933+
pos2 += localOffset;
1934+
for (unsigned i = 0, e = fac.getNumInequalities(); i < e; ++i)
1935+
fac.atIneq(i, pos1) += fac.atIneq(i, pos2);
1936+
for (unsigned i = 0, e = fac.getNumEqualities(); i < e; ++i)
1937+
fac.atEq(i, pos1) += fac.atEq(i, pos2);
1938+
fac.removeId(pos2);
1939+
}
1940+
1941+
/// Adds additional local ids to the sets such that they both have the union
1942+
/// of the local ids in each set, without changing the set of points that
1943+
/// lie in `this` and `other`.
1944+
///
1945+
/// To detect local ids that always take the same in both sets, each local id is
1946+
/// represented as a floordiv with constant denominator in terms of other ids.
1947+
/// After extracting these divisions, local ids with the same division
1948+
/// representation are considered duplicate and are merged. It is possible that
1949+
/// division representation for some local id cannot be obtained, and thus these
1950+
/// local ids are not considered for detecting duplicates.
19251951
void FlatAffineConstraints::mergeLocalIds(FlatAffineConstraints &other) {
19261952
assert(getNumDimIds() == other.getNumDimIds() &&
19271953
"Number of dimension ids should match");
19281954
assert(getNumSymbolIds() == other.getNumSymbolIds() &&
19291955
"Number of symbol ids should match");
1930-
unsigned initLocals = getNumLocalIds();
1931-
insertLocalId(getNumLocalIds(), other.getNumLocalIds());
1932-
other.insertLocalId(0, initLocals);
1956+
1957+
FlatAffineConstraints &facA = *this;
1958+
FlatAffineConstraints &facB = other;
1959+
1960+
// Merge local ids of facA and facB without using division information,
1961+
// i.e. append local ids of `facB` to `facA` and insert local ids of `facA`
1962+
// to `facB` at start of its local ids.
1963+
unsigned initLocals = facA.getNumLocalIds();
1964+
insertLocalId(facA.getNumLocalIds(), facB.getNumLocalIds());
1965+
facB.insertLocalId(0, initLocals);
1966+
1967+
// Get division representations from each FAC.
1968+
std::vector<SmallVector<int64_t, 8>> divsA, divsB;
1969+
SmallVector<unsigned, 4> denomsA, denomsB;
1970+
facA.getLocalReprs(divsA, denomsA);
1971+
facB.getLocalReprs(divsB, denomsB);
1972+
1973+
// Copy division information for facB into `divsA` and `denomsA`, so that
1974+
// these have the combined division information of both FACs. Since newly
1975+
// added local variables in facA and facB have no constraints, they will not
1976+
// have any division representation.
1977+
std::copy(divsB.begin() + initLocals, divsB.end(),
1978+
divsA.begin() + initLocals);
1979+
std::copy(denomsB.begin() + initLocals, denomsB.end(),
1980+
denomsA.begin() + initLocals);
1981+
1982+
// Find and merge duplicate divisions.
1983+
// TODO: Add division normalization to support divisions that differ by
1984+
// a constant.
1985+
// TODO: Add division ordering such that a division representation for local
1986+
// identifier at position `i` only depends on local identifiers at position <
1987+
// `i`. This would make sure that all divisions depending on other local
1988+
// variables that can be merged, are merged.
1989+
unsigned localOffset = getIdKindOffset(IdKind::Local);
1990+
for (unsigned i = 0; i < divsA.size(); ++i) {
1991+
// Check if a division representation exists for the `i^th` local id.
1992+
if (denomsA[i] == 0)
1993+
continue;
1994+
// Check if a division exists which is a duplicate of the division at `i`.
1995+
for (unsigned j = i + 1; j < divsA.size(); ++j) {
1996+
// Check if a division representation exists for the `j^th` local id.
1997+
if (denomsA[j] == 0)
1998+
continue;
1999+
// Check if the denominators match.
2000+
if (denomsA[i] != denomsA[j])
2001+
continue;
2002+
// Check if the representations are equal.
2003+
if (divsA[i] != divsA[j])
2004+
continue;
2005+
2006+
// Merge divisions at position `j` into division at position `i`.
2007+
eliminateRedundantLocalId(facA, i, j);
2008+
eliminateRedundantLocalId(facB, i, j);
2009+
for (unsigned k = 0, g = divsA.size(); k < g; ++k) {
2010+
SmallVector<int64_t, 8> &div = divsA[k];
2011+
if (denomsA[k] != 0) {
2012+
div[localOffset + i] += div[localOffset + j];
2013+
div.erase(div.begin() + localOffset + j);
2014+
}
2015+
}
2016+
2017+
divsA.erase(divsA.begin() + j);
2018+
denomsA.erase(denomsA.begin() + j);
2019+
// Since `j` can never be zero, we do not need to worry about overflows.
2020+
--j;
2021+
}
2022+
}
19332023
}
19342024

19352025
/// Removes local variables using equalities. Each equality is checked if it

mlir/unittests/Analysis/AffineStructuresTest.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,4 +809,127 @@ TEST(FlatAffineConstraintsTest, simplifyLocalsTest) {
809809
EXPECT_TRUE(fac3.isEmpty());
810810
}
811811

812+
TEST(FlatAffineConstraintsTest, mergeDivisionsSimple) {
813+
{
814+
// (x) : (exists z, y = [x / 2] : x = 3y and x + z + 1 >= 0).
815+
FlatAffineConstraints fac1(1, 0, 1);
816+
fac1.addLocalFloorDiv({1, 0, 0}, 2); // y = [x / 2].
817+
fac1.addEquality({1, 0, -3, 0}); // x = 3y.
818+
fac1.addInequality({1, 1, 0, 1}); // x + z + 1 >= 0.
819+
820+
// (x) : (exists y = [x / 2], z : x = 5y).
821+
FlatAffineConstraints fac2(1);
822+
fac2.addLocalFloorDiv({1, 0}, 2); // y = [x / 2].
823+
fac2.addEquality({1, -5, 0}); // x = 5y.
824+
fac2.appendLocalId(); // Add local id z.
825+
826+
fac1.mergeLocalIds(fac2);
827+
828+
// Local space should be same.
829+
EXPECT_EQ(fac1.getNumLocalIds(), fac2.getNumLocalIds());
830+
831+
// 1 division should be matched + 2 unmatched local ids.
832+
EXPECT_EQ(fac1.getNumLocalIds(), 3u);
833+
EXPECT_EQ(fac2.getNumLocalIds(), 3u);
834+
}
835+
836+
{
837+
// (x) : (exists z = [x / 5], y = [x / 2] : x = 3y).
838+
FlatAffineConstraints fac1(1);
839+
fac1.addLocalFloorDiv({1, 0}, 5); // z = [x / 5].
840+
fac1.addLocalFloorDiv({1, 0, 0}, 2); // y = [x / 2].
841+
fac1.addEquality({1, 0, -3, 0}); // x = 3y.
842+
843+
// (x) : (exists y = [x / 2], z = [x / 5]: x = 5z).
844+
FlatAffineConstraints fac2(1);
845+
fac2.addLocalFloorDiv({1, 0}, 2); // y = [x / 2].
846+
fac2.addLocalFloorDiv({1, 0, 0}, 5); // z = [x / 5].
847+
fac2.addEquality({1, 0, -5, 0}); // x = 5z.
848+
849+
fac1.mergeLocalIds(fac2);
850+
851+
// Local space should be same.
852+
EXPECT_EQ(fac1.getNumLocalIds(), fac2.getNumLocalIds());
853+
854+
// 2 divisions should be matched.
855+
EXPECT_EQ(fac1.getNumLocalIds(), 2u);
856+
EXPECT_EQ(fac2.getNumLocalIds(), 2u);
857+
}
858+
}
859+
860+
TEST(FlatAffineConstraintsTest, mergeDivisionsNestedDivsions) {
861+
{
862+
// (x) : (exists y = [x / 2], z = [x + y / 3]: y + z >= x).
863+
FlatAffineConstraints fac1(1);
864+
fac1.addLocalFloorDiv({1, 0}, 2); // y = [x / 2].
865+
fac1.addLocalFloorDiv({1, 1, 0}, 3); // z = [x + y / 3].
866+
fac1.addInequality({-1, 1, 1, 0}); // y + z >= x.
867+
868+
// (x) : (exists y = [x / 2], z = [x + y / 3]: y + z <= x).
869+
FlatAffineConstraints fac2(1);
870+
fac2.addLocalFloorDiv({1, 0}, 2); // y = [x / 2].
871+
fac2.addLocalFloorDiv({1, 1, 0}, 3); // z = [x + y / 3].
872+
fac2.addInequality({1, -1, -1, 0}); // y + z <= x.
873+
874+
fac1.mergeLocalIds(fac2);
875+
876+
// Local space should be same.
877+
EXPECT_EQ(fac1.getNumLocalIds(), fac2.getNumLocalIds());
878+
879+
// 2 divisions should be matched.
880+
EXPECT_EQ(fac1.getNumLocalIds(), 2u);
881+
EXPECT_EQ(fac2.getNumLocalIds(), 2u);
882+
}
883+
884+
{
885+
// (x) : (exists y = [x / 2], z = [x + y / 3], w = [z + 1 / 5]: y + z >= x).
886+
FlatAffineConstraints fac1(1);
887+
fac1.addLocalFloorDiv({1, 0}, 2); // y = [x / 2].
888+
fac1.addLocalFloorDiv({1, 1, 0}, 3); // z = [x + y / 3].
889+
fac1.addLocalFloorDiv({0, 0, 1, 1}, 5); // w = [z + 1 / 5].
890+
fac1.addInequality({-1, 1, 1, 0, 0}); // y + z >= x.
891+
892+
// (x) : (exists y = [x / 2], z = [x + y / 3], w = [z + 1 / 5]: y + z <= x).
893+
FlatAffineConstraints fac2(1);
894+
fac2.addLocalFloorDiv({1, 0}, 2); // y = [x / 2].
895+
fac2.addLocalFloorDiv({1, 1, 0}, 3); // z = [x + y / 3].
896+
fac2.addLocalFloorDiv({0, 0, 1, 1}, 5); // w = [z + 1 / 5].
897+
fac2.addInequality({1, -1, -1, 0, 0}); // y + z <= x.
898+
899+
fac1.mergeLocalIds(fac2);
900+
901+
// Local space should be same.
902+
EXPECT_EQ(fac1.getNumLocalIds(), fac2.getNumLocalIds());
903+
904+
// 3 divisions should be matched.
905+
EXPECT_EQ(fac1.getNumLocalIds(), 3u);
906+
EXPECT_EQ(fac2.getNumLocalIds(), 3u);
907+
}
908+
}
909+
910+
TEST(FlatAffineConstraintsTest, mergeDivisionsConstants) {
911+
{
912+
// (x) : (exists y = [x + 1 / 3], z = [x + 2 / 3]: y + z >= x).
913+
FlatAffineConstraints fac1(1);
914+
fac1.addLocalFloorDiv({1, 1}, 2); // y = [x + 1 / 2].
915+
fac1.addLocalFloorDiv({1, 0, 2}, 3); // z = [x + 2 / 3].
916+
fac1.addInequality({-1, 1, 1, 0}); // y + z >= x.
917+
918+
// (x) : (exists y = [x + 1 / 3], z = [x + 2 / 3]: y + z <= x).
919+
FlatAffineConstraints fac2(1);
920+
fac2.addLocalFloorDiv({1, 1}, 2); // y = [x + 1 / 2].
921+
fac2.addLocalFloorDiv({1, 0, 2}, 3); // z = [x + 2 / 3].
922+
fac2.addInequality({1, -1, -1, 0}); // y + z <= x.
923+
924+
fac1.mergeLocalIds(fac2);
925+
926+
// Local space should be same.
927+
EXPECT_EQ(fac1.getNumLocalIds(), fac2.getNumLocalIds());
928+
929+
// 2 divisions should be matched.
930+
EXPECT_EQ(fac1.getNumLocalIds(), 2u);
931+
EXPECT_EQ(fac2.getNumLocalIds(), 2u);
932+
}
933+
}
934+
812935
} // namespace mlir

0 commit comments

Comments
 (0)