#include "mlir/Dialect/Linalg/IR/LinalgInterfaces.h"
#include "mlir/Dialect/Affine/IR/AffineOps.h"
#include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
#include "mlir/Dialect/Complex/IR/Complex.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/IR/AffineExprVisitor.h"
#include "mlir/IR/AffineMap.h"
#include "mlir/IR/TypeUtilities.h"
#include "llvm/ADT/SmallBitVector.h"
using namespace mlir;
using namespace mlir::linalg;
#include "mlir/Dialect/Linalg/IR/LinalgInterfaces.cpp.inc"
bool linalg::detail::canOpOperandsBeDroppedImpl(
linalg::LinalgOp linalgOp, ArrayRef<OpOperand *> droppedOperands) {
SmallVector<AffineMap> indexingMaps;
for (auto *opOperand : linalgOp.getInputAndOutputOperands()) {
if (llvm::is_contained(droppedOperands, opOperand))
continue;
indexingMaps.push_back(linalgOp.getTiedIndexingMap(opOperand));
}
return inversePermutation(concatAffineMaps(indexingMaps)) != AffineMap();
}
static bool isChainOfUnaryOpsFrom(Value v, Value from) {
while (true) {
if (v == from)
return true;
Operation *op = v.getDefiningOp();
if (!op || op->getNumOperands() != 1)
return false;
v = op->getOperand(0);
};
}
template <typename OpType>
static OpType getSingleOpOfType(Block &block) {
OpType res = nullptr;
block.walk([&](OpType op) {
if (res) {
res = nullptr;
return WalkResult::interrupt();
}
res = op;
return WalkResult::advance();
});
return res;
}
template <typename AddOpType, typename MulOpType>
static bool isAddMul(Block &block) {
if (block.getNumArguments() != 3)
return false;
Operation *yieldOp = block.getTerminator();
if (yieldOp->getNumOperands() != 1)
return false;
AddOpType addOp = getSingleOpOfType<AddOpType>(block);
MulOpType mulOp = getSingleOpOfType<MulOpType>(block);
if (!addOp || !mulOp)
return false;
Value argA = block.getArgument(0), argB = block.getArgument(1);
Value a = mulOp->getOperand(0), b = mulOp->getOperand(1);
Value mul = mulOp->getResult(0);
Value argC = block.getArgument(2);
Value c1 = addOp->getOperand(0), c2 = addOp->getOperand(1);
Value add = addOp->getResult(0);
Value res = yieldOp->getOperand(0);
auto un = isChainOfUnaryOpsFrom;
bool success = un(res, add);
success |= (un(c1, argC) && un(c2, mul)) || ((un(c1, mul)) && un(c2, argC));
success |= (un(a, argA) && un(b, argB)) || ((un(a, argB)) && un(b, argA));
return success;
}
enum class MatchContractionResult {
Success = 0,
NotLinalgOp,
WrongNumOperands,
NoReduction,
NotProjectedPermutations,
NotAddMul
};
static MatchContractionResult isContractionInterfaceImpl(Operation *op) {
auto linalgOp = dyn_cast<linalg::LinalgOp>(op);
if (!linalgOp)
return MatchContractionResult::NotLinalgOp;
if (linalgOp.getNumInputs() != 2 || linalgOp.getNumOutputs() != 1)
return MatchContractionResult::WrongNumOperands;
auto mapRange = linalgOp.getIndexingMapsArray();
if (linalgOp.getNumReductionLoops() == 0)
return MatchContractionResult::NoReduction;
if (llvm::any_of(mapRange,
[](AffineMap m) { return !m.isProjectedPermutation(); }))
return MatchContractionResult::NotProjectedPermutations;
if (!isAddMul<arith::AddFOp, arith::MulFOp>(linalgOp->getRegion(0).front()) &&
!isAddMul<arith::AddIOp, arith::MulIOp>(linalgOp->getRegion(0).front()) &&
!isAddMul<complex::AddOp, complex::MulOp>(
linalgOp->getRegion(0).front()) &&
!isAddMul<arith::OrIOp, arith::AndIOp>(linalgOp->getRegion(0).front()))
return MatchContractionResult::NotAddMul;
return MatchContractionResult::Success;
}
bool mlir::linalg::isaContractionOpInterface(LinalgOp linalgOp) {
if (!linalgOp)
return false;
Operation *op = linalgOp.getOperation();
return isa<ContractionOpInterface>(op) ||
(isContractionInterfaceImpl(op) == MatchContractionResult::Success);
}
LogicalResult mlir::linalg::detail::verifyContractionInterface(Operation *op) {
auto res = isContractionInterfaceImpl(op);
if (res == MatchContractionResult::NotLinalgOp)
return op->emitError("expected a LinalgOp");
if (res == MatchContractionResult::WrongNumOperands)
return op->emitError("expected op with 2 inputs and 1 outputs");
if (res == MatchContractionResult::NoReduction)
return op->emitError("expected at least a reduction loop");
if (res == MatchContractionResult::NotProjectedPermutations)
return op->emitError("expected all indexings to be projected permutations");
if (res == MatchContractionResult::NotAddMul)
return op->emitError("(add, mul) operations not found");
return success();
}
template <typename T>
static T getAffineExprOfType(AffineExpr lhs, AffineExpr rhs) {
return lhs.isa<T>() ? lhs.cast<T>()
: (rhs.isa<T>() ? rhs.cast<T>() : nullptr);
}
namespace {
struct ConvAccessExprWalker
: public AffineExprVisitor<ConvAccessExprWalker, LogicalResult> {
llvm::SmallDenseSet<unsigned> convolvedDims;
llvm::SmallDenseSet<unsigned> unConvolvedDims;
LogicalResult visitDimExpr(AffineDimExpr dimExpr) {
unsigned position = dimExpr.getPosition();
if (unConvolvedDims.count(position) || convolvedDims.count(position)) {
return failure();
}
unConvolvedDims.insert(position);
return success();
}
LogicalResult visitSymbolExpr(AffineSymbolExpr expr) { return failure(); }
LogicalResult visitConstantExpr(AffineConstantExpr expr) { return failure(); }
LogicalResult visitAffineBinaryOpExpr(AffineBinaryOpExpr binaryExpr) {
if (binaryExpr.getKind() != AffineExprKind::Add)
return failure();
return success(succeeded(isDimExprOrMulExpr(binaryExpr.getLHS())) &&
succeeded(isDimExprOrMulExpr(binaryExpr.getRHS())));
}
LogicalResult isDimExprOrMulExpr(AffineExpr expr) {
if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) {
unsigned dim = dimExpr.getPosition();
if (convolvedDims.count(dim) || unConvolvedDims.count(dim))
return failure();
convolvedDims.insert(dim);
return success();
}
if (auto symbolMulExpr = expr.dyn_cast<AffineBinaryOpExpr>()) {
if (symbolMulExpr.getKind() != AffineExprKind::Mul)
return failure();
auto lhsExpr = symbolMulExpr.getLHS();
auto rhsExpr = symbolMulExpr.getRHS();
AffineExpr mulExpr =
getAffineExprOfType<AffineSymbolExpr>(lhsExpr, rhsExpr);
if (!mulExpr) {
mulExpr = getAffineExprOfType<AffineConstantExpr>(lhsExpr, rhsExpr);
}
auto dimExpr = getAffineExprOfType<AffineDimExpr>(lhsExpr, rhsExpr);
if (!mulExpr || !dimExpr)
return failure();
unsigned dim = dimExpr.getPosition();
if (convolvedDims.count(dim) || unConvolvedDims.count(dim))
return failure();
convolvedDims.insert(dim);
return success();
}
return failure();
}
};
}
static llvm::SmallDenseSet<unsigned> getPreservedDims(AffineMap map) {
assert(map.isProjectedPermutation() &&
"expected map to have projected permutations");
llvm::SmallDenseSet<unsigned> preservedDims;
for (auto expr : map.getResults())
preservedDims.insert(expr.cast<AffineDimExpr>().getPosition());
return preservedDims;
}
enum class MatchConvolutionResult {
Success = 0,
NotLinalgOp,
WrongNumOperands,
WrongInputIndexingMap,
NotProjectedPermutations,
NonConvolutionLoop,
OutputDimsNotParallel,
NonOutputDimNotReduction
};
static MatchConvolutionResult isConvolutionInterfaceImpl(Operation *op) {
auto linalgOp = dyn_cast<linalg::LinalgOp>(op);
if (!linalgOp)
return MatchConvolutionResult::NotLinalgOp;
if (linalgOp.getNumInputs() < 2 || linalgOp.getNumOutputs() != 1)
return MatchConvolutionResult::WrongNumOperands;
auto indexingMaps = linalgOp.getIndexingMapsArray();
ConvAccessExprWalker inputExprWalker;
if (llvm::any_of(indexingMaps[0].getResults(),
[&inputExprWalker](AffineExpr expr) {
return failed(inputExprWalker.visit(expr));
})) {
return MatchConvolutionResult::WrongInputIndexingMap;
}
if (!indexingMaps[1].isProjectedPermutation() ||
!indexingMaps.back().isProjectedPermutation())
return MatchConvolutionResult::NotProjectedPermutations;
auto iteratorTypesRange =
linalgOp.iterator_types().getAsValueRange<StringAttr>();
llvm::SmallDenseSet<unsigned> outputDims =
getPreservedDims(indexingMaps.back());
llvm::SmallDenseSet<unsigned> filterDims = getPreservedDims(indexingMaps[1]);
llvm::SmallDenseSet<unsigned> allLoopDims;
for (auto outputExpr : indexingMaps.back().getResults()) {
unsigned outputDim = outputExpr.cast<AffineDimExpr>().getPosition();
if (inputExprWalker.unConvolvedDims.count(outputDim) &&
!filterDims.count(outputDim)) {
if (*std::next(iteratorTypesRange.begin(), outputDim) !=
getParallelIteratorTypeName())
return MatchConvolutionResult::OutputDimsNotParallel;
allLoopDims.insert(outputDim);
continue;
}
if (inputExprWalker.convolvedDims.count(outputDim) &&
!filterDims.count(outputDim)) {
if (*std::next(iteratorTypesRange.begin(), outputDim) !=
getParallelIteratorTypeName())
return MatchConvolutionResult::OutputDimsNotParallel;
allLoopDims.insert(outputDim);
continue;
}
if (!inputExprWalker.convolvedDims.count(outputDim) &&
!inputExprWalker.unConvolvedDims.count(outputDim) &&
filterDims.count(outputDim)) {
if (*std::next(iteratorTypesRange.begin(), outputDim) !=
getParallelIteratorTypeName())
return MatchConvolutionResult::OutputDimsNotParallel;
allLoopDims.insert(outputDim);
continue;
}
if (inputExprWalker.unConvolvedDims.count(outputDim) &&
filterDims.count(outputDim)) {
if (*std::next(iteratorTypesRange.begin(), outputDim) !=
getParallelIteratorTypeName())
return MatchConvolutionResult::OutputDimsNotParallel;
allLoopDims.insert(outputDim);
continue;
}
return MatchConvolutionResult::NonConvolutionLoop;
}
for (auto filterExpr : indexingMaps[1].getResults()) {
unsigned filterDim = filterExpr.cast<AffineDimExpr>().getPosition();
if (outputDims.count(filterDim) &&
!inputExprWalker.unConvolvedDims.count(filterDim) &&
!inputExprWalker.convolvedDims.count(filterDim)) {
continue;
}
if (inputExprWalker.convolvedDims.count(filterDim) &&
!outputDims.count(filterDim)) {
if (*std::next(iteratorTypesRange.begin(), filterDim) !=
getReductionIteratorTypeName())
return MatchConvolutionResult::NonOutputDimNotReduction;
if (allLoopDims.count(filterDim))
return MatchConvolutionResult::NonConvolutionLoop;
allLoopDims.insert(filterDim);
continue;
}
if (inputExprWalker.unConvolvedDims.count(filterDim) &&
!outputDims.count(filterDim)) {
if (*std::next(iteratorTypesRange.begin(), filterDim) !=
getReductionIteratorTypeName())
return MatchConvolutionResult::NonOutputDimNotReduction;
if (allLoopDims.count(filterDim))
return MatchConvolutionResult::NonConvolutionLoop;
allLoopDims.insert(filterDim);
continue;
}
if (inputExprWalker.unConvolvedDims.count(filterDim) &&
outputDims.count(filterDim)) {
continue;
}
return MatchConvolutionResult::NonConvolutionLoop;
}
if (allLoopDims.size() != linalgOp.getNumLoops())
return MatchConvolutionResult::NonConvolutionLoop;
return MatchConvolutionResult::Success;
}
LogicalResult mlir::linalg::detail::verifyConvolutionInterface(Operation *op) {
auto res = isConvolutionInterfaceImpl(op);
if (res == MatchConvolutionResult::NotLinalgOp)
return op->emitError("expected a LinalgOp");
if (res == MatchConvolutionResult::WrongNumOperands)
return op->emitError("expected op with 2 inputs and 1 output");
if (res == MatchConvolutionResult::WrongInputIndexingMap)
return op->emitError("unexpected input index map for convolutions");
if (res == MatchConvolutionResult::NotProjectedPermutations) {
return op->emitError(
"expected output/filter indexing maps to be projected permutations");
}
if (res == MatchConvolutionResult::NonConvolutionLoop) {
return op->emitError("unexpected loop dimension for convolution op");
}
if (res == MatchConvolutionResult::OutputDimsNotParallel) {
return op->emitError(
"expected all iterators used to access outputs to be parallel");
}
if (res == MatchConvolutionResult::NonOutputDimNotReduction) {
return op->emitError(
"expected all iterators not used to access outputs to be reduction");
}
return success();
}
enum class MatchFillResult {
Success = 0,
NotLinalgOp,
WrongNumOperands,
NotScalarInput
};
static MatchFillResult isFillInterfaceImpl(Operation *op) {
auto linalgOp = dyn_cast<linalg::LinalgOp>(op);
if (!linalgOp)
return MatchFillResult::NotLinalgOp;
if (linalgOp.getNumInputs() != 1 || linalgOp.getNumOutputs() != 1)
return MatchFillResult::WrongNumOperands;
OpOperand *value = linalgOp.getInputOperand(0);
if (!linalgOp.isScalar(value))
return MatchFillResult::NotScalarInput;
return MatchFillResult::Success;
}
LogicalResult mlir::linalg::detail::verifyFillInterface(Operation *op) {
auto res = isFillInterfaceImpl(op);
if (res == MatchFillResult::NotLinalgOp)
return op->emitError("expected a LinalgOp");
if (res == MatchFillResult::WrongNumOperands)
return op->emitError("expected op with 1 input and 1 output");
if (res == MatchFillResult::NotScalarInput)
return op->emitError("expected op with scalar input");
return success();
}
OpOperandVector::operator SmallVector<Value>() {
SmallVector<Value> result;
result.reserve(this->size());
llvm::transform(*this, std::back_inserter(result),
[](OpOperand *opOperand) { return opOperand->get(); });
return result;
}
static Value createOrFoldDimOp(OpBuilder &b, Location loc, Value source,
int64_t dim) {
if (source.getType().isa<UnrankedMemRefType, MemRefType>())
return b.createOrFold<memref::DimOp>(loc, source, dim);
if (source.getType().isa<UnrankedTensorType, RankedTensorType>())
return b.createOrFold<tensor::DimOp>(loc, source, dim);
llvm_unreachable("Expected MemRefType or TensorType");
}
SmallVector<Value, 4> LinalgOp::createFlatListOfOperandDims(OpBuilder &b,
Location loc) {
SmallVector<Value, 4> res;
for (OpOperand *opOperand : getInputAndOutputOperands()) {
for (int64_t i = 0, e = getRank(opOperand); i < e; ++i)
res.push_back(createOrFoldDimOp(b, loc, opOperand->get(), i));
}
return res;
}
SmallVector<int64_t, 4> LinalgOp::createFlatListOfOperandStaticDims() {
SmallVector<int64_t, 4> res;
assert(!hasDynamicShape() && "expected operands to have static shapes");
for (OpOperand *opOperand : getInputAndOutputOperands())
llvm::append_range(res, getShape(opOperand));
return res;
}
SmallVector<Range, 4> LinalgOp::createLoopRanges(OpBuilder &b, Location loc) {
AffineMap map = getLoopsToShapesMap();
unsigned numDims = map.getNumDims(), numRes = map.getNumResults();
auto viewSizes = createFlatListOfOperandDims(b, loc);
SmallVector<Range, 4> res(numDims);
Value zeroVal = b.create<arith::ConstantIndexOp>(loc, 0);
Value oneVal = b.create<arith::ConstantIndexOp>(loc, 1);
for (unsigned idx = 0; idx < numRes; ++idx) {
auto result = map.getResult(idx);
if (auto d = result.dyn_cast<AffineDimExpr>()) {
if (res[d.getPosition()].offset)
continue;
res[d.getPosition()] = Range{zeroVal, viewSizes[idx], oneVal};
}
}
return res;
}
SmallVector<int64_t, 4> LinalgOp::computeStaticLoopSizes() {
AffineMap map = getLoopsToShapesMap();
unsigned numDims = map.getNumDims(), numRes = map.getNumResults();
SmallVector<int64_t, 4> allShapeSizes = createFlatListOfOperandStaticDims();
SmallVector<int64_t, 4> res(numDims, 0);
for (unsigned idx = 0; idx < numRes; ++idx) {
auto result = map.getResult(idx);
if (auto d = result.dyn_cast<AffineDimExpr>())
res[d.getPosition()] = allShapeSizes[idx];
}
return res;
}
struct HasAffineDimExprVisitor
: public AffineExprVisitor<HasAffineDimExprVisitor, bool> {
HasAffineDimExprVisitor(llvm::SmallBitVector positions)
: positions(std::move(positions)) {}
bool visitAffineBinaryOpExpr(AffineBinaryOpExpr binaryOpExpr) {
return visit(binaryOpExpr.getLHS()) || visit(binaryOpExpr.getRHS());
}
bool visitDimExpr(AffineDimExpr dimExpr) {
return positions.test(dimExpr.getPosition());
}
bool visitConstantExpr(AffineConstantExpr constExpr) { return false; }
bool visitSymbolExpr(AffineSymbolExpr symbolExpr) { return false; }
private:
llvm::SmallBitVector positions;
};
LogicalResult
LinalgOp::reifyResultShapes(OpBuilder &b,
ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
AffineMap loopsToShapesMap = getLoopsToShapesMap();
auto resultShapesSubMapPos = getResultsPositionInLoopsToShapeMap();
AffineMap loopToResultsShapeMap = loopsToShapesMap.getSliceMap(
resultShapesSubMapPos.first,
resultShapesSubMapPos.second - resultShapesSubMapPos.first);
AffineMap resultShapesFromInputShapesMap =
loopToResultsShapeMap.compose(getShapesToLoopsMap());
llvm::SmallBitVector outputDims(resultShapesFromInputShapesMap.getNumDims());
outputDims.set(resultShapesSubMapPos.first, resultShapesSubMapPos.second);
HasAffineDimExprVisitor checkDimExpr(std::move(outputDims));
Location loc = getOperation()->getLoc();
auto allResultDimValues =
applyMapToValues(b, loc, resultShapesFromInputShapesMap,
createFlatListOfOperandDims(b, loc));
int64_t pos = 0;
ArrayRef<AffineExpr> shapeExprs = resultShapesFromInputShapesMap.getResults();
for (OpOperand *opOperand : getOutputOperands()) {
SmallVector<Value> shapes;
for (int64_t dim : llvm::seq<int64_t>(0, getRank(opOperand))) {
if (checkDimExpr.visit(shapeExprs[pos]))
shapes.push_back(createOrFoldDimOp(b, loc, opOperand->get(), dim));
else
shapes.push_back(allResultDimValues[pos]);
pos++;
}
reifiedReturnShapes.emplace_back(std::move(shapes));
}
return success();
}
LogicalResult mlir::linalg::detail::verifyStructuredOpInterface(Operation *op) {
LinalgOp linalgOp = cast<LinalgOp>(op);
int64_t numInputs = linalgOp.getNumInputs();
int64_t numOutputs = linalgOp.getNumOutputs();
if (numOutputs == 0)
return op->emitOpError("expected at least one output operand");
if (failed(OpTrait::impl::verifyNOperands(op, numInputs + numOutputs)))
return failure();
if (op->getNumResults() != linalgOp.getOutputTensorOperands().size())
return op->emitOpError("expected the number of results (")
<< op->getNumResults()
<< ") to be equal to the number of output tensors ("
<< linalgOp.getOutputTensorOperands().size() << ")";
auto iteratorTypesRange =
linalgOp.iterator_types().getAsValueRange<StringAttr>();
for (StringRef iteratorType : iteratorTypesRange) {
if (!llvm::is_contained(getAllIteratorTypeNames(), iteratorType))
return op->emitOpError("unexpected iterator_type (")
<< iteratorType << ")";
}
if (linalgOp.hasDynamicIndexingMaps())
if (failed(linalgOp.verifyIndexingMapRequiredAttributes()))
return failure();
if (static_cast<int64_t>(linalgOp.getIndexingMapsArray().size()) !=
linalgOp.getNumInputsAndOutputs())
return op->emitOpError("expected the number of indexing_map (")
<< linalgOp.getIndexingMapsArray().size()
<< ") to be equal to the number of input/output operands ("
<< linalgOp.getNumInputsAndOutputs() << ")";
for (OpOperand *opOperand : linalgOp.getInputAndOutputOperands()) {
AffineMap indexingMap = linalgOp.getTiedIndexingMap(opOperand);
if (indexingMap.getNumSymbols() != 0)
return op->emitOpError("unexpected symbols in indexing_map #")
<< opOperand->getOperandNumber();
unsigned numLoops = linalgOp.getNumLoops();
if (indexingMap.getNumDims() != numLoops)
return op->emitOpError("expected indexing_map #")
<< opOperand->getOperandNumber() << " to have " << numLoops
<< " dim(s) to match the number of loops";
int64_t rank = linalgOp.getRank(opOperand);
if (indexingMap.getNumResults() != rank)
return op->emitOpError("expected operand rank (")
<< rank << ") to match the result rank of indexing_map #"
<< opOperand->getOperandNumber() << " ("
<< indexingMap.getNumResults() << ")";
}
SmallVector<unsigned> redDims;
linalgOp.getReductionDims(redDims);
if (!linalgOp.getOutputBufferOperands().empty() &&
!linalgOp.getOutputTensorOperands().empty())
return op->emitOpError(
"expected output operands to all have tensor type or "
"all have buffer type");
for (OpOperand *opOperand : linalgOp.getOutputTensorOperands()) {
OpResult result = linalgOp.getTiedOpResult(opOperand);
if (result.getType() != opOperand->get().getType())
return op->emitOpError("expected type of operand #")
<< opOperand->getOperandNumber() << " ("
<< opOperand->get().getType() << ")"
<< " to match type of corresponding result (" << result.getType()
<< ")";
}
for (OpOperand *opOperand : linalgOp.getOutputOperands()) {
AffineMap indexingMap = linalgOp.getTiedIndexingMap(opOperand);
for (AffineExpr expr : indexingMap.getResults()) {
for (unsigned pos : redDims) {
if (expr.isFunctionOfDim(pos)) {
std::string exprStr;
{
llvm::raw_string_ostream os(exprStr);
os << expr;
}
return op->emitOpError(
"unexpected output tensor expression in indexing map #")
<< (opOperand->getOperandNumber() - linalgOp.getNumInputs())
<< " a.k.a '" << exprStr
<< "' is function of reduction iterator 'd" << pos << "'";
}
}
}
}
if (linalgOp->getNumRegions() != 1 ||
!llvm::hasSingleElement(linalgOp->getRegion(0)))
return op->emitOpError("expects to have 1 region with 1 block");
if (!linalgOp.getShapesToLoopsMap())
return op->emitOpError("expected the shape-to-loops map to be non-null");
Block &block = linalgOp->getRegion(0).front();
if (linalgOp.getNumInputsAndOutputs() != block.getNumArguments())
return op->emitOpError("expected as many non-induction variable region "
"arguments as the number of input/output operands");
for (OpOperand *opOperand : linalgOp.getInputAndOutputOperands()) {
Type elementType = getElementTypeOrSelf(opOperand->get());
Type argType = block.getArgument(opOperand->getOperandNumber()).getType();
if (elementType != argType)
return op->emitOpError("expected type of bb argument #")
<< opOperand->getOperandNumber() << " (" << argType << ")"
<< " to match element or self type of the corresponding operand ("
<< elementType << ")";
}
SmallVector<int64_t, 4> endLoopRangeValues = linalgOp.getStaticLoopRanges();
SmallVector<int64_t, 4> startLoopRangeValues(endLoopRangeValues.size(), 0);
if (llvm::none_of(endLoopRangeValues, ShapedType::isDynamic)) {
for (int64_t &range : endLoopRangeValues)
range -= 1;
for (OpOperand *opOperand : linalgOp.getInputAndOutputOperands()) {
AffineMap indexingMap = linalgOp.getTiedIndexingMap(opOperand);
SmallVector<int64_t, 4> startIndices =
indexingMap.compose(startLoopRangeValues);
SmallVector<int64_t, 4> endIndices =
indexingMap.compose(endLoopRangeValues);
ArrayRef<int64_t> shape = linalgOp.getShape(opOperand);
for (auto dim : llvm::seq<int64_t>(0, shape.size())) {
if (ShapedType::isDynamic(shape[dim]) || shape[dim] == 0)
continue;
int64_t inferredDimSize =
std::max(startIndices[dim], endIndices[dim]) + 1;
if (std::min(startIndices[dim], endIndices[dim]) < 0) {
std::string mapStr;
{
llvm::raw_string_ostream os(mapStr);
os << indexingMap;
}
return op->emitOpError(
"unexpected result less than 0 at expression #")
<< dim << " in " << mapStr;
}
if (indexingMap.getResult(dim).dyn_cast<AffineDimExpr>()) {
if (inferredDimSize != shape[dim]) {
return op->emitOpError("inferred input/output operand #")
<< opOperand->getOperandNumber()
<< " has shape's dimension #" << dim << " to be "
<< inferredDimSize << ", but found " << shape[dim];
}
} else {
if (inferredDimSize > shape[dim]) {
return op->emitOpError("inferred input/output operand #")
<< opOperand->getOperandNumber()
<< " has shape's dimension #" << dim
<< " to be greater than or equal to " << inferredDimSize
<< ", but found " << shape[dim];
}
}
}
}
}
return success();
}