MLIR 23.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 memref -> unranked memref cast
136 // Ranked memref -> unranked memref cast: No copy needed.
137 assert(memref::CastOp::areCastCompatible(srcType, destType) &&
138 "expected that types are cast compatible");
139 rewriter.replaceOpWithNewOp<memref::CastOp>(toBuffer, destType,
140 bufferToTensor.getBuffer());
141 return success();
142}
143
145 OpBuilder &b, Location loc, Value shapedValue,
146 SmallVector<Value> &dynamicDims) {
147 auto shapedType = llvm::cast<ShapedType>(shapedValue.getType());
148 for (int64_t i = 0; i < shapedType.getRank(); ++i) {
149 if (shapedType.isDynamicDim(i)) {
150 if (llvm::isa<MemRefType>(shapedType)) {
151 dynamicDims.push_back(memref::DimOp::create(b, loc, shapedValue, i));
152 } else {
153 assert(llvm::isa<RankedTensorType>(shapedType) && "expected tensor");
154 dynamicDims.push_back(tensor::DimOp::create(b, loc, shapedValue, i));
155 }
156 }
157 }
158}
159
160//===----------------------------------------------------------------------===//
161// AllocTensorOp
162//===----------------------------------------------------------------------===//
163
164LogicalResult AllocTensorOp::bufferize(RewriterBase &rewriter,
166 BufferizationState &state) {
167 OpBuilder::InsertionGuard g(rewriter);
168 Location loc = getLoc();
169
170 // Nothing to do for dead AllocTensorOps.
171 if (getOperation()->getUses().empty()) {
172 rewriter.eraseOp(getOperation());
173 return success();
174 }
175
176 // Get "copy" buffer.
177 Value copyBuffer;
178 if (getCopy()) {
179 FailureOr<Value> maybeCopyBuffer =
180 getBuffer(rewriter, getCopy(), options, state);
181 if (failed(maybeCopyBuffer))
182 return failure();
183 copyBuffer = *maybeCopyBuffer;
184 }
185
186 // Create memory allocation.
187 auto allocType = bufferization::getBufferType(getResult(), options, state);
188 if (failed(allocType))
189 return failure();
190 SmallVector<Value> dynamicDims = getDynamicSizes();
191 if (getCopy()) {
192 assert(dynamicDims.empty() && "expected either `copy` or `dynamicDims`");
193 populateDynamicDimSizes(rewriter, loc, copyBuffer, dynamicDims);
194 }
195 FailureOr<Value> alloc =
196 options.allocationFn(rewriter, loc, llvm::cast<MemRefType>(*allocType),
197 dynamicDims, options.bufferAlignment);
198 if (failed(alloc))
199 return failure();
200
201 // Create memory copy (if any).
202 if (getCopy()) {
203 if (failed(options.memCpyFn(rewriter, loc, copyBuffer, *alloc)))
204 return failure();
205 }
206
207 // Replace op.
208 replaceOpWithBufferizedValues(rewriter, getOperation(), *alloc);
209
210 return success();
211}
212
213bool AllocTensorOp::resultBufferizesToMemoryWrite(OpResult opResult,
214 const AnalysisState &state) {
215 // AllocTensorOps do not write unless they have a `copy` value.
216 return static_cast<bool>(getCopy());
217}
218
219bool AllocTensorOp::bufferizesToMemoryRead(OpOperand &opOperand,
220 const AnalysisState &state) {
221 assert(opOperand.getOperandNumber() == getNumOperands() - 1 &&
222 "expected copy operand");
223 return true;
224}
225
226bool AllocTensorOp::bufferizesToMemoryWrite(OpOperand &opOperand,
227 const AnalysisState &state) {
228 assert(opOperand.getOperandNumber() == getNumOperands() - 1 &&
229 "expected copy operand");
230 return false;
231}
232
233AliasingValueList AllocTensorOp::getAliasingValues(OpOperand &opOperand,
234 const AnalysisState &state) {
235 // This is a new allocation. It does not alias with any other buffer.
236 return {};
237}
238
239FailureOr<BufferLikeType>
240AllocTensorOp::getBufferType(Value value, const BufferizationOptions &options,
241 const BufferizationState &state,
242 SmallVector<Value> &invocationStack) {
243 assert(value == getResult() && "invalid value");
244
245 // Compute memory space of this allocation.
246 Attribute memorySpace;
247 if (getMemorySpace().has_value()) {
248 memorySpace = *getMemorySpace();
249 } else if (getCopy()) {
250 auto copyBufferType =
251 bufferization::detail::asMemRefType(bufferization::getBufferType(
252 getCopy(), options, state, invocationStack));
253 if (failed(copyBufferType))
254 return failure();
255 memorySpace = copyBufferType->getMemorySpace();
256 } else if (auto ms = options.defaultMemorySpaceFn(
257 cast<TensorLikeType>(getType()))) {
258 memorySpace = *ms;
259 } else {
260 return getOperation()->emitError("could not infer memory space");
261 }
262
263 return cast<BufferLikeType>(
264 getMemRefTypeWithStaticIdentityLayout(getType(), memorySpace));
265}
266
267LogicalResult AllocTensorOp::verify() {
268 if (getCopy() && !getDynamicSizes().empty())
269 return emitError("dynamic sizes not needed when copying a tensor");
270 if (!getCopy() && failed(verifyDynamicDimensionCount(
271 getOperation(), getType(), getDynamicSizes())))
272 return failure();
273 if (getCopy() && getCopy().getType() != getType())
274 return emitError("expected that `copy` and return type match");
275 return success();
276}
277
278void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
279 RankedTensorType type, ValueRange dynamicSizes) {
280 build(builder, result, type, dynamicSizes, /*copy=*/Value(),
281 /*size_hint=*/Value(),
282 /*memory_space=*/IntegerAttr());
283}
284
285void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
286 RankedTensorType type, ValueRange dynamicSizes,
287 Value copy) {
288 build(builder, result, type, dynamicSizes, copy, /*size_hint=*/Value(),
289 /*memory_space=*/IntegerAttr());
290}
291
292void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
293 TensorType type, ValueRange dynamicSizes, Value copy,
294 IntegerAttr memorySpace) {
295 build(builder, result, type, dynamicSizes, copy, /*size_hint=*/Value(),
296 memorySpace);
297}
298
299namespace {
300/// Change the type of the result of a `bufferization.alloc_tensor` by making
301/// the result type statically sized along dimension that in the original
302/// operation where defined as dynamic, but the size was defined using a
303/// `constant` op. For example:
304///
305/// %c5 = arith.constant 5: index
306/// %0 = bufferization.alloc_tensor(%arg0, %c5) : tensor<?x?xf32>
307///
308/// to
309///
310/// %0 = bufferization.alloc_tensor(%arg0) : tensor<?x5xf32>
311struct ReplaceStaticShapeDims : OpRewritePattern<AllocTensorOp> {
312 using OpRewritePattern<AllocTensorOp>::OpRewritePattern;
313
314 LogicalResult matchAndRewrite(AllocTensorOp op,
315 PatternRewriter &rewriter) const override {
316 if (op.getCopy())
317 return failure();
318 SmallVector<int64_t> newShape = llvm::to_vector(op.getType().getShape());
319 SmallVector<Value> newDynamicSizes;
320 unsigned int dynValCounter = 0;
321 for (int64_t i = 0; i < op.getType().getRank(); ++i) {
322 if (!op.isDynamicDim(i))
323 continue;
324 Value value = op.getDynamicSizes()[dynValCounter++];
325 APInt intVal;
326 if (matchPattern(value, m_ConstantInt(&intVal))) {
327 int64_t dim = intVal.getSExtValue();
328 if (dim >= 0)
329 newShape[i] = intVal.getSExtValue();
330 else
331 newDynamicSizes.push_back(value);
332 } else {
333 newDynamicSizes.push_back(value);
334 }
335 }
336 RankedTensorType newType = RankedTensorType::get(
337 newShape, op.getType().getElementType(), op.getType().getEncoding());
338 if (newType == op.getType())
339 return failure();
340 auto newOp = AllocTensorOp::create(rewriter, op.getLoc(), newType,
341 newDynamicSizes, /*copy=*/Value());
342 rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp);
343 return success();
344 }
345};
346
347struct FoldDimOfAllocTensorOp : public OpRewritePattern<tensor::DimOp> {
348 using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
349
350 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
351 PatternRewriter &rewriter) const override {
352 std::optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex();
353 auto allocTensorOp = dimOp.getSource().getDefiningOp<AllocTensorOp>();
354 if (!allocTensorOp || !maybeConstantIndex)
355 return failure();
356 if (*maybeConstantIndex < 0 ||
357 *maybeConstantIndex >= allocTensorOp.getType().getRank())
358 return failure();
359 if (!allocTensorOp.getType().isDynamicDim(*maybeConstantIndex))
360 return failure();
361 rewriter.replaceOp(
362 dimOp, allocTensorOp.getDynamicSize(rewriter, *maybeConstantIndex));
363 return success();
364 }
365};
366} // namespace
367
368void AllocTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
369 MLIRContext *ctx) {
370 results.add<FoldDimOfAllocTensorOp, ReplaceStaticShapeDims>(ctx);
371}
372
373LogicalResult AllocTensorOp::reifyResultShapes(
374 OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
375 auto shapes =
376 llvm::map_to_vector<4>(llvm::seq<int64_t>(0, getType().getRank()),
377 [&](int64_t dim) -> OpFoldResult {
378 if (isDynamicDim(dim))
379 return getDynamicSize(builder, dim);
380 return builder.getIndexAttr(getStaticSize(dim));
381 });
382 reifiedReturnShapes.emplace_back(std::move(shapes));
383 return success();
384}
385
386ParseResult AllocTensorOp::parse(OpAsmParser &parser, OperationState &result) {
388 if (parser.parseLParen() || parser.parseOperandList(dynamicSizesOperands) ||
389 parser.parseRParen())
390 return failure();
391 ParseResult copyKeyword = parser.parseOptionalKeyword("copy");
393 if (copyKeyword.succeeded())
394 if (parser.parseLParen() || parser.parseOperand(copyOperand) ||
395 parser.parseRParen())
396 return failure();
397 ParseResult sizeHintKeyword = parser.parseOptionalKeyword("size_hint");
398 OpAsmParser::UnresolvedOperand sizeHintOperand;
399 if (sizeHintKeyword.succeeded())
400 if (parser.parseEqual() || parser.parseOperand(sizeHintOperand))
401 return failure();
402 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon())
403 return failure();
404
405 TensorType type;
406 if (parser.parseCustomTypeWithFallback(type))
407 return failure();
408 result.addTypes(type);
409
410 Type indexType = parser.getBuilder().getIndexType();
411 if (parser.resolveOperands(dynamicSizesOperands, indexType, result.operands))
412 return failure();
413 if (copyKeyword.succeeded())
414 if (parser.resolveOperand(copyOperand, type, result.operands))
415 return failure();
416 if (sizeHintKeyword.succeeded())
417 if (parser.resolveOperand(sizeHintOperand, indexType, result.operands))
418 return failure();
419 result.addAttribute(AllocTensorOp::getOperandSegmentSizeAttr(),
421 {static_cast<int32_t>(dynamicSizesOperands.size()),
422 static_cast<int32_t>(copyKeyword.succeeded()),
423 static_cast<int32_t>(sizeHintKeyword.succeeded())}));
424 return success();
425}
426
427void AllocTensorOp::print(OpAsmPrinter &p) {
428 p << "(" << getDynamicSizes() << ")";
429 if (getCopy())
430 p << " copy(" << getCopy() << ")";
431 if (getSizeHint())
432 p << " size_hint=" << getSizeHint();
433 p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{
434 AllocTensorOp::getOperandSegmentSizeAttr()});
435 p << " : ";
436 auto type = getResult().getType();
437 if (auto validType = llvm::dyn_cast<::mlir::TensorType>(type))
438 p.printStrippedAttrOrType(validType);
439 else
440 p << type;
441}
442
443Value AllocTensorOp::getDynamicSize(OpBuilder &b, unsigned idx) {
444 assert(isDynamicDim(idx) && "expected dynamic dim");
445 if (getCopy())
446 return tensor::DimOp::create(b, getLoc(), getCopy(), idx);
447 return getOperand(getIndexOfDynamicSize(idx));
448}
449
450//===----------------------------------------------------------------------===//
451// CloneOp
452//===----------------------------------------------------------------------===//
453
454OpFoldResult CloneOp::fold(FoldAdaptor adaptor) {
455 return succeeded(memref::foldMemRefCast(*this)) ? getResult() : Value();
456}
457
458namespace {
459
460/// Merge the clone and its source (by converting the clone to a cast) when
461/// possible.
462struct SimplifyClones : public OpRewritePattern<CloneOp> {
463 using OpRewritePattern<CloneOp>::OpRewritePattern;
464
465 LogicalResult matchAndRewrite(CloneOp cloneOp,
466 PatternRewriter &rewriter) const override {
467 if (cloneOp.use_empty()) {
468 rewriter.eraseOp(cloneOp);
469 return success();
470 }
471
472 Value source = cloneOp.getInput();
473 if (source.getType() != cloneOp.getType() &&
474 !memref::CastOp::areCastCompatible({source.getType()},
475 {cloneOp.getType()}))
476 return failure();
477
478 // Aims to find the dealloc op for the canonical source
479 // which otherwise could prevent removal of unnecessary allocs.
480 Value canonicalSource = source;
481 while (auto iface = dyn_cast_or_null<ViewLikeOpInterface>(
482 canonicalSource.getDefiningOp())) {
483 if (canonicalSource != iface.getViewDest()) {
484 break;
485 }
486 canonicalSource = iface.getViewSource();
487 }
488
489 std::optional<Operation *> maybeCloneDeallocOp =
490 memref::findDealloc(cloneOp.getOutput());
491 // Skip if either of them has > 1 deallocate operations.
492 if (!maybeCloneDeallocOp.has_value())
493 return failure();
494 std::optional<Operation *> maybeSourceDeallocOp =
495 memref::findDealloc(canonicalSource);
496 if (!maybeSourceDeallocOp.has_value())
497 return failure();
498 Operation *cloneDeallocOp = *maybeCloneDeallocOp;
499 Operation *sourceDeallocOp = *maybeSourceDeallocOp;
500
501 // If both are deallocated in the same block, their in-block lifetimes
502 // might not fully overlap, so we cannot decide which one to drop.
503 if (cloneDeallocOp && sourceDeallocOp &&
504 cloneDeallocOp->getBlock() == sourceDeallocOp->getBlock())
505 return failure();
506
507 Block *currentBlock = cloneOp->getBlock();
508 Operation *redundantDealloc = nullptr;
509 if (cloneDeallocOp && cloneDeallocOp->getBlock() == currentBlock) {
510 redundantDealloc = cloneDeallocOp;
511 } else if (sourceDeallocOp && sourceDeallocOp->getBlock() == currentBlock) {
512 redundantDealloc = sourceDeallocOp;
513 }
514
515 if (!redundantDealloc)
516 return failure();
517
518 // Safety check that there are no other deallocations inbetween
519 // cloneOp and redundantDealloc, as otherwise we might deallocate an alias
520 // of source before the uses of the clone. With alias information, we could
521 // restrict this to only fail of the dealloc's operand is an alias
522 // of the source.
523 for (Operation *pos = cloneOp->getNextNode(); pos != redundantDealloc;
524 pos = pos->getNextNode()) {
525 // Bail if we run out of operations while looking for a deallocation op.
526 if (!pos)
527 return failure();
528 auto effectInterface = dyn_cast<MemoryEffectOpInterface>(pos);
529 if (!effectInterface)
530 continue;
531 if (effectInterface.hasEffect<MemoryEffects::Free>())
532 return failure();
533 }
534
535 if (source.getType() != cloneOp.getType())
536 source = memref::CastOp::create(rewriter, cloneOp.getLoc(),
537 cloneOp.getType(), source);
538 rewriter.replaceOp(cloneOp, source);
539 rewriter.eraseOp(redundantDealloc);
540 return success();
541 }
542};
543
544} // namespace
545
546void CloneOp::getCanonicalizationPatterns(RewritePatternSet &results,
547 MLIRContext *context) {
548 results.add<SimplifyClones>(context);
549}
550
551//===----------------------------------------------------------------------===//
552// DeallocTensorOp
553//===----------------------------------------------------------------------===//
554
555LogicalResult DeallocTensorOp::bufferize(RewriterBase &rewriter,
557 BufferizationState &state) {
558 FailureOr<Value> buffer = getBuffer(rewriter, getTensor(), options, state);
559 if (failed(buffer))
560 return failure();
561 memref::DeallocOp::create(rewriter, getLoc(), *buffer);
562 rewriter.eraseOp(getOperation());
563 return success();
564}
565
566//===----------------------------------------------------------------------===//
567// MaterializeInDestinationOp
568//===----------------------------------------------------------------------===//
569
570bool MaterializeInDestinationOp::bufferizesToMemoryRead(
571 OpOperand &opOperand, const AnalysisState &state) {
572 return opOperand == getSourceMutable();
573}
574
575bool MaterializeInDestinationOp::bufferizesToMemoryWrite(
576 OpOperand &opOperand, const AnalysisState &state) {
577 if (opOperand == getDestMutable()) {
578 assert(isa<TensorType>(getDest().getType()) && "expected tensor type");
579 return true;
580 }
581 return false;
582}
583
584bool MaterializeInDestinationOp::mustBufferizeInPlace(
585 OpOperand &opOperand, const AnalysisState &state) {
586 // The source is only read and not written, so it always bufferizes in-place
587 // by default. The destination is written and is forced to bufferize in-place
588 // (if it is a tensor).
589 return true;
590}
591
592AliasingValueList
593MaterializeInDestinationOp::getAliasingValues(OpOperand &opOperand,
594 const AnalysisState &state) {
595 if (opOperand == getDestMutable()) {
596 assert(isa<TensorType>(getDest().getType()) && "expected tensor type");
597 return {{getOperation()->getResult(0), BufferRelation::Equivalent}};
598 }
599 return {};
600}
601
602LogicalResult
603MaterializeInDestinationOp::bufferize(RewriterBase &rewriter,
605 BufferizationState &state) {
606 bool tensorDest = isa<TensorType>(getDest().getType());
607 Value buffer;
608 if (tensorDest) {
609 FailureOr<Value> maybeBuffer =
610 getBuffer(rewriter, getDest(), options, state);
611 if (failed(maybeBuffer))
612 return failure();
613 buffer = *maybeBuffer;
614 } else {
615 assert(isa<BaseMemRefType>(getDest().getType()) && "expected memref type");
616 buffer = getDest();
617 }
618 auto srcBuffer = getBuffer(rewriter, getSource(), options, state);
619 if (failed(srcBuffer))
620 return failure();
621 if (failed(options.memCpyFn(rewriter, getLoc(), *srcBuffer, buffer)))
622 return failure();
623 replaceOpWithBufferizedValues(rewriter, getOperation(),
624 tensorDest ? ValueRange(buffer) : ValueRange());
625 return success();
626}
627
628bool MaterializeInDestinationOp::bufferizesToElementwiseAccess(
629 const AnalysisState &state, ArrayRef<OpOperand *> opOperands) {
630 // As elements are copied from the "source" buffer to the "dest" buffer,
631 // already copied elements are not read a second time.
632 return true;
633}
634
635LogicalResult MaterializeInDestinationOp::reifyResultShapes(
636 OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
637 if (getOperation()->getNumResults() == 1) {
638 assert(isa<TensorType>(getDest().getType()) && "expected tensor type");
639 reifiedReturnShapes.resize(1,
641 reifiedReturnShapes[0] =
642 tensor::getMixedSizes(builder, getLoc(), getDest());
643 }
644 return success();
645}
646
647Value MaterializeInDestinationOp::buildSubsetExtraction(OpBuilder &builder,
648 Location loc) {
649 if (isa<TensorType>(getDest().getType())) {
650 // The subset is the entire destination tensor.
651 return getDest();
652 }
653
654 // The "restrict" attribute is transferred from this op to the newly created
655 // to_tensor op. If this op does not the "restrict" attribute, the subset
656 // extraction cannot be built because there is no guarantee that there is no
657 // pre-existing "restrict" to_tensor op with the same/an aliasing destination.
658 if (!getRestrict())
659 return {};
660
661 // Build a bufferization.to_tensor op.
662 assert(isa<BaseMemRefType>(getDest().getType()) && "expected memref type");
663 assert(getRestrict() &&
664 "expected that ops with memrefs dest have 'restrict'");
665 setRestrict(false);
666 return ToTensorOp::create(
667 builder, loc, memref::getTensorTypeFromMemRefType(getDest().getType()),
668 getDest(),
669 /*restrict=*/true, getWritable());
670}
671
672bool MaterializeInDestinationOp::isEquivalentSubset(
673 Value candidate, function_ref<bool(Value, Value)> equivalenceFn) {
674 return equivalenceFn(getDest(), candidate);
675}
676
678MaterializeInDestinationOp::getValuesNeededToBuildSubsetExtraction() {
679 return {getDest()};
680}
681
682OpOperand &MaterializeInDestinationOp::getSourceOperand() {
683 return getOperation()->getOpOperand(0) /*source*/;
684}
685
686bool MaterializeInDestinationOp::operatesOnEquivalentSubset(
687 SubsetOpInterface subsetOp,
688 function_ref<bool(Value, Value)> equivalenceFn) {
689 return false;
690}
691
692bool MaterializeInDestinationOp::operatesOnDisjointSubset(
693 SubsetOpInterface subsetOp,
694 function_ref<bool(Value, Value)> equivalenceFn) {
695 return false;
696}
697
698LogicalResult MaterializeInDestinationOp::verify() {
699 if (!isa<TensorType, BaseMemRefType>(getDest().getType()))
700 return emitOpError("'dest' must be a tensor or a memref");
701 if (auto destType = dyn_cast<TensorType>(getDest().getType())) {
702 if (getOperation()->getNumResults() != 1)
703 return emitOpError("tensor 'dest' implies exactly one tensor result");
704 if (destType != getResult().getType())
705 return emitOpError("result and 'dest' types must match");
706 }
707 if (isa<BaseMemRefType>(getDest().getType()) &&
708 getOperation()->getNumResults() != 0)
709 return emitOpError("memref 'dest' implies zero results");
710 if (getRestrict() && !isa<BaseMemRefType>(getDest().getType()))
711 return emitOpError("'restrict' is valid only for memref destinations");
712 if (getWritable() != isa<BaseMemRefType>(getDest().getType()))
713 return emitOpError("'writable' must be specified if and only if the "
714 "destination is of memref type");
715 TensorType srcType = getSource().getType();
716 ShapedType destType = cast<ShapedType>(getDest().getType());
717 if (srcType.hasRank() != destType.hasRank())
718 return emitOpError("source/destination shapes are incompatible");
719 if (srcType.hasRank()) {
720 if (failed(verifyRanksMatch(getOperation(), srcType, destType, "source",
721 "destination")))
722 return failure();
723 for (auto [src, dest] :
724 llvm::zip(srcType.getShape(), destType.getShape())) {
725 if (src == ShapedType::kDynamic || dest == ShapedType::kDynamic) {
726 // Cannot verify dynamic dimension size. Assume that that they match at
727 // runtime.
728 continue;
729 }
730 if (src != dest)
731 return emitOpError("source/destination shapes are incompatible");
732 }
733 }
734 return success();
735}
736
737void MaterializeInDestinationOp::build(OpBuilder &builder,
738 OperationState &state, Value source,
739 Value dest) {
740 auto destTensorType = dyn_cast<TensorType>(dest.getType());
741 build(builder, state, /*result=*/destTensorType ? destTensorType : Type(),
742 source, dest);
743}
744
745bool MaterializeInDestinationOp::isWritable(Value value,
746 const AnalysisState &state) {
747 return isa<TensorType>(getDest().getType()) ? true : getWritable();
748}
749
750MutableOperandRange MaterializeInDestinationOp::getDpsInitsMutable() {
751 return getDestMutable();
752}
753
754void MaterializeInDestinationOp::getEffects(
756 &effects) {
757 if (isa<BaseMemRefType>(getDest().getType()))
758 effects.emplace_back(MemoryEffects::Write::get(), &getDestMutable(),
760}
761
762//===----------------------------------------------------------------------===//
763// ToTensorOp
764//===----------------------------------------------------------------------===//
765
766bool ToTensorOp::isWritable(Value value, const AnalysisState &state) {
767 return getWritable();
768}
769
770OpFoldResult ToTensorOp::fold(FoldAdaptor) {
771 if (auto toBuffer = getBuffer().getDefiningOp<ToBufferOp>())
772 // Approximate alias analysis by conservatively folding only when no there
773 // is no interleaved operation.
774 if (toBuffer->getBlock() == this->getOperation()->getBlock() &&
775 toBuffer->getNextNode() == this->getOperation())
776 return toBuffer.getTensor();
777 return {};
778}
779
780namespace {
781struct DimOfToTensorFolder : public OpRewritePattern<tensor::DimOp> {
782 using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
783
784 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
785 PatternRewriter &rewriter) const override {
786 auto memrefToTensorOp = dimOp.getSource().getDefiningOp<ToTensorOp>();
787 if (!memrefToTensorOp)
788 return failure();
789
790 rewriter.replaceOpWithNewOp<memref::DimOp>(
791 dimOp, memrefToTensorOp.getBuffer(), dimOp.getIndex());
792 return success();
793 }
794};
795} // namespace
796
797void ToTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
798 MLIRContext *context) {
799 results.add<DimOfToTensorFolder>(context);
800}
801
802//===----------------------------------------------------------------------===//
803// ToBufferOp
804//===----------------------------------------------------------------------===//
805
806OpFoldResult ToBufferOp::fold(FoldAdaptor) {
807 if (auto memrefToTensor = getTensor().getDefiningOp<ToTensorOp>())
808 if (memrefToTensor.getBuffer().getType() == getType())
809 return memrefToTensor.getBuffer();
810 return {};
811}
812
813namespace {
814
815/// Replace tensor.cast + to_buffer by to_buffer + memref.cast.
816struct ToBufferOfCast : public OpRewritePattern<ToBufferOp> {
817 using OpRewritePattern<ToBufferOp>::OpRewritePattern;
818
819 LogicalResult matchAndRewrite(ToBufferOp toBuffer,
820 PatternRewriter &rewriter) const final {
821 auto tensorCastOperand =
822 toBuffer.getOperand().getDefiningOp<tensor::CastOp>();
823 if (!tensorCastOperand)
824 return failure();
825 auto srcTensorType = llvm::dyn_cast<RankedTensorType>(
826 tensorCastOperand.getOperand().getType());
827 if (!srcTensorType)
828 return failure();
829 auto currentOutputMemRefType =
830 dyn_cast<BaseMemRefType>(toBuffer.getResult().getType());
831 if (!currentOutputMemRefType)
832 return failure();
833
834 auto memrefType = currentOutputMemRefType.cloneWith(
835 srcTensorType.getShape(), srcTensorType.getElementType());
836 Value memref = ToBufferOp::create(rewriter, toBuffer.getLoc(), memrefType,
837 tensorCastOperand.getOperand(),
838 toBuffer.getReadOnly());
839 rewriter.replaceOpWithNewOp<memref::CastOp>(toBuffer, toBuffer.getType(),
840 memref);
841 return success();
842 }
843};
844
845/// Canonicalize bufferization.to_tensor + bufferization.to_buffer. Insert a
846/// cast if necessary.
847struct ToBufferToTensorFolding : public OpRewritePattern<ToBufferOp> {
848 using OpRewritePattern<ToBufferOp>::OpRewritePattern;
849
850 LogicalResult matchAndRewrite(ToBufferOp toBuffer,
851 PatternRewriter &rewriter) const final {
852 BufferizationOptions options;
853 options.bufferAlignment = 0;
854 return foldToBufferToTensorPair(rewriter, toBuffer, options);
855 }
856};
857
858/// Fold a load on a to_buffer operation into an tensor.extract on the
859/// corresponding tensor.
860struct LoadOfToBuffer : public OpRewritePattern<memref::LoadOp> {
861 using OpRewritePattern<memref::LoadOp>::OpRewritePattern;
862
863 LogicalResult matchAndRewrite(memref::LoadOp load,
864 PatternRewriter &rewriter) const override {
865 auto toBuffer = load.getMemref().getDefiningOp<ToBufferOp>();
866 if (!toBuffer || !toBuffer.getReadOnly())
867 return failure();
868
869 rewriter.replaceOpWithNewOp<tensor::ExtractOp>(load, toBuffer.getTensor(),
870 load.getIndices());
871 return success();
872 }
873};
874
875/// Fold dim of a to_buffer into the dim of the tensor.
876struct DimOfCastOp : public OpRewritePattern<memref::DimOp> {
877 using OpRewritePattern<memref::DimOp>::OpRewritePattern;
878
879 LogicalResult matchAndRewrite(memref::DimOp dimOp,
880 PatternRewriter &rewriter) const override {
881 auto castOp = dimOp.getSource().getDefiningOp<ToBufferOp>();
882 if (!castOp)
883 return failure();
884 Value newSource = castOp.getOperand();
885 rewriter.replaceOpWithNewOp<tensor::DimOp>(dimOp, newSource,
886 dimOp.getIndex());
887 return success();
888 }
889};
890
891} // namespace
892
893void ToBufferOp::getCanonicalizationPatterns(RewritePatternSet &results,
894 MLIRContext *context) {
895 results.add<DimOfCastOp, LoadOfToBuffer, ToBufferOfCast,
896 ToBufferToTensorFolding>(context);
897}
898
899LogicalResult ToBufferOp::bufferize(RewriterBase &rewriter,
901 BufferizationState &state) {
902 // Fold to_buffer(to_tensor(x)) to x. Insert a cast if necessary.
903 (void)foldToBufferToTensorPair(rewriter, *this, options);
904 // Note: The return value of `bufferize` indicates whether there was an error
905 // or not. (And not whether the pattern matched or not.)
906 return success();
907}
908
909std::optional<Operation *> CloneOp::buildDealloc(OpBuilder &builder,
910 Value alloc) {
911 return memref::DeallocOp::create(builder, alloc.getLoc(), alloc)
912 .getOperation();
913}
914
915std::optional<Value> CloneOp::buildClone(OpBuilder &builder, Value alloc) {
916 return CloneOp::create(builder, alloc.getLoc(), alloc).getResult();
917}
918
919//===----------------------------------------------------------------------===//
920// DeallocOp
921//===----------------------------------------------------------------------===//
922
923LogicalResult DeallocOp::inferReturnTypes(
924 MLIRContext *context, std::optional<::mlir::Location> location,
925 ValueRange operands, DictionaryAttr attributes, PropertyRef properties,
926 RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
927 DeallocOpAdaptor adaptor(operands, attributes, properties, regions);
928 inferredReturnTypes = SmallVector<Type>(adaptor.getRetained().size(),
929 IntegerType::get(context, 1));
930 return success();
931}
932
933LogicalResult DeallocOp::verify() {
934 if (getMemrefs().size() != getConditions().size())
935 return emitOpError(
936 "must have the same number of conditions as memrefs to deallocate");
937 if (getRetained().size() != getUpdatedConditions().size())
938 return emitOpError("must have the same number of updated conditions "
939 "(results) as retained operands");
940 return success();
941}
942
943static LogicalResult updateDeallocIfChanged(DeallocOp deallocOp,
944 ValueRange memrefs,
945 ValueRange conditions,
946 PatternRewriter &rewriter) {
947 if (deallocOp.getMemrefs() == memrefs &&
948 deallocOp.getConditions() == conditions)
949 return failure();
950
951 rewriter.modifyOpInPlace(deallocOp, [&]() {
952 deallocOp.getMemrefsMutable().assign(memrefs);
953 deallocOp.getConditionsMutable().assign(conditions);
954 });
955 return success();
956}
957
958namespace {
959
960/// Remove duplicate values in the list of memrefs to be deallocated. We need to
961/// make sure the corresponding condition value is updated accordingly since
962/// their two conditions might not cover the same set of cases. In that case, we
963/// have to combine them (by computing the disjunction of them).
964/// Example:
965/// ```mlir
966/// bufferization.dealloc (%arg0, %arg0 : ...) if (%arg1, %arg2)
967/// ```
968/// is canonicalized to
969/// ```mlir
970/// %0 = arith.ori %arg1, %arg2 : i1
971/// bufferization.dealloc (%arg0 : memref<2xi32>) if (%0)
972/// ```
973struct DeallocRemoveDuplicateDeallocMemrefs
974 : public OpRewritePattern<DeallocOp> {
975 using OpRewritePattern<DeallocOp>::OpRewritePattern;
976
977 LogicalResult matchAndRewrite(DeallocOp deallocOp,
978 PatternRewriter &rewriter) const override {
979 // Unique memrefs to be deallocated.
980 DenseMap<Value, unsigned> memrefToCondition;
981 SmallVector<Value> newMemrefs, newConditions;
982 for (auto [i, memref, cond] :
983 llvm::enumerate(deallocOp.getMemrefs(), deallocOp.getConditions())) {
984 if (memrefToCondition.count(memref)) {
985 // If the dealloc conditions don't match, we need to make sure that the
986 // dealloc happens on the union of cases.
987 Value &newCond = newConditions[memrefToCondition[memref]];
988 if (newCond != cond)
989 newCond =
990 arith::OrIOp::create(rewriter, deallocOp.getLoc(), newCond, cond);
991 } else {
992 memrefToCondition.insert({memref, newConditions.size()});
993 newMemrefs.push_back(memref);
994 newConditions.push_back(cond);
995 }
996 }
997
998 // Return failure if we don't change anything such that we don't run into an
999 // infinite loop of pattern applications.
1000 return updateDeallocIfChanged(deallocOp, newMemrefs, newConditions,
1001 rewriter);
1002 }
1003};
1004
1005/// Remove duplicate values in the list of retained memrefs. We need to make
1006/// sure the corresponding result condition value is replaced properly.
1007/// Example:
1008/// ```mlir
1009/// %0:2 = bufferization.dealloc retain (%arg3, %arg3 : ...)
1010/// ```
1011/// is canonicalized to
1012/// ```mlir
1013/// %0 = bufferization.dealloc retain (%arg3 : memref<2xi32>)
1014/// ```
1015struct DeallocRemoveDuplicateRetainedMemrefs
1016 : public OpRewritePattern<DeallocOp> {
1017 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1018
1019 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1020 PatternRewriter &rewriter) const override {
1021 // Unique retained values
1023 SmallVector<Value> newRetained;
1024 SmallVector<unsigned> resultReplacementIdx;
1025 unsigned i = 0;
1026 for (auto retained : deallocOp.getRetained()) {
1027 if (seen.count(retained)) {
1028 resultReplacementIdx.push_back(seen[retained]);
1029 continue;
1030 }
1031
1032 seen[retained] = i;
1033 newRetained.push_back(retained);
1034 resultReplacementIdx.push_back(i++);
1035 }
1036
1037 // Return failure if we don't change anything such that we don't run into an
1038 // infinite loop of pattern applications.
1039 if (newRetained.size() == deallocOp.getRetained().size())
1040 return failure();
1041
1042 // We need to create a new op because the number of results is always the
1043 // same as the number of condition operands.
1044 auto newDeallocOp =
1045 DeallocOp::create(rewriter, deallocOp.getLoc(), deallocOp.getMemrefs(),
1046 deallocOp.getConditions(), newRetained);
1047 SmallVector<Value> replacements(
1048 llvm::map_range(resultReplacementIdx, [&](unsigned idx) {
1049 return newDeallocOp.getUpdatedConditions()[idx];
1050 }));
1051 rewriter.replaceOp(deallocOp, replacements);
1052 return success();
1053 }
1054};
1055
1056/// Erase deallocation operations where the variadic list of memrefs to
1057/// deallocate is empty. Example:
1058/// ```mlir
1059/// %0 = bufferization.dealloc retain (%arg0: memref<2xi32>)
1060/// ```
1061struct EraseEmptyDealloc : public OpRewritePattern<DeallocOp> {
1062 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1063
1064 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1065 PatternRewriter &rewriter) const override {
1066 if (deallocOp.getMemrefs().empty()) {
1067 Value constFalse = arith::ConstantOp::create(rewriter, deallocOp.getLoc(),
1068 rewriter.getBoolAttr(false));
1069 rewriter.replaceOp(
1070 deallocOp, SmallVector<Value>(deallocOp.getUpdatedConditions().size(),
1071 constFalse));
1072 return success();
1073 }
1074 return failure();
1075 }
1076};
1077
1078/// Removes memrefs from the deallocation list if their associated condition is
1079/// always 'false'.
1080///
1081/// Example:
1082/// ```
1083/// bufferization.dealloc (%arg0, %arg1 : memref<2xi32>, memref<2xi32>)
1084/// if (%arg2, %false)
1085/// ```
1086/// becomes
1087/// ```
1088/// bufferization.dealloc (%arg0 : memref<2xi32>) if (%arg2)
1089/// ```
1090struct EraseAlwaysFalseDealloc : public OpRewritePattern<DeallocOp> {
1091 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1092
1093 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1094 PatternRewriter &rewriter) const override {
1095 SmallVector<Value> newMemrefs, newConditions;
1096 for (auto [memref, cond] :
1097 llvm::zip(deallocOp.getMemrefs(), deallocOp.getConditions())) {
1098 if (!matchPattern(cond, m_Zero())) {
1099 newMemrefs.push_back(memref);
1100 newConditions.push_back(cond);
1101 }
1102 }
1103
1104 return updateDeallocIfChanged(deallocOp, newMemrefs, newConditions,
1105 rewriter);
1106 }
1107};
1108
1109/// The `memref.extract_strided_metadata` is often inserted to get the base
1110/// memref if the operand is not already guaranteed to be the result of a memref
1111/// allocation operation. This canonicalization pattern removes this extraction
1112/// operation if the operand is now produced by an allocation operation (e.g.,
1113/// due to other canonicalizations simplifying the IR).
1114///
1115/// Example:
1116/// ```mlir
1117/// %alloc = memref.alloc() : memref<2xi32>
1118/// %base_memref, %offset, %size, %stride = memref.extract_strided_metadata
1119/// %alloc : memref<2xi32> -> memref<i32>, index, index, index
1120/// bufferization.dealloc (%base_memref : memref<i32>) if (%cond)
1121/// ```
1122/// is canonicalized to
1123/// ```mlir
1124/// %alloc = memref.alloc() : memref<2xi32>
1125/// bufferization.dealloc (%alloc : memref<2xi32>) if (%cond)
1126/// ```
1127struct SkipExtractMetadataOfAlloc : public OpRewritePattern<DeallocOp> {
1128 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1129
1130 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1131 PatternRewriter &rewriter) const override {
1132 SmallVector<Value> newMemrefs(
1133 llvm::map_range(deallocOp.getMemrefs(), [&](Value memref) {
1134 auto extractStridedOp =
1135 memref.getDefiningOp<memref::ExtractStridedMetadataOp>();
1136 if (!extractStridedOp)
1137 return memref;
1138 Value allocMemref = extractStridedOp.getOperand();
1139 auto allocOp = allocMemref.getDefiningOp<MemoryEffectOpInterface>();
1140 if (!allocOp)
1141 return memref;
1142 if (allocOp.getEffectOnValue<MemoryEffects::Allocate>(allocMemref))
1143 return allocMemref;
1144 return memref;
1145 }));
1146
1147 return updateDeallocIfChanged(deallocOp, newMemrefs,
1148 deallocOp.getConditions(), rewriter);
1149 }
1150};
1151
1152/// Removes pairs of `bufferization.dealloc` and alloc operations if there is no
1153/// other user of the allocated value and the allocating operation can be safely
1154/// removed. If the same value is present multiple times, this pattern relies on
1155/// other canonicalization patterns to remove the duplicate first.
1156///
1157/// Example:
1158/// ```mlir
1159/// %alloc = memref.alloc() : memref<2xi32>
1160/// bufferization.dealloc (%alloc, %arg0, : ...) if (%true, %true)
1161/// ```
1162/// is canonicalized to
1163/// ```mlir
1164/// bufferization.dealloc (%arg0 : ...) if (%true)
1165/// ```
1166struct RemoveAllocDeallocPairWhenNoOtherUsers
1167 : public OpRewritePattern<DeallocOp> {
1168 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1169
1170 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1171 PatternRewriter &rewriter) const override {
1172 SmallVector<Value> newMemrefs, newConditions;
1173 SmallVector<Operation *> toDelete;
1174 for (auto [memref, cond] :
1175 llvm::zip(deallocOp.getMemrefs(), deallocOp.getConditions())) {
1176 if (auto allocOp = memref.getDefiningOp<MemoryEffectOpInterface>()) {
1177 // Check that it is indeed an allocate effect, that the op has no other
1178 // side effects (which would not allow us to remove the op), and that
1179 // there are no other users.
1180 if (allocOp.getEffectOnValue<MemoryEffects::Allocate>(memref) &&
1182 memref.hasOneUse()) {
1183 toDelete.push_back(allocOp);
1184 continue;
1185 }
1186 }
1187
1188 newMemrefs.push_back(memref);
1189 newConditions.push_back(cond);
1190 }
1191
1192 if (failed(updateDeallocIfChanged(deallocOp, newMemrefs, newConditions,
1193 rewriter)))
1194 return failure();
1195
1196 for (Operation *op : toDelete)
1197 rewriter.eraseOp(op);
1198
1199 return success();
1200 }
1201};
1202
1203} // anonymous namespace
1204
1205void DeallocOp::getCanonicalizationPatterns(RewritePatternSet &results,
1206 MLIRContext *context) {
1208}
1209
1211 RewritePatternSet &patterns, MLIRContext *context) {
1212 patterns.add<DeallocRemoveDuplicateDeallocMemrefs,
1213 DeallocRemoveDuplicateRetainedMemrefs, EraseEmptyDealloc,
1214 EraseAlwaysFalseDealloc, SkipExtractMetadataOfAlloc,
1215 RemoveAllocDeallocPairWhenNoOtherUsers>(context);
1216}
1217
1218//===----------------------------------------------------------------------===//
1219// TableGen'd op method definitions
1220//===----------------------------------------------------------------------===//
1221
1222#define GET_OP_CLASSES
1223#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.