blob: 137ae4b65aa5d5349864303bcc78eca8848ae264 [file] [edit]
#include "mlir/Dialect/Affine/IR/AffineOps.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/Polygeist/IR/Polygeist.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/PatternMatch.h"
#include "llvm/ADT/TypeSwitch.h"
#include <numeric>
using namespace mlir;
using namespace mlir::polygeist;
#define GET_OP_CLASSES
#include "mlir/Dialect/Polygeist/IR/PolygeistOps.cpp.inc"
namespace {
/// Simplify pointer2memref(memref2pointer(x)) to cast(x)
class Memref2Pointer2MemrefCast final
: public OpRewritePattern<Pointer2MemrefOp> {
public:
using OpRewritePattern<Pointer2MemrefOp>::OpRewritePattern;
LogicalResult matchAndRewrite(Pointer2MemrefOp op,
PatternRewriter &rewriter) const override {
auto src = op.getSource().getDefiningOp<Memref2PointerOp>();
if (!src)
return failure();
auto smt = cast<MemRefType>(src.getSource().getType());
auto omt = cast<MemRefType>(op.getType());
if (smt.getShape().size() != omt.getShape().size())
return failure();
for (unsigned i = 1; i < smt.getShape().size(); i++) {
if (smt.getShape()[i] != omt.getShape()[i])
return failure();
}
if (smt.getElementType() != omt.getElementType())
return failure();
if (smt.getMemorySpace() != omt.getMemorySpace())
return failure();
rewriter.replaceOpWithNewOp<memref::CastOp>(op, op.getType(),
src.getSource());
return success();
}
};
/// Simplify memref2pointer(pointer2memref(x)) to cast(x)
class Memref2PointerBitCast final : public OpRewritePattern<LLVM::BitcastOp> {
public:
using OpRewritePattern<LLVM::BitcastOp>::OpRewritePattern;
LogicalResult matchAndRewrite(LLVM::BitcastOp op,
PatternRewriter &rewriter) const override {
auto src = op.getOperand().getDefiningOp<Memref2PointerOp>();
if (!src)
return failure();
rewriter.replaceOpWithNewOp<Memref2PointerOp>(op, op.getType(),
src.getSource());
return success();
}
};
/// Simplify load(pointer2memref(gep(...(x)))) to load(x, idx)
template <typename T>
class LoadStorePointer2MemrefGEP final : public OpRewritePattern<T> {
public:
using OpRewritePattern<T>::OpRewritePattern;
SmallVector<Value> newIndex(T op, Value finalIndex,
PatternRewriter &rewriter) const;
void createNewOp(T op, Value baseMemref, SmallVector<Value> vals,
PatternRewriter &rewriter) const;
Value getMemref(T op) const;
LogicalResult matchAndRewrite(T op,
PatternRewriter &rewriter) const override {
if (op.getMemRefType().getRank() != 1)
return failure();
auto src = getMemref(op).template getDefiningOp<Pointer2MemrefOp>();
if (!src)
return failure();
Type elementType = op.getMemRefType().getElementType();
unsigned elementSize = elementType.isIntOrFloat()
? elementType.getIntOrFloatBitWidth() / 8
: 0;
if (elementSize == 0)
return failure();
SmallVector<std::pair<LLVM::GEPOp, unsigned>> gepOps;
Value ptr = src.getSource();
while (auto gep = ptr.getDefiningOp<LLVM::GEPOp>()) {
if (gep.getIndices().size() != 1)
break;
unsigned gepElemSize = 1;
auto elemTy = gep.getElemType();
if (elemTy.isIntOrFloat()) {
gepElemSize = elemTy.getIntOrFloatBitWidth() / 8;
} else if (auto arrayTy = dyn_cast<LLVM::LLVMArrayType>(elemTy)) {
auto baseTy = arrayTy.getElementType();
if (baseTy.isIntOrFloat()) {
gepElemSize =
(baseTy.getIntOrFloatBitWidth() / 8) * arrayTy.getNumElements();
} else {
break;
}
} else {
break;
}
gepOps.emplace_back(gep, gepElemSize);
ptr = gep.getBase();
}
if (gepOps.empty())
return failure();
Location loc = op.getLoc();
auto baseMemref = Pointer2MemrefOp::create(
rewriter, loc, cast<MemRefType>(src.getType()), ptr);
Value finalIndex = nullptr;
for (auto [gep, gepElemSize] : llvm::reverse(gepOps)) {
PointerUnion<IntegerAttr, Value> rawIdx = gep.getIndices()[0];
Value idx = dyn_cast_if_present<Value>(rawIdx);
if (!idx)
idx = arith::ConstantIndexOp::create(
rewriter, loc, cast<IntegerAttr>(rawIdx).getValue().getSExtValue());
if (auto constIdx = idx.getDefiningOp<arith::ConstantIndexOp>()) {
if ((constIdx.value() * gepElemSize) % elementSize != 0) {
return failure();
}
}
if (!idx.getType().isIndex()) {
idx = arith::IndexCastOp::create(rewriter, loc, rewriter.getIndexType(),
idx);
}
unsigned gcd = std::gcd(gepElemSize, elementSize);
unsigned scaledGep = gepElemSize / gcd;
unsigned scaledElement = elementSize / gcd;
Value scaledIdx =
(scaledGep != 1)
? arith::MulIOp::create(
rewriter, loc, idx,
arith::ConstantIndexOp::create(rewriter, loc, scaledGep))
: idx;
Value elemOffset =
(scaledElement != 1)
? arith::DivSIOp::create(
rewriter, loc, scaledIdx,
arith::ConstantIndexOp::create(rewriter, loc, scaledElement))
: scaledIdx;
if (finalIndex)
finalIndex =
arith::AddIOp::create(rewriter, loc, finalIndex, elemOffset);
else
finalIndex = elemOffset;
}
createNewOp(op, baseMemref, newIndex(op, finalIndex, rewriter), rewriter);
return success();
}
};
template <>
Value LoadStorePointer2MemrefGEP<memref::LoadOp>::getMemref(
memref::LoadOp op) const {
return op.getMemref();
}
template <>
Value LoadStorePointer2MemrefGEP<memref::StoreOp>::getMemref(
memref::StoreOp op) const {
return op.getMemref();
}
template <>
Value LoadStorePointer2MemrefGEP<affine::AffineLoadOp>::getMemref(
affine::AffineLoadOp op) const {
return op.getMemref();
}
template <>
Value LoadStorePointer2MemrefGEP<affine::AffineStoreOp>::getMemref(
affine::AffineStoreOp op) const {
return op.getMemref();
}
template <>
SmallVector<Value> LoadStorePointer2MemrefGEP<memref::LoadOp>::newIndex(
memref::LoadOp op, Value finalIndex, PatternRewriter &rewriter) const {
auto operands = llvm::to_vector(op.getIndices());
operands[0] =
arith::AddIOp::create(rewriter, op.getLoc(), operands[0], finalIndex);
return operands;
}
template <>
SmallVector<Value> LoadStorePointer2MemrefGEP<affine::AffineLoadOp>::newIndex(
affine::AffineLoadOp op, Value finalIndex,
PatternRewriter &rewriter) const {
auto apply = affine::AffineApplyOp::create(
rewriter, op.getLoc(), op.getAffineMap(), op.getMapOperands());
SmallVector<Value> operands;
for (auto op : apply->getResults())
operands.push_back(op);
operands[0] =
arith::AddIOp::create(rewriter, op.getLoc(), operands[0], finalIndex);
return operands;
}
template <>
SmallVector<Value> LoadStorePointer2MemrefGEP<memref::StoreOp>::newIndex(
memref::StoreOp op, Value finalIndex, PatternRewriter &rewriter) const {
auto operands = llvm::to_vector(op.getIndices());
operands[0] =
arith::AddIOp::create(rewriter, op.getLoc(), operands[0], finalIndex);
return operands;
}
template <>
SmallVector<Value> LoadStorePointer2MemrefGEP<affine::AffineStoreOp>::newIndex(
affine::AffineStoreOp op, Value finalIndex,
PatternRewriter &rewriter) const {
auto apply = affine::AffineApplyOp::create(
rewriter, op.getLoc(), op.getAffineMap(), op.getMapOperands());
SmallVector<Value> operands;
for (auto op : apply->getResults())
operands.push_back(op);
operands[0] =
arith::AddIOp::create(rewriter, op.getLoc(), operands[0], finalIndex);
return operands;
}
template <>
void LoadStorePointer2MemrefGEP<memref::LoadOp>::createNewOp(
memref::LoadOp op, Value baseMemref, SmallVector<Value> idxs,
PatternRewriter &rewriter) const {
rewriter.replaceOpWithNewOp<memref::LoadOp>(op, baseMemref, idxs);
}
template <>
void LoadStorePointer2MemrefGEP<affine::AffineLoadOp>::createNewOp(
affine::AffineLoadOp op, Value baseMemref, SmallVector<Value> idxs,
PatternRewriter &rewriter) const {
rewriter.replaceOpWithNewOp<memref::LoadOp>(op, baseMemref, idxs);
}
template <>
void LoadStorePointer2MemrefGEP<memref::StoreOp>::createNewOp(
memref::StoreOp op, Value baseMemref, SmallVector<Value> idxs,
PatternRewriter &rewriter) const {
rewriter.replaceOpWithNewOp<memref::StoreOp>(op, op.getValue(), baseMemref,
idxs);
}
template <>
void LoadStorePointer2MemrefGEP<affine::AffineStoreOp>::createNewOp(
affine::AffineStoreOp op, Value baseMemref, SmallVector<Value> idxs,
PatternRewriter &rewriter) const {
rewriter.replaceOpWithNewOp<memref::StoreOp>(op, op.getValue(), baseMemref,
idxs);
}
/// Simplify cast(pointer2memref(x)) to pointer2memref(x)
class Pointer2MemrefCast final : public OpRewritePattern<memref::CastOp> {
public:
using OpRewritePattern<memref::CastOp>::OpRewritePattern;
LogicalResult matchAndRewrite(memref::CastOp op,
PatternRewriter &rewriter) const override {
auto src = op.getSource().getDefiningOp<Pointer2MemrefOp>();
if (!src)
return failure();
rewriter.replaceOpWithNewOp<Pointer2MemrefOp>(op, op.getType(),
src.getSource());
return success();
}
};
/// Simplify memref2pointer(pointer2memref(x)) to cast(x)
class Pointer2Memref2PointerCast final
: public OpRewritePattern<Memref2PointerOp> {
public:
using OpRewritePattern<Memref2PointerOp>::OpRewritePattern;
LogicalResult matchAndRewrite(Memref2PointerOp op,
PatternRewriter &rewriter) const override {
auto src = op.getSource().getDefiningOp<Pointer2MemrefOp>();
if (!src)
return failure();
rewriter.replaceOpWithNewOp<LLVM::BitcastOp>(op, op.getType(),
src.getSource());
return success();
}
};
} // namespace
void Memref2PointerOp::getCanonicalizationPatterns(RewritePatternSet &results,
MLIRContext *context) {
results.insert<Memref2Pointer2MemrefCast, Memref2PointerBitCast>(context);
}
OpFoldResult Memref2PointerOp::fold(FoldAdaptor adaptor) {
/// Simplify memref2pointer(cast(x)) to memref2pointer(x)
if (auto mc = getSource().getDefiningOp<memref::CastOp>()) {
getSourceMutable().assign(mc.getSource());
return getResult();
}
if (auto mc = getSource().getDefiningOp<Pointer2MemrefOp>()) {
if (mc.getSource().getType() == getType()) {
return mc.getSource();
}
}
return nullptr;
}
void Pointer2MemrefOp::getCanonicalizationPatterns(RewritePatternSet &results,
MLIRContext *context) {
results.insert<Pointer2MemrefCast, Pointer2Memref2PointerCast,
LoadStorePointer2MemrefGEP<memref::LoadOp>,
LoadStorePointer2MemrefGEP<affine::AffineLoadOp>,
LoadStorePointer2MemrefGEP<memref::StoreOp>,
LoadStorePointer2MemrefGEP<affine::AffineStoreOp>>(context);
}
OpFoldResult Pointer2MemrefOp::fold(FoldAdaptor adaptor) {
/// Simplify pointer2memref(cast(x)) to pointer2memref(x)
if (auto mc = getSource().getDefiningOp<LLVM::BitcastOp>()) {
getSourceMutable().assign(mc.getOperand());
return getResult();
}
if (auto mc = getSource().getDefiningOp<LLVM::AddrSpaceCastOp>()) {
getSourceMutable().assign(mc.getOperand());
return getResult();
}
if (auto mc = getSource().getDefiningOp<LLVM::GEPOp>()) {
for (auto idx : mc.getDynamicIndices()) {
assert(idx);
if (!matchPattern(idx, m_Zero()))
return nullptr;
}
auto staticIndices = mc.getRawConstantIndices();
for (auto pair : llvm::enumerate(staticIndices)) {
if (pair.value() != LLVM::GEPOp::kDynamicIndex)
if (pair.value() != 0)
return nullptr;
}
getSourceMutable().assign(mc.getBase());
return getResult();
}
if (auto mc = getSource().getDefiningOp<Memref2PointerOp>()) {
if (mc.getSource().getType() == getType()) {
return mc.getSource();
}
}
return nullptr;
}