An AffineApplyNormalizer
is a helper class that supports renumbering operands of AffineApplyOp. This acts as a reindexing map of Value to positional dims or symbols and allows simplifications such as:
%1 = affine.apply (d0, d1) -> (d0 - d1) (%0, %0)
into:
%1 = affine.apply () -> (0)
The AffineNormalizer composes AffineApplyOp recursively. Its purpose is to keep a correspondence between the mathematical map
and the operands
of a given AffineApplyOp. This correspondence is maintained by iterating over the operands and forming an auxiliaryMap
that can be composed mathematically with map
. To keep this correspondence in cases where symbols are produced by affine.apply operations, we perform a local rewrite of symbols as dims.
Rationale for locally rewriting symbols as dims:
The mathematical composition of AffineMap must always concatenate symbols because it does not have enough information to do otherwise. For example, composing (d0)[s0] -> (d0 + s0)
with itself must produce (d0)[s0, s1] -> (d0 + s0 + s1)
.
The result is only equivalent to (d0)[s0] -> (d0 + 2 * s0)
when applied to the same mlir::Value for both s0 and s1. As a consequence mathematical composition of AffineMap always concatenates symbols.
When AffineMaps are used in AffineApplyOp however, they may specify composition via symbols, which is ambiguous mathematically. This corner case is handled by locally rewriting such symbols that come from AffineApplyOp into dims and composing through dims. TODO(andydavis, ntv): Composition via symbols comes at a significant code complexity. Alternatively we should investigate whether we want to explicitly disallow symbols coming from affine.apply and instead force the user to compose symbols beforehand. The annoyances may be small (i.e. 1 or 2 extra API calls for such uses, which haven't popped up until now) and the benefit potentially big: simpler and more maintainable code for a non-trivial, recursive, procedure.