MLIR 23.0.0git
BufferizableOpInterface.cpp
Go to the documentation of this file.
1//===- BufferizableOpInterface.cpp - Bufferizable Ops ---=----------------===//
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
15#include "mlir/IR/AsmState.h"
16#include "mlir/IR/Operation.h"
18#include "mlir/IR/Value.h"
20#include "llvm/ADT/ScopeExit.h"
21#include "llvm/ADT/SmallVectorExtras.h"
22
23//===----------------------------------------------------------------------===//
24// BufferizableOpInterface
25//===----------------------------------------------------------------------===//
26
27namespace mlir {
28namespace bufferization {
29
30#include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.cpp.inc"
31
32} // namespace bufferization
33} // namespace mlir
34
35MLIR_DEFINE_EXPLICIT_TYPE_ID(mlir::bufferization::AnalysisState)
36
37#define DEBUG_TYPE "bufferizable-op-interface"
38
39using namespace mlir;
40using namespace bufferization;
41
42static bool isRepetitiveRegion(Region *region,
44 Operation *op = region->getParentOp();
45 if (auto bufferizableOp = options.dynCastBufferizableOp(op))
46 if (bufferizableOp.isRepetitiveRegion(region->getRegionNumber()))
47 return true;
48 return false;
49}
50
51Region *AnalysisState::getEnclosingRepetitiveRegion(
53 if (!op->getBlock())
54 return nullptr;
55 if (auto iter = enclosingRepetitiveRegionCache.find_as(op);
56 iter != enclosingRepetitiveRegionCache.end())
57 return iter->second;
58 return enclosingRepetitiveRegionCache[op] =
60}
61
62Region *AnalysisState::getEnclosingRepetitiveRegion(
63 Value value, const BufferizationOptions &options) {
64 if (auto iter = enclosingRepetitiveRegionCache.find_as(value);
65 iter != enclosingRepetitiveRegionCache.end())
66 return iter->second;
67
68 Region *region = value.getParentRegion();
69 // Collect all visited regions since we only know the repetitive region we
70 // want to map it to later on
71 SmallVector<Region *> visitedRegions;
72 while (region) {
73 visitedRegions.push_back(region);
74 if (isRepetitiveRegion(region, options))
75 break;
76 region = region->getParentRegion();
77 }
78 enclosingRepetitiveRegionCache[value] = region;
79 for (Region *r : visitedRegions)
80 enclosingRepetitiveRegionCache[r] = region;
81 return region;
82}
83
84Region *AnalysisState::getEnclosingRepetitiveRegion(
85 Block *block, const BufferizationOptions &options) {
86 if (auto iter = enclosingRepetitiveRegionCache.find_as(block);
87 iter != enclosingRepetitiveRegionCache.end())
88 return iter->second;
89
90 Region *region = block->getParent();
91 Operation *op = nullptr;
92 // Collect all visited regions since we only know the repetitive region we
93 // want to map it to later on
94 SmallVector<Region *> visitedRegions;
95 do {
96 op = region->getParentOp();
97 if (isRepetitiveRegion(region, options))
98 break;
99 } while ((region = op->getParentRegion()));
100
101 enclosingRepetitiveRegionCache[block] = region;
102 for (Region *r : visitedRegions)
103 enclosingRepetitiveRegionCache[r] = region;
104 return region;
105}
106
107bool AnalysisState::insideMutuallyExclusiveRegions(Operation *op0,
108 Operation *op1) {
109 auto key = std::make_pair(op0, op1);
110 if (auto iter = insideMutuallyExclusiveRegionsCache.find(key);
111 iter != insideMutuallyExclusiveRegionsCache.end())
112 return iter->second;
114 // Populate results for both orderings of the ops.
115 insideMutuallyExclusiveRegionsCache[key] = result;
116 insideMutuallyExclusiveRegionsCache[std::make_pair(op1, op0)] = result;
117 return result;
118}
119
120void AnalysisState::resetCache() {
121 enclosingRepetitiveRegionCache.clear();
122 insideMutuallyExclusiveRegionsCache.clear();
123}
124
125SymbolTableCollection &BufferizationState::getSymbolTables() {
126 return symbolTables;
127}
128
129SymbolTableCollection &BufferizationState::getSymbolTables() const {
130 return symbolTables;
131}
132
133Region *bufferization::getNextEnclosingRepetitiveRegion(
134 Region *region, const BufferizationOptions &options) {
135 assert(isRepetitiveRegion(region, options) && "expected repetitive region");
136 while ((region = region->getParentRegion())) {
137 if (isRepetitiveRegion(region, options))
138 break;
139 }
140 return region;
141}
142
143Region *bufferization::getParallelRegion(Region *region,
144 const BufferizationOptions &options) {
145 while (region) {
146 auto bufferizableOp = options.dynCastBufferizableOp(region->getParentOp());
147 if (bufferizableOp &&
148 bufferizableOp.isParallelRegion(region->getRegionNumber())) {
149 assert(isRepetitiveRegion(region, options) &&
150 "expected that all parallel regions are also repetitive regions");
151 return region;
152 }
153 region = region->getParentRegion();
154 }
155 return nullptr;
156}
157
158Operation *bufferization::getOwnerOfValue(Value value) {
159 if (auto opResult = llvm::dyn_cast<OpResult>(value))
160 return opResult.getDefiningOp();
161 return llvm::cast<BlockArgument>(value).getOwner()->getParentOp();
162}
163
164// TODO: Properly support with options, for now it is hardcoded to builtin
165// Tensor/MemRef types based approach
166/// Create an AllocTensorOp for the given shaped value. If `copy` is set, the
167/// shaped value is copied. Otherwise, a tensor with undefined contents is
168/// allocated.
169FailureOr<Value> bufferization::allocateTensorForShapedValue(
170 OpBuilder &b, Location loc, Value shapedValue,
171 const BufferizationOptions &options, const BufferizationState &state,
172 bool copy) {
174 if (llvm::isa<RankedTensorType>(shapedValue.getType())) {
175 tensor = shapedValue;
176 } else if (llvm::isa<MemRefType>(shapedValue.getType())) {
177 tensor = ToTensorOp::create(
178 b, loc, memref::getTensorTypeFromMemRefType(shapedValue.getType()),
179 shapedValue);
180 } else if (llvm::isa<UnrankedTensorType>(shapedValue.getType()) ||
181 llvm::isa<UnrankedMemRefType>(shapedValue.getType())) {
182 return getOwnerOfValue(shapedValue)
183 ->emitError("copying of unranked tensors is not implemented");
184 } else {
185 llvm_unreachable("expected RankedTensorType or MemRefType");
186 }
187 RankedTensorType tensorType = llvm::cast<RankedTensorType>(tensor.getType());
188 SmallVector<Value> dynamicSizes;
189 if (!copy) {
190 // Compute the dynamic part of the shape.
191 // First try to query the shape via ReifyRankedShapedTypeOpInterface.
192 bool reifiedShapes = false;
193 if (llvm::isa<RankedTensorType>(shapedValue.getType()) &&
194 llvm::isa<OpResult>(shapedValue)) {
196 if (succeeded(
197 reifyResultShapes(b, shapedValue.getDefiningOp(), resultDims))) {
198 reifiedShapes = true;
199 auto &shape =
200 resultDims[llvm::cast<OpResult>(shapedValue).getResultNumber()];
201 for (const auto &dim : enumerate(tensorType.getShape())) {
202 if (ShapedType::isDynamic(dim.value())) {
203 dynamicSizes.push_back(
204 getValueOrCreateConstantIndexOp(b, loc, shape[dim.index()]));
205 }
206 }
207 }
208 }
209
210 // If the shape could not be reified, create DimOps.
211 if (!reifiedShapes)
212 populateDynamicDimSizes(b, loc, tensor, dynamicSizes);
213 }
214
215 // Create AllocTensorOp.
216 auto allocTensorOp = AllocTensorOp::create(b, loc, tensorType, dynamicSizes,
217 copy ? tensor : Value());
218
219 // Add 'memory_space' attribute. Not needed if 'copy' operand is specified.
220 if (copy)
221 return allocTensorOp.getResult();
222 auto copyBufferType =
223 detail::asMemRefType(getBufferType(tensor, options, state));
224 if (failed(copyBufferType))
225 return failure();
226 std::optional<Attribute> memorySpace = copyBufferType->getMemorySpace();
227 if (!memorySpace)
228 memorySpace =
229 options.defaultMemorySpaceFn(cast<TensorLikeType>(tensorType));
230 if (memorySpace.has_value())
231 allocTensorOp.setMemorySpaceAttr(memorySpace.value());
232 return allocTensorOp.getResult();
233}
234
235// TODO: Properly support with options, for now it is hardcoded to builtin
236// Tensor/MemRef types based approach
237LogicalResult BufferizableOpInterface::resolveTensorOpOperandConflicts(
238 RewriterBase &rewriter, const AnalysisState &analysisState,
239 const BufferizationState &bufferizationState) {
240 OpBuilder::InsertionGuard g(rewriter);
241 Operation *op = getOperation();
242 SmallVector<OpOperand *> outOfPlaceOpOperands;
243 DenseSet<OpOperand *> copiedOpOperands;
244 SmallVector<Value> outOfPlaceValues;
245 DenseSet<Value> copiedOpValues;
246
247 // Find all out-of-place OpOperands.
248 for (OpOperand &opOperand : op->getOpOperands()) {
249 Type operandType = opOperand.get().getType();
250 if (!llvm::isa<TensorType>(operandType))
251 continue;
252 if (analysisState.isInPlace(opOperand))
253 continue;
254 if (llvm::isa<UnrankedTensorType>(operandType))
255 return op->emitError("copying of unranked tensors is not implemented");
256
257 AliasingValueList aliasingValues =
258 analysisState.getAliasingValues(opOperand);
259 if (aliasingValues.getNumAliases() == 1 &&
260 isa<OpResult>(aliasingValues.getAliases()[0].value) &&
261 !analysisState.bufferizesToMemoryWrite(opOperand) &&
262 analysisState
263 .getAliasingOpOperands(aliasingValues.getAliases()[0].value)
264 .getNumAliases() == 1 &&
265 !isa<UnrankedTensorType>(
266 aliasingValues.getAliases()[0].value.getType())) {
267 // The op itself does not write but may create exactly one alias. Instead
268 // of copying the OpOperand, copy the OpResult. The OpResult can sometimes
269 // be smaller than the OpOperand (e.g., in the case of an extract_slice,
270 // where the result is usually a smaller part of the source). Do not apply
271 // this optimization if the OpResult is an unranked tensor (because those
272 // cannot be copied at the moment).
273 Value value = aliasingValues.getAliases()[0].value;
274 outOfPlaceValues.push_back(value);
275 if (!analysisState.canOmitTensorCopy(opOperand))
276 copiedOpValues.insert(value);
277 } else {
278 // In all other cases, make a copy of the OpOperand.
279 outOfPlaceOpOperands.push_back(&opOperand);
280 if (!analysisState.canOmitTensorCopy(opOperand))
281 copiedOpOperands.insert(&opOperand);
282 }
283 }
284
285 // Insert copies of OpOperands.
286 rewriter.setInsertionPoint(op);
287 for (OpOperand *opOperand : outOfPlaceOpOperands) {
288 FailureOr<Value> copy = allocateTensorForShapedValue(
289 rewriter, op->getLoc(), opOperand->get(), analysisState.getOptions(),
290 bufferizationState, copiedOpOperands.contains(opOperand));
291 if (failed(copy))
292 return failure();
293 rewriter.modifyOpInPlace(op, [&]() { opOperand->set(*copy); });
294 }
295
296 // Insert copies of Values.
297 rewriter.setInsertionPointAfter(op);
298 for (Value value : outOfPlaceValues) {
299 FailureOr<Value> copy = allocateTensorForShapedValue(
300 rewriter, op->getLoc(), value, analysisState.getOptions(),
301 bufferizationState, copiedOpValues.count(value));
302 if (failed(copy))
303 return failure();
304 SmallVector<OpOperand *> uses = llvm::map_to_vector(
305 value.getUses(), [](OpOperand &use) { return &use; });
306 for (OpOperand *use : uses) {
307 // Do not update the alloc_tensor op that we just created.
308 if (use->getOwner() == copy->getDefiningOp())
309 continue;
310 // tensor.dim ops may have been created to be used as alloc_tensor op
311 // dynamic extents. Do not update these either.
312 if (isa<tensor::DimOp>(use->getOwner()))
313 continue;
314 rewriter.modifyOpInPlace(use->getOwner(), [&]() { use->set(*copy); });
315 }
316 }
317
318 return success();
319}
320
321//===----------------------------------------------------------------------===//
322// OpFilter
323//===----------------------------------------------------------------------===//
324
325bool OpFilter::isOpAllowed(Operation *op) const {
326 // All other ops: Allow/disallow according to filter.
327 bool isAllowed = !hasAllowRule();
328 for (const Entry &entry : entries) {
329 bool filterResult = entry.fn(op);
330 switch (entry.type) {
331 case Entry::ALLOW:
332 isAllowed |= filterResult;
333 break;
334 case Entry::DENY:
335 if (filterResult)
336 // DENY filter matches. This op is no allowed. (Even if other ALLOW
337 // filters may match.)
338 return false;
339 };
340 }
341 return isAllowed;
342}
343
344//===----------------------------------------------------------------------===//
345// BufferizationOptions
346//===----------------------------------------------------------------------===//
347
348namespace {
349
350// A helper overload for bufferization::getMemRefTypeWithFullyDynamicLayout().
351BaseMemRefType getMemRefTypeWithFullyDynamicLayout(ArrayRef<int64_t> shape,
352 mlir::Type elementType,
353 Attribute memorySpace) {
354 int64_t dynamicOffset = ShapedType::kDynamic;
355 SmallVector<int64_t> dynamicStrides(shape.size(), ShapedType::kDynamic);
356 auto stridedLayout = StridedLayoutAttr::get(elementType.getContext(),
357 dynamicOffset, dynamicStrides);
358 return MemRefType::get(shape, elementType, stridedLayout, memorySpace);
359}
360
361/// Create a memref allocation with the given type and dynamic extents.
362FailureOr<Value> defaultCreateAlloc(OpBuilder &b, Location loc, MemRefType type,
363 ValueRange dynShape,
364 unsigned int bufferAlignment) {
365 // Default buffer allocation via AllocOp.
366 if (bufferAlignment != 0)
367 return memref::AllocOp::create(b, loc, type, dynShape,
368 b.getI64IntegerAttr(bufferAlignment))
369 .getResult();
370 return memref::AllocOp::create(b, loc, type, dynShape).getResult();
371}
372
373/// Create a memory copy between two memref buffers.
374LogicalResult defaultCreateMemCpy(OpBuilder &b, Location loc, Value from,
375 Value to) {
376 memref::CopyOp::create(b, loc, from, to);
377 return success();
378}
379
380FailureOr<Value> defaultCreateCast(OpBuilder &b, Location loc, Type dest,
381 Value value) {
382 assert(isa<BaseMemRefType>(dest) && "expected BaseMemRefType");
383 assert(isa<BaseMemRefType>(value.getType()) && "expected BaseMemRefType");
384 assert(memref::CastOp::areCastCompatible(value.getType(), dest) &&
385 "cast incompatible");
386 return memref::CastOp::create(b, loc, dest, value).getResult();
387}
388
389/// Default function arg type converter: Use a fully dynamic layout map.
390BufferLikeType
391defaultFunctionArgTypeConverter(TensorLikeType type, Attribute memorySpace,
392 func::FuncOp funcOp,
393 const BufferizationOptions &options) {
394 if (auto tensorType = mlir::dyn_cast<TensorType>(type)) {
395 return cast<BufferLikeType>(
396 bufferization::getMemRefTypeWithFullyDynamicLayout(tensorType,
397 memorySpace));
398 }
399
400 // If not builtin, fallback to unknown type conversion.
401 return options.unknownTypeConverterFn(type, memorySpace, options);
402}
403/// Default unknown type converter: Use a fully dynamic layout map.
404BufferLikeType
405defaultUnknownTypeConverter(TensorLikeType tensorType, Attribute memorySpace,
406 const BufferizationOptions &options) {
407 return cast<BufferLikeType>(
408 bufferization::getMemRefTypeWithFullyDynamicLayout(
409 cast<TensorType>(tensorType), memorySpace));
410}
411
412/// Default reconcile hook: memory space mismatch is an error, layout mismatch
413/// is resolved by promoting to fully dynamic.
414FailureOr<BufferLikeType>
415defaultReconcileBufferTypeMismatch(BufferLikeType x, BufferLikeType y,
416 const BufferizationOptions &) {
417 const auto xMemRef = cast<BaseMemRefType>(x);
418 const auto yMemRef = cast<BaseMemRefType>(y);
419
420 if (xMemRef.getMemorySpace() != yMemRef.getMemorySpace())
421 return failure();
422
423 if (isa<UnrankedMemRefType>(xMemRef)) {
424 // unranked memrefs have no layout.
425 return x;
426 }
427
428 return cast<BufferLikeType>(::getMemRefTypeWithFullyDynamicLayout(
429 xMemRef.getShape(), xMemRef.getElementType(), xMemRef.getMemorySpace()));
430}
431
432} // namespace
433
434// Default constructor for BufferizationOptions.
435BufferizationOptions::BufferizationOptions()
436 : allocationFn(defaultCreateAlloc), memCpyFn(defaultCreateMemCpy),
437 castFn(defaultCreateCast),
438 functionArgTypeConverterFn(defaultFunctionArgTypeConverter),
439 unknownTypeConverterFn(defaultUnknownTypeConverter),
440 reconcileBufferTypeMismatchFn(defaultReconcileBufferTypeMismatch) {}
441
442bool BufferizationOptions::isOpAllowed(Operation *op) const {
443 // Special case: If function boundary bufferization is deactivated, do not
444 // allow ops that belong to the `func` dialect.
445 bool isFuncBoundaryOp = isa_and_nonnull<func::FuncDialect>(op->getDialect());
446 if (!bufferizeFunctionBoundaries && isFuncBoundaryOp)
447 return false;
448
449 return opFilter.isOpAllowed(op);
450}
451
452BufferizableOpInterface
453BufferizationOptions::dynCastBufferizableOp(Operation *op) const {
454 if (!isOpAllowed(op))
455 return nullptr;
456 auto bufferizableOp = dyn_cast<BufferizableOpInterface>(op);
457 if (!bufferizableOp)
458 return nullptr;
459 return bufferizableOp;
460}
461
462BufferizableOpInterface
463BufferizationOptions::dynCastBufferizableOp(Value value) const {
464 return dynCastBufferizableOp(getOwnerOfValue(value));
465}
466
467void BufferizationOptions::setFunctionBoundaryTypeConversion(
468 LayoutMapOption layoutMapOption) {
469 functionArgTypeConverterFn = [=](TensorLikeType type, Attribute memorySpace,
470 func::FuncOp funcOp,
471 const BufferizationOptions &options) {
472 if (auto tensorType = mlir::dyn_cast<TensorType>(type)) {
473 if (layoutMapOption == LayoutMapOption::IdentityLayoutMap)
474 return cast<BufferLikeType>(
475 bufferization::getMemRefTypeWithStaticIdentityLayout(tensorType,
476 memorySpace));
477 return cast<BufferLikeType>(
478 bufferization::getMemRefTypeWithFullyDynamicLayout(tensorType,
479 memorySpace));
480 }
481
482 // If not builtin, fallback to unknown type conversion.
483 return options.unknownTypeConverterFn(type, memorySpace, options);
484 };
485 inferFunctionResultLayout =
486 layoutMapOption == LayoutMapOption::InferLayoutMap;
487}
488
489//===----------------------------------------------------------------------===//
490// Helper functions for BufferizableOpInterface
491//===----------------------------------------------------------------------===//
492
494 if (auto bbArg = llvm::dyn_cast<BlockArgument>(value)) {
495 b.setInsertionPointToStart(bbArg.getOwner());
496 } else {
497 b.setInsertionPointAfter(value.getDefiningOp());
498 }
499}
500
501/// Determine which OpOperand* will alias with `value` if the op is bufferized
502/// in place. Return all tensor OpOperand* if the op is not bufferizable.
503AliasingOpOperandList AnalysisState::getAliasingOpOperands(Value value) const {
504 if (Operation *op = getOwnerOfValue(value))
505 if (auto bufferizableOp = getOptions().dynCastBufferizableOp(op))
506 return bufferizableOp.getAliasingOpOperands(value, *this);
507
508 // The op is not bufferizable.
509 return detail::unknownGetAliasingOpOperands(value);
510}
511
512/// Determine which Values will alias with `opOperand` if the op is bufferized
513/// in place. Return all tensor Values if the op is not bufferizable.
514AliasingValueList AnalysisState::getAliasingValues(OpOperand &opOperand) const {
515 if (auto bufferizableOp =
516 getOptions().dynCastBufferizableOp(opOperand.getOwner()))
517 return bufferizableOp.getAliasingValues(opOperand, *this);
518
519 // The op is not bufferizable.
520 return detail::unknownGetAliasingValues(opOperand);
521}
522
523/// Return true if `opOperand` bufferizes to a memory read. Return `true` if the
524/// op is not bufferizable.
525bool AnalysisState::bufferizesToMemoryRead(OpOperand &opOperand) const {
526 if (auto bufferizableOp =
527 getOptions().dynCastBufferizableOp(opOperand.getOwner()))
528 return bufferizableOp.bufferizesToMemoryRead(opOperand, *this);
529
530 // Unknown op that returns a tensor. The inplace analysis does not support it.
531 // Conservatively return true.
532 return true;
533}
534
535/// Return true if `opOperand` bufferizes to a memory write. Return
536/// `true` if the op is not bufferizable.
537bool AnalysisState::bufferizesToMemoryWrite(OpOperand &opOperand) const {
538 if (auto bufferizableOp =
539 getOptions().dynCastBufferizableOp(opOperand.getOwner()))
540 return bufferizableOp.bufferizesToMemoryWrite(opOperand, *this);
541
542 // Unknown op that returns a tensor. The inplace analysis does not support it.
543 // Conservatively return true.
544 return true;
545}
546
547/// Return true if `opOperand` does neither read nor write but bufferizes to an
548/// alias. Return false if the op is not bufferizable.
549bool AnalysisState::bufferizesToAliasOnly(OpOperand &opOperand) const {
550 if (auto bufferizableOp =
551 getOptions().dynCastBufferizableOp(opOperand.getOwner()))
552 return bufferizableOp.bufferizesToAliasOnly(opOperand, *this);
553
554 // Unknown op that returns a tensor. The inplace analysis does not support it.
555 // Conservatively return false.
556 return false;
557}
558
559bool AnalysisState::bufferizesToMemoryWrite(Value value) const {
560 auto opResult = llvm::dyn_cast<OpResult>(value);
561 if (!opResult)
562 return true;
563 auto bufferizableOp = getOptions().dynCastBufferizableOp(value);
564 if (!bufferizableOp)
565 return true;
566 return bufferizableOp.resultBufferizesToMemoryWrite(opResult, *this);
567}
568
569/// Return true if the given value is read by an op that bufferizes to a memory
570/// read. Also takes into account ops that create an alias but do not read by
571/// themselves (e.g., ExtractSliceOp).
572bool AnalysisState::isValueRead(Value value) const {
573 assert(llvm::isa<TensorLikeType>(value.getType()) &&
574 "expected TensorLikeType");
575 SmallVector<OpOperand *> workingSet;
576 DenseSet<OpOperand *> visited;
577 for (OpOperand &use : value.getUses())
578 workingSet.push_back(&use);
579
580 while (!workingSet.empty()) {
581 OpOperand *uMaybeReading = workingSet.pop_back_val();
582 if (!visited.insert(uMaybeReading).second)
583 continue;
584
585 // Skip over all ops that neither read nor write (but create an alias).
586 if (bufferizesToAliasOnly(*uMaybeReading))
587 for (AliasingValue alias : getAliasingValues(*uMaybeReading))
588 for (OpOperand &use : alias.value.getUses())
589 workingSet.push_back(&use);
590 if (bufferizesToMemoryRead(*uMaybeReading))
591 return true;
592 }
593
594 return false;
595}
596
597// Starting from `opOperand`, follow the use-def chain in reverse, always
598// selecting the aliasing OpOperands. Find and return Values for which
599// `condition` evaluates to true. Uses of such matching Values are not
600// traversed any further, the visited aliasing opOperands will be preserved
601// through `visitedOpOperands`.
602llvm::SetVector<Value> AnalysisState::findValueInReverseUseDefChain(
603 OpOperand *opOperand, llvm::function_ref<bool(Value)> condition,
604 TraversalConfig config,
605 llvm::DenseSet<OpOperand *> *visitedOpOperands) const {
606 llvm::DenseSet<Value> visited;
607 llvm::SetVector<Value> result, workingSet;
608 workingSet.insert(opOperand->get());
609
610 if (visitedOpOperands)
611 visitedOpOperands->insert(opOperand);
612
613 while (!workingSet.empty()) {
614 Value value = workingSet.pop_back_val();
615
616 if (!config.revisitAlreadyVisitedValues && visited.contains(value)) {
617 // Stop traversal if value was already visited.
618 if (config.alwaysIncludeLeaves)
619 result.insert(value);
620 continue;
621 }
622 visited.insert(value);
623
624 if (condition(value)) {
625 result.insert(value);
626 continue;
627 }
628
629 if (!config.followUnknownOps && !options.dynCastBufferizableOp(value)) {
630 // Stop iterating if `followUnknownOps` is unset and the op is either
631 // not bufferizable or excluded in the OpFilter.
632 if (config.alwaysIncludeLeaves)
633 result.insert(value);
634 continue;
635 }
636
637 AliasingOpOperandList aliases = getAliasingOpOperands(value);
638 if (aliases.getNumAliases() == 0) {
639 // The traversal ends naturally if there are no more OpOperands that
640 // could be followed.
641 if (config.alwaysIncludeLeaves)
642 result.insert(value);
643 continue;
644 }
645
646 for (AliasingOpOperand a : aliases) {
647 if (config.followEquivalentOnly &&
648 a.relation != BufferRelation::Equivalent) {
649 // Stop iterating if `followEquivalentOnly` is set but the alias is not
650 // equivalent.
651 if (config.alwaysIncludeLeaves)
652 result.insert(value);
653 continue;
654 }
655
656 if (config.followInPlaceOnly && !isInPlace(*a.opOperand)) {
657 // Stop iterating if `followInPlaceOnly` is set but the alias is
658 // out-of-place.
659 if (config.alwaysIncludeLeaves)
660 result.insert(value);
661 continue;
662 }
663
664 if (config.followSameTypeOrCastsOnly &&
665 a.opOperand->get().getType() != value.getType() &&
666 !value.getDefiningOp<CastOpInterface>()) {
667 // Stop iterating if `followSameTypeOrCastsOnly` is set but the alias is
668 // has a different type and the op is not a cast.
669 if (config.alwaysIncludeLeaves)
670 result.insert(value);
671 continue;
672 }
673
674 workingSet.insert(a.opOperand->get());
675 if (visitedOpOperands)
676 visitedOpOperands->insert(a.opOperand);
677 }
678 }
679
680 return result;
681}
682
683// Find the values that define the contents of the given operand's value.
684llvm::SetVector<Value>
685AnalysisState::findDefinitions(OpOperand *opOperand) const {
686 TraversalConfig config;
687 config.alwaysIncludeLeaves = false;
688 return findValueInReverseUseDefChain(
689 opOperand, [&](Value v) { return this->bufferizesToMemoryWrite(v); },
690 config);
691}
692
693AnalysisState::AnalysisState(const BufferizationOptions &options)
695
697 : options(options), type(type) {
698 for (const BufferizationOptions::AnalysisStateInitFn &fn :
699 options.stateInitializers)
700 fn(*this);
701}
702
703bool AnalysisState::canOmitTensorCopy(OpOperand &opOperand) const {
704 // Do not copy if the tensor has undefined contents.
705 if (hasUndefinedContents(&opOperand))
706 return true;
707
708 // Do not copy if the buffer of the tensor is entirely overwritten (with
709 // values that do not depend on the old tensor).
710 if (bufferizesToMemoryWrite(opOperand) && !bufferizesToMemoryRead(opOperand))
711 return true;
712
713 // Do not copy if the tensor is never read.
714 AliasingValueList aliases = getAliasingValues(opOperand);
715 if (!bufferizesToMemoryRead(opOperand) &&
716 llvm::none_of(aliases,
717 [&](AliasingValue a) { return isValueRead(a.value); }))
718 return true;
719
720 // Default: Cannot omit the copy.
721 return false;
722}
723
724bool AnalysisState::isInPlace(OpOperand &opOperand) const {
725 // ToBufferOps are always in-place.
726 if (isa<ToBufferOp>(opOperand.getOwner()))
727 return true;
728
729 // In the absence of analysis information, OpOperands that bufferize to a
730 // memory write are out-of-place, i.e., an alloc and copy is inserted.
731 return !bufferizesToMemoryWrite(opOperand);
732}
733
734bool AnalysisState::areEquivalentBufferizedValues(Value v1, Value v2) const {
735 // In the absence of analysis information, we do not know if the values are
736 // equivalent. The conservative answer is "false".
737 return false;
738}
739
740bool AnalysisState::areAliasingBufferizedValues(Value v1, Value v2) const {
741 // In the absence of analysis information, we do not know if the values may be
742 // aliasing. The conservative answer is "true".
743 return true;
744}
745
746bool AnalysisState::hasUndefinedContents(OpOperand *opOperand) const {
747 // In the absence of analysis information, the conservative answer is "false".
748 return false;
749}
750
751FailureOr<Value> bufferization::getBuffer(RewriterBase &rewriter, Value value,
752 const BufferizationOptions &options,
753 const BufferizationState &state) {
754#ifndef NDEBUG
755 auto tensorType = llvm::dyn_cast<TensorLikeType>(value.getType());
756 assert(tensorType && "unexpected non-tensor type");
757#endif // NDEBUG
758
759 // Replace "%t = to_tensor %m" with %m.
760 if (auto toTensorOp = value.getDefiningOp<bufferization::ToTensorOp>())
761 return toTensorOp.getBuffer();
762
763 // Insert to_buffer op.
764 OpBuilder::InsertionGuard g(rewriter);
765 setInsertionPointAfter(rewriter, value);
766 FailureOr<BufferLikeType> bufferType = getBufferType(value, options, state);
767 if (failed(bufferType))
768 return failure();
769
770 return bufferization::ToBufferOp::create(rewriter, value.getLoc(),
771 *bufferType, value)
772 .getResult();
773}
774
775/// Return the buffer type for a given Value (tensor) after bufferization.
776FailureOr<BufferLikeType>
777bufferization::getBufferType(Value value, const BufferizationOptions &options,
778 const BufferizationState &state) {
779 SmallVector<Value> invocationStack;
780 return getBufferType(value, options, state, invocationStack);
781}
782
783/// Return the buffer type for a given Value (tensor) after bufferization.
784FailureOr<BufferLikeType>
785bufferization::getBufferType(Value value, const BufferizationOptions &options,
786 const BufferizationState &state,
787 SmallVector<Value> &invocationStack) {
788 assert(llvm::isa<TensorLikeType>(value.getType()) &&
789 "unexpected non-tensor type");
790 invocationStack.push_back(value);
791 llvm::scope_exit popFromStack([&]() { invocationStack.pop_back(); });
792
793 // Try querying BufferizableOpInterface.
794 Operation *op = getOwnerOfValue(value);
795 auto bufferizableOp = options.dynCastBufferizableOp(op);
796 if (bufferizableOp)
797 return bufferizableOp.getBufferType(value, options, state, invocationStack);
798
799 // Op is not bufferizable.
800 auto memSpace =
801 options.defaultMemorySpaceFn(cast<TensorLikeType>(value.getType()));
802 if (!memSpace.has_value())
803 return op->emitError("could not infer memory space");
804
805 return options.unknownTypeConverterFn(cast<TensorLikeType>(value.getType()),
806 *memSpace, options);
807}
808
809bool bufferization::hasTensorSemantics(Operation *op) {
810 if (auto bufferizableOp = dyn_cast<BufferizableOpInterface>(op))
811 return bufferizableOp.hasTensorSemantics();
812 return detail::defaultHasTensorSemantics(op);
813}
814
815void bufferization::replaceOpWithBufferizedValues(RewriterBase &rewriter,
816 Operation *op,
817 ValueRange values) {
818 assert(values.size() == op->getNumResults() &&
819 "expected one value per OpResult");
820 OpBuilder::InsertionGuard g(rewriter);
821
822 // Replace all OpResults with the given values.
823 SmallVector<Value> replacements;
824 for (OpResult opResult : op->getOpResults()) {
825 Value replacement = values[opResult.getResultNumber()];
826 if (llvm::isa<TensorLikeType>(opResult.getType())) {
827 // The OpResult is a tensor. Such values are replaced with memrefs during
828 // bufferization.
829 assert(llvm::isa<BufferLikeType>(replacement.getType()) &&
830 "tensor op result should be replaced with a buffer value");
831 // The existing uses of the OpResult still expect a tensor. Insert a
832 // ToTensorOp. Throughout bufferization, this ToTensorOp will gradually
833 // loose all of its users and eventually DCE away.
834 rewriter.setInsertionPointAfter(op);
835 replacement = bufferization::ToTensorOp::create(
836 rewriter, replacement.getLoc(), opResult.getType(), replacement);
837 }
838 replacements.push_back(replacement);
839 }
840
841 rewriter.replaceOp(op, replacements);
842}
843
844//===----------------------------------------------------------------------===//
845// Bufferization-specific IRMapping support with debugging.
846//===----------------------------------------------------------------------===//
847
849bufferization::getMemRefTypeWithFullyDynamicLayout(TensorType tensorType,
850 Attribute memorySpace) {
851 // Case 1: Unranked memref type.
852 if (auto unrankedTensorType =
853 llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
854 return UnrankedMemRefType::get(unrankedTensorType.getElementType(),
855 memorySpace);
856 }
857
858 // Case 2: Ranked memref type.
859 return ::getMemRefTypeWithFullyDynamicLayout(
860 tensorType.getShape(), tensorType.getElementType(), memorySpace);
861}
862
863/// Return a MemRef type with a static identity layout (i.e., no layout map). If
864/// the given tensor type is unranked, return an unranked MemRef type.
866bufferization::getMemRefTypeWithStaticIdentityLayout(TensorType tensorType,
867 Attribute memorySpace) {
868 // Case 1: Unranked memref type.
869 if (auto unrankedTensorType =
870 llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
871 return UnrankedMemRefType::get(unrankedTensorType.getElementType(),
872 memorySpace);
873 }
874
875 // Case 2: Ranked memref type.
876 auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
877 MemRefLayoutAttrInterface layout = {};
878 return MemRefType::get(rankedTensorType.getShape(),
879 rankedTensorType.getElementType(), layout,
880 memorySpace);
881}
882
883//===----------------------------------------------------------------------===//
884// Default implementations of interface methods
885//===----------------------------------------------------------------------===//
886
887bool bufferization::detail::defaultResultBufferizesToMemoryWrite(
888 OpResult opResult, const AnalysisState &state) {
889 auto bufferizableOp = cast<BufferizableOpInterface>(opResult.getDefiningOp());
890 AliasingOpOperandList opOperands =
891 bufferizableOp.getAliasingOpOperands(opResult, state);
892
893 // Case 1: OpResults that have no aliasing OpOperand usually bufferize to
894 // memory writes.
895 if (opOperands.getAliases().empty())
896 return true;
897
898 // Case 2: If an aliasing OpOperand bufferizes to a memory write, the OpResult
899 // may bufferize to a memory write.
900 if (llvm::any_of(opOperands, [&](AliasingOpOperand alias) {
901 return state.bufferizesToMemoryWrite(*alias.opOperand);
902 }))
903 return true;
904
905 // Case 3: Check if a nested aliasing OpOperand value bufferizes to a memory
906 // write. (Or: The reverse SSA use-def chain ends inside the reigon.) In that
907 // case, the OpResult bufferizes to a memory write. E.g.:
908 //
909 // %0 = "some_writing_op" : tensor<?xf32>
910 // %r = scf.if ... -> tensor<?xf32> {
911 // scf.yield %0 : tensor<?xf32>
912 // } else {
913 // %1 = "another_writing_op"(%0) : tensor<?xf32>
914 // scf.yield %1 : tensor<?xf32>
915 // }
916 // "some_reading_op"(%r)
917 //
918 // %r bufferizes to a memory write because an aliasing OpOperand value (%1)
919 // bufferizes to a memory write and the defining op is inside the scf.if.
920 //
921 // Note: This treatment of surrouding ops is useful for ops that have a
922 // region but no OpOperand such as scf.if or scf.execute_region. It simplifies
923 // the analysis considerably.
924 //
925 // "another_writing_op" in the above example should be able to bufferize
926 // inplace in the absence of another read of %0. However, if the scf.if op
927 // would not be considered a "write", the analysis would detect the
928 // following conflict:
929 //
930 // * read = some_reading_op
931 // * lastWrite = %0 (Note: The last write of %r would be a set: {%0, %1}.)
932 // * conflictingWrite = %1
933 //
934 auto isMemoryWriteInsideOp = [&](Value v) {
935 Operation *op = getOwnerOfValue(v);
936 if (!opResult.getDefiningOp()->isAncestor(op))
937 return false;
938 return state.bufferizesToMemoryWrite(v);
939 };
940 TraversalConfig config;
941 config.alwaysIncludeLeaves = false;
942 for (AliasingOpOperand alias : opOperands) {
943 if (!state
944 .findValueInReverseUseDefChain(alias.opOperand,
945 isMemoryWriteInsideOp, config)
946 .empty())
947 return true;
948 }
949 return false;
950}
951
952// Compute the AliasingOpOperandList for a given Value based on
953// getAliasingValues.
954AliasingOpOperandList bufferization::detail::defaultGetAliasingOpOperands(
955 Value value, const AnalysisState &state) {
956 Operation *op = getOwnerOfValue(value);
958 for (OpOperand &opOperand : op->getOpOperands()) {
959 if (!llvm::isa<TensorLikeType>(opOperand.get().getType()))
960 continue;
961 AliasingValueList aliasingValues = state.getAliasingValues(opOperand);
962 for (const auto &it : aliasingValues)
963 if (it.value == value)
964 result.emplace_back(&opOperand, it.relation, it.isDefinite);
965 }
966 return AliasingOpOperandList(std::move(result));
967}
968
969FailureOr<BufferLikeType> bufferization::detail::defaultGetBufferType(
970 Value value, const BufferizationOptions &options,
971 const BufferizationState &bufferizationState,
972 SmallVector<Value> &invocationStack) {
973 assert(llvm::isa<TensorType>(value.getType()) && "expected tensor type");
974 auto tensorType = cast<TensorType>(value.getType());
975
976 auto elementType = tensorType.getElementType();
977
978 if (!BaseMemRefType::isValidElementType(elementType))
979 return getOwnerOfValue(value)->emitError()
980 << "cannot bufferize value of type " << tensorType
981 << ": element type " << elementType
982 << " is not a valid memref element type";
983
984 // No further analysis is possible for a block argument.
985 if (llvm::isa<BlockArgument>(value)) {
986 return options.unknownTypeConverterFn(cast<TensorLikeType>(tensorType),
987 /*memorySpace=*/nullptr, options);
988 }
989
990 // Value is an OpResult.
991 Operation *op = getOwnerOfValue(value);
992 auto opResult = llvm::cast<OpResult>(value);
993 AnalysisState analysisState(options);
994 AliasingOpOperandList aliases = analysisState.getAliasingOpOperands(opResult);
995 if (aliases.getNumAliases() > 0 &&
996 aliases.getAliases()[0].relation == BufferRelation::Equivalent) {
997 // If the OpResult has an equivalent OpOperand, both OpResult and
998 // OpOperand bufferize to the exact same buffer type.
999 Value equivalentOperand = aliases.getAliases().front().opOperand->get();
1000 return getBufferType(equivalentOperand, options, bufferizationState,
1001 invocationStack);
1002 }
1003
1004 // If we do not know the memory space and there is no default memory space,
1005 // report a failure.
1006 auto memSpace =
1007 options.defaultMemorySpaceFn(cast<TensorLikeType>(tensorType));
1008 if (!memSpace.has_value())
1009 return op->emitError("could not infer memory space");
1010
1011 return options.unknownTypeConverterFn(cast<TensorLikeType>(tensorType),
1012 *memSpace, options);
1013}
1014
1015bool bufferization::detail::defaultIsRepetitiveRegion(
1016 BufferizableOpInterface bufferizableOp, unsigned index) {
1017 assert(index < bufferizableOp->getNumRegions() && "invalid region index");
1018 auto regionInterface =
1019 dyn_cast<RegionBranchOpInterface>(bufferizableOp.getOperation());
1020 if (!regionInterface)
1021 return false;
1022 return regionInterface.isRepetitiveRegion(index);
1023}
1024
1025AliasingOpOperandList
1026bufferization::detail::unknownGetAliasingOpOperands(Value value) {
1027 // TODO: Take into account successor blocks.
1028 // No aliasing in case of non-entry blocks.
1029 if (auto bbArg = dyn_cast<BlockArgument>(value))
1030 if (bbArg.getOwner() != &bbArg.getOwner()->getParent()->getBlocks().front())
1031 return {};
1032
1033 // Unknown op: Conservatively assume that each OpResult may alias with every
1034 // OpOperand. In addition, each block argument of an entry block may alias
1035 // with every OpOperand.
1036 AliasingOpOperandList r;
1037 for (OpOperand &operand : value.getDefiningOp()->getOpOperands())
1038 if (isa<TensorLikeType>(operand.get().getType()))
1039 r.addAlias({&operand, BufferRelation::Unknown, /*isDefinite=*/false});
1040 return r;
1041}
1042
1043AliasingValueList
1044bufferization::detail::unknownGetAliasingValues(OpOperand &opOperand) {
1045 // TODO: Take into account successor blocks.
1046 // Unknown op: Conservatively assume that each OpResult may alias with every
1047 // OpOperand. In addition, each block argument of an entry block may alias
1048 // with every OpOperand.
1049 AliasingValueList r;
1050 for (OpResult result : opOperand.getOwner()->getOpResults())
1051 if (llvm::isa<TensorLikeType>(result.getType()))
1052 r.addAlias({result, BufferRelation::Unknown, /*isDefinite=*/false});
1053 for (Region &region : opOperand.getOwner()->getRegions())
1054 if (!region.getBlocks().empty())
1055 for (BlockArgument bbArg : region.getBlocks().front().getArguments())
1056 if (isa<TensorLikeType>(bbArg.getType()))
1057 r.addAlias({bbArg, BufferRelation::Unknown, /*isDefinite=*/false});
1058 return r;
1059}
1060
1061bool bufferization::detail::defaultHasTensorSemantics(Operation *op) {
1062 auto isaTensor = [](Type t) { return isa<TensorLikeType>(t); };
1063 bool hasTensorBlockArgument = any_of(op->getRegions(), [&](Region &r) {
1064 return any_of(r.getBlocks(), [&](Block &b) {
1065 return any_of(b.getArguments(), [&](BlockArgument bbArg) {
1066 return isaTensor(bbArg.getType());
1067 });
1068 });
1069 });
1070 if (hasTensorBlockArgument)
1071 return true;
1072
1073 if (any_of(op->getResultTypes(), isaTensor))
1074 return true;
1075 return any_of(op->getOperandTypes(), isaTensor);
1076}
1077
1078FailureOr<BaseMemRefType>
1079bufferization::detail::asMemRefType(FailureOr<BufferLikeType> bufferType) {
1080 if (failed(bufferType))
1081 return failure();
1082 return cast<BaseMemRefType>(*bufferType);
1083}
1084
1085bool bufferization::detail::typesMatchAfterBufferization(Operation &op,
1086 Value tensor,
1087 Value buffer) {
1088 return mlir::succeeded(
1089 cast<TensorLikeType>(tensor.getType())
1090 .verifyCompatibleBufferType(cast<BufferLikeType>(buffer.getType()),
1091 [&]() { return op.emitError(); }));
1092}
return success()
static void setInsertionPointAfter(OpBuilder &b, Value value)
static bool isRepetitiveRegion(Region *region, const BufferizationOptions &options)
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...
*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 bool isaTensor(Type t)
static llvm::ManagedStatic< PassManagerOptions > options
static RankedTensorType getBufferType(const SparseTensorType &stt, bool needTmpCOO)
#define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME)
Definition TypeID.h:323
static Operation * getOwnerOfValue(Value value)
Base class for generic analysis states.
AnalysisState(LatticeAnchor anchor)
Create the analysis state on the given lattice anchor.
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class provides a shared interface for ranked and unranked memref types.
static bool isValidElementType(Type type)
Return true if the specified element type is ok in a memref.
This class represents an argument of a Block.
Definition Value.h:306
Block represents an ordered list of Operations.
Definition Block.h:33
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
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
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:400
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:414
This class represents an operand of an operation.
Definition Value.h:254
This is a value defined by a result of an operation.
Definition Value.h:454
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition Operation.h:237
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
MutableArrayRef< OpOperand > getOpOperands()
Definition Operation.h:408
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
operand_type_range getOperandTypes()
Definition Operation.h:422
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:702
result_type_range getResultTypes()
Definition Operation.h:453
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
Definition Operation.h:288
result_range getOpResults()
Definition Operation.h:445
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:429
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
Definition Region.cpp:45
unsigned getRegionNumber()
Return the number of this region in the parent operation.
Definition Region.cpp:62
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition Region.h:213
BlockListType & getBlocks()
Definition Region.h:45
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...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
This class represents a collection of SymbolTables.
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.
Type getElementType() const
Returns the element type of this tensor type.
This class provides an efficient unique identifier for a specific C++ type.
Definition TypeID.h:107
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
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
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition Value.h:188
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
Region * getParentRegion()
Return the Region in which this Value is defined.
Definition Value.cpp:39
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
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...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition Matchers.h:344
Type getTensorTypeFromMemRefType(Type type)
Return an unranked/ranked tensor type for the given unranked/ranked memref type.
Definition MemRefOps.cpp:62
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
LogicalResult reifyResultShapes(OpBuilder &b, Operation *op, ReifiedRankedShapedTypeDims &reifiedReturnShapes)
Reify the shape of the result of an operation (typically in terms of the shape of its operands).
bool insideMutuallyExclusiveRegions(Operation *a, Operation *b)
Return true if a and b are in mutually exclusive regions as per RegionBranchOpInterface.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:122
Region * getEnclosingRepetitiveRegion(Operation *op)
Return the first enclosing region of the given op that may be executed repetitively as per RegionBran...
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:114
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...