MLIR 24.0.0git
BufferizableOpInterfaceImpl.cpp
Go to the documentation of this file.
1//===- BufferizableOpInterfaceImpl.cpp - Impl. of BufferizableOpInterface -===//
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#include "mlir/IR/Dialect.h"
15#include "mlir/IR/Operation.h"
16
17using namespace mlir;
18using namespace mlir::bufferization;
19
20namespace mlir {
21namespace bufferization {
22namespace {
23
24struct AllocTensorOpInterface
25 : public BufferizableOpInterface::ExternalModel<AllocTensorOpInterface,
26 AllocTensorOp> {
27 bool bufferizesToAllocation(Operation *op, Value value) const { return true; }
28
29 bool resultBufferizesToMemoryWrite(Operation *op, OpResult opResult,
30 const AnalysisState &state) const {
31 // AllocTensorOps do not write unless they have a `copy` value.
32 return static_cast<bool>(cast<AllocTensorOp>(op).getCopy());
33 }
34
35 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
36 const AnalysisState &state) const {
37 assert(opOperand.getOperandNumber() == op->getNumOperands() - 1 &&
38 "expected copy operand");
39 return true;
40 }
41
42 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
43 const AnalysisState &state) const {
44 assert(opOperand.getOperandNumber() == op->getNumOperands() - 1 &&
45 "expected copy operand");
46 return false;
47 }
48
49 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
50 const AnalysisState &state) const {
51 // This is a new allocation. It does not alias with any other buffer.
52 return {};
53 }
54
55 FailureOr<BufferLikeType>
56 getBufferType(Operation *op, Value value, const BufferizationOptions &options,
57 const BufferizationState &state,
58 SmallVector<Value> &invocationStack) const {
59 auto allocTensorOp = cast<AllocTensorOp>(op);
60 assert(value == allocTensorOp.getResult() && "invalid value");
61
62 // Compute memory space of this allocation.
63 Attribute memorySpace;
64 if (allocTensorOp.getMemorySpace().has_value()) {
65 memorySpace = *allocTensorOp.getMemorySpace();
66 } else if (allocTensorOp.getCopy()) {
67 auto copyBufferType =
68 bufferization::detail::asMemRefType(bufferization::getBufferType(
69 allocTensorOp.getCopy(), options, state, invocationStack));
70 if (failed(copyBufferType))
71 return failure();
72 memorySpace = copyBufferType->getMemorySpace();
73 } else if (auto ms = options.defaultMemorySpaceFn(
74 cast<TensorLikeType>(allocTensorOp.getType()))) {
75 memorySpace = *ms;
76 } else {
77 return op->emitError("could not infer memory space");
78 }
79
80 return cast<BufferLikeType>(getMemRefTypeWithStaticIdentityLayout(
81 allocTensorOp.getType(), memorySpace));
82 }
83
84 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
85 const BufferizationOptions &options,
86 BufferizationState &state) const {
87 auto allocTensorOp = cast<AllocTensorOp>(op);
88 OpBuilder::InsertionGuard g(rewriter);
89 Location loc = allocTensorOp.getLoc();
90
91 // Nothing to do for dead AllocTensorOps.
92 if (op->getUses().empty()) {
93 rewriter.eraseOp(op);
94 return success();
95 }
96
97 // Get "copy" buffer.
98 Value copyBuffer;
99 if (allocTensorOp.getCopy()) {
100 FailureOr<Value> maybeCopyBuffer = bufferization::getBuffer(
101 rewriter, allocTensorOp.getCopy(), options, state);
102 if (failed(maybeCopyBuffer))
103 return failure();
104 copyBuffer = *maybeCopyBuffer;
105 }
106
107 // Create memory allocation.
108 auto allocType =
109 bufferization::getBufferType(allocTensorOp.getResult(), options, state);
110 if (failed(allocType))
111 return failure();
112 SmallVector<Value> dynamicDims = allocTensorOp.getDynamicSizes();
113 if (allocTensorOp.getCopy()) {
114 assert(dynamicDims.empty() && "expected either `copy` or `dynamicDims`");
115 populateDynamicDimSizes(rewriter, loc, copyBuffer, dynamicDims);
116 }
117 FailureOr<Value> alloc =
118 options.allocationFn(rewriter, loc, llvm::cast<MemRefType>(*allocType),
119 dynamicDims, options.bufferAlignment);
120 if (failed(alloc))
121 return failure();
122
123 // Create memory copy (if any).
124 if (allocTensorOp.getCopy()) {
125 if (failed(options.memCpyFn(rewriter, loc, copyBuffer, *alloc)))
126 return failure();
127 }
128
129 // Replace op.
130 replaceOpWithBufferizedValues(rewriter, op, *alloc);
131
132 return success();
133 }
134};
135
136struct DeallocTensorOpInterface
137 : public BufferizableOpInterface::ExternalModel<DeallocTensorOpInterface,
138 DeallocTensorOp> {
139 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
140 const AnalysisState &state) const {
141 return false;
142 }
143
144 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
145 const AnalysisState &state) const {
146 return false;
147 }
148
149 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
150 const AnalysisState &state) const {
151 return {};
152 }
153
154 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
155 const BufferizationOptions &options,
156 BufferizationState &state) const {
157 auto deallocTensorOp = cast<DeallocTensorOp>(op);
158 FailureOr<Value> buffer = bufferization::getBuffer(
159 rewriter, deallocTensorOp.getTensor(), options, state);
160 if (failed(buffer))
161 return failure();
162 memref::DeallocOp::create(rewriter, deallocTensorOp.getLoc(), *buffer);
163 rewriter.eraseOp(op);
164 return success();
165 }
166};
167
168struct MaterializeInDestinationOpInterface
169 : public BufferizableOpInterface::ExternalModel<
170 MaterializeInDestinationOpInterface, MaterializeInDestinationOp> {
171 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
172 const AnalysisState &state) const {
173 return opOperand == cast<MaterializeInDestinationOp>(op).getSourceMutable();
174 }
175
176 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
177 const AnalysisState &state) const {
178 auto materializeOp = cast<MaterializeInDestinationOp>(op);
179 if (opOperand == materializeOp.getDestMutable()) {
180 assert(isa<TensorType>(materializeOp.getDest().getType()) &&
181 "expected tensor type");
182 return true;
183 }
184 return false;
185 }
186
187 bool mustBufferizeInPlace(Operation *op, OpOperand &opOperand,
188 const AnalysisState &state) const {
189 // The source is only read and not written, so it always bufferizes in-place
190 // by default. The destination is written and is forced to bufferize
191 // in-place (if it is a tensor).
192 return true;
193 }
194
195 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
196 const AnalysisState &state) const {
197 auto materializeOp = cast<MaterializeInDestinationOp>(op);
198 if (opOperand == materializeOp.getDestMutable()) {
199 assert(isa<TensorType>(materializeOp.getDest().getType()) &&
200 "expected tensor type");
201 return {{op->getResult(0), BufferRelation::Equivalent}};
202 }
203 return {};
204 }
205
206 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
207 const BufferizationOptions &options,
208 BufferizationState &state) const {
209 auto materializeOp = cast<MaterializeInDestinationOp>(op);
210 bool tensorDest = isa<TensorType>(materializeOp.getDest().getType());
211 Value buffer;
212 if (tensorDest) {
213 FailureOr<Value> maybeBuffer = bufferization::getBuffer(
214 rewriter, materializeOp.getDest(), options, state);
215 if (failed(maybeBuffer))
216 return failure();
217 buffer = *maybeBuffer;
218 } else {
219 assert(isa<BaseMemRefType>(materializeOp.getDest().getType()) &&
220 "expected memref type");
221 buffer = materializeOp.getDest();
222 }
223 auto srcBuffer = bufferization::getBuffer(
224 rewriter, materializeOp.getSource(), options, state);
225 if (failed(srcBuffer))
226 return failure();
227 if (failed(options.memCpyFn(rewriter, materializeOp.getLoc(), *srcBuffer,
228 buffer)))
229 return failure();
230 replaceOpWithBufferizedValues(
231 rewriter, op, tensorDest ? ValueRange(buffer) : ValueRange());
232 return success();
233 }
234
235 bool bufferizesToElementwiseAccess(Operation *op, const AnalysisState &state,
236 ArrayRef<OpOperand *> opOperands) const {
237 // As elements are copied from the "source" buffer to the "dest" buffer,
238 // already copied elements are not read a second time.
239 return true;
240 }
241
242 bool isWritable(Operation *op, Value value,
243 const AnalysisState &state) const {
244 auto materializeOp = cast<MaterializeInDestinationOp>(op);
245 return isa<TensorType>(materializeOp.getDest().getType())
246 ? true
247 : materializeOp.getWritable();
248 }
249};
250
251// Note: ToBufferOp / ToTensorOp are temporary ops that are inserted at the
252// bufferization boundary. When One-Shot bufferization is complete, there should
253// be no such ops left over. If `allowUnknownOps` (or after running a partial
254// bufferization pass), such ops may be part of the resulting IR, but such IR
255// may no longer be analyzable by One-Shot analysis.
256
257struct ToTensorOpInterface
258 : public BufferizableOpInterface::ExternalModel<ToTensorOpInterface,
259 ToTensorOp> {
260 bool isWritable(Operation *op, Value value,
261 const AnalysisState &state) const {
262 return cast<ToTensorOp>(op).getWritable();
263 }
264
265 FailureOr<BufferLikeType>
266 getBufferType(Operation *op, Value value, const BufferizationOptions &options,
267 const BufferizationState &state,
268 SmallVector<Value> &invocationStack) const {
269 return cast<ToTensorOp>(op).getBuffer().getType();
270 }
271
272 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
273 const BufferizationOptions &options,
274 BufferizationState &state) const {
275 // to_tensor/to_buffer pairs fold away after bufferization.
276 return success();
277 }
278};
279
280struct ToBufferOpInterface
281 : public BufferizableOpInterface::ExternalModel<ToBufferOpInterface,
282 ToBufferOp> {
283 bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
284 const AnalysisState &state) const {
285 // It is unknown whether the resulting memref will be read or not.
286 return true;
287 }
288
289 bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
290 const AnalysisState &state) const {
291 return !cast<ToBufferOp>(op).getReadOnly();
292 }
293
294 AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand,
295 const AnalysisState &state) const {
296 return {};
297 }
298
299 LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
300 const BufferizationOptions &options,
301 BufferizationState &state) const {
302 // Fold to_buffer(to_tensor(x)) to x. Insert a cast if necessary.
303 (void)foldToBufferToTensorPair(rewriter, cast<ToBufferOp>(op), options);
304 // Note: The return value of `bufferize` indicates whether there was an
305 // error or not. (And not whether the pattern matched or not.)
306 return success();
307 }
308};
309
310} // namespace
311} // namespace bufferization
312} // namespace mlir
313
315 DialectRegistry &registry) {
316 registry.addExtension(+[](MLIRContext *ctx, BufferizationDialect *dialect) {
317 AllocTensorOp::attachInterface<AllocTensorOpInterface>(*ctx);
318 DeallocTensorOp::attachInterface<DeallocTensorOpInterface>(*ctx);
319 MaterializeInDestinationOp::attachInterface<
320 MaterializeInDestinationOpInterface>(*ctx);
321 ToBufferOp::attachInterface<ToBufferOpInterface>(*ctx);
322 ToTensorOp::attachInterface<ToTensorOpInterface>(*ctx);
323 });
324}
return success()
true
Given two iterators into the same block, return "true" if a is before `b.
static llvm::ManagedStatic< PassManagerOptions > options
static RankedTensorType getBufferType(const SparseTensorType &stt, bool needTmpCOO)
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:63
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Definition Value.cpp:226
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
unsigned getNumOperands()
Definition Operation.h:371
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
use_range getUses()
Returns a range of all uses, which is useful for iterating over all uses.
Definition Operation.h:871
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
LogicalResult foldToBufferToTensorPair(RewriterBase &rewriter, ToBufferOp toBuffer, const BufferizationOptions &options)
Try to fold to_buffer(to_tensor(x)).
void registerBufferizableOpInterfaceExternalModels(DialectRegistry &registry)
void populateDynamicDimSizes(OpBuilder &b, Location loc, Value shapedValue, SmallVector< Value > &dynamicDims)
Populate dynamicDims with tensor::DimOp / memref::DimOp results for all dynamic dimensions of the giv...
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.