MLIR 24.0.0git
ValueBoundsOpInterfaceImpl.cpp
Go to the documentation of this file.
1//===- ValueBoundsOpInterfaceImpl.cpp - Impl. of ValueBoundsOpInterface ---===//
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
14
15using namespace mlir;
16
17namespace mlir {
18namespace tensor {
19namespace {
20
21struct CastOpInterface
22 : public ValueBoundsOpInterface::ExternalModel<CastOpInterface, CastOp> {
23 void populateBoundsForShapedValueDim(Operation *op, Value value, int64_t dim,
24 ValueBoundsConstraintSet &cstr) const {
25 auto castOp = cast<CastOp>(op);
26 assert(value == castOp.getResult() && "invalid value");
27
28 if (llvm::isa<RankedTensorType>(castOp.getResult().getType()) &&
29 llvm::isa<RankedTensorType>(castOp.getSource().getType())) {
30 cstr.bound(value)[dim] == cstr.getExpr(castOp.getSource(), dim);
31 }
32 }
33};
34
35struct CollapseShapeOpInterface
36 : public ValueBoundsOpInterface::ExternalModel<CollapseShapeOpInterface,
37 CollapseShapeOp> {
38 void populateBoundsForShapedValueDim(Operation *op, Value value, int64_t dim,
39 ValueBoundsConstraintSet &cstr) const {
40 auto collapseOp = cast<CollapseShapeOp>(op);
41 assert(value == collapseOp.getResult() && "invalid value");
42
43 // Multiply the expressions for the dimensions in the reassociation group.
44 const ReassociationIndices reassocIndices =
45 collapseOp.getReassociationIndices()[dim];
46 AffineExpr productExpr =
47 cstr.getExpr(collapseOp.getSrc(), reassocIndices[0]);
48 for (size_t i = 1; i < reassocIndices.size(); ++i) {
49 productExpr =
50 productExpr * cstr.getExpr(collapseOp.getSrc(), reassocIndices[i]);
51 }
52 cstr.bound(value)[dim] == productExpr;
53 }
54};
55
56struct ConcatOpInterface
57 : public ValueBoundsOpInterface::ExternalModel<ConcatOpInterface,
58 ConcatOp> {
59 void populateBoundsForShapedValueDim(Operation *op, Value value, int64_t dim,
60 ValueBoundsConstraintSet &cstr) const {
61 auto concatOp = cast<ConcatOp>(op);
62 assert(value == concatOp.getResult() && "invalid value");
63
64 ValueRange inputs = concatOp.getInputs();
65 if (dim != static_cast<int64_t>(concatOp.getDim())) {
66 // All inputs have the same size as the result in a dimension that is not
67 // concatenated. Relate the result to every input: relating it to a single
68 // input loses the bound when that input is the unbounded one.
69 for (Value input : inputs)
70 cstr.bound(value)[dim] == cstr.getExpr(input, dim);
71 return;
72 }
73
74 // The concatenated dimension is the sum of the input sizes.
75 AffineExpr sum = cstr.getExpr(inputs.front(), dim);
76 for (Value input : inputs.drop_front())
77 sum = sum + cstr.getExpr(input, dim);
78 cstr.bound(value)[dim] == sum;
79 }
80};
81
82struct DimOpInterface
83 : public ValueBoundsOpInterface::ExternalModel<DimOpInterface, DimOp> {
84 void populateBoundsForIndexValue(Operation *op, Value value,
85 ValueBoundsConstraintSet &cstr) const {
86 auto dimOp = cast<DimOp>(op);
87 assert(value == dimOp.getResult() && "invalid value");
88
89 cstr.bound(value) >= 0;
90 auto constIndex = dimOp.getConstantIndex();
91 if (!constIndex.has_value())
92 return;
93 cstr.bound(value) == cstr.getExpr(dimOp.getSource(), *constIndex);
94 }
95};
96
97struct EmptyOpInterface
98 : public ValueBoundsOpInterface::ExternalModel<EmptyOpInterface, EmptyOp> {
99 void populateBoundsForShapedValueDim(Operation *op, Value value, int64_t dim,
100 ValueBoundsConstraintSet &cstr) const {
101 auto emptyOp = cast<EmptyOp>(op);
102 assert(value == emptyOp.getResult() && "invalid value");
103
104 cstr.bound(value)[dim] == emptyOp.getMixedSizes()[dim];
105 }
106};
107
108struct ExpandShapeOpInterface
109 : public ValueBoundsOpInterface::ExternalModel<ExpandShapeOpInterface,
110 ExpandShapeOp> {
111 void populateBoundsForShapedValueDim(Operation *op, Value value, int64_t dim,
112 ValueBoundsConstraintSet &cstr) const {
113 auto expandOp = cast<ExpandShapeOp>(op);
114 assert(value == expandOp.getResult() && "invalid value");
115 cstr.bound(value)[dim] == expandOp.getMixedOutputShape()[dim];
116 }
117};
118
119struct ExtractSliceOpInterface
120 : public ValueBoundsOpInterface::ExternalModel<ExtractSliceOpInterface,
121 ExtractSliceOp> {
122 void populateBoundsForShapedValueDim(Operation *op, Value value, int64_t dim,
123 ValueBoundsConstraintSet &cstr) const {
124 auto extractSliceOp = cast<ExtractSliceOp>(op);
125 assert(value == extractSliceOp.getResult() && "invalid value");
126
127 llvm::SmallBitVector dropped = extractSliceOp.getDroppedDims();
128 int64_t ctr = -1;
129 for (int64_t i = 0, e = extractSliceOp.getMixedSizes().size(); i < e; ++i) {
130 // Skip over rank-reduced dimensions.
131 if (!dropped.test(i))
132 ++ctr;
133 if (ctr == dim) {
134 cstr.bound(value)[dim] == extractSliceOp.getMixedSizes()[i];
135 int64_t staticStride = extractSliceOp.getStaticStrides()[i];
136 if (staticStride > 0) {
137 // Force analysis of the size value to get a better bound. Example:
138 // %size = affine.min affine_map<(d0) -> (5, -d0 + 24)>(%iv)
139 // %slice = tensor.extract_slice %source[%iv][%size][1]
140 // : tensor<24xf32> to tensor<?xf32>
141 // Here we get a bound of 24 immediately, so %size is skipped under
142 // the default stop condition unless we force its analysis.
143 if (auto sizeValue =
144 llvm::dyn_cast<Value>(extractSliceOp.getMixedSizes()[i]))
145 cstr.populateConstraints(sizeValue, std::nullopt);
146 // Encode in-bounds slice constraint:
147 OpFoldResult offset = extractSliceOp.getMixedOffsets()[i];
148 AffineExpr offsetExpr = cstr.getExpr(offset);
149 AffineExpr sourceDimExpr =
150 cstr.getExpr(extractSliceOp.getSource(), i);
151 cstr.bound(value)[dim] <=
152 (sourceDimExpr - offsetExpr).ceilDiv(staticStride);
153 cstr.bound(value)[dim] <= sourceDimExpr - offsetExpr;
154 cstr.bound(value)[dim] <= sourceDimExpr;
155 }
156 return;
157 }
158 }
159 llvm_unreachable("could not find non-rank-reduced dim");
160 }
161};
162
163struct PadOpInterface
164 : public ValueBoundsOpInterface::ExternalModel<PadOpInterface, PadOp> {
165 void populateBoundsForShapedValueDim(Operation *op, Value value, int64_t dim,
166 ValueBoundsConstraintSet &cstr) const {
167 auto padOp = cast<PadOp>(op);
168 assert(value == padOp.getResult() && "invalid value");
169
170 AffineExpr srcSize = cstr.getExpr(padOp.getSource(), dim);
171 AffineExpr lowPad = cstr.getExpr(padOp.getMixedLowPad()[dim]);
172 AffineExpr highPad = cstr.getExpr(padOp.getMixedHighPad()[dim]);
173 cstr.bound(value)[dim] == srcSize + lowPad + highPad;
174 }
175};
176
177struct RankOpInterface
178 : public ValueBoundsOpInterface::ExternalModel<RankOpInterface, RankOp> {
179 void populateBoundsForIndexValue(Operation *op, Value value,
180 ValueBoundsConstraintSet &cstr) const {
181 auto rankOp = cast<RankOp>(op);
182 assert(value == rankOp.getResult() && "invalid value");
183
184 auto tensorType =
185 llvm::dyn_cast<RankedTensorType>(rankOp.getTensor().getType());
186 if (!tensorType)
187 return;
188 cstr.bound(value) == tensorType.getRank();
189 }
190};
191
192struct SplatOpInterface
193 : public ValueBoundsOpInterface::ExternalModel<SplatOpInterface, SplatOp> {
194 void populateBoundsForShapedValueDim(Operation *op, Value value, int64_t dim,
195 ValueBoundsConstraintSet &cstr) const {
196 auto splatOp = cast<SplatOp>(op);
197 assert(value == splatOp.getAggregate() && "invalid value");
198
199 RankedTensorType type = splatOp.getType();
200 SmallVector<OpFoldResult> sizes = getMixedValues(
201 type.getShape(), splatOp.getDynamicSizes(), type.getContext());
202 cstr.bound(value)[dim] == sizes[dim];
203 }
204};
205
206} // namespace
207} // namespace tensor
208} // namespace mlir
209
211 DialectRegistry &registry) {
212 registry.addExtension(+[](MLIRContext *ctx, tensor::TensorDialect *dialect) {
213 tensor::CastOp::attachInterface<tensor::CastOpInterface>(*ctx);
214 tensor::CollapseShapeOp::attachInterface<tensor::CollapseShapeOpInterface>(
215 *ctx);
216 tensor::ConcatOp::attachInterface<tensor::ConcatOpInterface>(*ctx);
217 tensor::DimOp::attachInterface<tensor::DimOpInterface>(*ctx);
218 tensor::EmptyOp::attachInterface<tensor::EmptyOpInterface>(*ctx);
219 tensor::ExpandShapeOp::attachInterface<tensor::ExpandShapeOpInterface>(
220 *ctx);
221 tensor::ExtractSliceOp::attachInterface<tensor::ExtractSliceOpInterface>(
222 *ctx);
223 tensor::PadOp::attachInterface<tensor::PadOpInterface>(*ctx);
224 tensor::RankOp::attachInterface<tensor::RankOpInterface>(*ctx);
225 tensor::SplatOp::attachInterface<tensor::SplatOpInterface>(*ctx);
226 // Note: ValueBoundsOpInterface implementation is not required for ops that
227 // implement `DestinationStyleOpInterface` (for querying shaped OpResults).
228 });
229}
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
AffineExpr getExpr(Value value, std::optional< int64_t > dim=std::nullopt)
Return an expression that represents the given index-typed value or shaped value dimension.
BoundBuilder bound(Value value)
Add a bound for the given index-typed value or shaped value.
void populateConstraints(Value value, std::optional< int64_t > dim)
Traverse the IR starting from the given value/dim and populate constraints as long as the stop condit...
void registerValueBoundsOpInterfaceExternalModels(DialectRegistry &registry)
Include the generated interface declarations.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
SmallVector< int64_t, 2 > ReassociationIndices
Definition Utils.h:27