MLIR  21.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 
11 using namespace mlir;
12 
13 namespace mlir {
14 #include "mlir/Interfaces/IndexingMapOpInterface.cpp.inc"
15 } // namespace mlir
16 
17 LogicalResult 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("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("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);
41 
42  // Symbols disallowed.
43  if (indexingMap.getNumSymbols() != 0)
44  return getOperation()->emitOpError("unexpected symbols in indexing_map #")
45  << opOperand.getOperandNumber();
46 
47  // Domain must be consistent.
48  if (indexingMap.getNumDims() != endLoopRangeValues.size())
49  return getOperation()->emitOpError("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);
55  int64_t rank = shape.size();
56 
57  if (indexingMap.getNumResults() != rank)
58  return getOperation()->emitOpError("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(endLoopRangeValues, 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);
74  SmallVector<int64_t> startIndices =
75  indexingMap.compose(startLoopRangeValues);
76  SmallVector<int64_t> endIndices = indexingMap.compose(endLoopRangeValues);
77  SmallVector<int64_t> shape = getStaticOperandShape(&opOperand);
78  for (auto dim : llvm::seq<int64_t>(0, shape.size())) {
79  // Ignore dynamic dimension or the case that the dimension size is 0
80  if (ShapedType::isDynamic(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(startIndices[dim], endIndices[dim]) + 1;
95  if (std::min(startIndices[dim], endIndices[dim]) < 0) {
96  std::string mapStr;
97  {
98  llvm::raw_string_ostream os(mapStr);
99  os << indexingMap;
100  }
101  return this->emitOpError(
102  "unexpected result less than 0 at expression #")
103  << dim << " in " << mapStr;
104  }
105  if (isa<AffineDimExpr>(indexingMap.getResult(dim))) {
106  if (inferredDimSize != shape[dim]) {
107  return this->emitOpError("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("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 }
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition: AffineMap.h:46
unsigned getNumSymbols() const
Definition: AffineMap.cpp:394
unsigned getNumDims() const
Definition: AffineMap.cpp:390
unsigned getNumResults() const
Definition: AffineMap.cpp:398
AffineExpr getResult(unsigned idx) const
Definition: AffineMap.cpp:407
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
Definition: AffineMap.cpp:552
This class represents an operand of an operation.
Definition: Value.h:257
Include the generated interface declarations.