This concerns `from/to_extent_tensor`, `size_to_index`, `index_to_size`, and `const_size` conversion patterns. The new lowering will work directly on indices and extent tensors. The shape and size values will allow for error values but are not yet supported by the dialect conversion. Differential Revision: https://reviews.llvm.org/D84436
223 lines
7.1 KiB
C++
223 lines
7.1 KiB
C++
//===- ShapeToStandard.cpp - conversion from Shape to Standard dialect ----===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/Conversion/ShapeToStandard/ShapeToStandard.h"
|
|
|
|
#include "../PassDetail.h"
|
|
#include "mlir/Dialect/SCF/SCF.h"
|
|
#include "mlir/Dialect/Shape/IR/Shape.h"
|
|
#include "mlir/Dialect/StandardOps/IR/Ops.h"
|
|
#include "mlir/Transforms/DialectConversion.h"
|
|
|
|
using namespace mlir;
|
|
using namespace mlir::shape;
|
|
|
|
/// Conversion patterns.
|
|
namespace {
|
|
class AnyOpConversion : public OpConversionPattern<AnyOp> {
|
|
public:
|
|
using OpConversionPattern<AnyOp>::OpConversionPattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(AnyOp op, ArrayRef<Value> operands,
|
|
ConversionPatternRewriter &rewriter) const override;
|
|
};
|
|
} // namespace
|
|
|
|
LogicalResult
|
|
AnyOpConversion::matchAndRewrite(AnyOp op, ArrayRef<Value> operands,
|
|
ConversionPatternRewriter &rewriter) const {
|
|
AnyOp::Adaptor transformed(operands);
|
|
|
|
// Replace `any` with its first operand.
|
|
// Any operand would be a valid substitution.
|
|
rewriter.replaceOp(op, {transformed.inputs().front()});
|
|
return success();
|
|
}
|
|
|
|
namespace {
|
|
template <typename SrcOpTy, typename DstOpTy>
|
|
class BinaryOpConversion : public OpConversionPattern<SrcOpTy> {
|
|
public:
|
|
using OpConversionPattern<SrcOpTy>::OpConversionPattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(SrcOpTy op, ArrayRef<Value> operands,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
typename SrcOpTy::Adaptor adaptor(operands);
|
|
rewriter.replaceOpWithNewOp<DstOpTy>(op, adaptor.lhs(), adaptor.rhs());
|
|
return success();
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
namespace {
|
|
class ShapeOfOpConversion : public OpConversionPattern<ShapeOfOp> {
|
|
public:
|
|
using OpConversionPattern<ShapeOfOp>::OpConversionPattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(ShapeOfOp op, ArrayRef<Value> operands,
|
|
ConversionPatternRewriter &rewriter) const override;
|
|
};
|
|
} // namespace
|
|
|
|
LogicalResult ShapeOfOpConversion::matchAndRewrite(
|
|
ShapeOfOp op, ArrayRef<Value> operands,
|
|
ConversionPatternRewriter &rewriter) const {
|
|
ShapeOfOp::Adaptor transformed(operands);
|
|
auto loc = op.getLoc();
|
|
auto tensorVal = transformed.arg();
|
|
auto tensorTy = tensorVal.getType();
|
|
|
|
// For unranked tensors `shape_of` lowers to `scf` and the pattern can be
|
|
// found in the corresponding pass.
|
|
if (tensorTy.isa<UnrankedTensorType>())
|
|
return failure();
|
|
|
|
// Build values for individual dimensions.
|
|
SmallVector<Value, 8> dimValues;
|
|
auto rankedTensorTy = tensorTy.cast<RankedTensorType>();
|
|
int64_t rank = rankedTensorTy.getRank();
|
|
for (int64_t i = 0; i < rank; i++) {
|
|
if (rankedTensorTy.isDynamicDim(i)) {
|
|
auto dimVal = rewriter.create<DimOp>(loc, tensorVal, i);
|
|
dimValues.push_back(dimVal);
|
|
} else {
|
|
int64_t dim = rankedTensorTy.getDimSize(i);
|
|
auto dimVal = rewriter.create<ConstantIndexOp>(loc, dim);
|
|
dimValues.push_back(dimVal);
|
|
}
|
|
}
|
|
|
|
// Materialize extent tensor.
|
|
Value staticExtentTensor =
|
|
rewriter.create<TensorFromElementsOp>(loc, dimValues);
|
|
rewriter.replaceOpWithNewOp<TensorCastOp>(op, staticExtentTensor,
|
|
op.getType());
|
|
return success();
|
|
}
|
|
|
|
namespace {
|
|
class GetExtentOpConverter : public OpConversionPattern<GetExtentOp> {
|
|
using OpConversionPattern<GetExtentOp>::OpConversionPattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(GetExtentOp op, ArrayRef<Value> operands,
|
|
ConversionPatternRewriter &rewriter) const override;
|
|
};
|
|
} // namespace
|
|
|
|
LogicalResult GetExtentOpConverter::matchAndRewrite(
|
|
GetExtentOp op, ArrayRef<Value> operands,
|
|
ConversionPatternRewriter &rewriter) const {
|
|
GetExtentOp::Adaptor transformed(operands);
|
|
|
|
// Derive shape extent directly from shape origin if possible.
|
|
// This circumvents the necessity to materialize the shape in memory.
|
|
if (auto shapeOfOp = op.shape().getDefiningOp<ShapeOfOp>()) {
|
|
rewriter.replaceOpWithNewOp<DimOp>(op, shapeOfOp.arg(), transformed.dim());
|
|
return success();
|
|
}
|
|
|
|
rewriter.replaceOpWithNewOp<ExtractElementOp>(op, rewriter.getIndexType(),
|
|
transformed.shape(),
|
|
ValueRange{transformed.dim()});
|
|
return success();
|
|
}
|
|
|
|
namespace {
|
|
class RankOpConverter : public OpConversionPattern<shape::RankOp> {
|
|
public:
|
|
using OpConversionPattern<shape::RankOp>::OpConversionPattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(shape::RankOp op, ArrayRef<Value> operands,
|
|
ConversionPatternRewriter &rewriter) const override;
|
|
};
|
|
} // namespace
|
|
|
|
LogicalResult
|
|
RankOpConverter::matchAndRewrite(shape::RankOp op, ArrayRef<Value> operands,
|
|
ConversionPatternRewriter &rewriter) const {
|
|
shape::RankOp::Adaptor transformed(operands);
|
|
rewriter.replaceOpWithNewOp<DimOp>(op, transformed.shape(), 0);
|
|
return success();
|
|
}
|
|
|
|
namespace {
|
|
/// Type conversions.
|
|
class ShapeTypeConverter : public TypeConverter {
|
|
public:
|
|
using TypeConverter::convertType;
|
|
|
|
ShapeTypeConverter(MLIRContext *ctx) {
|
|
// Add default pass-through conversion.
|
|
addConversion([&](Type type) { return type; });
|
|
|
|
addConversion([ctx](SizeType type) { return IndexType::get(ctx); });
|
|
addConversion([ctx](ShapeType type) {
|
|
return RankedTensorType::get({ShapedType::kDynamicSize},
|
|
IndexType::get(ctx));
|
|
});
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
namespace {
|
|
/// Conversion pass.
|
|
class ConvertShapeToStandardPass
|
|
: public ConvertShapeToStandardBase<ConvertShapeToStandardPass> {
|
|
|
|
void runOnOperation() override;
|
|
};
|
|
} // namespace
|
|
|
|
void ConvertShapeToStandardPass::runOnOperation() {
|
|
// Setup type conversion.
|
|
MLIRContext &ctx = getContext();
|
|
ShapeTypeConverter typeConverter(&ctx);
|
|
|
|
// Setup target legality.
|
|
ConversionTarget target(ctx);
|
|
target.addLegalDialect<scf::SCFDialect, StandardOpsDialect>();
|
|
target.addLegalOp<ModuleOp, ModuleTerminatorOp, ReturnOp>();
|
|
target.addDynamicallyLegalOp<FuncOp>([&](FuncOp op) {
|
|
return typeConverter.isSignatureLegal(op.getType()) &&
|
|
typeConverter.isLegal(&op.getBody());
|
|
});
|
|
|
|
// Setup conversion patterns.
|
|
OwningRewritePatternList patterns;
|
|
populateShapeToStandardConversionPatterns(patterns, &ctx);
|
|
populateFuncOpTypeConversionPattern(patterns, &ctx, typeConverter);
|
|
|
|
// Apply conversion.
|
|
auto module = getOperation();
|
|
if (failed(applyFullConversion(module, target, patterns)))
|
|
signalPassFailure();
|
|
}
|
|
|
|
void mlir::populateShapeToStandardConversionPatterns(
|
|
OwningRewritePatternList &patterns, MLIRContext *ctx) {
|
|
// clang-format off
|
|
patterns.insert<
|
|
AnyOpConversion,
|
|
BinaryOpConversion<AddOp, AddIOp>,
|
|
BinaryOpConversion<MulOp, MulIOp>,
|
|
GetExtentOpConverter,
|
|
RankOpConverter,
|
|
ShapeOfOpConversion>(ctx);
|
|
// clang-format on
|
|
}
|
|
|
|
std::unique_ptr<OperationPass<ModuleOp>>
|
|
mlir::createConvertShapeToStandardPass() {
|
|
return std::make_unique<ConvertShapeToStandardPass>();
|
|
}
|