MLIR 23.0.0git
IndexingMapOpInterface.cpp
Go to the documentation of this file.
1//===- IndexingMapOpInterface.cpp -- IndexingMapOpInterface impl ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
11using namespace mlir;
12
13namespace mlir {
14#include "mlir/Interfaces/IndexingMapOpInterface.cpp.inc"
15} // namespace mlir
16
17static LogicalResult verifyIndexingMapOperandType(Operation *op, Type t,
18 unsigned operandNumber) {
19 // Scalars are allowed (treated as rank-0). verifyImpl checks the rank.
20 if (t.isIntOrIndexOrFloat() || isa<ComplexType>(t))
21 return success();
22
23 // Vectors are allowed.
24 if (isa<VectorType>(t))
25 return success();
26
27 // MemRefs: must be ranked.
28 if (isa<UnrankedMemRefType>(t)) {
29 return op->emitOpError("operand #")
30 << operandNumber << " must be a ranked memref, but got " << t;
31 }
32 if (isa<MemRefType>(t))
33 return success();
34
35 // Tensors: must be ranked.
36 if (isa<UnrankedTensorType>(t)) {
37 return op->emitOpError("operand #")
38 << operandNumber << " must be a ranked tensor, but got " << t;
39 }
40 if (isa<RankedTensorType>(t))
41 return success();
42
43 // Any other shaped type is not supported by this interface.
44 return op->emitOpError("operand #")
45 << operandNumber
46 << " must be ranked tensor/memref, vector, or scalar, but got " << t;
47}
48
49LogicalResult mlir::IndexingMapOpInterface::verifyImpl() {
50 // All input/output operands must be indexed.
51 if (static_cast<int64_t>(getIndexingMapsArray().size()) !=
52 getOperation()->getNumOperands())
53 return this->emitOpError("expected the number of indexing_map (")
54 << getIndexingMapsArray().size()
55 << ") to be equal to the number of input/output operands ("
56 << getOperation()->getNumOperands() << ")";
57
58 SmallVector<int64_t> allShapesSizes;
59
60 for (OpOperand &opOperand : getOperation()->getOpOperands()) {
61 Type ty = opOperand.get().getType();
62 if (failed(verifyIndexingMapOperandType(getOperation(), ty,
63 opOperand.getOperandNumber())))
64 return failure();
65 AffineMap indexingMap = getMatchingIndexingMap(&opOperand);
66 // Symbols disallowed.
67 if (indexingMap.getNumSymbols() != 0)
68 return this->emitOpError("unexpected symbols in indexing_map #")
69 << opOperand.getOperandNumber();
70 // Handle scalars.
71 if (ty.isIntOrIndexOrFloat() || isa<ComplexType>(ty)) {
72 int64_t rank = 0;
73 if (indexingMap.getNumResults() != rank)
74 return this->emitOpError("expected operand #")
75 << opOperand.getOperandNumber() << " rank (" << rank
76 << ") to match the result rank of indexing_map ("
77 << indexingMap.getNumResults() << ")";
78 continue;
79 }
80 SmallVector<int64_t> shape = getStaticOperandShape(&opOperand);
81 int64_t rank = shape.size();
82
83 // Result rank must match operand rank.
84 if (indexingMap.getNumResults() != rank)
85 return this->emitOpError("expected operand #")
86 << opOperand.getOperandNumber() << " rank (" << rank
87 << ") to match the result rank of indexing_map ("
88 << indexingMap.getNumResults() << ")";
89
90 llvm::append_range(allShapesSizes, shape);
91 }
92
93 AffineMap invertedMap = getShapesToLoopsMap();
94 if (!invertedMap) {
95 std::string str;
96 llvm::raw_string_ostream os(str);
97 getLoopsToShapesMap().print(os);
98 return this->emitOpError("invalid indexing maps are non-invertible: ")
99 << "(" << str << ")";
100 }
101
102 SmallVector<int64_t> endLoopRangeValues = invertedMap.compose(allShapesSizes);
103
104 // Check if given shapes match to inferred shapes.
105 SmallVector<int64_t> startLoopRangeValues(endLoopRangeValues.size(), 0);
106 // Verify only static cases since we can't get exact dimension sizes and
107 // loop ranges for dynamic cases in this stage.
108 if (llvm::none_of(endLoopRangeValues, ShapedType::isDynamic)) {
109 // Exclusive end range.
110 for (int64_t &range : endLoopRangeValues)
111 range -= 1;
112 for (OpOperand &opOperand : getOperation()->getOpOperands()) {
113 AffineMap indexingMap = getMatchingIndexingMap(&opOperand);
114 SmallVector<int64_t> startIndices =
115 indexingMap.compose(startLoopRangeValues);
116 SmallVector<int64_t> endIndices = indexingMap.compose(endLoopRangeValues);
117 SmallVector<int64_t> shape = getStaticOperandShape(&opOperand);
118 for (auto dim : llvm::seq<int64_t>(0, shape.size())) {
119 // Ignore dynamic dimension or the case that the dimension size is 0
120 if (ShapedType::isDynamic(shape[dim]) || shape[dim] == 0)
121 continue;
122
123 // The first index or last index should be the maximum or the minimum in
124 // the inferred index ranges since the range is increasing or
125 // decreasing. The size of dimensions of input/output operands and the
126 // maximum value + 1 in the inferred range should be the same. But, for
127 // now we check if the inferred ranges are in boundary of input/output
128 // operands' size or not in case that Affine Expressions are complicated
129 // such as d0 * 3
130 // + d1 since it is not easy to handle the issues.
131 // Found the case that this solution can't check, for example, (d0, d1)
132 // -> (d1 - d0)
133 int64_t inferredDimSize =
134 std::max(startIndices[dim], endIndices[dim]) + 1;
135 if (std::min(startIndices[dim], endIndices[dim]) < 0) {
136 std::string mapStr;
137 {
138 llvm::raw_string_ostream os(mapStr);
139 os << indexingMap;
140 }
141 return this->emitOpError(
142 "unexpected result less than 0 at expression #")
143 << dim << " in " << mapStr;
144 }
145 if (isa<AffineDimExpr>(indexingMap.getResult(dim))) {
146 if (inferredDimSize != shape[dim]) {
147 return this->emitOpError("inferred input/output operand #")
148 << opOperand.getOperandNumber() << " has shape's dimension #"
149 << dim << " to be " << inferredDimSize << ", but found "
150 << shape[dim];
151 }
152 } else {
153 if (inferredDimSize > shape[dim]) {
154 return this->emitOpError("inferred input/output operand #")
155 << opOperand.getOperandNumber() << " has shape's dimension #"
156 << dim << " to be greater than or equal to "
157 << inferredDimSize << ", but found " << shape[dim];
158 }
159 }
160 }
161 }
162 }
163
164 return success();
165}
return success()
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static LogicalResult verifyIndexingMapOperandType(Operation *op, Type t, unsigned operandNumber)
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
unsigned getNumSymbols() const
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
This class represents an operand of an operation.
Definition Value.h:257
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
Definition Types.cpp:122
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.