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
9#include "mlir/Interfaces/IndexingMapOpInterface.h"
10
11using namespace mlir;
12
13namespace mlir {
14#include "mlir/Interfaces/IndexingMapOpInterface.cpp.inc"
15} // namespace mlir
16
17LogicalResult mlir::IndexingMapOpInterface::verifyImpl() {
18 // All input/output operands must be indexed.
19 if (static_cast<int64_t>(getIndexingMapsArray().size()) !=
20 getOperation()->getNumOperands())
21 return this->emitOpError(message: "expected the number of indexing_map (")
22 << getIndexingMapsArray().size()
23 << ") to be equal to the number of input/output operands ("
24 << getOperation()->getNumOperands() << ")";
25
26 AffineMap invertedMap = getShapesToLoopsMap();
27 if (!invertedMap) {
28 std::string str;
29 llvm::raw_string_ostream os(str);
30 getLoopsToShapesMap().print(os);
31 return this->emitOpError(message: "invalid indexing maps are non-invertible: ")
32 << "(" << str << ")";
33 }
34
35 SmallVector<int64_t> endLoopRangeValues = getStaticLoopRanges();
36
37 // Set this flag if this op has user defined maps. This is required to guard
38 // the below error condition which assume default indexing maps.
39 for (OpOperand &opOperand : getOperation()->getOpOperands()) {
40 AffineMap indexingMap = getMatchingIndexingMap(opOperand: &opOperand);
41
42 // Symbols disallowed.
43 if (indexingMap.getNumSymbols() != 0)
44 return getOperation()->emitOpError(message: "unexpected symbols in indexing_map #")
45 << opOperand.getOperandNumber();
46
47 // Domain must be consistent.
48 if (indexingMap.getNumDims() != endLoopRangeValues.size())
49 return getOperation()->emitOpError(message: "expected indexing_map #")
50 << opOperand.getOperandNumber() << " to have "
51 << endLoopRangeValues.size()
52 << " dim(s) to match the number of loops";
53
54 SmallVector<int64_t> shape = getStaticOperandShape(opOperand: &opOperand);
55 int64_t rank = shape.size();
56
57 if (indexingMap.getNumResults() != rank)
58 return getOperation()->emitOpError(message: "expected operand rank (")
59 << rank << ") to match the result rank of indexing_map #"
60 << opOperand.getOperandNumber() << " ("
61 << indexingMap.getNumResults() << ")";
62 }
63
64 // Check if given shapes match to inferred shapes.
65 SmallVector<int64_t> startLoopRangeValues(endLoopRangeValues.size(), 0);
66 // Verify only static cases since we can't get exact dimension sizes and
67 // loop ranges for dynamic cases in this stage.
68 if (llvm::none_of(Range&: endLoopRangeValues, P: ShapedType::isDynamic)) {
69 // Exclusive end range.
70 for (int64_t &range : endLoopRangeValues)
71 range -= 1;
72 for (OpOperand &opOperand : getOperation()->getOpOperands()) {
73 AffineMap indexingMap = getMatchingIndexingMap(opOperand: &opOperand);
74 SmallVector<int64_t> startIndices =
75 indexingMap.compose(values: startLoopRangeValues);
76 SmallVector<int64_t> endIndices = indexingMap.compose(values: endLoopRangeValues);
77 SmallVector<int64_t> shape = getStaticOperandShape(opOperand: &opOperand);
78 for (auto dim : llvm::seq<int64_t>(Begin: 0, End: shape.size())) {
79 // Ignore dynamic dimension or the case that the dimension size is 0
80 if (ShapedType::isDynamic(dValue: shape[dim]) || shape[dim] == 0)
81 continue;
82
83 // The first index or last index should be the maximum or the minimum in
84 // the inferred index ranges since the range is increasing or
85 // decreasing. The size of dimensions of input/output operands and the
86 // maximum value + 1 in the inferred range should be the same. But, for
87 // now we check if the inferred ranges are in boundary of input/output
88 // operands' size or not in case that Affine Expressions are complicated
89 // such as d0 * 3
90 // + d1 since it is not easy to handle the issues.
91 // Found the case that this solution can't check, for example, (d0, d1)
92 // -> (d1 - d0)
93 int64_t inferredDimSize =
94 std::max(a: startIndices[dim], b: endIndices[dim]) + 1;
95 if (std::min(a: startIndices[dim], b: endIndices[dim]) < 0) {
96 std::string mapStr;
97 {
98 llvm::raw_string_ostream os(mapStr);
99 os << indexingMap;
100 }
101 return this->emitOpError(
102 message: "unexpected result less than 0 at expression #")
103 << dim << " in " << mapStr;
104 }
105 if (isa<AffineDimExpr>(Val: indexingMap.getResult(idx: dim))) {
106 if (inferredDimSize != shape[dim]) {
107 return this->emitOpError(message: "inferred input/output operand #")
108 << opOperand.getOperandNumber() << " has shape's dimension #"
109 << dim << " to be " << inferredDimSize << ", but found "
110 << shape[dim];
111 }
112 } else {
113 if (inferredDimSize > shape[dim]) {
114 return this->emitOpError(message: "inferred input/output operand #")
115 << opOperand.getOperandNumber() << " has shape's dimension #"
116 << dim << " to be greater than or equal to "
117 << inferredDimSize << ", but found " << shape[dim];
118 }
119 }
120 }
121 }
122 }
123
124 return success();
125}
126

source code of mlir/lib/Interfaces/IndexingMapOpInterface.cpp