Skip to content

Commit 51fdabc

Browse files
matthias-springerGroverkss
authored andcommitted
[mlir][affine] ValueBoundsConstraintSet: Fully compose affine.apply (llvm#68899)
Fully compose `affine.apply` ops before adding them to the underlying `FlatLinearConstraints`. This works around a limitation of `FlatLinearConstraints`, which cannot deduce a constant bound if it involves two identical local variables. Details for future improvements of `FlatLinearConstraints`: The constraint set infrastructure fails to compute a constant bound of -8 for the first variable. ``` Domain: 0, Range: 1, Symbols: 4, Locals: 2 8 constraints (None None None None None Local Local const) 1 -1 0 0 0 0 0 0 = 0 0 1 -1 1 0 0 0 0 = 0 0 0 1 0 0 0 -16 0 = 0 0 0 0 1 0 -16 0 -8 = 0 0 0 0 0 -1 0 32 31 >= 0 0 0 0 0 1 0 -32 0 >= 0 0 0 0 0 -1 32 0 31 >= 0 0 0 0 0 1 -32 0 0 >= 0 ```
1 parent fbe47bf commit 51fdabc

File tree

4 files changed

+97
-7
lines changed

4 files changed

+97
-7
lines changed

mlir/include/mlir/Dialect/Affine/IR/ValueBoundsOpInterfaceImpl.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,25 @@
99
#ifndef MLIR_DIALECT_AFFINE_IR_VALUEBOUNDSOPINTERFACEIMPL_H
1010
#define MLIR_DIALECT_AFFINE_IR_VALUEBOUNDSOPINTERFACEIMPL_H
1111

12+
#include "mlir/Support/LogicalResult.h"
13+
1214
namespace mlir {
1315
class DialectRegistry;
16+
class Value;
1417

1518
namespace affine {
1619
void registerValueBoundsOpInterfaceExternalModels(DialectRegistry &registry);
20+
21+
/// Compute whether the given values are equal. Return "failure" if equality
22+
/// could not be determined. `value1`/`value2` must be index-typed.
23+
///
24+
/// This function is similar to `ValueBoundsConstraintSet::areEqual`. To work
25+
/// around limitations in `FlatLinearConstraints`, this function fully composes
26+
/// `value1` and `value2` (if they are the result of affine.apply ops) before
27+
/// populating the constraint set. The folding/composing logic can see
28+
/// opportunities for simplifications that the constraint set implementation
29+
/// cannot see.
30+
FailureOr<bool> fullyComposeAndCheckIfEqual(Value value1, Value value2);
1731
} // namespace affine
1832
} // namespace mlir
1933

mlir/lib/Dialect/Affine/IR/ValueBoundsOpInterfaceImpl.cpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,22 @@ struct AffineApplyOpInterface
2727
assert(applyOp.getAffineMap().getNumResults() == 1 &&
2828
"expected single result");
2929

30+
// Fully compose this affine.apply with other ops because the folding logic
31+
// can see opportunities for simplifying the affine map that
32+
// `FlatLinearConstraints` can currently not see.
33+
AffineMap map = applyOp.getAffineMap();
34+
SmallVector<Value> operands = llvm::to_vector(applyOp.getOperands());
35+
fullyComposeAffineMapAndOperands(&map, &operands);
36+
3037
// Align affine map result with dims/symbols in the constraint set.
31-
AffineExpr expr = applyOp.getAffineMap().getResult(0);
32-
SmallVector<AffineExpr> dimReplacements = llvm::to_vector(llvm::map_range(
33-
applyOp.getDimOperands(), [&](Value v) { return cstr.getExpr(v); }));
34-
SmallVector<AffineExpr> symReplacements = llvm::to_vector(llvm::map_range(
35-
applyOp.getSymbolOperands(), [&](Value v) { return cstr.getExpr(v); }));
38+
AffineExpr expr = map.getResult(0);
39+
SmallVector<AffineExpr> dimReplacements, symReplacements;
40+
for (int64_t i = 0, e = map.getNumDims(); i < e; ++i)
41+
dimReplacements.push_back(cstr.getExpr(operands[i]));
42+
for (int64_t i = map.getNumDims(),
43+
e = map.getNumDims() + map.getNumSymbols();
44+
i < e; ++i)
45+
symReplacements.push_back(cstr.getExpr(operands[i]));
3646
AffineExpr bound =
3747
expr.replaceDimsAndSymbols(dimReplacements, symReplacements);
3848
cstr.bound(value) == bound;
@@ -92,3 +102,30 @@ void mlir::affine::registerValueBoundsOpInterfaceExternalModels(
92102
AffineMinOp::attachInterface<AffineMinOpInterface>(*ctx);
93103
});
94104
}
105+
106+
FailureOr<bool> mlir::affine::fullyComposeAndCheckIfEqual(Value value1,
107+
Value value2) {
108+
assert(value1.getType().isIndex() && "expected index type");
109+
assert(value2.getType().isIndex() && "expected index type");
110+
111+
// Subtract the two values/dimensions from each other. If the result is 0,
112+
// both are equal.
113+
Builder b(value1.getContext());
114+
AffineMap map = AffineMap::get(/*dimCount=*/2, /*symbolCount=*/0,
115+
b.getAffineDimExpr(0) - b.getAffineDimExpr(1));
116+
// Fully compose the affine map with other ops because the folding logic
117+
// can see opportunities for simplifying the affine map that
118+
// `FlatLinearConstraints` can currently not see.
119+
SmallVector<Value> mapOperands;
120+
mapOperands.push_back(value1);
121+
mapOperands.push_back(value2);
122+
affine::fullyComposeAffineMapAndOperands(&map, &mapOperands);
123+
ValueDimList valueDims;
124+
for (Value v : mapOperands)
125+
valueDims.push_back({v, std::nullopt});
126+
FailureOr<int64_t> bound = ValueBoundsConstraintSet::computeConstantBound(
127+
presburger::BoundType::EQ, map, valueDims);
128+
if (failed(bound))
129+
return failure();
130+
return *bound == 0;
131+
}

mlir/test/Dialect/Affine/value-bounds-op-interface-impl.mlir

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,35 @@ func.func @affine_min_lb(%a: index) -> (index) {
5858
%2 = "test.reify_bound"(%1) {type = "LB"}: (index) -> (index)
5959
return %2 : index
6060
}
61+
62+
// -----
63+
64+
// CHECK-LABEL: func @composed_affine_apply(
65+
// CHECK: %[[cst:.*]] = arith.constant -8 : index
66+
// CHECK: return %[[cst]]
67+
func.func @composed_affine_apply(%i1 : index) -> (index) {
68+
// The ValueBoundsOpInterface implementation of affine.apply fully composes
69+
// the affine map (and its operands) with other affine.apply ops drawn from
70+
// its operands before adding it to the constraint set. This is to work
71+
// around a limitation in `FlatLinearConstraints`, which can currently not
72+
// compute a constant bound for %s. (The affine map simplification logic can
73+
// simplify %s to -8.)
74+
%i2 = affine.apply affine_map<(d0) -> ((d0 floordiv 32) * 16)>(%i1)
75+
%i3 = affine.apply affine_map<(d0) -> ((d0 floordiv 32) * 16 + 8)>(%i1)
76+
%s = affine.apply affine_map<()[s0, s1] -> (s0 - s1)>()[%i2, %i3]
77+
%reified = "test.reify_constant_bound"(%s) {type = "EQ"} : (index) -> (index)
78+
return %reified : index
79+
}
80+
81+
82+
// -----
83+
84+
// Test for affine::fullyComposeAndCheckIfEqual
85+
func.func @composed_are_equal(%i1 : index) {
86+
%i2 = affine.apply affine_map<(d0) -> ((d0 floordiv 32) * 16)>(%i1)
87+
%i3 = affine.apply affine_map<(d0) -> ((d0 floordiv 32) * 16 + 8)>(%i1)
88+
%s = affine.apply affine_map<()[s0, s1] -> (s0 - s1)>()[%i2, %i3]
89+
// expected-remark @below{{different}}
90+
"test.are_equal"(%i2, %i3) {compose} : (index, index) -> ()
91+
return
92+
}

mlir/test/lib/Dialect/Affine/TestReifyValueBounds.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "mlir/Dialect/Affine/IR/AffineOps.h"
10+
#include "mlir/Dialect/Affine/IR/ValueBoundsOpInterfaceImpl.h"
1011
#include "mlir/Dialect/Affine/Transforms/Transforms.h"
1112
#include "mlir/Dialect/Arith/Transforms/Transforms.h"
1213
#include "mlir/Dialect/Func/IR/FuncOps.h"
@@ -186,8 +187,14 @@ static LogicalResult testEquality(func::FuncOp funcOp) {
186187
op->emitOpError("invalid op");
187188
return WalkResult::skip();
188189
}
189-
FailureOr<bool> equal = ValueBoundsConstraintSet::areEqual(
190-
op->getOperand(0), op->getOperand(1));
190+
FailureOr<bool> equal = failure();
191+
if (op->hasAttr("compose")) {
192+
equal = affine::fullyComposeAndCheckIfEqual(op->getOperand(0),
193+
op->getOperand(1));
194+
} else {
195+
equal = ValueBoundsConstraintSet::areEqual(op->getOperand(0),
196+
op->getOperand(1));
197+
}
191198
if (failed(equal)) {
192199
op->emitError("could not determine equality");
193200
} else if (*equal) {

0 commit comments

Comments
 (0)