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::verify() {
166 if (getCopy() && !getDynamicSizes().empty())
167 return emitError("dynamic sizes not needed when copying a tensor");
168 if (!getCopy() && failed(verifyDynamicDimensionCount(
169 getOperation(), getType(), getDynamicSizes())))
170 return failure();
171 if (getCopy() && getCopy().getType() != getType())
172 return emitError("expected that `copy` and return type match");
173 return success();
174}
175
176void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
177 RankedTensorType type, ValueRange dynamicSizes) {
178 build(builder, result, type, dynamicSizes, /*copy=*/Value(),
179 /*size_hint=*/Value(),
180 /*memory_space=*/IntegerAttr());
181}
182
183void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
184 RankedTensorType type, ValueRange dynamicSizes,
185 Value copy) {
186 build(builder, result, type, dynamicSizes, copy, /*size_hint=*/Value(),
187 /*memory_space=*/IntegerAttr());
188}
189
190void AllocTensorOp::build(OpBuilder &builder, OperationState &result,
191 TensorType type, ValueRange dynamicSizes, Value copy,
192 IntegerAttr memorySpace) {
193 build(builder, result, type, dynamicSizes, copy, /*size_hint=*/Value(),
194 memorySpace);
195}
196
197namespace {
198/// Change the type of the result of a `bufferization.alloc_tensor` by making
199/// the result type statically sized along dimension that in the original
200/// operation where defined as dynamic, but the size was defined using a
201/// `constant` op. For example:
202///
203/// %c5 = arith.constant 5: index
204/// %0 = bufferization.alloc_tensor(%arg0, %c5) : tensor<?x?xf32>
205///
206/// to
207///
208/// %0 = bufferization.alloc_tensor(%arg0) : tensor<?x5xf32>
209struct ReplaceStaticShapeDims : OpRewritePattern<AllocTensorOp> {
210 using OpRewritePattern<AllocTensorOp>::OpRewritePattern;
211
212 LogicalResult matchAndRewrite(AllocTensorOp op,
213 PatternRewriter &rewriter) const override {
214 if (op.getCopy())
215 return failure();
216 SmallVector<int64_t> newShape = llvm::to_vector(op.getType().getShape());
217 SmallVector<Value> newDynamicSizes;
218 unsigned int dynValCounter = 0;
219 for (int64_t i = 0; i < op.getType().getRank(); ++i) {
220 if (!op.isDynamicDim(i))
221 continue;
222 Value value = op.getDynamicSizes()[dynValCounter++];
223 APInt intVal;
224 if (matchPattern(value, m_ConstantInt(&intVal))) {
225 int64_t dim = intVal.getSExtValue();
226 if (dim >= 0)
227 newShape[i] = intVal.getSExtValue();
228 else
229 newDynamicSizes.push_back(value);
230 } else {
231 newDynamicSizes.push_back(value);
232 }
233 }
234 RankedTensorType newType = RankedTensorType::get(
235 newShape, op.getType().getElementType(), op.getType().getEncoding());
236 if (newType == op.getType())
237 return failure();
238 auto newOp = AllocTensorOp::create(rewriter, op.getLoc(), newType,
239 newDynamicSizes, /*copy=*/Value());
240 rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp);
241 return success();
242 }
243};
244
245struct FoldDimOfAllocTensorOp : public OpRewritePattern<tensor::DimOp> {
246 using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
247
248 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
249 PatternRewriter &rewriter) const override {
250 std::optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex();
251 auto allocTensorOp = dimOp.getSource().getDefiningOp<AllocTensorOp>();
252 if (!allocTensorOp || !maybeConstantIndex)
253 return failure();
254 if (*maybeConstantIndex < 0 ||
255 *maybeConstantIndex >= allocTensorOp.getType().getRank())
256 return failure();
257 if (!allocTensorOp.getType().isDynamicDim(*maybeConstantIndex))
258 return failure();
259 rewriter.replaceOp(
260 dimOp, allocTensorOp.getDynamicSize(rewriter, *maybeConstantIndex));
261 return success();
262 }
263};
264} // namespace
265
266void AllocTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
267 MLIRContext *ctx) {
268 results.add<FoldDimOfAllocTensorOp, ReplaceStaticShapeDims>(ctx);
269}
270
271LogicalResult AllocTensorOp::reifyResultShapes(
272 OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
273 auto shapes =
274 llvm::map_to_vector<4>(llvm::seq<int64_t>(0, getType().getRank()),
275 [&](int64_t dim) -> OpFoldResult {
276 if (isDynamicDim(dim))
277 return getDynamicSize(builder, dim);
278 return builder.getIndexAttr(getStaticSize(dim));
279 });
280 reifiedReturnShapes.emplace_back(std::move(shapes));
281 return success();
282}
283
284ParseResult AllocTensorOp::parse(OpAsmParser &parser, OperationState &result) {
286 if (parser.parseLParen() || parser.parseOperandList(dynamicSizesOperands) ||
287 parser.parseRParen())
288 return failure();
289 ParseResult copyKeyword = parser.parseOptionalKeyword("copy");
291 if (copyKeyword.succeeded())
292 if (parser.parseLParen() || parser.parseOperand(copyOperand) ||
293 parser.parseRParen())
294 return failure();
295 ParseResult sizeHintKeyword = parser.parseOptionalKeyword("size_hint");
296 OpAsmParser::UnresolvedOperand sizeHintOperand;
297 if (sizeHintKeyword.succeeded())
298 if (parser.parseEqual() || parser.parseOperand(sizeHintOperand))
299 return failure();
300
301 Attribute parsedProperties;
302 if (AllocTensorOp::genericParseProperties(parser, parsedProperties))
303 return failure();
304 auto propertyDictionary = dyn_cast_or_null<DictionaryAttr>(parsedProperties);
305 if (parsedProperties && !propertyDictionary)
306 return parser.emitError(parser.getNameLoc(),
307 "expected properties dictionary");
308
309 auto attrsLoc = parser.getCurrentLocation();
310 if (parser.parseOptionalAttrDict(result.attributes))
311 return failure();
312 for (StringRef attrName : AllocTensorOp::getAttributeNames()) {
313 if (result.attributes.get(attrName))
314 return parser.emitError(attrsLoc)
315 << "inherent attribute '" << attrName
316 << "' cannot be parsed from attr-dict when strict properties in "
317 "assembly format is enabled";
318 }
319 if (parser.parseColon())
320 return failure();
321
322 TensorType type;
323 if (parser.parseCustomTypeWithFallback(type))
324 return failure();
325 result.addTypes(type);
326
327 Type indexType = parser.getBuilder().getIndexType();
328 if (parser.resolveOperands(dynamicSizesOperands, indexType, result.operands))
329 return failure();
330 if (copyKeyword.succeeded())
331 if (parser.resolveOperand(copyOperand, type, result.operands))
332 return failure();
333 if (sizeHintKeyword.succeeded())
334 if (parser.resolveOperand(sizeHintOperand, indexType, result.operands))
335 return failure();
336 Builder &builder = parser.getBuilder();
337 NamedAttrList properties(propertyDictionary ? propertyDictionary
338 : builder.getDictionaryAttr({}));
339 properties.set(AllocTensorOp::getOperandSegmentSizeAttr(),
340 builder.getDenseI32ArrayAttr(
341 {static_cast<int32_t>(dynamicSizesOperands.size()),
342 static_cast<int32_t>(copyKeyword.succeeded()),
343 static_cast<int32_t>(sizeHintKeyword.succeeded())}));
344 propertyDictionary = properties.getDictionary(builder.getContext());
345 auto emitError = [&]() {
346 return mlir::emitError(result.location, "invalid properties ")
347 << propertyDictionary << " for op " << result.name.getStringRef()
348 << ": ";
349 };
350 if (failed(AllocTensorOp::setPropertiesFromParsedAttr(
351 result.getOrAddProperties<Properties>(), propertyDictionary,
352 emitError)))
353 return failure();
354 return success();
355}
356
357void AllocTensorOp::print(OpAsmPrinter &p) {
358 p << "(" << getDynamicSizes() << ")";
359 if (getCopy())
360 p << " copy(" << getCopy() << ")";
361 if (getSizeHint())
362 p << " size_hint=" << getSizeHint();
363 AllocTensorOp::printProperties(getContext(), p, getProperties(),
364 /*elidedProps=*/getOperandSegmentSizeAttr());
365 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
366 p << " : ";
367 auto type = getResult().getType();
368 if (auto validType = llvm::dyn_cast<::mlir::TensorType>(type))
369 p.printStrippedAttrOrType(validType);
370 else
371 p << type;
372}
373
374Value AllocTensorOp::getDynamicSize(OpBuilder &b, unsigned idx) {
375 assert(isDynamicDim(idx) && "expected dynamic dim");
376 if (getCopy())
377 return tensor::DimOp::create(b, getLoc(), getCopy(), idx);
378 return getOperand(getIndexOfDynamicSize(idx));
379}
380
381//===----------------------------------------------------------------------===//
382// CloneOp
383//===----------------------------------------------------------------------===//
384
385OpFoldResult CloneOp::fold(FoldAdaptor adaptor) {
386 return succeeded(memref::foldMemRefCast(*this)) ? getResult() : Value();
387}
388
389namespace {
390
391/// Merge the clone and its source (by converting the clone to a cast) when
392/// possible.
393struct SimplifyClones : public OpRewritePattern<CloneOp> {
394 using OpRewritePattern<CloneOp>::OpRewritePattern;
395
396 LogicalResult matchAndRewrite(CloneOp cloneOp,
397 PatternRewriter &rewriter) const override {
398 if (cloneOp.use_empty()) {
399 rewriter.eraseOp(cloneOp);
400 return success();
401 }
402
403 Value source = cloneOp.getInput();
404 if (source.getType() != cloneOp.getType() &&
405 !memref::CastOp::areCastCompatible({source.getType()},
406 {cloneOp.getType()}))
407 return failure();
408
409 // Aims to find the dealloc op for the canonical source
410 // which otherwise could prevent removal of unnecessary allocs.
411 Value canonicalSource = source;
412 while (auto iface = dyn_cast_or_null<ViewLikeOpInterface>(
413 canonicalSource.getDefiningOp())) {
414 if (canonicalSource != iface.getViewDest()) {
415 break;
416 }
417 canonicalSource = iface.getViewSource();
418 }
419
420 std::optional<Operation *> maybeCloneDeallocOp =
421 memref::findDealloc(cloneOp.getOutput());
422 // Skip if either of them has > 1 deallocate operations.
423 if (!maybeCloneDeallocOp.has_value())
424 return failure();
425 std::optional<Operation *> maybeSourceDeallocOp =
426 memref::findDealloc(canonicalSource);
427 if (!maybeSourceDeallocOp.has_value())
428 return failure();
429 Operation *cloneDeallocOp = *maybeCloneDeallocOp;
430 Operation *sourceDeallocOp = *maybeSourceDeallocOp;
431
432 // If both are deallocated in the same block, their in-block lifetimes
433 // might not fully overlap, so we cannot decide which one to drop.
434 if (cloneDeallocOp && sourceDeallocOp &&
435 cloneDeallocOp->getBlock() == sourceDeallocOp->getBlock())
436 return failure();
437
438 Block *currentBlock = cloneOp->getBlock();
439 Operation *redundantDealloc = nullptr;
440 if (cloneDeallocOp && cloneDeallocOp->getBlock() == currentBlock) {
441 redundantDealloc = cloneDeallocOp;
442 } else if (sourceDeallocOp && sourceDeallocOp->getBlock() == currentBlock) {
443 redundantDealloc = sourceDeallocOp;
444 }
445
446 if (!redundantDealloc)
447 return failure();
448
449 // Safety check that there are no other deallocations inbetween
450 // cloneOp and redundantDealloc, as otherwise we might deallocate an alias
451 // of source before the uses of the clone. With alias information, we could
452 // restrict this to only fail of the dealloc's operand is an alias
453 // of the source.
454 for (Operation *pos = cloneOp->getNextNode(); pos != redundantDealloc;
455 pos = pos->getNextNode()) {
456 // Bail if we run out of operations while looking for a deallocation op.
457 if (!pos)
458 return failure();
459 auto effectInterface = dyn_cast<MemoryEffectOpInterface>(pos);
460 if (!effectInterface)
461 continue;
462 if (effectInterface.hasEffect<MemoryEffects::Free>())
463 return failure();
464 }
465
466 if (source.getType() != cloneOp.getType())
467 source = memref::CastOp::create(rewriter, cloneOp.getLoc(),
468 cloneOp.getType(), source);
469 rewriter.replaceOp(cloneOp, source);
470 rewriter.eraseOp(redundantDealloc);
471 return success();
472 }
473};
474
475} // namespace
476
477void CloneOp::getCanonicalizationPatterns(RewritePatternSet &results,
478 MLIRContext *context) {
479 results.add<SimplifyClones>(context);
480}
481
482//===----------------------------------------------------------------------===//
483// MaterializeInDestinationOp
484//===----------------------------------------------------------------------===//
485
486LogicalResult MaterializeInDestinationOp::reifyResultShapes(
487 OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
488 if (getOperation()->getNumResults() == 1) {
489 assert(isa<TensorType>(getDest().getType()) && "expected tensor type");
490 reifiedReturnShapes.resize(1,
492 reifiedReturnShapes[0] =
493 tensor::getMixedSizes(builder, getLoc(), getDest());
494 }
495 return success();
496}
497
498Value MaterializeInDestinationOp::buildSubsetExtraction(OpBuilder &builder,
499 Location loc) {
500 if (isa<TensorType>(getDest().getType())) {
501 // The subset is the entire destination tensor.
502 return getDest();
503 }
504
505 // The "restrict" attribute is transferred from this op to the newly created
506 // to_tensor op. If this op does not the "restrict" attribute, the subset
507 // extraction cannot be built because there is no guarantee that there is no
508 // pre-existing "restrict" to_tensor op with the same/an aliasing destination.
509 if (!getRestrict())
510 return {};
511
512 // Build a bufferization.to_tensor op.
513 assert(isa<BaseMemRefType>(getDest().getType()) && "expected memref type");
514 assert(getRestrict() &&
515 "expected that ops with memrefs dest have 'restrict'");
516 setRestrict(false);
517 return ToTensorOp::create(
518 builder, loc, memref::getTensorTypeFromMemRefType(getDest().getType()),
519 getDest(),
520 /*restrict=*/true, getWritable());
521}
522
523bool MaterializeInDestinationOp::isEquivalentSubset(
524 Value candidate, function_ref<bool(Value, Value)> equivalenceFn) {
525 return equivalenceFn(getDest(), candidate);
526}
527
529MaterializeInDestinationOp::getValuesNeededToBuildSubsetExtraction() {
530 return {getDest()};
531}
532
533OpOperand &MaterializeInDestinationOp::getSourceOperand() {
534 return getOperation()->getOpOperand(0) /*source*/;
535}
536
537bool MaterializeInDestinationOp::operatesOnEquivalentSubset(
538 SubsetOpInterface subsetOp,
539 function_ref<bool(Value, Value)> equivalenceFn) {
540 return false;
541}
542
543bool MaterializeInDestinationOp::operatesOnDisjointSubset(
544 SubsetOpInterface subsetOp,
545 function_ref<bool(Value, Value)> equivalenceFn) {
546 return false;
547}
548
549LogicalResult MaterializeInDestinationOp::verify() {
550 if (!isa<TensorType, BaseMemRefType>(getDest().getType()))
551 return emitOpError("'dest' must be a tensor or a memref");
552 if (auto destType = dyn_cast<TensorType>(getDest().getType())) {
553 if (getOperation()->getNumResults() != 1)
554 return emitOpError("tensor 'dest' implies exactly one tensor result");
555 if (destType != getResult().getType())
556 return emitOpError("result and 'dest' types must match");
557 }
558 if (isa<BaseMemRefType>(getDest().getType()) &&
559 getOperation()->getNumResults() != 0)
560 return emitOpError("memref 'dest' implies zero results");
561 if (getRestrict() && !isa<BaseMemRefType>(getDest().getType()))
562 return emitOpError("'restrict' is valid only for memref destinations");
563 if (getWritable() != isa<BaseMemRefType>(getDest().getType()))
564 return emitOpError("'writable' must be specified if and only if the "
565 "destination is of memref type");
566 TensorType srcType = getSource().getType();
567 ShapedType destType = cast<ShapedType>(getDest().getType());
568 if (srcType.hasRank() != destType.hasRank())
569 return emitOpError("source/destination shapes are incompatible");
570 if (srcType.hasRank()) {
571 if (failed(verifyRanksMatch(getOperation(), srcType, destType, "source",
572 "destination")))
573 return failure();
574 for (auto [src, dest] :
575 llvm::zip(srcType.getShape(), destType.getShape())) {
576 if (src == ShapedType::kDynamic || dest == ShapedType::kDynamic) {
577 // Cannot verify dynamic dimension size. Assume that that they match at
578 // runtime.
579 continue;
580 }
581 if (src != dest)
582 return emitOpError("source/destination shapes are incompatible");
583 }
584 }
585 return success();
586}
587
588void MaterializeInDestinationOp::build(OpBuilder &builder,
589 OperationState &state, Value source,
590 Value dest) {
591 auto destTensorType = dyn_cast<TensorType>(dest.getType());
592 build(builder, state, /*result=*/destTensorType ? destTensorType : Type(),
593 source, dest);
594}
595
596MutableOperandRange MaterializeInDestinationOp::getDpsInitsMutable() {
597 return getDestMutable();
598}
599
600void MaterializeInDestinationOp::getEffects(
602 &effects) {
603 if (isa<BaseMemRefType>(getDest().getType()))
604 effects.emplace_back(MemoryEffects::Write::get(), &getDestMutable(),
606}
607
608//===----------------------------------------------------------------------===//
609// ToTensorOp
610//===----------------------------------------------------------------------===//
611
612OpFoldResult ToTensorOp::fold(FoldAdaptor) {
613 if (auto toBuffer = getBuffer().getDefiningOp<ToBufferOp>())
614 // Approximate alias analysis by conservatively folding only when no there
615 // is no interleaved operation.
616 if (toBuffer->getBlock() == this->getOperation()->getBlock() &&
617 toBuffer->getNextNode() == this->getOperation())
618 return toBuffer.getTensor();
619 return {};
620}
621
622namespace {
623struct DimOfToTensorFolder : public OpRewritePattern<tensor::DimOp> {
624 using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
625
626 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
627 PatternRewriter &rewriter) const override {
628 auto memrefToTensorOp = dimOp.getSource().getDefiningOp<ToTensorOp>();
629 if (!memrefToTensorOp)
630 return failure();
631
632 rewriter.replaceOpWithNewOp<memref::DimOp>(
633 dimOp, memrefToTensorOp.getBuffer(), dimOp.getIndex());
634 return success();
635 }
636};
637} // namespace
638
639void ToTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
640 MLIRContext *context) {
641 results.add<DimOfToTensorFolder>(context);
642}
643
644//===----------------------------------------------------------------------===//
645// ToBufferOp
646//===----------------------------------------------------------------------===//
647
648OpFoldResult ToBufferOp::fold(FoldAdaptor) {
649 if (auto memrefToTensor = getTensor().getDefiningOp<ToTensorOp>())
650 if (memrefToTensor.getBuffer().getType() == getType())
651 return memrefToTensor.getBuffer();
652 return {};
653}
654
655namespace {
656
657/// Replace tensor.cast + to_buffer by to_buffer + memref.cast.
658struct ToBufferOfCast : public OpRewritePattern<ToBufferOp> {
659 using OpRewritePattern<ToBufferOp>::OpRewritePattern;
660
661 LogicalResult matchAndRewrite(ToBufferOp toBuffer,
662 PatternRewriter &rewriter) const final {
663 auto tensorCastOperand =
664 toBuffer.getOperand().getDefiningOp<tensor::CastOp>();
665 if (!tensorCastOperand)
666 return failure();
667 auto srcTensorType = llvm::dyn_cast<RankedTensorType>(
668 tensorCastOperand.getOperand().getType());
669 if (!srcTensorType)
670 return failure();
671 auto currentOutputMemRefType =
672 dyn_cast<BaseMemRefType>(toBuffer.getResult().getType());
673 if (!currentOutputMemRefType)
674 return failure();
675
676 auto memrefType = currentOutputMemRefType.cloneWith(
677 srcTensorType.getShape(), srcTensorType.getElementType());
678 Value memref = ToBufferOp::create(rewriter, toBuffer.getLoc(), memrefType,
679 tensorCastOperand.getOperand(),
680 toBuffer.getReadOnly());
681 rewriter.replaceOpWithNewOp<memref::CastOp>(toBuffer, toBuffer.getType(),
682 memref);
683 return success();
684 }
685};
686
687/// Canonicalize bufferization.to_tensor + bufferization.to_buffer. Insert a
688/// cast if necessary.
689struct ToBufferToTensorFolding : public OpRewritePattern<ToBufferOp> {
690 using OpRewritePattern<ToBufferOp>::OpRewritePattern;
691
692 LogicalResult matchAndRewrite(ToBufferOp toBuffer,
693 PatternRewriter &rewriter) const final {
694 BufferizationOptions options;
695 options.bufferAlignment = 0;
696 return foldToBufferToTensorPair(rewriter, toBuffer, options);
697 }
698};
699
700/// Fold a load on a to_buffer operation into an tensor.extract on the
701/// corresponding tensor.
702struct LoadOfToBuffer : public OpRewritePattern<memref::LoadOp> {
703 using OpRewritePattern<memref::LoadOp>::OpRewritePattern;
704
705 LogicalResult matchAndRewrite(memref::LoadOp load,
706 PatternRewriter &rewriter) const override {
707 auto toBuffer = load.getMemref().getDefiningOp<ToBufferOp>();
708 if (!toBuffer || !toBuffer.getReadOnly())
709 return failure();
710
711 rewriter.replaceOpWithNewOp<tensor::ExtractOp>(load, toBuffer.getTensor(),
712 load.getIndices());
713 return success();
714 }
715};
716
717/// Fold dim of a to_buffer into the dim of the tensor.
718struct DimOfCastOp : public OpRewritePattern<memref::DimOp> {
719 using OpRewritePattern<memref::DimOp>::OpRewritePattern;
720
721 LogicalResult matchAndRewrite(memref::DimOp dimOp,
722 PatternRewriter &rewriter) const override {
723 auto castOp = dimOp.getSource().getDefiningOp<ToBufferOp>();
724 if (!castOp)
725 return failure();
726 Value newSource = castOp.getOperand();
727 rewriter.replaceOpWithNewOp<tensor::DimOp>(dimOp, newSource,
728 dimOp.getIndex());
729 return success();
730 }
731};
732
733} // namespace
734
735void ToBufferOp::getCanonicalizationPatterns(RewritePatternSet &results,
736 MLIRContext *context) {
737 results.add<DimOfCastOp, LoadOfToBuffer, ToBufferOfCast,
738 ToBufferToTensorFolding>(context);
739}
740
741std::optional<Operation *> CloneOp::buildDealloc(OpBuilder &builder,
742 Value alloc) {
743 return memref::DeallocOp::create(builder, alloc.getLoc(), alloc)
744 .getOperation();
745}
746
747std::optional<Value> CloneOp::buildClone(OpBuilder &builder, Value alloc) {
748 return CloneOp::create(builder, alloc.getLoc(), alloc).getResult();
749}
750
751//===----------------------------------------------------------------------===//
752// DeallocOp
753//===----------------------------------------------------------------------===//
754
755LogicalResult DeallocOp::inferReturnTypes(
756 MLIRContext *context, std::optional<::mlir::Location> location,
757 ValueRange operands, DictionaryAttr attributes, PropertyRef properties,
758 RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
759 DeallocOpAdaptor adaptor(operands, attributes, properties, regions);
760 inferredReturnTypes = SmallVector<Type>(adaptor.getRetained().size(),
761 IntegerType::get(context, 1));
762 return success();
763}
764
765LogicalResult DeallocOp::verify() {
766 if (getMemrefs().size() != getConditions().size())
767 return emitOpError(
768 "must have the same number of conditions as memrefs to deallocate");
769 if (getRetained().size() != getUpdatedConditions().size())
770 return emitOpError("must have the same number of updated conditions "
771 "(results) as retained operands");
772 return success();
773}
774
775static LogicalResult updateDeallocIfChanged(DeallocOp deallocOp,
776 ValueRange memrefs,
777 ValueRange conditions,
778 PatternRewriter &rewriter) {
779 if (deallocOp.getMemrefs() == memrefs &&
780 deallocOp.getConditions() == conditions)
781 return failure();
782
783 rewriter.modifyOpInPlace(deallocOp, [&]() {
784 deallocOp.getMemrefsMutable().assign(memrefs);
785 deallocOp.getConditionsMutable().assign(conditions);
786 });
787 return success();
788}
789
790namespace {
791
792/// Remove duplicate values in the list of memrefs to be deallocated. We need to
793/// make sure the corresponding condition value is updated accordingly since
794/// their two conditions might not cover the same set of cases. In that case, we
795/// have to combine them (by computing the disjunction of them).
796/// Example:
797/// ```mlir
798/// bufferization.dealloc (%arg0, %arg0 : ...) if (%arg1, %arg2)
799/// ```
800/// is canonicalized to
801/// ```mlir
802/// %0 = arith.ori %arg1, %arg2 : i1
803/// bufferization.dealloc (%arg0 : memref<2xi32>) if (%0)
804/// ```
805struct DeallocRemoveDuplicateDeallocMemrefs
806 : public OpRewritePattern<DeallocOp> {
807 using OpRewritePattern<DeallocOp>::OpRewritePattern;
808
809 LogicalResult matchAndRewrite(DeallocOp deallocOp,
810 PatternRewriter &rewriter) const override {
811 // Unique memrefs to be deallocated.
812 DenseMap<Value, unsigned> memrefToCondition;
813 SmallVector<Value> newMemrefs, newConditions;
814 for (auto [i, memref, cond] :
815 llvm::enumerate(deallocOp.getMemrefs(), deallocOp.getConditions())) {
816 if (memrefToCondition.count(memref)) {
817 // If the dealloc conditions don't match, we need to make sure that the
818 // dealloc happens on the union of cases.
819 Value &newCond = newConditions[memrefToCondition[memref]];
820 if (newCond != cond)
821 newCond =
822 arith::OrIOp::create(rewriter, deallocOp.getLoc(), newCond, cond);
823 } else {
824 memrefToCondition.insert({memref, newConditions.size()});
825 newMemrefs.push_back(memref);
826 newConditions.push_back(cond);
827 }
828 }
829
830 // Return failure if we don't change anything such that we don't run into an
831 // infinite loop of pattern applications.
832 return updateDeallocIfChanged(deallocOp, newMemrefs, newConditions,
833 rewriter);
834 }
835};
836
837/// Remove duplicate values in the list of retained memrefs. We need to make
838/// sure the corresponding result condition value is replaced properly.
839/// Example:
840/// ```mlir
841/// %0:2 = bufferization.dealloc retain (%arg3, %arg3 : ...)
842/// ```
843/// is canonicalized to
844/// ```mlir
845/// %0 = bufferization.dealloc retain (%arg3 : memref<2xi32>)
846/// ```
847struct DeallocRemoveDuplicateRetainedMemrefs
848 : public OpRewritePattern<DeallocOp> {
849 using OpRewritePattern<DeallocOp>::OpRewritePattern;
850
851 LogicalResult matchAndRewrite(DeallocOp deallocOp,
852 PatternRewriter &rewriter) const override {
853 // Unique retained values
855 SmallVector<Value> newRetained;
856 SmallVector<unsigned> resultReplacementIdx;
857 unsigned i = 0;
858 for (auto retained : deallocOp.getRetained()) {
859 if (seen.count(retained)) {
860 resultReplacementIdx.push_back(seen[retained]);
861 continue;
862 }
863
864 seen[retained] = i;
865 newRetained.push_back(retained);
866 resultReplacementIdx.push_back(i++);
867 }
868
869 // Return failure if we don't change anything such that we don't run into an
870 // infinite loop of pattern applications.
871 if (newRetained.size() == deallocOp.getRetained().size())
872 return failure();
873
874 // We need to create a new op because the number of results is always the
875 // same as the number of condition operands.
876 auto newDeallocOp =
877 DeallocOp::create(rewriter, deallocOp.getLoc(), deallocOp.getMemrefs(),
878 deallocOp.getConditions(), newRetained);
879 SmallVector<Value> replacements(
880 llvm::map_range(resultReplacementIdx, [&](unsigned idx) {
881 return newDeallocOp.getUpdatedConditions()[idx];
882 }));
883 rewriter.replaceOp(deallocOp, replacements);
884 return success();
885 }
886};
887
888/// Erase deallocation operations where the variadic list of memrefs to
889/// deallocate is empty. Example:
890/// ```mlir
891/// %0 = bufferization.dealloc retain (%arg0: memref<2xi32>)
892/// ```
893struct EraseEmptyDealloc : public OpRewritePattern<DeallocOp> {
894 using OpRewritePattern<DeallocOp>::OpRewritePattern;
895
896 LogicalResult matchAndRewrite(DeallocOp deallocOp,
897 PatternRewriter &rewriter) const override {
898 if (deallocOp.getMemrefs().empty()) {
899 Value constFalse = arith::ConstantOp::create(rewriter, deallocOp.getLoc(),
900 rewriter.getBoolAttr(false));
901 rewriter.replaceOp(
902 deallocOp, SmallVector<Value>(deallocOp.getUpdatedConditions().size(),
903 constFalse));
904 return success();
905 }
906 return failure();
907 }
908};
909
910/// Removes memrefs from the deallocation list if their associated condition is
911/// always 'false'.
912///
913/// Example:
914/// ```
915/// bufferization.dealloc (%arg0, %arg1 : memref<2xi32>, memref<2xi32>)
916/// if (%arg2, %false)
917/// ```
918/// becomes
919/// ```
920/// bufferization.dealloc (%arg0 : memref<2xi32>) if (%arg2)
921/// ```
922struct EraseAlwaysFalseDealloc : public OpRewritePattern<DeallocOp> {
923 using OpRewritePattern<DeallocOp>::OpRewritePattern;
924
925 LogicalResult matchAndRewrite(DeallocOp deallocOp,
926 PatternRewriter &rewriter) const override {
927 SmallVector<Value> newMemrefs, newConditions;
928 for (auto [memref, cond] :
929 llvm::zip(deallocOp.getMemrefs(), deallocOp.getConditions())) {
930 if (!matchPattern(cond, m_Zero())) {
931 newMemrefs.push_back(memref);
932 newConditions.push_back(cond);
933 }
934 }
935
936 return updateDeallocIfChanged(deallocOp, newMemrefs, newConditions,
937 rewriter);
938 }
939};
940
941/// The `memref.extract_strided_metadata` is often inserted to get the base
942/// memref if the operand is not already guaranteed to be the result of a memref
943/// allocation operation. This canonicalization pattern removes this extraction
944/// operation if the operand is now produced by an allocation operation (e.g.,
945/// due to other canonicalizations simplifying the IR).
946///
947/// Example:
948/// ```mlir
949/// %alloc = memref.alloc() : memref<2xi32>
950/// %base_memref, %offset, %size, %stride = memref.extract_strided_metadata
951/// %alloc : memref<2xi32> -> memref<i32>, index, index, index
952/// bufferization.dealloc (%base_memref : memref<i32>) if (%cond)
953/// ```
954/// is canonicalized to
955/// ```mlir
956/// %alloc = memref.alloc() : memref<2xi32>
957/// bufferization.dealloc (%alloc : memref<2xi32>) if (%cond)
958/// ```
959struct SkipExtractMetadataOfAlloc : public OpRewritePattern<DeallocOp> {
960 using OpRewritePattern<DeallocOp>::OpRewritePattern;
961
962 LogicalResult matchAndRewrite(DeallocOp deallocOp,
963 PatternRewriter &rewriter) const override {
964 SmallVector<Value> newMemrefs(
965 llvm::map_range(deallocOp.getMemrefs(), [&](Value memref) {
966 auto extractStridedOp =
967 memref.getDefiningOp<memref::ExtractStridedMetadataOp>();
968 if (!extractStridedOp)
969 return memref;
970 Value allocMemref = extractStridedOp.getOperand();
971 auto allocOp = allocMemref.getDefiningOp<MemoryEffectOpInterface>();
972 if (!allocOp)
973 return memref;
974 if (allocOp.getEffectOnValue<MemoryEffects::Allocate>(allocMemref))
975 return allocMemref;
976 return memref;
977 }));
978
979 return updateDeallocIfChanged(deallocOp, newMemrefs,
980 deallocOp.getConditions(), rewriter);
981 }
982};
983
984/// Removes pairs of `bufferization.dealloc` and alloc operations if there is no
985/// other user of the allocated value and the allocating operation can be safely
986/// removed. If the same value is present multiple times, this pattern relies on
987/// other canonicalization patterns to remove the duplicate first.
988///
989/// Example:
990/// ```mlir
991/// %alloc = memref.alloc() : memref<2xi32>
992/// bufferization.dealloc (%alloc, %arg0, : ...) if (%true, %true)
993/// ```
994/// is canonicalized to
995/// ```mlir
996/// bufferization.dealloc (%arg0 : ...) if (%true)
997/// ```
998struct RemoveAllocDeallocPairWhenNoOtherUsers
999 : public OpRewritePattern<DeallocOp> {
1000 using OpRewritePattern<DeallocOp>::OpRewritePattern;
1001
1002 LogicalResult matchAndRewrite(DeallocOp deallocOp,
1003 PatternRewriter &rewriter) const override {
1004 SmallVector<Value> newMemrefs, newConditions;
1005 SmallVector<Operation *> toDelete;
1006 for (auto [memref, cond] :
1007 llvm::zip(deallocOp.getMemrefs(), deallocOp.getConditions())) {
1008 if (auto allocOp = memref.getDefiningOp<MemoryEffectOpInterface>()) {
1009 // Check that it is indeed an allocate effect, that the op has no other
1010 // side effects (which would not allow us to remove the op), and that
1011 // there are no other users.
1012 if (allocOp.getEffectOnValue<MemoryEffects::Allocate>(memref) &&
1014 memref.hasOneUse()) {
1015 toDelete.push_back(allocOp);
1016 continue;
1017 }
1018 }
1019
1020 newMemrefs.push_back(memref);
1021 newConditions.push_back(cond);
1022 }
1023
1024 if (failed(updateDeallocIfChanged(deallocOp, newMemrefs, newConditions,
1025 rewriter)))
1026 return failure();
1027
1028 for (Operation *op : toDelete)
1029 rewriter.eraseOp(op);
1030
1031 return success();
1032 }
1033};
1034
1035} // anonymous namespace
1036
1037void DeallocOp::getCanonicalizationPatterns(RewritePatternSet &results,
1038 MLIRContext *context) {
1040}
1041
1043 RewritePatternSet &patterns, MLIRContext *context) {
1044 patterns.add<DeallocRemoveDuplicateDeallocMemrefs,
1045 DeallocRemoveDuplicateRetainedMemrefs, EraseEmptyDealloc,
1046 EraseAlwaysFalseDealloc, SkipExtractMetadataOfAlloc,
1047 RemoveAllocDeallocPairWhenNoOtherUsers>(context);
1048}
1049
1050//===----------------------------------------------------------------------===//
1051// TableGen'd op method definitions
1052//===----------------------------------------------------------------------===//
1053
1054#define GET_OP_CLASSES
1055#include "mlir/Dialect/Bufferization/IR/BufferizationOps.cpp.inc"
return success()
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.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
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...
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 InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
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 SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseColon()=0
Parse a : token.
virtual SMLoc getNameLoc() const =0
Return the location of the original name 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
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:116
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:171
BoolAttr getBoolAttr(bool value)
Definition Builders.cpp:108
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:59
DictionaryAttr getDictionaryAttr(ArrayRef< NamedAttribute > value)
Definition Builders.cpp:112
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
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
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.
This class helps build Operations.
Definition Builders.h:210
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Definition Value.h:254
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:378
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:732
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Definition TensorOps.cpp:91
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:310
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.