Skip to content

Commit 9f5cefe

Browse files
authored
[mlir][Affine] Generalize the linearize(delinearize()) simplifications (llvm#117637)
The existing canonicalization patterns would only cancel out cases where the entire result list of an affine.delineraize_index was passed to an affine.lineraize_index and the basis elements matched exactly (except possibly for the outer bounds). This was correct, but limited, and left open many cases where a delinearize_index would take a series of divisions and modulos only for a subsequent linearize_index to use additions and multiplications to undo all that work. This sort of simplification is reasably easy to observe at the level of splititng and merging indexes, but difficult to perform once the underlying arithmetic operations have been created. Therefore, this commit generalizes the existing simplification logic. Now, any run of two or more delinearize_index results that appears within the argument list of a linearize_index operation with the same basis (or where they're both at the outermost position and so can be unbonded, or when `linearize_index disjoint` implies a bound not present on the `delinearize_index`) will be reduced to one signle delinearize_index output, whose basis element (that is, size or length) is equal to the product of the sizes that were simplified away. That is, we can now simplify %0:2 = affine.delinearize_index %n into (8, 8) : inde, index %1 = affine.linearize_index [%x, %0#0, %0#1, %y] by (3, 8, 8, 5) : index to the simpler %1 = affine.linearize_index [%x, %n, %y] by (3, 64, 5) : index This new pattern also works with dynamically-sized basis values. While I'm here, I fixed a bunch of typos in existing tests, and added a new getPaddedBasis() method to make processing potentially-underspecified basis elements simpler in some cases.
1 parent bf274b3 commit 9f5cefe

File tree

3 files changed

+506
-42
lines changed

3 files changed

+506
-42
lines changed

mlir/include/mlir/Dialect/Affine/IR/AffineOps.td

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,9 @@ def AffineDelinearizeIndexOp : Affine_Op<"delinearize_index", [Pure]> {
10831083
%indices_2 = affine.apply #map2()[%linear_index]
10841084
```
10851085

1086+
In other words, `%0:3 = affine.delinearize_index %x into (B, C)` produces
1087+
`%0 = {%x / (B * C), (%x mod (B * C)) / C, %x mod C}`.
1088+
10861089
The basis may either contain `N` or `N-1` elements, where `N` is the number of results.
10871090
If there are N basis elements, the first one will not be used during computations,
10881091
but may be used during analysis and canonicalization to eliminate terms from
@@ -1098,7 +1101,12 @@ def AffineDelinearizeIndexOp : Affine_Op<"delinearize_index", [Pure]> {
10981101
%0:3 = affine.delinearize_index %linear_index into (244, 244) : index, index
10991102
```
11001103

1101-
Note that, due to the constraints of affine maps, all the basis elements must
1104+
Note that, for symmetry with `getPaddedBasis()`, if `hasOuterBound` is `true`
1105+
when one of the `OpFoldResult` builders is called but the first element of the
1106+
basis is `nullptr`, that first element is ignored and the builder proceeds as if
1107+
there was no outer bound.
1108+
1109+
Due to the constraints of affine maps, all the basis elements must
11021110
be strictly positive. A dynamic basis element being 0 or negative causes
11031111
undefined behavior.
11041112
}];
@@ -1136,6 +1144,11 @@ def AffineDelinearizeIndexOp : Affine_Op<"delinearize_index", [Pure]> {
11361144
/// Return a vector that contains the basis of the operation, removing
11371145
/// the outer bound if one is present.
11381146
SmallVector<OpFoldResult> getEffectiveBasis();
1147+
1148+
/// Return the vector with one basis element per result of the operation. If
1149+
/// there is no outer bound specified, the leading entry of this result will be
1150+
/// nullptr.
1151+
SmallVector<OpFoldResult> getPaddedBasis();
11391152
}];
11401153

11411154
let hasVerifier = 1;
@@ -1160,6 +1173,9 @@ def AffineLinearizeIndexOp : Affine_Op<"linearize_index",
11601173
sum(i = 0 to N-1) %idx_i * product(j = i + 1 to N-1) B_j
11611174
```
11621175

1176+
In other words, `%0 = affine.linearize_index [%z, %y, %x] by (Z, Y, X)`
1177+
gives `%0 = %x + %y * X + %z * X * Y`, or `%0 = %x + X * (%y + Y * (%z))`.
1178+
11631179
The basis may either have `N` or `N-1` elements, where `N` is the number of
11641180
inputs to linearize_index. If `N` inputs are provided, the first one is not used
11651181
in computation, but may be used during analysis or canonicalization as a bound
@@ -1168,6 +1184,10 @@ def AffineLinearizeIndexOp : Affine_Op<"linearize_index",
11681184
If all `N` basis elements are provided, the linearize_index operation is said to
11691185
"have an outer bound".
11701186

1187+
As a convenience, and for symmetry with `getPaddedBasis()`, ifg the first
1188+
element of a set of `OpFoldResult`s passed to the builders of this operation is
1189+
`nullptr`, that element is ignored.
1190+
11711191
If the `disjoint` property is present, this is an optimization hint that,
11721192
for all `i`, `0 <= %idx_i < B_i` - that is, no index affects any other index,
11731193
except that `%idx_0` may be negative to make the index as a whole negative.
@@ -1224,6 +1244,11 @@ def AffineLinearizeIndexOp : Affine_Op<"linearize_index",
12241244
/// Return a vector that contains the basis of the operation, removing
12251245
/// the outer bound if one is present.
12261246
SmallVector<OpFoldResult> getEffectiveBasis();
1247+
1248+
/// Return the vector with one basis element per index operand of the operation.
1249+
/// If there is no outer bound specified, the leading entry of this basis will be
1250+
/// nullptr.
1251+
SmallVector<OpFoldResult> getPaddedBasis();
12271252
}];
12281253

12291254
let hasVerifier = 1;

0 commit comments

Comments
 (0)