17#include "llvm/ADT/Repeated.h"
23#define GEN_PASS_DEF_ELIDEREINTERPRETCASTPASS
24#include "mlir/Dialect/MemRef/Transforms/Passes.h.inc"
76static bool isScalarSlice(memref::ReinterpretCastOp rc) {
77 auto rcInputTy = dyn_cast<MemRefType>(rc.getSource().getType());
78 auto rcOutputTy = dyn_cast<MemRefType>(rc.getType());
81 if (!rcInputTy.getLayout().isIdentity())
85 unsigned srcRank = rcInputTy.getRank();
86 if (srcRank != rcOutputTy.getRank())
92 if (!llvm::all_of(rcOutputTy.getShape(),
93 [](
int64_t dim) { return dim == 1; }))
97 if (!llvm::all_of(sizes, [](
int64_t size) {
98 return !ShapedType::isDynamic(size) && size == 1;
105 if (rcOutputTy.getDimSize(0) > 1)
110 std::count_if(rcInputTy.getShape().begin(), rcInputTy.getShape().end(),
111 [](
int dim) { return dim != 1; });
112 return nonUnitCount == 1;
157 LogicalResult matchAndRewrite(memref::CopyOp op,
158 PatternRewriter &rewriter)
const final {
159 Value rcOutput = op.getTarget();
160 auto rc = rcOutput.
getDefiningOp<memref::ReinterpretCastOp>();
162 return rewriter.notifyMatchFailure(
163 op,
"target is not a memref.reinterpret_cast");
165 if (!isScalarSlice(rc))
166 return rewriter.notifyMatchFailure(
167 op,
"reinterpret_cast does not match scalar slice");
169 Location loc = op.
getLoc();
171 Value src = op.getSource();
172 Value dst = rc.getSource();
174 auto dstType = cast<MemRefType>(dst.
getType());
175 unsigned dstRank = dstType.getRank();
179 auto srcType = cast<MemRefType>(src.
getType());
180 Repeated<Value> loadIndices(srcType.getRank(), zero);
181 auto offsets = rc.getMixedOffsets();
182 assert(offsets.size() == 1 &&
"Expecting single offset");
183 OpFoldResult offset = offsets[0];
185 unsigned offsetDim = dstType.getDimSize(0) == 1 ? dstRank - 1 : 0;
186 SmallVector<Value> storeIndices(dstRank, zero);
187 storeIndices[offsetDim] = storeOffset;
191 rewriter.eraseOp(rc);
193 Value val = memref::LoadOp::create(rewriter, loc, src, loadIndices);
194 memref::StoreOp::create(rewriter, loc, val, dst, storeIndices);
196 rewriter.eraseOp(op);
205static bool hasStaticZeroOffset(memref::ReinterpretCastOp rc) {
209 assert(offsets.size() == 1 &&
"Expecting single offset");
210 return !ShapedType::isDynamic(offsets[0]) && offsets[0] == 0;
213static std::optional<int64_t> getConstantIndex(
Value v) {
223static bool isConstantIndexExplicitlyOutOfBounds(
Value idx,
226 std::optional<int64_t> idxVal = getConstantIndex(idx);
227 return idxVal && (*idxVal < 0 || *idxVal >= upperBound);
244static std::optional<NonUnitDimMapping>
245getNonUnitDimMapping(memref::ReinterpretCastOp rc) {
246 auto inputTy = cast<MemRefType>(rc.getSource().getType());
247 auto outputTy = cast<MemRefType>(rc.getResult().getType());
252 if (!hasStaticZeroOffset(rc))
257 if (llvm::any_of(rc.getStaticSizes(), ShapedType::isDynamic) ||
258 llvm::any_of(rc.getStaticStrides(), ShapedType::isDynamic))
265 int64_t inputRank = inputTy.getRank();
266 int64_t outputRank = outputTy.getRank();
267 NonUnitDimMapping mapping;
271 while (inputDim < inputRank || outputDim < outputRank) {
272 if (inputDim < inputRank && inputShape[inputDim] == 1) {
276 if (outputDim < outputRank && outputShape[outputDim] == 1) {
281 if (inputDim == inputRank || outputDim == outputRank)
284 if (ShapedType::isDynamic(inputShape[inputDim]) ||
285 ShapedType::isDynamic(outputShape[outputDim]) ||
286 inputShape[inputDim] != outputShape[outputDim])
289 mapping.push_back({inputDim, outputDim});
299[[maybe_unused]]
static bool areIndicesInBounds(memref::LoadOp
load) {
300 auto rc =
load.getMemRef().getDefiningOp<memref::ReinterpretCastOp>();
301 auto rcOutputTy = cast<MemRefType>(rc.getResult().getType());
303 for (
auto [pos, idx] : llvm::enumerate(
load.getIndices())) {
309 if (isConstantIndexExplicitlyOutOfBounds(idx, rcOutputTy.getDimSize(pos)))
335struct RewriteLoadFromReinterpretCast
340 LogicalResult matchAndRewrite(memref::LoadOp op,
341 PatternRewriter &rewriter)
const override {
342 auto rc = op.getMemRef().getDefiningOp<memref::ReinterpretCastOp>();
345 op,
"target is not a memref.reinterpret_cast");
346 std::optional<NonUnitDimMapping> dimMapping = getNonUnitDimMapping(rc);
349 op,
"reinterpret_cast is not a unit-dim insertion/removal preserving "
350 "non-unit dimensions");
352 assert(areIndicesInBounds(op) &&
353 "load from reinterpret_cast indexes out of bounds!");
355 auto rcInputTy = cast<MemRefType>(rc.getSource().getType());
357 int64_t rcInputRank = rcInputTy.getRank();
359 SmallVector<Value> oldIdxs(op.getIndices().begin(), op.getIndices().end());
363 for (Value idx : oldIdxs) {
364 std::optional<int64_t> idxVal = getConstantIndex(idx);
365 if (idxVal && *idxVal == 0) {
374 SmallVector<Value> rcInputIdxs(rcInputRank, zeroIndex);
375 for (
auto [inputDim, outputDim] : *dimMapping)
376 rcInputIdxs[inputDim] = oldIdxs[outputDim];
378 auto rcInput = rc.getSource();
381 if (rc.getResult().hasOneUse())
388struct ElideReinterpretCastPass
390 ElideReinterpretCastPass> {
391 void runOnOperation()
override {
394 RewritePatternSet patterns(&ctx);
396 ConversionTarget
target(ctx);
397 target.addDynamicallyLegalOp<memref::CopyOp>([](memref::CopyOp op) {
398 auto rc = op.getTarget().getDefiningOp<memref::ReinterpretCastOp>();
401 return !isScalarSlice(rc);
403 target.addDynamicallyLegalOp<memref::LoadOp>([](memref::LoadOp op) {
404 auto rc = op.getMemRef().getDefiningOp<memref::ReinterpretCastOp>();
407 return !getNonUnitDimMapping(rc);
409 target.addLegalDialect<arith::ArithDialect, memref::MemRefDialect>();
410 if (
failed(applyPartialConversion(getOperation(),
target,
411 std::move(patterns))))
420 patterns.
add<CopyToScalarLoadAndStore, RewriteLoadFromReinterpretCast>(
Location getLoc()
The source location the operation was defined or derived from.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
bool hasOneUse() const
Returns true if this value has exactly one use.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Specialization of arith.constant op that returns an integer of index type.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
void populateElideReinterpretCastPatterns(RewritePatternSet &patterns)
Collects a set of patterns that bypass memref.reinterpet_cast Ops.
Include the generated interface declarations.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...