MLIR 24.0.0git
BufferizationOps.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
16#include "mlir/IR/Matchers.h"
17#include "llvm/ADT/SmallVectorExtras.h"
18#include <optional>
19
20using namespace mlir;
21using namespace mlir::bufferization;
22
23//===----------------------------------------------------------------------===//
24// Helper functions
25//===----------------------------------------------------------------------===//
26
28 OpBuilder &b, Value value, MemRefType destType,
30 auto srcType = llvm::cast<MemRefType>(value.getType());
31
32 // Element type and rank must match.
33 if (srcType.getElementType() != destType.getElementType())
34 return failure();
35 if (srcType.getRank() != destType.getRank())
36 return failure();
37
38 // In case the affine maps are different, we may need to use a copy if we go
39 // from dynamic to static offset or stride (the canonicalization cannot know
40 // at this point that it is really cast compatible).
41 auto isGuaranteedCastCompatible = [](MemRefType source, MemRefType target) {
42 int64_t sourceOffset, targetOffset;
43 SmallVector<int64_t, 4> sourceStrides, targetStrides;
44 if (failed(source.getStridesAndOffset(sourceStrides, sourceOffset)) ||
45 failed(target.getStridesAndOffset(targetStrides, targetOffset)))
46 return false;
47 auto dynamicToStatic = [](int64_t a, int64_t b) {
48 return ShapedType::isDynamic(a) && ShapedType::isStatic(b);
49 };
50 if (dynamicToStatic(sourceOffset, targetOffset))
51 return false;
52 for (auto it : zip(sourceStrides, targetStrides))
53 if (dynamicToStatic(std::get<0>(it), std::get<1>(it)))
54 return false;
55 return true;
56 };
57
58 // Note: If `areCastCompatible`, a cast is valid, but may fail at runtime. To
59 // ensure that we only generate casts that always succeed at runtime, we check
60 // a fix extra conditions in `isGuaranteedCastCompatible`.
61 if (memref::CastOp::areCastCompatible(srcType, destType) &&
62 isGuaranteedCastCompatible(srcType, destType)) {
63 Value casted = *options.castFn(b, value.getLoc(), destType, value);
64 return casted;
65 }
66
67 auto loc = value.getLoc();
68 SmallVector<Value, 4> dynamicOperands;
69 for (int i = 0; i < destType.getRank(); ++i) {
70 if (destType.getShape()[i] != ShapedType::kDynamic)
71 continue;
72 Value size = memref::DimOp::create(b, loc, value, i);
73 dynamicOperands.push_back(size);
74 }
75
76 FailureOr<Value> copy = options.allocationFn(
77 b, loc, destType, dynamicOperands, options.bufferAlignment);
78 if (failed(copy))
79 return failure();
80 if (failed(options.memCpyFn(b, loc, value, *copy)))
81 return failure();
82 return copy;
83}
84
85/// Try to fold to_buffer(to_tensor(x)). If x's type and the result type of the
86/// to_buffer op are different, a memref.cast is needed.
88 RewriterBase &rewriter, ToBufferOp toBuffer,
90 auto bufferToTensor = toBuffer.getTensor().getDefiningOp<ToTensorOp>();
91 if (!bufferToTensor)
92 return failure();
93
94 Type srcType = bufferToTensor.getBuffer().getType();
95 Type destType = toBuffer.getType();
96
97 // Directly rewrite if the type did not change.
98 if (srcType == destType) {
99 rewriter.replaceOp(toBuffer, bufferToTensor.getBuffer());
100 return success();
101 }
102
103 if (!llvm::isa<BaseMemRefType>(srcType) ||
104 !llvm::isa<BaseMemRefType>(destType)) {
105 // Non-builtin case: the best is to try the user-provided cast.
106 auto replacement =
107 options.castFn(rewriter, bufferToTensor.getBuffer().getLoc(), destType,
108 bufferToTensor.getBuffer());
109 if (failed(replacement))
110 return failure();
111 rewriter.replaceOp(toBuffer, *replacement);
112 return success();
113 }
114
115 auto rankedSrcType = llvm::dyn_cast<MemRefType>(srcType);
116 auto rankedDestType = llvm::dyn_cast<MemRefType>(destType);
117 auto unrankedSrcType = llvm::dyn_cast<UnrankedMemRefType>(srcType);
118
119 // Ranked memref -> Ranked memref cast.
120 if (rankedSrcType && rankedDestType) {
121 FailureOr<Value> replacement = castOrReallocMemRefValue(
122 rewriter, bufferToTensor.getBuffer(), rankedDestType, options);
123 if (failed(replacement))
124 return failure();
125
126 rewriter.replaceOp(toBuffer, *replacement);
127 return success();
128 }
129
130 // Unranked memref -> Ranked memref cast: May require a copy.
131 // TODO: Not implemented at the moment.
132 if (unrankedSrcType && rankedDestType)
133 return failure();
134
135 // Unranked/ranked memref -> unranked memref cast: No copy needed if the types
136 // are cast-compatible.
137 if (!memref::CastOp::areCastCompatible(srcType, destType))
138 return failure();
139
140 rewriter.replaceOpWithNewOp<memref::CastOp>(toBuffer, destType,
141 bufferToTensor.getBuffer());
142 return success();
143}
144
146 OpBuilder &b, Location loc, Value shapedValue,
147 SmallVector<Value> &dynamicDims) {
148 auto shapedType = llvm::cast<ShapedType>(shapedValue.getType());
149 for (int64_t i = 0; i < shapedType.getRank(); ++i) {
150 if (shapedType.isDynamicDim(i)) {
151 if (llvm::isa<MemRefType>(shapedType)) {
152 dynamicDims.push_back(memref::DimOp::create(b, loc, shapedValue, i));
153 } else {
154 assert(llvm::isa<RankedTensorType>(shapedType) && "expected tensor");
155 dynamicDims.push_back(tensor::DimOp::create(b, loc, shapedValue, i));
156 }
157 }
158 }
159}
160
161//===----------------------------------------------------------------------===//
162// AllocTensorOp
163//===----------------------------------------------------------------------===//
164
165LogicalResult AllocTensorOp::bufferize(RewriterBase &rewriter,
167 BufferizationState &state) {
168 OpBuilder::InsertionGuard g(rewriter);
169 Location loc = getLoc();
170
171 // Nothing to do for dead AllocTensorOps.
172 if (getOperation()->getUses().empty()) {
173 rewriter.eraseOp(getOperation());
174 return success();
175 }
176
177 // Get "copy" buffer.
178 Value copyBuffer;
179 if (getCopy()) {
180 FailureOr<Value> maybeCopyBuffer =
181 getBuffer(rewriter, getCopy(), options, state);
182 if (failed(maybeCopyBuffer))
183 return failure();
184 copyBuffer = *maybeCopyBuffer;
185 }
186
187 // Create memory allocation.
188 auto allocType = bufferization::getBufferType(getResult(), options, state);
189 if (failed(allocType))
190 return failure();
191 SmallVector<Value> dynamicDims = getDynamicSizes();
192 if (getCopy()) {
193 assert(dynamicDims.empty() && "expected either `copy` or `dynamicDims`");
194 populateDynamicDimSizes(rewriter, loc, copyBuffer, dynamicDims);
195 }
196 FailureOr<Value> alloc =
197 options.allocationFn(rewriter, loc, llvm::cast<MemRefType>(*allocType),
198 dynamicDims, options.bufferAlignment);
199 if (failed(alloc))
200 return failure();
201
202 // Create memory copy (if any).
203 if (getCopy()) {
204 if (failed(options.memCpyFn(rewriter, loc, copyBuffer, *alloc)))
205 return failure();
206 }
207
208 // Replace op.
209 replaceOpWithBufferizedValues(rewriter, getOperation(), *alloc);
210
211 return success();
212}
213
214bool AllocTensorOp::resultBufferizesToMemoryWrite(OpResult opResult,
215 const AnalysisState &state) {
216 // AllocTensorOps do not write unless they have a `copy` value.
217 return static_cast<bool>(getCopy());
218}
219
220bool AllocTensorOp::bufferizesToMemoryRead(OpOperand &opOperand,
221 const AnalysisState &state) {
222 assert(opOperand.getOperandNumber() == getNumOperands() - 1 &&
223 "expected copy operand");
224 return true;
225}
226
227bool AllocTensorOp::bufferizesToMemoryWrite(OpOperand &opOperand,
228 const AnalysisState &state) {
229 assert(opOperand.getOperandNumber() == getNumOperands() - 1 &&
230 "expected copy operand");
231 return false;
232}
233
234AliasingValueList AllocTensorOp::getAliasingValues(OpOperand &opOperand,
235 const AnalysisState &state) {
236 // This is a new allocation. It does not alias with any other buffer.
237 return {};
238}
239
240FailureOr<BufferLikeType>
241AllocTensorOp::getBufferType(Value value, const BufferizationOptions &options,
242 const BufferizationState &state,
243 SmallVector<Value> &invocationStack) {
244 assert(value == getResult() && "invalid value");
245
246 // Compute memory space of this allocation.
247 Attribute memorySpace;
248 if (getMemorySpace().has_value()) {
249 memorySpace = *getMemorySpace();
250 } else if (getCopy()) {
251 auto copyBufferType =
252 bufferization::detail::asMemRefType(bufferization::getBufferType(
253 getCopy(), options, state, invocationStack));
254 if (failed(copyBufferType))
255 return failure();
256 memorySpace = copyBufferType->getMemorySpace();
257 } else if (auto ms = options.defaultMemorySpaceFn(
258 cast<TensorLikeType>(getType()))) {
259 memorySpace = *ms;
260 } else {
261 return getOperation()->emitError("could not infer memory space");
262 }
263
264 return cast<BufferLikeType>(
265 getMemRefTypeWithStaticIdentityLayout(getType(), memorySpace));
266}
267
268LogicalResult AllocTensorOp::verify() {
269 if (getCopy() && !getDynamicSizes().empty())
270 return emitError("dynamic sizes not needed when copying a tensor");
271 if (!getCopy() && failed(verifyDynamicDimensionCount(
272 getOperation(), getType(), getDynamicSizes())))
273 return failure();
274 if (getCopy() && getCopy().getType() != getType())
275 return emitError("expected that `copy` and return type match");
276 return success();
277}
278
279void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
280 RankedTensorType type, ValueRange dynamicSizes) {
281 build(builder, result, type, dynamicSizes, /*copy=*/Value(),
282 /*size_hint=*/Value(),
283 /*memory_space=*/IntegerAttr());
284}
285
286void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
287 RankedTensorType type, ValueRange dynamicSizes,
288 Value copy) {
289 build(builder, result, type, dynamicSizes, copy, /*size_hint=*/Value(),
290 /*memory_space=*/IntegerAttr());
291}
292
293void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
294 TensorType type, ValueRange dynamicSizes, Value copy,
295 IntegerAttr memorySpace) {
296 build(builder, result, type, dynamicSizes, copy, /*size_hint=*/Value(),
297 memorySpace);
298}
299
300namespace {
301/// Change the type of the result of a `bufferization.alloc_tensor` by making
302/// the result type statically sized along dimension that in the original
303/// operation where defined as dynamic, but the size was defined using a
304/// `constant` op. For example:
305///
306/// %c5 = arith.constant 5: index
307/// %0 = bufferization.alloc_tensor(%arg0, %c5) : tensor<?x?xf32>
308///
309/// to
310///
311/// %0 = bufferization.alloc_tensor(%arg0) : tensor<?x5xf32>
312struct ReplaceStaticShapeDims : OpRewritePattern<AllocTensorOp> {
313 using OpRewritePattern<AllocTensorOp>::OpRewritePattern;
314
315 LogicalResult matchAndRewrite(AllocTensorOp op,
316 PatternRewriter &rewriter) const override {
317 if (op.getCopy())
318 return failure();
319 SmallVector<int64_t> newShape = llvm::to_vector(op.getType().getShape());
320 SmallVector<Value> newDynamicSizes;
321 unsigned int dynValCounter = 0;
322 for (int64_t i = 0; i < op.getType().getRank(); ++i) {
323 if (!op.isDynamicDim(i))
324 continue;
325 Value value = op.getDynamicSizes()[dynValCounter++];
326 APInt intVal;
327 if (matchPattern(value, m_ConstantInt(&intVal))) {
328 int64_t dim = intVal.getSExtValue();
329 if (dim >= 0)
330 newShape[i] = intVal.getSExtValue();
331 else
332 newDynamicSizes.push_back(value);
333 } else {
334 newDynamicSizes.push_back(value);
335 }
336 }
337 RankedTensorType newType = RankedTensorType::get(
338 newShape, op.getType().getElementType(), op.getType().getEncoding());
339 if (newType == op.getType())
340 return failure();
341 auto newOp = AllocTensorOp::create(rewriter, op.getLoc(), newType,
342 newDynamicSizes, /*copy=*/Value());
343 rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp);
344 return success();
345 }
346};
347
348struct FoldDimOfAllocTensorOp : public OpRewritePattern<tensor::DimOp> {
349 using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
350
351 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
352 PatternRewriter &rewriter) const override {
353 std::optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex();
354 auto allocTensorOp = dimOp.getSource().getDefiningOp<AllocTensorOp>();
355 if (!allocTensorOp || !maybeConstantIndex)
356 return failure();
357 if (*maybeConstantIndex < 0 ||
358 *maybeConstantIndex >= allocTensorOp.getType().getRank())
359 return failure();
360 if (!allocTensorOp.getType().isDynamicDim(*maybeConstantIndex))
361 return failure();
362 rewriter.replaceOp(
363 dimOp, allocTensorOp.getDynamicSize(rewriter, *maybeConstantIndex));
364 return success();
365 }
366};
367} // namespace
368
369void AllocTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
370 MLIRContext *ctx) {
371 results.add<FoldDimOfAllocTensorOp, ReplaceStaticShapeDims>(ctx);
372}
373
374LogicalResult AllocTensorOp::reifyResultShapes(
375 OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
376 auto shapes =
377 llvm::map_to_vector<4>(llvm::seq<int64_t>(0, getType().getRank()),
378 [&](int64_t dim) -> OpFoldResult {
379 if (isDynamicDim(dim))
380 return getDynamicSize(builder, dim);
381 return builder.getIndexAttr(getStaticSize(dim));
382 });
383 reifiedReturnShapes.emplace_back(std::move(shapes));
384 return success();
385}
386
387ParseResult AllocTensorOp::parse(OpAsmParser &parser, OperationState &result) {
389 if (parser.parseLParen() || parser.parseOperandList(dynamicSizesOperands) ||
390 parser.parseRParen())
391 return failure();
392 ParseResult copyKeyword = parser.parseOptionalKeyword("copy");
394 if (copyKeyword.succeeded())
395 if (parser.parseLParen() || parser.parseOperand(copyOperand) ||
396 parser.parseRParen())
397 return failure();
398 ParseResult sizeHintKeyword = parser.parseOptionalKeyword("size_hint");
399 OpAsmParser::UnresolvedOperand sizeHintOperand;
400 if (sizeHintKeyword.succeeded())
401 if (parser.parseEqual() || parser.parseOperand(sizeHintOperand))
402 return failure();
403 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon())
404 return failure();
405
406 TensorType type;
407 if (parser.parseCustomTypeWithFallback(type))
408 return failure();
409 result.addTypes(type);
410
411 Type indexType = parser.getBuilder().getIndexType();
412 if (parser.resolveOperands(dynamicSizesOperands, indexType, result.operands))
413 return failure();
414 if (copyKeyword.succeeded())
415 if (parser.resolveOperand(copyOperand, type, result.operands))
416 return failure();
417 if (sizeHintKeyword.succeeded())
418 if (parser.resolveOperand(sizeHintOperand, indexType, result.operands))
419 return failure();
420 result.addAttribute(AllocTensorOp::getOperandSegmentSizeAttr(),
422 {static_cast<int32_t>(dynamicSizesOperands.size()),
423 static_cast<int32_t>(copyKeyword.succeeded()),
424 static_cast<int32_t>(sizeHintKeyword.succeeded())}));
425 return success();
426}
427
428void AllocTensorOp::print(OpAsmPrinter &p) {
429 p << "(" << getDynamicSizes() << ")";
430 if (getCopy())
431 p << " copy(" << getCopy() << ")";
432 if (getSizeHint())
433 p << " size_hint=" << getSizeHint();
434 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{
435 AllocTensorOp::getOperandSegmentSizeAttr()});
436 p << " : ";
437 auto type = getResult().getType();
438 if (auto validType = llvm::dyn_cast<::mlir::TensorType>(type))
439 p.printStrippedAttrOrType(validType);
440 else
441 p << type;
442}
443
444Value AllocTensorOp::getDynamicSize(OpBuilder &b, unsigned idx) {
445 assert(isDynamicDim(idx) && "expected dynamic dim");
446 if (getCopy())
447 return tensor::DimOp::create(b, getLoc(), getCopy(), idx);
448 return getOperand(getIndexOfDynamicSize(idx));
449}
450
451//===----------------------------------------------------------------------===//
452// CloneOp
453//===----------------------------------------------------------------------===//
454
455OpFoldResult CloneOp::fold(FoldAdaptor adaptor) {
456 return succeeded(memref::foldMemRefCast(*this)) ? getResult() : Value();
457}
458
459namespace {
460
461/// Merge the clone and its source (by converting the clone to a cast) when
462/// possible.
463struct SimplifyClones : public OpRewritePattern<CloneOp> {
464 using OpRewritePattern<CloneOp>::OpRewritePattern;
465
466 LogicalResult matchAndRewrite(CloneOp cloneOp,
467 PatternRewriter &rewriter) const override {
468 if (cloneOp.use_empty()) {
469 rewriter.eraseOp(cloneOp);
470 return success();
471 }
472
473 Value source = cloneOp.getInput();
474 if (source.getType() != cloneOp.getType() &&
475 !memref::CastOp::areCastCompatible({source.getType()},
476 {cloneOp.getType()}))
477 return failure();
478
479 // Aims to find the dealloc op for the canonical source
480 // which otherwise could prevent removal of unnecessary allocs.
481 Value canonicalSource = source;
482 while (auto iface = dyn_cast_or_null<ViewLikeOpInterface>(
483 canonicalSource.getDefiningOp())) {
484 if (canonicalSource != iface.getViewDest()) {
485 break;
486 }
487 canonicalSource = iface.getViewSource();
488 }
489
490 std::optional<Operation *> maybeCloneDeallocOp =
491 memref::findDealloc(cloneOp.getOutput());
492 // Skip if either of them has > 1 deallocate operations.
493 if (!maybeCloneDeallocOp.has_value())
494 return failure();
495 std::optional<Operation *> maybeSourceDeallocOp =
496 memref::findDealloc(canonicalSource);
497 if (!maybeSourceDeallocOp.has_value())
498 return failure();
499 Operation *cloneDeallocOp = *maybeCloneDeallocOp;
500 Operation *sourceDeallocOp = *maybeSourceDeallocOp;
501
502 // If both are deallocated in the same block, their in-block lifetimes
503 // might not fully overlap, so we cannot decide which one to drop.
504 if (cloneDeallocOp && sourceDeallocOp &&
505 cloneDeallocOp->getBlock() == sourceDeallocOp->getBlock())
506 return failure();
507
508 Block *currentBlock = cloneOp->getBlock();
509 Operation *redundantDealloc = nullptr;
510 if (cloneDeallocOp && cloneDeallocOp->getBlock() == currentBlock) {
511 redundantDealloc = cloneDeallocOp;
512 } else if (sourceDeallocOp && sourceDeallocOp->getBlock() == currentBlock) {
513 redundantDealloc = sourceDeallocOp;
514 }
515
516 if (!redundantDealloc)
517 return failure();
518
519 // Safety check that there are no other deallocations inbetween
520 // cloneOp and redundantDealloc, as otherwise we might deallocate an alias
521 // of source before the uses of the clone. With alias information, we could
522 // restrict this to only fail of the dealloc's operand is an alias
523 // of the source.
524 for (Operation *pos = cloneOp->getNextNode(); pos != redundantDealloc;
525 pos = pos->getNextNode()) {
526 // Bail if we run out of operations while looking for a deallocation op.
527 if (!pos)
528 return failure();
529 auto effectInterface = dyn_cast<MemoryEffectOpInterface>(pos);
530 if (!effectInterface)
531 continue;
532 if (effectInterface.hasEffect<MemoryEffects::Free>())
533 return failure();
534 }
535
536 if (source.getType() != cloneOp.getType())
537 source = memref::CastOp::create(rewriter, cloneOp.getLoc(),
538 cloneOp.getType(), source);
539 rewriter.replaceOp(cloneOp, source);
540 rewriter.eraseOp(redundantDealloc);
541 return success();
542 }
543};
544
545} // namespace
546
547void CloneOp::getCanonicalizationPatterns(RewritePatternSet &results,
548 MLIRContext *context) {
549 results.add<SimplifyClones>(context);
550}
551
552//===----------------------------------------------------------------------===//
553// DeallocTensorOp
554//===----------------------------------------------------------------------===//
555
556LogicalResult DeallocTensorOp::bufferize(RewriterBase &rewriter,
558 BufferizationState &state) {
559 FailureOr<Value> buffer = getBuffer(rewriter, getTensor(), options, state);
560 if (failed(buffer))
561 return failure();
562 memref::DeallocOp::create(rewriter, getLoc(), *buffer);
563 rewriter.eraseOp(getOperation());
564 return success();
565}
566
567//===----------------------------------------------------------------------===//
568// MaterializeInDestinationOp
569//===----------------------------------------------------------------------===//
570
571bool MaterializeInDestinationOp::bufferizesToMemoryRead(
572 OpOperand &opOperand, const AnalysisState &state) {
573 return opOperand == getSourceMutable();
574}
575
576bool MaterializeInDestinationOp::bufferizesToMemoryWrite(
577 OpOperand &opOperand, const AnalysisState &state) {
578 if (opOperand == getDestMutable()) {
579 assert(isa<TensorType>(getDest().getType()) && "expected tensor type");
580 return true;
581 }
582 return false;
583}
584
585bool MaterializeInDestinationOp::mustBufferizeInPlace(
586 OpOperand &opOperand, const AnalysisState &state) {
587 // The source is only read and not written, so it always bufferizes in-place
588 // by default. The destination is written and is forced to bufferize in-place
589 // (if it is a tensor).
590 return true;
591}
592
593AliasingValueList
594MaterializeInDestinationOp::getAliasingValues(OpOperand &opOperand,
595 const AnalysisState &state) {
596 if (opOperand == getDestMutable()) {
597 assert(isa<TensorType>(getDest().getType()) && "expected tensor type");
598 return {{getOperation()->getResult(0), BufferRelation::Equivalent}};
599 }
600 return {};
601}
602
603LogicalResult
604MaterializeInDestinationOp::bufferize(RewriterBase &rewriter,
606 BufferizationState &state) {
607 bool tensorDest = isa<TensorType>(getDest().getType());
608 Value buffer;
609 if (tensorDest) {
610 FailureOr<Value> maybeBuffer =
611 getBuffer(rewriter, getDest(), options, state);
612 if (failed(maybeBuffer))
613 return failure();
614 buffer = *maybeBuffer;
615 } else {
616 assert(isa<BaseMemRefType>(getDest().getType()) && "expected memref type");
617 buffer = getDest();
618 }
619 auto srcBuffer = getBuffer(rewriter, getSource(), options, state);
620 if (failed(srcBuffer))
621 return failure();
622 if (failed(options.memCpyFn(rewriter, getLoc(), *srcBuffer, buffer)))
623 return failure();
624 replaceOpWithBufferizedValues(rewriter, getOperation(),
625 tensorDest ? ValueRange(buffer) : ValueRange());
626 return success();
627}
628
629bool MaterializeInDestinationOp::bufferizesToElementwiseAccess(
630 const AnalysisState &state, ArrayRef<OpOperand *> opOperands) {
631 // As elements are copied from the "source" buffer to the "dest" buffer,
632 // already copied elements are not read a second time.
633 return true;
634}
635
636LogicalResult MaterializeInDestinationOp::reifyResultShapes(
637 OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
638 if (getOperation()->getNumResults() == 1) {
639 assert(isa<TensorType>(getDest().getType()) && "expected tensor type");
640 reifiedReturnShapes.resize(1,
642 reifiedReturnShapes[0] =
643 tensor::getMixedSizes(builder, getLoc(), getDest());
644 }
645 return success();
646}
647
648Value MaterializeInDestinationOp::buildSubsetExtraction(OpBuilder &builder,
649 Location loc) {
650 if (isa<TensorType>(getDest().getType())) {
651 // The subset is the entire destination tensor.
652 return getDest();
653 }
654
655 // The "restrict" attribute is transferred from this op to the newly created
656 // to_tensor op. If this op does not the "restrict" attribute, the subset
657 // extraction cannot be built because there is no guarantee that there is no
658 // pre-existing "restrict" to_tensor op with the same/an aliasing destination.
659 if (!getRestrict())
660 return {};
661
662 // Build a bufferization.to_tensor op.
663 assert(isa<BaseMemRefType>(getDest().getType()) && "expected memref type");
664 assert(getRestrict() &&
665 "expected that ops with memrefs dest have 'restrict'");
666 setRestrict(false);
667 return ToTensorOp::create(
668 builder, loc, memref::getTensorTypeFromMemRefType(getDest().getType()),
669 getDest(),
670 /*restrict=*/true, getWritable());
671}
672
673bool MaterializeInDestinationOp::isEquivalentSubset(
674 Value candidate, function_ref<bool(Value, Value)> equivalenceFn) {
675 return equivalenceFn(getDest(), candidate);
676}
677
679MaterializeInDestinationOp::getValuesNeededToBuildSubsetExtraction() {
680 return {getDest()};
681}
682
683OpOperand &MaterializeInDestinationOp::getSourceOperand() {
684 return getOperation()->getOpOperand(0) /*source*/;
685}
686
687bool MaterializeInDestinationOp::operatesOnEquivalentSubset(
688 SubsetOpInterface subsetOp,
689 function_ref<bool(Value, Value)> equivalenceFn) {
690 return false;
691}
692
693bool MaterializeInDestinationOp::operatesOnDisjointSubset(
694 SubsetOpInterface subsetOp,
695 function_ref<bool(Value, Value)> equivalenceFn) {
696 return false;
697}
698
699LogicalResult MaterializeInDestinationOp::verify() {
700 if (!isa<TensorType, BaseMemRefType>(getDest().getType()))
701 return emitOpError("'dest' must be a tensor or a memref");
702 if (auto destType = dyn_cast<TensorType>(getDest().getType())) {
703 if (getOperation()->getNumResults() != 1)
704 return emitOpError("tensor 'dest' implies exactly one tensor result");
705 if (destType != getResult().getType())
706 return emitOpError("result and 'dest' types must match");
707 }
708 if (isa<BaseMemRefType>(getDest().getType()) &&
709 getOperation()->getNumResults() != 0)
710 return emitOpError("memref 'dest' implies zero results");
711 if (getRestrict() && !isa<BaseMemRefType>(getDest().getType()))
712 return emitOpError("'restrict' is valid only for memref destinations");
713 if (getWritable() != isa<BaseMemRefType>(getDest().getType()))
714 return emitOpError("'writable' must be specified if and only if the "
715 "destination is of memref type");
716 TensorType srcType = getSource().getType();
717 ShapedType destType = cast<ShapedType>(getDest().getType());
718 if (srcType.hasRank() != destType.hasRank())
719 return emitOpError("source/destination shapes are incompatible");
720 if (srcType.hasRank()) {
721 if (failed(verifyRanksMatch(getOperation(), srcType, destType, "source",
722 "destination")))
723 return failure();
724 for (auto [src, dest] :
725 llvm::zip(srcType.getShape(), destType.getShape())) {
726 if (src == ShapedType::kDynamic || dest == ShapedType::kDynamic) {
727 // Cannot verify dynamic dimension size. Assume that that they match at
728 // runtime.
729 continue;
730 }
731 if (src != dest)
732 return emitOpError("source/destination shapes are incompatible");
733 }
734 }
735 return success();
736}
737
738void MaterializeInDestinationOp::build(OpBuilder &builder,
739 OperationState &state, Value source,
740 Value dest) {
741 auto destTensorType = dyn_cast<TensorType>(dest.getType());
742 build(builder, state, /*result=*/destTensorType ? destTensorType : Type(),
743 source, dest);
744}
745
746bool MaterializeInDestinationOp::isWritable(Value value,
747 const AnalysisState &state) {
748 return isa<TensorType>(getDest().getType()) ? true : getWritable();
749}
750
751MutableOperandRange MaterializeInDestinationOp::getDpsInitsMutable() {
752 return getDestMutable();
753}
754
755void MaterializeInDestinationOp::getEffects(
757 &effects) {
758 if (isa<BaseMemRefType>(getDest().getType()))
759 effects.emplace_back(MemoryEffects::Write::get(), &getDestMutable(),
761}
762
763//===----------------------------------------------------------------------===//
764// ToTensorOp
765//===----------------------------------------------------------------------===//
766
767bool ToTensorOp::isWritable(Value value, const AnalysisState &state) {
768 return getWritable();
769}
770
771OpFoldResult ToTensorOp::fold(FoldAdaptor) {
772 if (auto toBuffer = getBuffer().getDefiningOp<ToBufferOp>())
773 // Approximate alias analysis by conservatively folding only when no there
774 // is no interleaved operation.
775 if (toBuffer->getBlock() == this->getOperation()->getBlock() &&
776 toBuffer->getNextNode() == this->getOperation())
777 return toBuffer.getTensor();
778 return {};
779}
780
781namespace {
782struct DimOfToTensorFolder : public OpRewritePattern<tensor::DimOp> {
783 using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
784
785 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
786 PatternRewriter &rewriter) const override {
787 auto memrefToTensorOp = dimOp.getSource().getDefiningOp<ToTensorOp>();
788 if (!memrefToTensorOp)
789 return failure();
790
791 rewriter.replaceOpWithNewOp<memref::DimOp>(
792 dimOp, memrefToTensorOp.getBuffer(), dimOp.getIndex());
793 return success();
794 }
795};
796} // namespace
797
798void ToTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
799 MLIRContext *context) {
800 results.add<DimOfToTensorFolder>(context);
801}
802
803//===----------------------------------------------------------------------===//
804// ToBufferOp
805//===----------------------------------------------------------------------===//
806
807OpFoldResult ToBufferOp::fold(FoldAdaptor) {
808 if (auto memrefToTensor = getTensor().getDefiningOp<ToTensorOp>())
809 if (memrefToTensor.getBuffer().getType() == getType())
810 return memrefToTensor.getBuffer();
811 return {};
812}
813
814namespace {
815
816/// Replace tensor.cast + to_buffer by to_buffer + memref.cast.
817struct ToBufferOfCast : public OpRewritePattern<ToBufferOp> {
818 using OpRewritePattern<ToBufferOp>::OpRewritePattern;
819
820 LogicalResult matchAndRewrite(ToBufferOp toBuffer,
821 PatternRewriter &rewriter) const final {
822 auto tensorCastOperand =
823 toBuffer.getOperand().getDefiningOp<tensor::CastOp>();
824 if (!tensorCastOperand)
825 return failure();
826 auto srcTensorType = llvm::dyn_cast<RankedTensorType>(
827 tensorCastOperand.getOperand().getType());
828 if (!srcTensorType)
829 return failure();
830 auto currentOutputMemRefType =
831 dyn_cast<BaseMemRefType>(toBuffer.getResult().getType());
832 if (!currentOutputMemRefType)
833 return failure();
834
835 auto memrefType = currentOutputMemRefType.cloneWith(
836 srcTensorType.getShape(), srcTensorType.getElementType());
837 Value memref = ToBufferOp::create(rewriter, toBuffer.getLoc(), memrefType,
838 tensorCastOperand.getOperand(),
839 toBuffer.getReadOnly());
840 rewriter.replaceOpWithNewOp<memref::CastOp>(toBuffer, toBuffer.getType(),
841 memref);
842 return success();
843 }
844};
845
846/// Canonicalize bufferization.to_tensor + bufferization.to_buffer. Insert a
847/// cast if necessary.
848struct ToBufferToTensorFolding : public OpRewritePattern<ToBufferOp> {
849 using OpRewritePattern<ToBufferOp>::OpRewritePattern;
850
851 LogicalResult matchAndRewrite(ToBufferOp toBuffer,
852 PatternRewriter &rewriter) const final {
853 BufferizationOptions options;
854 options.bufferAlignment = 0;
855 return foldToBufferToTensorPair(rewriter, toBuffer, options);
856 }
857};
858
859/// Fold a load on a to_buffer operation into an tensor.extract on the
860/// corresponding tensor.
861struct LoadOfToBuffer : public OpRewritePattern<memref::LoadOp> {
862 using OpRewritePattern<memref::LoadOp>::OpRewritePattern;
863
864 LogicalResult matchAndRewrite(memref::LoadOp load,
865 PatternRewriter &rewriter) const override {
866 auto toBuffer = load.getMemref().getDefiningOp<ToBufferOp>();
867 if (!toBuffer || !toBuffer.getReadOnly())
868 return failure();
869
870 rewriter.replaceOpWithNewOp<tensor::ExtractOp>(load, toBuffer.getTensor(),
871 load.getIndices());
872 return success();
873 }
874};
875
876/// Fold dim of a to_buffer into the dim of the tensor.
877struct DimOfCastOp : public OpRewritePattern<memref::DimOp> {
878 using OpRewritePattern<memref::DimOp>::OpRewritePattern;
879
880 LogicalResult matchAndRewrite(memref::DimOp dimOp,
881 PatternRewriter &rewriter) const override {
882 auto castOp = dimOp.getSource().getDefiningOp<ToBufferOp>();
883 if (!castOp)
884 return failure();
885 Value newSource = castOp.getOperand();
886 rewriter.replaceOpWithNewOp<tensor::DimOp>(dimOp, newSource,
887 dimOp.getIndex());
888 return success();
889 }
890};
891
892} // namespace
893
894void ToBufferOp::getCanonicalizationPatterns(RewritePatternSet &results,
895 MLIRContext *context) {
896 results.add<DimOfCastOp, LoadOfToBuffer, ToBufferOfCast,
897 ToBufferToTensorFolding>(context);
898}
899
900LogicalResult ToBufferOp::bufferize(RewriterBase &rewriter,
902 BufferizationState &state) {
903 // Fold to_buffer(to_tensor(x)) to x. Insert a cast if necessary.
904 (void)foldToBufferToTensorPair(rewriter, *this, options);
905 // Note: The return value of `bufferize` indicates whether there was an error
906 // or not. (And not whether the pattern matched or not.)
907 return success();
908}
909
910std::optional<Operation *> CloneOp::buildDealloc(OpBuilder &builder,
911 Value alloc) {
912 return memref::DeallocOp::create(builder, alloc.getLoc(), alloc)
913 .getOperation();
914}
915
916std::optional<Value> CloneOp::buildClone(OpBuilder &builder, Value alloc) {
917 return CloneOp::create(builder, alloc.getLoc(), alloc).getResult();
918}
919
920//===----------------------------------------------------------------------===//
921// DeallocOp
922//===----------------------------------------------------------------------===//
923
924LogicalResult DeallocOp::inferReturnTypes(
925 MLIRContext *context, std::optional<::mlir::Location> location,
926 ValueRange operands, DictionaryAttr attributes, PropertyRef properties,
927 RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
928 DeallocOpAdaptor adaptor(operands, attributes, properties, regions);
929 inferredReturnTypes = SmallVector<Type>(adaptor.getRetained().size(),
930 IntegerType::get(context, 1));
931 return success();
932}
933
934LogicalResult DeallocOp::verify() {
935 if (getMemrefs().size() != getConditions().size())
936 return emitOpError(
937 "must have the same number of conditions as memrefs to deallocate");
938 if (getRetained().size() != getUpdatedConditions().size())
939 return emitOpError("must have the same number of updated conditions "
940 "(results) as retained operands");
941 return success();
942}
943
944static LogicalResult updateDeallocIfChanged(DeallocOp deallocOp,
945 ValueRange memrefs,
946 ValueRange conditions,
947 PatternRewriter &rewriter) {
948 if (deallocOp.getMemrefs() == memrefs &&
949 deallocOp.getConditions() == conditions)
950 return failure();
951
952 rewriter.modifyOpInPlace(deallocOp, [&]() {
953 deallocOp.getMemrefsMutable().assign(memrefs);
954 deallocOp.getConditionsMutable().assign(conditions);
955 });
956 return success();
957}
958
959namespace {
960
961/// Remove duplicate values in the list of memrefs to be deallocated. We need to
962/// make sure the corresponding condition value is updated accordingly since
963/// their two conditions might not cover the same set of cases. In that case, we
964/// have to combine them (by computing the disjunction of them).
965/// Example:
966/// ```mlir
967/// bufferization.dealloc (%arg0, %arg0 : ...) if (%arg1, %arg2)
968/// ```
969/// is canonicalized to
970/// ```mlir
971/// %0 = arith.ori %arg1, %arg2 : i1
972/// bufferization.dealloc (%arg0 : memref<2xi32>) if (%0)
973/// ```
974struct DeallocRemoveDuplicateDeallocMemrefs
975 : public OpRewritePattern<DeallocOp> {
976 using OpRewritePattern<DeallocOp>::OpRewritePattern;
977
978 LogicalResult matchAndRewrite(DeallocOp deallocOp,
979 PatternRewriter &rewriter) const override {
980 // Unique memrefs to be deallocated.
981 DenseMap<Value, unsigned> memrefToCondition;
982 SmallVector<Value> newMemrefs, newConditions;
983 for (auto [i, memref, cond] :
984 llvm::enumerate(deallocOp.getMemrefs(), deallocOp.getConditions())) {
985 if (memrefToCondition.count(memref)) {
986 // If the dealloc conditions don't match, we need to make sure that the
987 // dealloc happens on the union of cases.
988 Value &newCond = newConditions[memrefToCondition[memref]];
989 if (newCond != cond)
990 newCond =
991 arith::OrIOp::create(rewriter, deallocOp.getLoc(), newCond, cond);
992 } else {
993 memrefToCondition.insert({memref, newConditions.size()});
994 newMemrefs.push_back(memref);
995 newConditions.push_back(cond);
996 }
997 }
998
999 // Return failure if we don't change anything such that we don't run into an
1000 // infinite loop of pattern applications.
1001 return updateDeallocIfChanged(deallocOp, newMemrefs, newConditions,
1002 rewriter);
1003 }
1004};
1005
1006/// Remove duplicate values in the list of retained memrefs. We need to make
1007/// sure the corresponding result condition value is replaced properly.
1008/// Example:
1009/// ```mlir
1010/// %0:2 = bufferization.dealloc retain (%arg3, %arg3 : ...)
1011/// ```
1012/// is canonicalized to
1013/// ```mlir
1014/// %0 = bufferization.dealloc retain (%arg3 : memref<2xi32>)
1015/// ```
1016struct DeallocRemoveDuplicateRetainedMemrefs
1017 : public OpRewritePattern<DeallocOp> {
1018 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1019
1020 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1021 PatternRewriter &rewriter) const override {
1022 // Unique retained values
1024 SmallVector<Value> newRetained;
1025 SmallVector<unsigned> resultReplacementIdx;
1026 unsigned i = 0;
1027 for (auto retained : deallocOp.getRetained()) {
1028 if (seen.count(retained)) {
1029 resultReplacementIdx.push_back(seen[retained]);
1030 continue;
1031 }
1032
1033 seen[retained] = i;
1034 newRetained.push_back(retained);
1035 resultReplacementIdx.push_back(i++);
1036 }
1037
1038 // Return failure if we don't change anything such that we don't run into an
1039 // infinite loop of pattern applications.
1040 if (newRetained.size() == deallocOp.getRetained().size())
1041 return failure();
1042
1043 // We need to create a new op because the number of results is always the
1044 // same as the number of condition operands.
1045 auto newDeallocOp =
1046 DeallocOp::create(rewriter, deallocOp.getLoc(), deallocOp.getMemrefs(),
1047 deallocOp.getConditions(), newRetained);
1048 SmallVector<Value> replacements(
1049 llvm::map_range(resultReplacementIdx, [&](unsigned idx) {
1050 return newDeallocOp.getUpdatedConditions()[idx];
1051 }));
1052 rewriter.replaceOp(deallocOp, replacements);
1053 return success();
1054 }
1055};
1056
1057/// Erase deallocation operations where the variadic list of memrefs to
1058/// deallocate is empty. Example:
1059/// ```mlir
1060/// %0 = bufferization.dealloc retain (%arg0: memref<2xi32>)
1061/// ```
1062struct EraseEmptyDealloc : public OpRewritePattern<DeallocOp> {
1063 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1064
1065 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1066 PatternRewriter &rewriter) const override {
1067 if (deallocOp.getMemrefs().empty()) {
1068 Value constFalse = arith::ConstantOp::create(rewriter, deallocOp.getLoc(),
1069 rewriter.getBoolAttr(false));
1070 rewriter.replaceOp(
1071 deallocOp, SmallVector<Value>(deallocOp.getUpdatedConditions().size(),
1072 constFalse));
1073 return success();
1074 }
1075 return failure();
1076 }
1077};
1078
1079/// Removes memrefs from the deallocation list if their associated condition is
1080/// always 'false'.
1081///
1082/// Example:
1083/// ```
1084/// bufferization.dealloc (%arg0, %arg1 : memref<2xi32>, memref<2xi32>)
1085/// if (%arg2, %false)
1086/// ```
1087/// becomes
1088/// ```
1089/// bufferization.dealloc (%arg0 : memref<2xi32>) if (%arg2)
1090/// ```
1091struct EraseAlwaysFalseDealloc : public OpRewritePattern<DeallocOp> {
1092 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1093
1094 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1095 PatternRewriter &rewriter) const override {
1096 SmallVector<Value> newMemrefs, newConditions;
1097 for (auto [memref, cond] :
1098 llvm::zip(deallocOp.getMemrefs(), deallocOp.getConditions())) {
1099 if (!matchPattern(cond, m_Zero())) {
1100 newMemrefs.push_back(memref);
1101 newConditions.push_back(cond);
1102 }
1103 }
1104
1105 return updateDeallocIfChanged(deallocOp, newMemrefs, newConditions,
1106 rewriter);
1107 }
1108};
1109
1110/// The `memref.extract_strided_metadata` is often inserted to get the base
1111/// memref if the operand is not already guaranteed to be the result of a memref
1112/// allocation operation. This canonicalization pattern removes this extraction
1113/// operation if the operand is now produced by an allocation operation (e.g.,
1114/// due to other canonicalizations simplifying the IR).
1115///
1116/// Example:
1117/// ```mlir
1118/// %alloc = memref.alloc() : memref<2xi32>
1119/// %base_memref, %offset, %size, %stride = memref.extract_strided_metadata
1120/// %alloc : memref<2xi32> -> memref<i32>, index, index, index
1121/// bufferization.dealloc (%base_memref : memref<i32>) if (%cond)
1122/// ```
1123/// is canonicalized to
1124/// ```mlir
1125/// %alloc = memref.alloc() : memref<2xi32>
1126/// bufferization.dealloc (%alloc : memref<2xi32>) if (%cond)
1127/// ```
1128struct SkipExtractMetadataOfAlloc : public OpRewritePattern<DeallocOp> {
1129 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1130
1131 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1132 PatternRewriter &rewriter) const override {
1133 SmallVector<Value> newMemrefs(
1134 llvm::map_range(deallocOp.getMemrefs(), [&](Value memref) {
1135 auto extractStridedOp =
1136 memref.getDefiningOp<memref::ExtractStridedMetadataOp>();
1137 if (!extractStridedOp)
1138 return memref;
1139 Value allocMemref = extractStridedOp.getOperand();
1140 auto allocOp = allocMemref.getDefiningOp<MemoryEffectOpInterface>();
1141 if (!allocOp)
1142 return memref;
1143 if (allocOp.getEffectOnValue<MemoryEffects::Allocate>(allocMemref))
1144 return allocMemref;
1145 return memref;
1146 }));
1147
1148 return updateDeallocIfChanged(deallocOp, newMemrefs,
1149 deallocOp.getConditions(), rewriter);
1150 }
1151};
1152
1153/// Removes pairs of `bufferization.dealloc` and alloc operations if there is no
1154/// other user of the allocated value and the allocating operation can be safely
1155/// removed. If the same value is present multiple times, this pattern relies on
1156/// other canonicalization patterns to remove the duplicate first.
1157///
1158/// Example:
1159/// ```mlir
1160/// %alloc = memref.alloc() : memref<2xi32>
1161/// bufferization.dealloc (%alloc, %arg0, : ...) if (%true, %true)
1162/// ```
1163/// is canonicalized to
1164/// ```mlir
1165/// bufferization.dealloc (%arg0 : ...) if (%true)
1166/// ```
1167struct RemoveAllocDeallocPairWhenNoOtherUsers
1168 : public OpRewritePattern<DeallocOp> {
1169 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1170
1171 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1172 PatternRewriter &rewriter) const override {
1173 SmallVector<Value> newMemrefs, newConditions;
1174 SmallVector<Operation *> toDelete;
1175 for (auto [memref, cond] :
1176 llvm::zip(deallocOp.getMemrefs(), deallocOp.getConditions())) {
1177 if (auto allocOp = memref.getDefiningOp<MemoryEffectOpInterface>()) {
1178 // Check that it is indeed an allocate effect, that the op has no other
1179 // side effects (which would not allow us to remove the op), and that
1180 // there are no other users.
1181 if (allocOp.getEffectOnValue<MemoryEffects::Allocate>(memref) &&
1183 memref.hasOneUse()) {
1184 toDelete.push_back(allocOp);
1185 continue;
1186 }
1187 }
1188
1189 newMemrefs.push_back(memref);
1190 newConditions.push_back(cond);
1191 }
1192
1193 if (failed(updateDeallocIfChanged(deallocOp, newMemrefs, newConditions,
1194 rewriter)))
1195 return failure();
1196
1197 for (Operation *op : toDelete)
1198 rewriter.eraseOp(op);
1199
1200 return success();
1201 }
1202};
1203
1204} // anonymous namespace
1205
1206void DeallocOp::getCanonicalizationPatterns(RewritePatternSet &results,
1207 MLIRContext *context) {
1209}
1210
1212 RewritePatternSet &patterns, MLIRContext *context) {
1213 patterns.add<DeallocRemoveDuplicateDeallocMemrefs,
1214 DeallocRemoveDuplicateRetainedMemrefs, EraseEmptyDealloc,
1215 EraseAlwaysFalseDealloc, SkipExtractMetadataOfAlloc,
1216 RemoveAllocDeallocPairWhenNoOtherUsers>(context);
1217}
1218
1219//===----------------------------------------------------------------------===//
1220// TableGen'd op method definitions
1221//===----------------------------------------------------------------------===//
1222
1223#define GET_OP_CLASSES
1224#include "mlir/Dialect/Bufferization/IR/BufferizationOps.cpp.inc"
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 SmallVector< Value > getDynamicSize(Value memref, func::FuncOp funcOp)
Return the dynamic shapes of the memref based on the defining op.
static LogicalResult updateDeallocIfChanged(DeallocOp deallocOp, ValueRange memrefs, ValueRange conditions, PatternRewriter &rewriter)
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
true
Given two iterators into the same block, return "true" if a is before `b.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
auto load
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static llvm::ManagedStatic< PassManagerOptions > options
template bool mlir::hasSingleEffect< MemoryEffects::Allocate >(Operation *)
static void getDynamicSizes(RankedTensorType tp, ValueRange sizes, SmallVectorImpl< Value > &dynSizes)
Collects the dynamic dimension sizes for tp with the assumption that sizes are the dimension sizes fo...
Base class for generic analysis states.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseCustomTypeWithFallback(Type &result, function_ref< ParseResult(Type &result)> parseType)=0
Parse a custom type with the provided callback, unless the next token is #, in which case the generic...
virtual ParseResult parseColon()=0
Parse a : token.
virtual ParseResult parseLParen()=0
Parse a ( token.
void printStrippedAttrOrType(AttrOrType attrOrType)
Print the provided attribute in the context of an operation custom printer/parser: this will invoke d...
Attributes are known-constant values of operations.
Definition Attributes.h:25
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:112
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:167
BoolAttr getBoolAttr(bool value)
Definition Builders.cpp:104
IndexType getIndexType()
Definition Builders.cpp:55
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class provides a mutable adaptor for a range of operands.
Definition ValueRange.h:119
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:350
This class helps build Operations.
Definition Builders.h:209
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Definition Value.h:254
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Definition Value.cpp:226
This is a value defined by a result of an operation.
Definition Value.h:454
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
Type-safe wrapper around a void* for passing properties, including the properties structs of operatio...
This class provides an abstraction over the different types of ranges over Regions.
Definition Region.h:375
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class represents a specific instance of an effect.
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
ArrayRef< int64_t > getShape() const
Returns the shape of this tensor type.
bool hasRank() const
Returns if this type is ranked, i.e. it has a known number of dimensions.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
void populateDeallocOpCanonicalizationPatterns(RewritePatternSet &patterns, MLIRContext *context)
Add the canonicalization patterns for bufferization.dealloc to the given pattern set to make them ava...
FailureOr< Value > castOrReallocMemRefValue(OpBuilder &b, Value value, MemRefType type, const BufferizationOptions &options)
Try to cast the given ranked MemRef-typed value to the given ranked MemRef type.
LogicalResult foldToBufferToTensorPair(RewriterBase &rewriter, ToBufferOp toBuffer, const BufferizationOptions &options)
Try to fold to_buffer(to_tensor(x)).
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...
Type getTensorTypeFromMemRefType(Type type)
Return an unranked/ranked tensor type for the given unranked/ranked memref type.
Definition MemRefOps.cpp:62
std::optional< Operation * > findDealloc(Value allocValue)
Finds a single dealloc operation for the given allocated value.
LogicalResult foldMemRefCast(Operation *op, Value inner=nullptr)
This is a common utility used for patterns of the form "someop(memref.cast) -> someop".
Definition MemRefOps.cpp:47
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Definition TensorOps.cpp:69
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
Definition Matchers.h:527
LogicalResult verifyDynamicDimensionCount(Operation *op, ShapedType type, ValueRange dynamicSizes)
Verify that the number of dynamic size operands matches the number of dynamic dimensions in the shape...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
detail::constant_int_predicate_matcher m_Zero()
Matches a constant scalar / vector splat / tensor splat integer zero.
Definition Matchers.h:442
LogicalResult verifyRanksMatch(Operation *op, ShapedType lhs, ShapedType rhs, StringRef lhsName, StringRef rhsName)
Verify that two shaped types have matching ranks.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
This is the representation of an operand reference.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.