MLIR  22.0.0git
FuncBufferizableOpInterfaceImpl.cpp
Go to the documentation of this file.
1 //===- BufferizableOpInterfaceImpl.cpp - Impl. of BufferizableOpInterface -===//
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/Dialect.h"
17 #include "mlir/IR/Operation.h"
18 #include <optional>
19 
20 namespace mlir {
21 /// Return all func.return ops in the given function.
24  for (Block &b : funcOp.getBody())
25  if (auto returnOp = dyn_cast<func::ReturnOp>(b.getTerminator()))
26  result.push_back(returnOp);
27  return result;
28 }
29 
30 namespace bufferization {
31 namespace func_ext {
32 
35  auto createdEquiv = equivalentFuncArgs.try_emplace(funcOp, IndexMapping());
36  auto createdAliasingResults =
37  aliasingReturnVals.try_emplace(funcOp, IndexToIndexListMapping());
38  auto createdRead = readBbArgs.try_emplace(funcOp, BbArgIndexSet());
39  auto createdWritten = writtenBbArgs.try_emplace(funcOp, BbArgIndexSet());
40  (void)createdEquiv;
41  (void)createdAliasingResults;
42  (void)createdRead;
43  (void)createdWritten;
44 #ifndef NDEBUG
45  assert(createdEquiv.second && "equivalence info exists already");
46  assert(createdAliasingResults.second && "aliasing info exists already");
47  assert(createdRead.second && "bbarg access info exists already");
48  assert(createdWritten.second && "bbarg access info exists already");
49 #endif // NDEBUG
50 }
51 
52 /// Return the index-th bufferized function argument type. This assumes that the
53 /// specified argument is a tensor. If the tensor is ranked, a layout map may be
54 /// specified by the user (as per `options.functionArgTypeConverterFn`).
55 static BaseMemRefType
56 getBufferizedFunctionArgType(FuncOp funcOp, int64_t index,
58  auto tensorType =
59  dyn_cast<TensorType>(funcOp.getFunctionType().getInput(index));
60  assert(tensorType && "expected TensorType");
61 
62  BaseMemRefType memrefType = options.functionArgTypeConverterFn(
63  tensorType, *options.defaultMemorySpaceFn(tensorType), funcOp, options);
64 
65  auto layoutAttr = funcOp.getArgAttrOfType<MemRefLayoutAttrInterface>(
66  index, BufferizationDialect::kBufferLayoutAttrName);
67  if (!layoutAttr)
68  return memrefType;
69 
70  auto rankedMemrefType = dyn_cast<MemRefType>(memrefType);
71  assert(rankedMemrefType && "buffer layout not supported on unranked tensors");
72  return MemRefType::get(rankedMemrefType.getShape(),
73  rankedMemrefType.getElementType(), layoutAttr,
74  rankedMemrefType.getMemorySpace());
75 }
76 
77 /// Return the FuncOp called by `callOp`.
78 static FuncOp getCalledFunction(CallOpInterface callOp,
79  SymbolTableCollection &symbolTables) {
80  SymbolRefAttr sym =
81  llvm::dyn_cast_if_present<SymbolRefAttr>(callOp.getCallableForCallee());
82  if (!sym)
83  return nullptr;
84  return dyn_cast_or_null<FuncOp>(
85  symbolTables.lookupNearestSymbolFrom(callOp, sym));
86 }
87 
88 /// Return the FuncOp called by `callOp`.
89 static FuncOp getCalledFunction(CallOpInterface callOp,
90  const AnalysisState &state) {
91  auto &oneShotAnalysisState = static_cast<const OneShotAnalysisState &>(state);
92 
93  if (auto *funcAnalysisState =
94  oneShotAnalysisState.getExtension<FuncAnalysisState>()) {
95  // Use the cached symbol tables.
96  return getCalledFunction(callOp, funcAnalysisState->symbolTables);
97  }
98 
99  SymbolTableCollection symbolTables;
100  return getCalledFunction(callOp, symbolTables);
101 }
102 
103 /// Get FuncAnalysisState.
104 static const FuncAnalysisState &
106  assert(isa<OneShotAnalysisState>(state) && "expected OneShotAnalysisState");
107  auto *result = static_cast<const OneShotAnalysisState &>(state)
108  .getExtension<FuncAnalysisState>();
109  assert(result && "FuncAnalysisState does not exist");
110  return *result;
111 }
112 
113 /// Return the state (phase) of analysis of the FuncOp.
115  FuncOp funcOp) {
116  if (!isa<OneShotAnalysisState>(state))
118  auto *funcState = static_cast<const OneShotAnalysisState &>(state)
119  .getExtension<FuncAnalysisState>();
120  if (!funcState)
122  const auto &analyzedFuncOps = funcState->analyzedFuncOps;
123  auto it = analyzedFuncOps.find(funcOp);
124  if (it == analyzedFuncOps.end())
126  return it->second;
127 }
128 
129 /// Return the index of the bbArg in the given FuncOp that is equivalent to the
130 /// specified return value (if any).
131 static std::optional<int64_t>
132 getEquivalentFuncArgIdx(FuncOp funcOp, const FuncAnalysisState &state,
133  int64_t returnValIdx) {
134  auto funcOpIt = state.equivalentFuncArgs.find(funcOp);
135  if (funcOpIt == state.equivalentFuncArgs.end())
136  // No equivalence info stores for funcOp.
137  return std::nullopt;
138 
139  auto retValIt = funcOpIt->getSecond().find(returnValIdx);
140  if (retValIt == funcOpIt->getSecond().end())
141  // Return value has no equivalent bbArg.
142  return std::nullopt;
143 
144  return retValIt->getSecond();
145 }
146 
148  : public BufferizableOpInterface::ExternalModel<CallOpInterface,
149  func::CallOp> {
151  const AnalysisState &state) const {
152  func::CallOp callOp = cast<func::CallOp>(op);
153  FuncOp funcOp = getCalledFunction(callOp, state);
154  assert(funcOp && "expected CallOp to a FuncOp");
155 
157  // FuncOp not analyzed yet. Assume that OpOperand is read.
158  return true;
159 
160  const FuncAnalysisState &funcState = getFuncAnalysisState(state);
161  return funcState.readBbArgs.lookup(funcOp).contains(
162  opOperand.getOperandNumber());
163  }
164 
166  const AnalysisState &state) const {
167  func::CallOp callOp = cast<func::CallOp>(op);
168  FuncOp funcOp = getCalledFunction(callOp, state);
169  assert(funcOp && "expected CallOp to a FuncOp");
170 
172  // FuncOp not analyzed yet. Assume that OpOperand is written.
173  return true;
174 
175  const FuncAnalysisState &funcState = getFuncAnalysisState(state);
176  return funcState.writtenBbArgs.lookup(funcOp).contains(
177  opOperand.getOperandNumber());
178  }
179 
181  const AnalysisState &state) const {
182  func::CallOp callOp = cast<func::CallOp>(op);
183  FuncOp funcOp = getCalledFunction(callOp, state);
184  assert(funcOp && "expected CallOp to a FuncOp");
186  // FuncOp not analyzed yet. Any OpResult may be aliasing.
187  return detail::unknownGetAliasingValues(opOperand);
188 
189  // Get aliasing results from state.
190  const FuncAnalysisState &funcState = getFuncAnalysisState(state);
191  auto aliasingReturnVals =
192  funcState.aliasingReturnVals.lookup(funcOp).lookup(
193  opOperand.getOperandNumber());
194 
195  // Check if the aliasing OpResult is equivalent to the OpOperand.
196  std::optional<int64_t> equivalent = {};
197  if (aliasingReturnVals.size() == 1) {
198  equivalent = getEquivalentFuncArgIdx(funcOp, funcState,
199  aliasingReturnVals.front());
200  assert((!equivalent.has_value() ||
201  *equivalent == opOperand.getOperandNumber()) &&
202  "inconsistent analysis state");
203  }
204  AliasingValueList result;
205  for (int64_t resultIdx : aliasingReturnVals)
206  result.addAlias({callOp->getOpResult(resultIdx),
207  equivalent.has_value() ? BufferRelation::Equivalent
209  /*isDefinite=*/equivalent.has_value()});
210  return result;
211  }
212 
213  FailureOr<BufferLikeType>
215  const BufferizationState &state,
216  SmallVector<Value> &invocationStack) const {
217  auto callOp = cast<func::CallOp>(op);
218 
219  // TODO Avoid recomputing the symbol tables every time.
220  SymbolTableCollection symbolTable;
221 
222  FuncOp funcOp = getCalledFunction(callOp, symbolTable);
223  assert(funcOp && "expected CallOp to a FuncOp");
224 
225  // If the callee was already bufferized, we can directly take the type from
226  // its signature.
227  FunctionType funcType = funcOp.getFunctionType();
228  Type resultType =
229  funcType.getResult(cast<OpResult>(value).getResultNumber());
230  if (auto bufferizedType = dyn_cast<BaseMemRefType>(resultType))
231  return cast<BufferLikeType>(bufferizedType);
232 
233  // Otherwise, call the type converter to compute the bufferized type.
234  auto tensorType = cast<TensorType>(resultType);
235  return cast<BufferLikeType>(options.functionArgTypeConverterFn(
236  tensorType, *options.defaultMemorySpaceFn(tensorType), funcOp,
237  options));
238  }
239 
240  /// All function arguments are writable. It is the responsibility of the
241  /// CallOp to insert buffer copies where necessary.
242  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
244  BufferizationState &state) const {
245  func::CallOp callOp = cast<func::CallOp>(op);
246 
247  // 1. Compute the result types of the new CallOp.
248  SmallVector<Type> resultTypes;
249  for (Value result : callOp.getResults()) {
250  Type returnType = result.getType();
251  if (!isa<TensorType>(returnType)) {
252  // Non-tensor values are returned.
253  resultTypes.push_back(returnType);
254  continue;
255  }
256 
257  // Returning a memref.
258  FailureOr<BufferLikeType> resultType =
259  bufferization::getBufferType(result, options, state);
260  if (failed(resultType))
261  return failure();
262  resultTypes.push_back(*resultType);
263  }
264 
265  // 2. Rewrite tensor operands as memrefs based on type of the already
266  // bufferized callee.
267  SmallVector<Value> newOperands;
268 
269  FuncOp funcOp = getCalledFunction(callOp, state.getSymbolTables());
270  assert(funcOp && "expected CallOp to a FuncOp");
271  FunctionType funcType = funcOp.getFunctionType();
272 
273  for (OpOperand &opOperand : callOp->getOpOperands()) {
274  // Non-tensor operands are just copied.
275  if (!isa<TensorType>(opOperand.get().getType())) {
276  newOperands.push_back(opOperand.get());
277  continue;
278  }
279 
280  // Retrieve buffers for tensor operands.
281  FailureOr<Value> maybeBuffer =
282  getBuffer(rewriter, opOperand.get(), options, state);
283  if (failed(maybeBuffer))
284  return failure();
285  Value buffer = *maybeBuffer;
286 
287  // Caller / callee type mismatch is handled with castOrReallocMemRefValue.
288  auto memRefType = funcType.getInput(opOperand.getOperandNumber());
289  if (!isa<BaseMemRefType>(memRefType)) {
290  // The called function was not bufferized yet. This can happen when
291  // there cycles in the function call graph. Compute the bufferized
292  // result type.
293  FailureOr<BufferLikeType> maybeBufferType =
295  funcOp.getArgument(opOperand.getOperandNumber()), options,
296  state);
297  if (failed(maybeBufferType))
298  return failure();
299  memRefType = *maybeBufferType;
300  }
301 
302  // Since we don't yet have a clear layout story, to_buffer may
303  // conservatively turn tensors into more dynamic memref than necessary.
304  // If the memref type of the callee fails, introduce an extra memref.cast
305  // that will either canonicalize away or fail compilation until we can do
306  // something better. Insert a reallocation + copy if it cannot be
307  // statically guaranteed that a direct cast would be valid.
308  if (buffer.getType() != memRefType) {
309  auto memrefDstType = dyn_cast<MemRefType>(memRefType);
310  assert(memrefDstType &&
311  "buffer layout not supported on unranked tensors");
312  FailureOr<Value> replacement = bufferization::castOrReallocMemRefValue(
313  rewriter, buffer, memrefDstType, options);
314  if (failed(replacement))
315  return failure();
316  buffer = *replacement;
317  }
318  newOperands.push_back(buffer);
319  }
320 
321  // 3. Create the new CallOp.
322  Operation *newCallOp = rewriter.create<func::CallOp>(
323  callOp.getLoc(), funcOp.getSymName(), resultTypes, newOperands);
324  newCallOp->setAttrs(callOp->getAttrs());
325 
326  // 4. Replace the old op with the new op.
327  replaceOpWithBufferizedValues(rewriter, callOp, newCallOp->getResults());
328 
329  return success();
330  }
331 };
332 
334  : public BufferizableOpInterface::ExternalModel<ReturnOpInterface,
335  func::ReturnOp> {
337  const AnalysisState &state) const {
338  return true;
339  }
340 
342  const AnalysisState &state) const {
343  return false;
344  }
345 
347  const AnalysisState &state) const {
348  return {};
349  }
350 
351  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
353  BufferizationState &state) const {
354 #ifndef NDEBUG
355  auto returnOp = cast<func::ReturnOp>(op);
356  assert(isa<FuncOp>(returnOp->getParentOp()) &&
357  "only support FuncOp parent for ReturnOp");
358 #endif // NDEBUG
359 
360  // ReturnOps are bufferized as part of FuncOps.
361  return success();
362  }
363 };
364 
367  FuncOpInterface, FuncOp> {
368 
369  static bool supportsUnstructuredControlFlow() { return true; }
370 
371  bool hasTensorSemantics(Operation *op) const {
372  auto isaTensor = llvm::IsaPred<TensorType>;
373 
374  // A function has tensor semantics if it has tensor arguments/results.
375  auto funcOp = cast<FuncOp>(op);
376  bool hasTensorArg = any_of(funcOp.getArgumentTypes(), isaTensor);
377  bool hasTensorResult = any_of(funcOp.getResultTypes(), isaTensor);
378  if (hasTensorArg || hasTensorResult)
379  return true;
380 
381  // It also has tensor semantics if it has tensor block arguments.
382  // TODO: Decouple bufferization of unstructured control flow from
383  // BufferizableOpInterface implementations. We should only care about
384  // region entry block arguments here (which are already covered by the
385  // argument types of the function).
386  for (Block &block : funcOp.getBody())
387  if (any_of(block.getArgumentTypes(), isaTensor))
388  return true;
389 
390  return false;
391  }
392 
395  const AnalysisState &state) const {
396  return getAliasingBranchOpOperands(op, cast<BlockArgument>(value), state);
397  }
398 
399  FailureOr<BufferLikeType>
401  const BufferizationState &state,
402  SmallVector<Value> &invocationStack) const {
403  auto funcOp = cast<FuncOp>(op);
404  auto bbArg = cast<BlockArgument>(value);
405 
406  // Function arguments are special.
407  if (bbArg.getOwner() == &funcOp.getBody().front())
408  return cast<BufferLikeType>(
409  getBufferizedFunctionArgType(funcOp, bbArg.getArgNumber(), options));
410 
412  getBufferType(op, value, options, state, invocationStack);
413  }
414 
415  /// Rewrite function bbArgs and return values into buffer form. This function
416  /// bufferizes the function signature and the ReturnOp. When the entire
417  /// function body has been bufferized, function return types can be switched
418  /// to more concise memref types as part of `foldMemRefCasts`.
419  ///
420  /// All function bbArgs are writable unless they are explicitly marked as
421  /// read-only. Callers must insert copies when needed.
422  LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
424  BufferizationState &state) const {
425  auto funcOp = cast<FuncOp>(op);
426  FunctionType funcType = funcOp.getFunctionType();
427 
428  // Compute the argument types.
429  SmallVector<Type> argTypes;
430  for (const auto &it : llvm::enumerate(funcType.getInputs())) {
431  Type argType = it.value();
432  if (isa<TensorType>(argType)) {
433  argTypes.push_back(
434  getBufferizedFunctionArgType(funcOp, it.index(), options));
435  continue;
436  }
437  argTypes.push_back(argType);
438  }
439 
440  // Compute the result types.
441  SmallVector<Type> retTypes;
442  for (Type resultType : funcType.getResults()) {
443  if (auto tensorType = dyn_cast<TensorType>(resultType)) {
444  BaseMemRefType resultType = options.functionArgTypeConverterFn(
445  tensorType, *options.defaultMemorySpaceFn(tensorType), funcOp,
446  options);
447  retTypes.push_back(resultType);
448  continue;
449  }
450  retTypes.push_back(resultType);
451  }
452 
453  // Compute the new function type.
454  auto newFuncType = FunctionType::get(op->getContext(), argTypes, retTypes);
455 
456  // If the function has no body, set the new function type and we are done.
457  if (funcOp.isExternal()) {
458  funcOp.setType(newFuncType);
459  return success();
460  }
461 
462  // 1. Bufferize every block.
463  for (Block &block : funcOp.getBody())
464  if (failed(bufferization::bufferizeBlockSignature(&block, rewriter,
465  options, state)))
466  return failure();
467 
468  // 2. Bufferize the operands of the all return op.
469  for (func::ReturnOp returnOp : getReturnOps(funcOp)) {
470  assert(returnOp->getNumOperands() == retTypes.size() &&
471  "incorrect number of return values");
472  SmallVector<Value> returnValues;
473  for (auto [returnVal, bufferizedType] :
474  llvm::zip_equal(returnOp->getOperands(), retTypes)) {
475  auto tensorType = dyn_cast<TensorType>(returnVal.getType());
476  rewriter.setInsertionPoint(returnOp);
477 
478  // If not a tensor type just forward it.
479  if (!tensorType) {
480  returnValues.push_back(returnVal);
481  continue;
482  }
483 
484  // Note: If `inferFunctionResultLayout = true`, casts are later folded
485  // away.
486  Value toBufferOp = rewriter.create<bufferization::ToBufferOp>(
487  returnOp.getLoc(), bufferizedType, returnVal);
488  returnValues.push_back(toBufferOp);
489  }
490 
491  returnOp.getOperandsMutable().assign(returnValues);
492  }
493 
494  // 3. Set the new function type.
495  funcOp.setType(newFuncType);
496  return success();
497  }
498 
499  /// Return `true` if the given function argument is writable.
500  bool isWritable(Operation *op, Value value,
501  const AnalysisState &state) const {
502  auto funcOp = cast<FuncOp>(op);
503  BlockArgument bbArg = dyn_cast<BlockArgument>(value);
504  assert(bbArg && "expected BlockArgument");
505 
506  // Non-entry block arguments are always writable. (They may alias with
507  // values that are not writable, which will turn them into read-only.)
508  if (bbArg.getOwner() != &funcOp.getBody().front())
509  return true;
510 
511  // "bufferization.writable" overrides other writability decisions. This is
512  // currently used for testing only.
513  if (BoolAttr writable = funcOp.getArgAttrOfType<BoolAttr>(
514  bbArg.getArgNumber(), BufferizationDialect::kWritableAttrName))
515  return writable.getValue();
516 
517  // All function arguments are writable by default.
518  return true;
519  }
520 };
521 
522 } // namespace func_ext
523 } // namespace bufferization
524 } // namespace mlir
525 
528  registry.addExtension(+[](MLIRContext *ctx, func::FuncDialect *dialect) {
529  func::CallOp::attachInterface<func_ext::CallOpInterface>(*ctx);
530  func::FuncOp::attachInterface<func_ext::FuncOpInterface>(*ctx);
531  func::ReturnOp::attachInterface<func_ext::ReturnOpInterface>(*ctx);
532  });
533 }
static bool isaTensor(Type t)
static llvm::ManagedStatic< PassManagerOptions > options
This class provides a shared interface for ranked and unranked memref types.
Definition: BuiltinTypes.h:104
This class represents an argument of a Block.
Definition: Value.h:309
Block * getOwner() const
Returns the block that owns this argument.
Definition: Value.h:318
unsigned getArgNumber() const
Returns the number of this argument.
Definition: Value.h:321
Block represents an ordered list of Operations.
Definition: Block.h:33
Operation & front()
Definition: Block.h:153
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition: Builders.h:396
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:452
This class represents an operand of an operation.
Definition: Value.h:257
unsigned getOperandNumber()
Return which operand this is in the OpOperand list of the Operation.
Definition: Value.cpp:228
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
void setAttrs(DictionaryAttr newAttrs)
Set the attributes from a dictionary on this operation.
Definition: Operation.cpp:304
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:216
result_range getResults()
Definition: Operation.h:415
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
Definition: PatternMatch.h:358
This class represents a collection of SymbolTables.
Definition: SymbolTable.h:283
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
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
AnalysisState provides a variety of helper functions for dealing with tensor values.
BufferizationState provides information about the state of the IR during the bufferization process.
State for analysis-enabled bufferization.
AliasingValueList unknownGetAliasingValues(OpOperand &opOperand)
This is the default implementation of getAliasingValues in case the owner op does not implement the B...
static std::optional< int64_t > getEquivalentFuncArgIdx(FuncOp funcOp, const FuncAnalysisState &state, int64_t returnValIdx)
Return the index of the bbArg in the given FuncOp that is equivalent to the specified return value (i...
FuncOpAnalysisState
The state of analysis of a FuncOp.
void registerBufferizableOpInterfaceExternalModels(DialectRegistry &registry)
static FuncOp getCalledFunction(CallOpInterface callOp, SymbolTableCollection &symbolTables)
Return the FuncOp called by callOp.
static FuncOpAnalysisState getFuncOpAnalysisState(const AnalysisState &state, FuncOp funcOp)
Return the state (phase) of analysis of the FuncOp.
static const FuncAnalysisState & getFuncAnalysisState(const AnalysisState &state)
Get FuncAnalysisState.
static BaseMemRefType getBufferizedFunctionArgType(FuncOp funcOp, int64_t index, const BufferizationOptions &options)
Return the index-th bufferized function argument type.
void replaceOpWithBufferizedValues(RewriterBase &rewriter, Operation *op, ValueRange values)
Replace an op with replacement values.
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.
SmallVector< func::ReturnOp > getReturnOps(func::FuncOp funcOp)
Helper function that returns all func.return ops in the given function.
LogicalResult bufferizeBlockSignature(Block *block, RewriterBase &rewriter, const BufferizationOptions &options, BufferizationState &state)
Bufferize the signature of block and its callers (i.e., ops that have the given block as a successor)...
Definition: Bufferize.cpp:396
FailureOr< Value > getBuffer(RewriterBase &rewriter, Value value, const BufferizationOptions &options, const BufferizationState &state)
Lookup the buffer for the given value.
FailureOr< BufferLikeType > getBufferType(Value value, const BufferizationOptions &options, const BufferizationState &state)
Return the buffer type for a given Value (tensor) after bufferization without bufferizing any IR.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:344
Include the generated interface declarations.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
Options for BufferizableOpInterface-based bufferization.
A template that provides a default implementation of getAliasingOpOperands for ops that support unstr...
AliasingOpOperandList getAliasingBranchOpOperands(Operation *op, BlockArgument bbArg, const AnalysisState &state) const
Assuming that bbArg is a block argument of a block that belongs to the given op, return all OpOperand...
FailureOr< BufferLikeType > getBufferType(Operation *op, Value value, const BufferizationOptions &options, const BufferizationState &state, SmallVector< Value > &invocationStack) const
FailureOr< BufferLikeType > getBufferType(Operation *op, Value value, const BufferizationOptions &options, const BufferizationState &state, SmallVector< Value > &invocationStack) const
AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, const AnalysisState &state) const
LogicalResult bufferize(Operation *op, RewriterBase &rewriter, const BufferizationOptions &options, BufferizationState &state) const
All function arguments are writable.
bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand, const AnalysisState &state) const
bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand, const AnalysisState &state) const
Extra analysis state that is required for bufferization of function boundaries.
DenseMap< FuncOp, IndexMapping > equivalentFuncArgs
A mapping of ReturnOp OpOperand indices to equivalent FuncOp BBArg indices.
DenseMap< FuncOp, IndexToIndexListMapping > aliasingReturnVals
A mapping of FuncOp BBArg indices to aliasing ReturnOp OpOperand indices.
DenseMap< int64_t, SmallVector< int64_t > > IndexToIndexListMapping
A mapping of indices to a list of indices.
DenseMap< FuncOp, BbArgIndexSet > readBbArgs
A set of all read BlockArguments of FuncOps.
DenseSet< int64_t > BbArgIndexSet
A set of block argument indices.
DenseMap< FuncOp, BbArgIndexSet > writtenBbArgs
A set of all written-to BlockArguments of FuncOps.
DenseMap< FuncOp, FuncOpAnalysisState > analyzedFuncOps
Keep track of which FuncOps are fully analyzed or currently being analyzed.
void startFunctionAnalysis(FuncOp funcOp)
This function is called right before analyzing the given FuncOp.
DenseMap< int64_t, int64_t > IndexMapping
A mapping of indices to indices.
AliasingOpOperandList getAliasingOpOperands(Operation *op, Value value, const AnalysisState &state) const
bool isWritable(Operation *op, Value value, const AnalysisState &state) const
Return true if the given function argument is writable.
FailureOr< BufferLikeType > getBufferType(Operation *op, Value value, const BufferizationOptions &options, const BufferizationState &state, SmallVector< Value > &invocationStack) const
LogicalResult bufferize(Operation *op, RewriterBase &rewriter, const BufferizationOptions &options, BufferizationState &state) const
Rewrite function bbArgs and return values into buffer form.
AliasingValueList getAliasingValues(Operation *op, OpOperand &opOperand, const AnalysisState &state) const
bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand, const AnalysisState &state) const
bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand, const AnalysisState &state) const
LogicalResult bufferize(Operation *op, RewriterBase &rewriter, const BufferizationOptions &options, BufferizationState &state) const