21 #include "llvm/ADT/ScopeExit.h"
22 #include "llvm/Support/Debug.h"
29 namespace bufferization {
31 #include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.cpp.inc"
38 #define DEBUG_TYPE "bufferizable-op-interface"
39 #define DBGS() (llvm::dbgs() << '[' << DEBUG_TYPE << "] ")
40 #define LDBG(X) LLVM_DEBUG(DBGS() << (X))
43 using namespace bufferization;
48 if (
auto bufferizableOp =
options.dynCastBufferizableOp(op))
58 if (
auto iter = enclosingRepetitiveRegionCache.find_as(op);
59 iter != enclosingRepetitiveRegionCache.end())
61 return enclosingRepetitiveRegionCache[op] =
67 if (
auto iter = enclosingRepetitiveRegionCache.find_as(value);
68 iter != enclosingRepetitiveRegionCache.end())
76 visitedRegions.push_back(region);
81 enclosingRepetitiveRegionCache[value] = region;
82 for (
Region *r : visitedRegions)
83 enclosingRepetitiveRegionCache[r] = region;
89 if (
auto iter = enclosingRepetitiveRegionCache.find_as(block);
90 iter != enclosingRepetitiveRegionCache.end())
104 enclosingRepetitiveRegionCache[block] = region;
105 for (
Region *r : visitedRegions)
106 enclosingRepetitiveRegionCache[r] = region;
126 if (bufferizableOp &&
129 "expected that all parallel regions are also repetitive regions");
138 if (
auto opResult = llvm::dyn_cast<OpResult>(value))
139 return opResult.getDefiningOp();
140 return llvm::cast<BlockArgument>(value).getOwner()->
getParentOp();
150 if (llvm::isa<RankedTensorType>(shapedValue.
getType())) {
151 tensor = shapedValue;
152 }
else if (llvm::isa<MemRefType>(shapedValue.
getType())) {
153 tensor = b.
create<ToTensorOp>(loc, shapedValue);
154 }
else if (llvm::isa<UnrankedTensorType>(shapedValue.
getType()) ||
155 llvm::isa<UnrankedMemRefType>(shapedValue.
getType())) {
157 ->
emitError(
"copying of unranked tensors is not implemented");
159 llvm_unreachable(
"expected RankedTensorType or MemRefType");
161 RankedTensorType tensorType = llvm::cast<RankedTensorType>(tensor.
getType());
166 bool reifiedShapes =
false;
167 if (llvm::isa<RankedTensorType>(shapedValue.
getType()) &&
168 llvm::isa<OpResult>(shapedValue)) {
172 reifiedShapes =
true;
174 resultDims[llvm::cast<OpResult>(shapedValue).getResultNumber()];
175 for (
const auto &dim :
enumerate(tensorType.getShape()))
176 if (ShapedType::isDynamic(dim.value()))
177 dynamicSizes.push_back(cast<Value>(shape[dim.index()]));
187 auto allocTensorOp = b.
create<AllocTensorOp>(loc, tensorType, dynamicSizes,
194 if (failed(copyBufferType))
196 std::optional<Attribute> memorySpace = copyBufferType->getMemorySpace();
198 memorySpace =
options.defaultMemorySpaceFn(tensorType);
199 if (memorySpace.has_value())
200 allocTensorOp.setMemorySpaceAttr(memorySpace.value());
201 return allocTensorOp.getResult();
204 LogicalResult BufferizableOpInterface::resolveTensorOpOperandConflicts(
215 Type operandType = opOperand.get().getType();
216 if (!llvm::isa<TensorType>(operandType))
218 if (state.isInPlace(opOperand))
220 if (llvm::isa<UnrankedTensorType>(operandType))
221 return op->
emitError(
"copying of unranked tensors is not implemented");
225 isa<OpResult>(aliasingValues.
getAliases()[0].value) &&
226 !state.bufferizesToMemoryWrite(opOperand) &&
227 state.getAliasingOpOperands(aliasingValues.
getAliases()[0].value)
228 .getNumAliases() == 1 &&
229 !isa<UnrankedTensorType>(
230 aliasingValues.
getAliases()[0].value.getType())) {
238 outOfPlaceValues.push_back(value);
239 if (!state.canOmitTensorCopy(opOperand))
240 copiedOpValues.insert(value);
243 outOfPlaceOpOperands.push_back(&opOperand);
244 if (!state.canOmitTensorCopy(opOperand))
245 copiedOpOperands.insert(&opOperand);
251 for (
OpOperand *opOperand : outOfPlaceOpOperands) {
253 rewriter, op->
getLoc(), opOperand->get(), state.getOptions(),
254 copiedOpOperands.contains(opOperand));
262 for (
Value value : outOfPlaceValues) {
264 rewriter, op->
getLoc(), value, state.getOptions(),
265 copiedOpValues.count(value));
272 if (use->getOwner() ==
copy->getDefiningOp())
276 if (isa<tensor::DimOp>(use->getOwner()))
291 bool isAllowed = !hasAllowRule();
292 for (
const Entry &entry : entries) {
293 bool filterResult = entry.fn(op);
294 switch (entry.type) {
296 isAllowed |= filterResult;
326 llvm::cast<TensorType>(value.
getType()), memorySpace);
333 : functionArgTypeConverterFn(defaultFunctionArgTypeConverter),
334 unknownTypeConverterFn(defaultUnknownTypeConverter) {}
339 bool isFuncBoundaryOp = isa_and_nonnull<func::FuncDialect>(op->
getDialect());
346 BufferizableOpInterface
350 auto bufferizableOp = dyn_cast<BufferizableOpInterface>(op);
353 return bufferizableOp;
356 BufferizableOpInterface
362 LayoutMapOption layoutMapOption) {
366 if (layoutMapOption == LayoutMapOption::IdentityLayoutMap)
373 layoutMapOption == LayoutMapOption::InferLayoutMap;
381 if (
auto bbArg = llvm::dyn_cast<BlockArgument>(value)) {
392 if (
auto bufferizableOp =
getOptions().dynCastBufferizableOp(op))
393 return bufferizableOp.getAliasingOpOperands(value, *
this);
402 if (
auto bufferizableOp =
404 return bufferizableOp.getAliasingValues(opOperand, *
this);
413 if (
auto bufferizableOp =
415 return bufferizableOp.bufferizesToMemoryRead(opOperand, *
this);
425 if (
auto bufferizableOp =
427 return bufferizableOp.bufferizesToMemoryWrite(opOperand, *
this);
437 if (
auto bufferizableOp =
439 return bufferizableOp.bufferizesToAliasOnly(opOperand, *
this);
447 auto opResult = llvm::dyn_cast<OpResult>(value);
453 return bufferizableOp.resultBufferizesToMemoryWrite(opResult, *
this);
460 assert(llvm::isa<TensorType>(value.
getType()) &&
"expected TensorType");
464 workingSet.push_back(&use);
466 while (!workingSet.empty()) {
467 OpOperand *uMaybeReading = workingSet.pop_back_val();
468 if (!visited.insert(uMaybeReading).second)
474 for (
OpOperand &use : alias.value.getUses())
475 workingSet.push_back(&use);
494 workingSet.insert(value);
496 while (!workingSet.empty()) {
497 Value value = workingSet.pop_back_val();
499 if (!
config.revisitAlreadyVisitedValues && visited.contains(value)) {
501 if (
config.alwaysIncludeLeaves)
502 result.insert(value);
505 visited.insert(value);
507 if (condition(value)) {
508 result.insert(value);
515 if (
config.alwaysIncludeLeaves)
516 result.insert(value);
524 if (
config.alwaysIncludeLeaves)
525 result.insert(value);
530 if (
config.followEquivalentOnly &&
534 if (
config.alwaysIncludeLeaves)
535 result.insert(value);
542 if (
config.alwaysIncludeLeaves)
543 result.insert(value);
547 if (
config.followSameTypeOrCastsOnly &&
548 a.opOperand->get().getType() != value.
getType() &&
552 if (
config.alwaysIncludeLeaves)
553 result.insert(value);
557 workingSet.insert(a.opOperand->get());
558 if (visitedOpOperands)
559 visitedOpOperands->insert(a.opOperand);
569 config.alwaysIncludeLeaves =
false;
597 llvm::none_of(aliases,
607 if (isa<ToMemrefOp>(opOperand.
getOwner()))
635 auto rankedTensorType = llvm::dyn_cast<RankedTensorType>(tensor.
getType());
636 assert((!rankedTensorType || llvm::cast<MemRefType>(memrefType).getRank() ==
637 rankedTensorType.getRank()) &&
638 "to_memref would be invalid: mismatching ranks");
645 auto tensorType = llvm::dyn_cast<TensorType>(value.
getType());
646 assert(tensorType &&
"unexpected non-tensor type");
650 if (
auto toTensorOp = value.
getDefiningOp<bufferization::ToTensorOp>())
651 return toTensorOp.getMemref();
657 if (failed(memrefType))
661 .
create<bufferization::ToMemrefOp>(value.
getLoc(), *memrefType, value)
666 FailureOr<BaseMemRefType>
673 FailureOr<BaseMemRefType>
676 assert(llvm::isa<TensorType>(value.
getType()) &&
677 "unexpected non-tensor type");
678 invocationStack.push_back(value);
680 llvm::make_scope_exit([&]() { invocationStack.pop_back(); });
684 auto bufferizableOp =
options.dynCastBufferizableOp(op);
686 return bufferizableOp.getBufferType(value,
options, invocationStack);
691 if (!memSpace.has_value())
692 return op->emitError(
"could not infer memory space");
698 if (
auto bufferizableOp = dyn_cast<BufferizableOpInterface>(op))
699 return bufferizableOp.hasTensorSemantics();
707 "expected one value per OpResult");
713 Value replacement = values[opResult.getResultNumber()];
714 if (llvm::isa<TensorType>(opResult.getType())) {
717 assert((llvm::isa<MemRefType>(replacement.
getType()) ||
718 llvm::isa<UnrankedMemRefType>(replacement.
getType())) &&
719 "tensor op result should be replaced with a memref value");
724 replacement = rewriter.
create<bufferization::ToTensorOp>(
725 replacement.
getLoc(), opResult.getType(), replacement);
727 replacements.push_back(replacement);
747 .
create<memref::AllocOp>(loc, type, dynShape,
750 return b.
create<memref::AllocOp>(loc, type, dynShape).getResult();
757 return (*
memCpyFn)(b, loc, from, to);
759 b.
create<memref::CopyOp>(loc, from, to);
769 MemRefLayoutAttrInterface layout,
771 auto tensorType = llvm::cast<TensorType>(value.
getType());
774 if (
auto unrankedTensorType =
775 llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
776 assert(!layout &&
"UnrankedTensorType cannot have a layout map");
782 auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
785 rankedTensorType.getElementType(), layout,
789 return options.unknownTypeConverterFn(value, memorySpace,
options);
796 if (
auto unrankedTensorType =
797 llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
803 auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
804 int64_t dynamicOffset = ShapedType::kDynamic;
806 ShapedType::kDynamic);
808 dynamicOffset, dynamicStrides);
810 rankedTensorType.getElementType(), stridedLayout,
820 if (
auto unrankedTensorType =
821 llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
827 auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
828 MemRefLayoutAttrInterface layout = {};
830 rankedTensorType.getElementType(), layout,
840 auto bufferizableOp = cast<BufferizableOpInterface>(opResult.
getDefiningOp());
842 bufferizableOp.getAliasingOpOperands(opResult, state);
852 return state.bufferizesToMemoryWrite(*alias.
opOperand);
885 auto isMemoryWriteInsideOp = [&](
Value v) {
889 return state.bufferizesToMemoryWrite(v);
892 config.alwaysIncludeLeaves =
false;
896 isMemoryWriteInsideOp,
config)
910 if (!llvm::isa<TensorType>(opOperand.get().getType()))
913 for (
const auto &it : aliasingValues)
914 if (it.value == value)
915 result.emplace_back(&opOperand, it.relation, it.isDefinite);
923 assert(llvm::isa<TensorType>(value.
getType()) &&
"expected tensor type");
926 if (llvm::isa<BlockArgument>(value))
931 auto opResult = llvm::cast<OpResult>(value);
938 Value equivalentOperand = aliases.
getAliases().front().opOperand->get();
946 if (!memSpace.has_value())
947 return op->
emitError(
"could not infer memory space");
953 BufferizableOpInterface bufferizableOp,
unsigned index) {
954 assert(index < bufferizableOp->getNumRegions() &&
"invalid region index");
955 auto regionInterface =
956 dyn_cast<RegionBranchOpInterface>(bufferizableOp.getOperation());
957 if (!regionInterface)
959 return regionInterface.isRepetitiveRegion(index);
966 if (
auto bbArg = dyn_cast<BlockArgument>(value))
967 if (bbArg.getOwner() != &bbArg.getOwner()->getParent()->getBlocks().front())
975 if (isa<TensorType>(operand.get().getType()))
988 if (llvm::isa<TensorType>(result.getType()))
991 if (!region.getBlocks().empty())
992 for (
BlockArgument bbArg : region.getBlocks().front().getArguments())
993 if (isa<TensorType>(bbArg.getType()))
1001 return any_of(r.getBlocks(), [&](Block &b) {
1002 return any_of(b.getArguments(), [&](BlockArgument bbArg) {
1003 return isaTensor(bbArg.getType());
1007 if (hasTensorBlockArgument)
static void ensureToMemrefOpIsValid(Value tensor, Type memrefType)
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.
static bool isaTensor(Type t)
static llvm::ManagedStatic< PassManagerOptions > options
#define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME)
Base class for generic analysis states.
Attributes are known-constant values of operations.
This class provides a shared interface for ranked and unranked memref types.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
IntegerAttr getI64IntegerAttr(int64_t value)
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...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents an operand of an operation.
This is a value defined by a result of an operation.
Operation is the basic unit of execution within MLIR.
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Block * getBlock()
Returns the operation block that contains this operation.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
operand_type_range getOperandTypes()
MutableArrayRef< OpOperand > getOpOperands()
result_type_range getResultTypes()
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
result_range getOpResults()
Region * getParentRegion()
Returns the region to which the instruction belongs.
unsigned getNumResults()
Return the number of results held by this operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
unsigned getRegionNumber()
Return the number of this region in the parent operation.
Operation * getParentOp()
Return the parent operation this region is attached to.
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.
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
This class provides an efficient unique identifier for a specific C++ type.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Region * getParentRegion()
Return the Region in which this Value is defined.
size_t getNumAliases() const
ArrayRef< T > getAliases() const
AnalysisState provides a variety of helper functions for dealing with tensor values.
bool isValueRead(Value value) const
Return true if the given value is read by an op that bufferizes to a memory read.
AliasingValueList getAliasingValues(OpOperand &opOperand) const
Determine which Value will alias with opOperand if the op is bufferized in place.
virtual bool areAliasingBufferizedValues(Value v1, Value v2) const
Return true if v1 and v2 may bufferize to aliasing buffers.
virtual bool hasUndefinedContents(OpOperand *opOperand) const
Return true if the given tensor has undefined contents.
bool canOmitTensorCopy(OpOperand &opOperand) const
Return true if a copy can always be avoided when allocating a new tensor for the given OpOperand.
bool bufferizesToMemoryWrite(OpOperand &opOperand) const
Return true if opOperand bufferizes to a memory write.
virtual bool isInPlace(OpOperand &opOperand) const
Return true if the given OpResult has been decided to bufferize inplace.
bool bufferizesToAliasOnly(OpOperand &opOperand) const
Return true if opOperand does neither read nor write but bufferizes to an alias.
AliasingOpOperandList getAliasingOpOperands(Value value) const
Determine which OpOperand* will alias with value if the op is bufferized in place.
AnalysisState(const BufferizationOptions &options)
Region * getEnclosingRepetitiveRegion(Operation *op, const BufferizationOptions &options)
Return the closest enclosing repetitive region around the given op.
const BufferizationOptions & getOptions() const
Return a reference to the BufferizationOptions.
bool bufferizesToMemoryRead(OpOperand &opOperand) const
Return true if opOperand bufferizes to a memory read.
SetVector< Value > findValueInReverseUseDefChain(Value value, llvm::function_ref< bool(Value)> condition, TraversalConfig config=TraversalConfig(), llvm::DenseSet< OpOperand * > *visitedOpOperands=nullptr) const
Starting from value, follow the use-def chain in reverse, always selecting the aliasing OpOperands.
SetVector< Value > findDefinitions(Value value) const
Find the values that may define the contents of the given value at runtime.
virtual bool areEquivalentBufferizedValues(Value v1, Value v2) const
Return true if v1 and v2 bufferize to equivalent buffers.
virtual void resetCache()
bool isOpAllowed(Operation *op) const
Return whether the op is allowed or not.
Operation * getOwner() const
Return the owner of this operand.
AliasingOpOperandList defaultGetAliasingOpOperands(Value value, const AnalysisState &state)
This is the default implementation of BufferizableOpInterface::getAliasingOpOperands.
bool defaultResultBufferizesToMemoryWrite(OpResult opResult, const AnalysisState &state)
This is the default implementation of BufferizableOpInterface::resultBufferizesToMemoryWrite.
AliasingValueList unknownGetAliasingValues(OpOperand &opOperand)
This is the default implementation of getAliasingValues in case the owner op does not implement the B...
bool defaultIsRepetitiveRegion(BufferizableOpInterface bufferizableOp, unsigned index)
This is the default implementation of BufferizableOpInterface::isRepetitiveRegion.
AliasingOpOperandList unknownGetAliasingOpOperands(Value value)
This is the default implementation of getAliasingOpOperands in case the defining op does not implemen...
bool defaultHasTensorSemantics(Operation *op)
This is the default implementation of BufferizableOpInterface::hasTensorSemantics.
FailureOr< BaseMemRefType > defaultGetBufferType(Value value, const BufferizationOptions &options, SmallVector< Value > &invocationStack)
This is the default implementation of BufferizableOpInterface::getBufferType.
void replaceOpWithBufferizedValues(RewriterBase &rewriter, Operation *op, ValueRange values)
Replace an op with replacement values.
BaseMemRefType getMemRefTypeWithStaticIdentityLayout(TensorType tensorType, Attribute memorySpace=nullptr)
Return a MemRef type with a static identity layout (i.e., no layout map).
Operation * getOwnerOfValue(Value value)
Return the owner of the given value.
BaseMemRefType getMemRefType(Value value, const BufferizationOptions &options, MemRefLayoutAttrInterface layout={}, Attribute memorySpace=nullptr)
Return a MemRefType to which the type of the given value can be bufferized.
Region * getParallelRegion(Region *region, const BufferizationOptions &options)
If region is a parallel region, return region.
Region * getNextEnclosingRepetitiveRegion(Region *region, const BufferizationOptions &options)
Assuming that the given region is repetitive, find the next enclosing repetitive region.
AliasList< AliasingOpOperand > AliasingOpOperandList
A list of possible aliasing OpOperands.
FailureOr< Value > allocateTensorForShapedValue(OpBuilder &b, Location loc, Value shapedValue, const BufferizationOptions &options, bool copy=true)
Create an AllocTensorOp for the given shaped value (memref or tensor).
FailureOr< BaseMemRefType > getBufferType(Value value, const BufferizationOptions &options)
Return the buffer type for a given Value (tensor) after bufferization without bufferizing any IR.
FailureOr< Value > getBuffer(RewriterBase &rewriter, Value value, const BufferizationOptions &options)
Lookup the buffer for the given value.
BaseMemRefType getMemRefTypeWithFullyDynamicLayout(TensorType tensorType, Attribute memorySpace=nullptr)
Return a MemRef type with fully dynamic layout.
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...
bool hasTensorSemantics(Operation *op)
Return "true" if the given op has tensor semantics and should be bufferized.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
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).
const FrozenRewritePatternSet GreedyRewriteConfig config
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
A maybe aliasing OpOperand.
Options for BufferizableOpInterface-based bufferization.
std::function< void(AnalysisState &)> AnalysisStateInitFn
Initializer function for analysis state.
void setFunctionBoundaryTypeConversion(LayoutMapOption layoutMapOption)
This function controls buffer types on function signatures.
BufferizableOpInterface dynCastBufferizableOp(Operation *op) const
Try to cast the given op to BufferizableOpInterface if the op is allow listed.
bool inferFunctionResultLayout
If true, function result types are inferred from the body of the function.
unsigned int bufferAlignment
Buffer alignment for new memory allocations.
FunctionArgTypeConverterFn functionArgTypeConverterFn
Type converter from tensors to memrefs.
std::optional< AllocationFn > allocationFn
Helper functions for allocation and memory copying.
OpFilter opFilter
A filter that specifies which ops should be bufferized and which ops should be ignored.
bool isOpAllowed(Operation *op) const
Return true if the given op should be bufferized.
std::optional< MemCpyFn > memCpyFn
bool bufferizeFunctionBoundaries
Specifies whether function boundaries (ops in the func dialect) should be bufferized or not.
FailureOr< Value > createAlloc(OpBuilder &b, Location loc, MemRefType type, ValueRange dynShape) const
Create a memref allocation with the given type and dynamic extents.
LogicalResult createMemCpy(OpBuilder &b, Location loc, Value from, Value to) const
Creates a memcpy between two given buffers.
SmallVector< AnalysisStateInitFn > stateInitializers
Initializer functions for analysis state.
Traversal parameters for findValueInReverseUseDefChain.