#include "mlir/Dialect/Tensor/IR/TensorTilingInterfaceImpl.h"
#include "mlir/Dialect/Affine/IR/AffineOps.h"
#include "mlir/Dialect/Arithmetic/Utils/Utils.h"
#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/Interfaces/TilingInterface.h"
using namespace mlir;
using namespace mlir::tensor;
namespace {
struct PadOpTiling : public TilingInterface::ExternalModel<PadOpTiling, PadOp> {
SmallVector<Value> getDestinationOperands(Operation *op, OpBuilder &b) const {
ReifiedRankedShapedTypeDims reifiedShapes;
ReifyRankedShapedTypeOpInterface reifyShapedTypeInterface =
dyn_cast<ReifyRankedShapedTypeOpInterface>(op);
(void)reifyShapedTypeInterface.reifyResultShapes(b, reifiedShapes);
auto padOp = cast<PadOp>(op);
SmallVector<OpFoldResult> mixedSizes = getAsOpFoldResult(reifiedShapes[0]);
Value initTensor = b.create<linalg::InitTensorOp>(
op->getLoc(), mixedSizes, padOp.getResultType().getElementType());
return {initTensor};
}
SmallVector<StringRef> getLoopIteratorTypes(Operation *op) const {
auto padOp = cast<PadOp>(op);
SmallVector<StringRef> iteratorTypes(padOp.getResultType().getRank(),
getParallelIteratorTypeName());
return iteratorTypes;
}
SmallVector<Range> getIterationDomain(Operation *op, OpBuilder &b) const {
ReifiedRankedShapedTypeDims reifiedShapes;
ReifyRankedShapedTypeOpInterface reifyShapedTypeInterface =
dyn_cast<ReifyRankedShapedTypeOpInterface>(op);
(void)reifyShapedTypeInterface.reifyResultShapes(b, reifiedShapes);
Location loc = op->getLoc();
Value zero = b.create<arith::ConstantIndexOp>(loc, 0);
Value one = b.create<arith::ConstantIndexOp>(loc, 1);
SmallVector<Range> loopRanges(reifiedShapes[0].size(), {zero, one, one});
for (const auto &ub : enumerate(reifiedShapes[0]))
loopRanges[ub.index()].size = ub.value();
return loopRanges;
}
SmallVector<Operation *>
getTiledImplementation(Operation *op, OpBuilder &b, ValueRange dest,
ArrayRef<OpFoldResult> offsets,
ArrayRef<OpFoldResult> sizes,
bool ) const {
Operation *result =
tensor::bubbleUpPadSlice(b, cast<PadOp>(op), offsets, sizes);
if (!result)
return {};
return {result};
}
};
}
Operation *tensor::bubbleUpPadSlice(OpBuilder &b, tensor::PadOp padOp,
ArrayRef<OpFoldResult> offsets,
ArrayRef<OpFoldResult> sizes,
bool generateZeroSliceGuard) {
Value padValue = padOp.getConstantPaddingValue();
if (!padValue)
return nullptr;
Location loc = padOp->getLoc();
AffineExpr dim0, dim1;
bindDims(b.getContext(), dim0, dim1);
auto addMap = AffineMap::get(2, 0, {dim0 + dim1});
auto add = [&](Value v1, Value v2) {
return b.createOrFold<AffineApplyOp>(loc, addMap, ValueRange{v1, v2});
};
auto subMap = AffineMap::get(2, 0, {dim0 - dim1});
auto sub = [&](Value v1, Value v2) {
return b.createOrFold<AffineApplyOp>(loc, subMap, ValueRange{v1, v2});
};
auto idMap = AffineMap::getMultiDimIdentityMap(2, b.getContext());
auto min = [&](Value v1, Value v2) {
return b.createOrFold<AffineMinOp>(loc, idMap, ValueRange{v1, v2});
};
auto max = [&](Value v1, Value v2) {
return b.createOrFold<AffineMaxOp>(loc, idMap, ValueRange{v1, v2});
};
auto zero = b.create<arith::ConstantIndexOp>(loc, 0);
auto appendIndex = [&](Value val, SmallVector<Value> &dynIndices,
SmallVector<int64_t> &staticIndices) {
if (auto constInt = getConstantIntValue(val)) {
staticIndices.push_back(*constInt);
} else {
staticIndices.push_back(ShapedType::kDynamicSize);
dynIndices.push_back(val);
}
};
SmallVector<OpFoldResult> newOffsets, newLengths, newStrides;
SmallVector<Value> newLows, newHighs;
SmallVector<int64_t> staticNewLows, staticNewHighs;
bool hasZeroLen = false;
Value dynHasZeroLenCond;
int64_t rank = padOp.getSourceType().getRank();
for (unsigned dim = 0; dim < rank; ++dim) {
auto low =
getValueOrCreateConstantIndexOp(b, loc, padOp.getMixedLowPad()[dim]);
bool hasLowPad = getConstantIntValue(low) != static_cast<int64_t>(0);
auto high =
getValueOrCreateConstantIndexOp(b, loc, padOp.getMixedHighPad()[dim]);
bool hasHighPad = getConstantIntValue(high) != static_cast<int64_t>(0);
auto offset = getValueOrCreateConstantIndexOp(b, loc, offsets[dim]);
auto length = getValueOrCreateConstantIndexOp(b, loc, sizes[dim]);
auto srcSize = b.createOrFold<tensor::DimOp>(loc, padOp.getSource(), dim);
Value newLow = hasLowPad ? max(zero, sub(low, offset)) : zero;
appendIndex(newLow, newLows, staticNewLows);
Value newOffset = hasLowPad ? min(max(sub(offset, low), zero), srcSize)
: min(offset, srcSize);
newOffsets.push_back(getAsOpFoldResult(newOffset));
Value endLoc = hasLowPad
? min(max(add(sub(offset, low), length), zero), srcSize)
: min(add(offset, length), srcSize);
Value newLength = sub(endLoc, newOffset);
newLengths.push_back(getAsOpFoldResult(newLength));
if (auto newLengthInt = getConstantIntValue(newLength)) {
hasZeroLen |= *newLengthInt == 0;
} else {
Value check = b.create<arith::CmpIOp>(loc, arith::CmpIPredicate::eq,
newLength, zero);
dynHasZeroLenCond =
dynHasZeroLenCond
? b.create<arith::OrIOp>(loc, check, dynHasZeroLenCond)
: check;
}
Value newHigh = hasHighPad ? sub(sub(length, newLength), newLow) : zero;
appendIndex(newHigh, newHighs, staticNewHighs);
newStrides.push_back(b.getIndexAttr(1));
}
SmallVector<Value> dynDims;
SmallVector<int64_t> shape;
dispatchIndexOpFoldResults(sizes, dynDims, shape, ShapedType::kDynamicSize);
RankedTensorType resultType =
RankedTensorType::get(shape, padOp.getResultType().getElementType());
auto castResult = [&](Value val) -> Operation * {
return b.create<tensor::CastOp>(loc, resultType, val);
};
auto createGenerateOp = [&]() {
auto generateOp = b.create<tensor::GenerateOp>(
loc, resultType, dynDims,
[&](OpBuilder &builder, Location gLoc, ValueRange indices) {
builder.create<tensor::YieldOp>(gLoc, padValue);
});
return castResult(generateOp);
};
auto createPadOfExtractSlice = [&]() {
auto newSliceOp = b.create<tensor::ExtractSliceOp>(
loc, padOp.getSource(), newOffsets, newLengths, newStrides);
auto newPadOp = b.create<PadOp>(loc, newSliceOp, staticNewLows,
staticNewHighs, newLows, newHighs);
BlockAndValueMapping bvm;
padOp.getRegion().cloneInto(&newPadOp.getRegion(), bvm);
return castResult(newPadOp);
};
if (hasZeroLen)
return createGenerateOp();
if (generateZeroSliceGuard && dynHasZeroLenCond) {
auto result = b.create<scf::IfOp>(
loc, resultType, dynHasZeroLenCond,
[&](OpBuilder &b, Location loc) {
b.create<scf::YieldOp>(loc, createGenerateOp()->getResult(0));
},
[&](OpBuilder &b, Location loc) {
b.create<scf::YieldOp>(loc, createPadOfExtractSlice()->getResult(0));
});
return result;
}
return createPadOfExtractSlice();
}
void mlir::tensor::registerTilingOpInterfaceExternalModels(
DialectRegistry ®istry) {
registry.addExtension(+[](MLIRContext *ctx, TensorDialect *dialect) {
tensor::PadOp::attachInterface<PadOpTiling>(*ctx);
});
}