MLIR  17.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 
15 #include "mlir/IR/Dialect.h"
16 #include "mlir/IR/Operation.h"
17 #include <optional>
18 
19 namespace mlir {
20 namespace bufferization {
21 namespace func_ext {
22 
25  auto createdEquiv = equivalentFuncArgs.try_emplace(funcOp, IndexMapping());
26  auto createdAliasingOperands =
27  aliasingFuncArgs.try_emplace(funcOp, IndexToIndexListMapping());
28  auto createdAliasingResults =
29  aliasingReturnVals.try_emplace(funcOp, IndexToIndexListMapping());
30  auto createdRead = readBbArgs.try_emplace(funcOp, BbArgIndexSet());
31  auto createdWritten = writtenBbArgs.try_emplace(funcOp, BbArgIndexSet());
32  (void)createdEquiv;
33  (void)createdAliasingOperands;
34  (void)createdAliasingResults;
35  (void)createdRead;
36  (void)createdWritten;
37 #ifndef NDEBUG
38  assert(createdEquiv.second && "equivalence info exists already");
39  assert(createdAliasingOperands.second && "aliasing info exists already");
40  assert(createdAliasingResults.second && "aliasing info exists already");
41  assert(createdRead.second && "bbarg access info exists already");
42  assert(createdWritten.second && "bbarg access info exists already");
43 #endif // NDEBUG
44 }
45 
46 /// Return the unique ReturnOp that terminates `funcOp`.
47 /// Return nullptr if there is no such unique ReturnOp.
48 static func::ReturnOp getAssumedUniqueReturnOp(FuncOp funcOp) {
49  func::ReturnOp returnOp;
50  for (Block &b : funcOp.getBody()) {
51  if (auto candidateOp = dyn_cast<func::ReturnOp>(b.getTerminator())) {
52  if (returnOp)
53  return nullptr;
54  returnOp = candidateOp;
55  }
56  }
57  return returnOp;
58 }
59 
60 /// Return the index-th bufferized function argument type. This assumes that the
61 /// specified argument is a tensor. If the tensor is ranked, a layout map may be
62 /// specified by the user. If no layout map is specified, the default layout map
63 /// (as per `options.functionBoundaryTypeConversion`) is used.
64 static BaseMemRefType
65 getBufferizedFunctionArgType(FuncOp funcOp, int64_t index,
67  auto tensorType =
68  funcOp.getFunctionType().getInput(index).dyn_cast<TensorType>();
69  assert(tensorType && "expected TensorType");
70 
71  BaseMemRefType memrefType;
72  if (options.functionBoundaryTypeConversion ==
73  LayoutMapOption::IdentityLayoutMap) {
74  memrefType = getMemRefTypeWithStaticIdentityLayout(tensorType);
75  } else {
76  // Note: Layout maps on function parameters cannot be inferred. The best we
77  // can do at the moment is "fully dynamic".
78  memrefType = getMemRefTypeWithFullyDynamicLayout(tensorType);
79  }
80 
81  auto layoutAttr = funcOp.getArgAttrOfType<AffineMapAttr>(
82  index, BufferizationDialect::kBufferLayoutAttrName);
83  if (!layoutAttr)
84  return memrefType;
85 
86  auto rankedMemrefType = memrefType.dyn_cast<MemRefType>();
87  assert(rankedMemrefType && "buffer layout not supported on unranked tensors");
88  return MemRefType::get(
89  rankedMemrefType.getShape(), rankedMemrefType.getElementType(),
90  layoutAttr.getValue(), rankedMemrefType.getMemorySpace());
91 }
92 
93 /// Return the FuncOp called by `callOp`.
94 static FuncOp getCalledFunction(CallOpInterface callOp) {
95  SymbolRefAttr sym = callOp.getCallableForCallee().dyn_cast<SymbolRefAttr>();
96  if (!sym)
97  return nullptr;
98  return dyn_cast_or_null<FuncOp>(
100 }
101 
102 /// Get FuncAnalysisState.
103 static const FuncAnalysisState &
105  assert(isa<OneShotAnalysisState>(state) && "expected OneShotAnalysisState");
106  auto *result = static_cast<const OneShotAnalysisState &>(state)
107  .getExtension<FuncAnalysisState>();
108  assert(result && "FuncAnalysisState does not exist");
109  return *result;
110 }
111 
112 /// Return the state (phase) of analysis of the FuncOp.
114  FuncOp funcOp) {
115  if (!isa<OneShotAnalysisState>(state))
117  auto *funcState = static_cast<const OneShotAnalysisState &>(state)
118  .getExtension<FuncAnalysisState>();
119  if (!funcState)
121  const auto &analyzedFuncOps = funcState->analyzedFuncOps;
122  auto it = analyzedFuncOps.find(funcOp);
123  if (it == analyzedFuncOps.end())
125  return it->second;
126 }
127 
128 /// Return the index of the bbArg in the given FuncOp that is equivalent to the
129 /// specified return value (if any).
130 static std::optional<int64_t>
131 getEquivalentFuncArgIdx(FuncOp funcOp, const FuncAnalysisState &state,
132  int64_t returnValIdx) {
133  auto funcOpIt = state.equivalentFuncArgs.find(funcOp);
134  if (funcOpIt == state.equivalentFuncArgs.end())
135  // No equivalence info stores for funcOp.
136  return std::nullopt;
137 
138  auto retValIt = funcOpIt->getSecond().find(returnValIdx);
139  if (retValIt == funcOpIt->getSecond().end())
140  // Return value has no equivalent bbArg.
141  return std::nullopt;
142 
143  return retValIt->getSecond();
144 }
145 
147  : public BufferizableOpInterface::ExternalModel<CallOpInterface,
148  func::CallOp> {
150  const AnalysisState &state) const {
151  func::CallOp callOp = cast<func::CallOp>(op);
152  FuncOp funcOp = getCalledFunction(callOp);
153  assert(funcOp && "expected CallOp to a FuncOp");
154 
156  // FuncOp not analyzed yet. Assume that OpOperand is read.
157  return true;
158 
159  const FuncAnalysisState &funcState = getFuncAnalysisState(state);
160  return funcState.readBbArgs.lookup(funcOp).contains(
161  opOperand.getOperandNumber());
162  }
163 
165  const AnalysisState &state) const {
166  func::CallOp callOp = cast<func::CallOp>(op);
167  FuncOp funcOp = getCalledFunction(callOp);
168  assert(funcOp && "expected CallOp to a FuncOp");
169 
171  // FuncOp not analyzed yet. Assume that OpOperand is written.
172  return true;
173 
174  const FuncAnalysisState &funcState = getFuncAnalysisState(state);
175  return funcState.writtenBbArgs.lookup(funcOp).contains(
176  opOperand.getOperandNumber());
177  }
178 
180  const AnalysisState &state) const {
181  func::CallOp callOp = cast<func::CallOp>(op);
182  FuncOp funcOp = getCalledFunction(callOp);
183  assert(funcOp && "expected CallOp to a FuncOp");
184  if (getFuncOpAnalysisState(state, funcOp) !=
186  // FuncOp not analyzed yet. Any OpResult may be aliasing.
187  SmallVector<OpResult> result;
188  for (OpResult opResult : op->getOpResults())
189  if (opResult.getType().isa<TensorType>())
190  result.push_back(opResult);
191  return result;
192  }
193 
194  // Get aliasing results from state.
195  const FuncAnalysisState &funcState = getFuncAnalysisState(state);
196  auto aliasingReturnVals =
197  funcState.aliasingReturnVals.lookup(funcOp).lookup(
198  opOperand.getOperandNumber());
199  SmallVector<OpResult> result;
200  for (int64_t resultIdx : aliasingReturnVals)
201  result.push_back(callOp->getOpResult(resultIdx));
202  return result;
203  }
204 
207  const AnalysisState &state) const {
208  func::CallOp callOp = cast<func::CallOp>(op);
209  FuncOp funcOp = getCalledFunction(callOp);
210  assert(funcOp && "expected CallOp to a FuncOp");
211  if (getFuncOpAnalysisState(state, funcOp) !=
213  // FuncOp not analyzed yet. Any OpOperand may be aliasing.
215  for (OpOperand &opOperand : op->getOpOperands())
216  if (opOperand.get().getType().isa<TensorType>())
217  result.push_back(&opOperand);
218  return result;
219  }
220 
221  // Get aliasing bbArgs from state.
222  const FuncAnalysisState &funcState = getFuncAnalysisState(state);
223  auto aliasingFuncArgs = funcState.aliasingFuncArgs.lookup(funcOp).lookup(
224  opResult.getResultNumber());
226  for (int64_t bbArgIdx : aliasingFuncArgs)
227  result.push_back(&callOp->getOpOperand(bbArgIdx));
228  return result;
229  }
230 
232  const AnalysisState &state) const {
233  func::CallOp callOp = cast<func::CallOp>(op);
234  FuncOp funcOp = getCalledFunction(callOp);
235  assert(funcOp && "expected CallOp to a FuncOp");
236  if (getFuncOpAnalysisState(state, funcOp) !=
238  // Function not analyzed yet. The conservative answer is "None".
239  return BufferRelation::None;
240  }
241 
242  const FuncAnalysisState &funcState = getFuncAnalysisState(state);
243  std::optional<int64_t> maybeEquiv =
244  getEquivalentFuncArgIdx(funcOp, funcState, opResult.getResultNumber());
245  if (maybeEquiv) {
246 #ifndef NDEBUG
247  SmallVector<OpOperand *> aliasingOpOperands =
248  getAliasingOpOperand(op, opResult, state);
249  assert(aliasingOpOperands.size() == 1 &&
250  "expected exactly 1 aliasing OpOperand");
251  assert(aliasingOpOperands.front()->getOperandNumber() == *maybeEquiv &&
252  "inconsistent analysis state");
253 #endif
255  }
256  return BufferRelation::None;
257  }
258 
259  /// All function arguments are writable. It is the responsibility of the
260  /// CallOp to insert buffer copies where necessary.
262  const BufferizationOptions &options) const {
263  func::CallOp callOp = cast<func::CallOp>(op);
264  unsigned numResults = callOp.getNumResults();
265  unsigned numOperands = callOp->getNumOperands();
266  FuncOp funcOp = getCalledFunction(callOp);
267  assert(funcOp && "expected CallOp to a FuncOp");
268  FunctionType funcType = funcOp.getFunctionType();
269 
270  // Result types of the bufferized CallOp.
271  SmallVector<Type> resultTypes;
272  // Replacement values for the existing CallOp. These are usually the results
273  // of the bufferized CallOp, unless a tensor result folds onto an operand.
274  SmallVector<Value> replacementValues(numResults, Value());
275  // For non-tensor results: A mapping from return val indices of the old
276  // CallOp to return val indices of the bufferized CallOp.
277  SmallVector<std::optional<unsigned>> retValMapping(numResults,
278  std::nullopt);
279  // Operands of the bufferized CallOp.
280  SmallVector<Value> newOperands(numOperands, Value());
281 
282  // 1. Compute the result types of the new CallOp.
283  for (const auto &it : llvm::enumerate(callOp.getResultTypes())) {
284  unsigned returnValIdx = it.index();
285  Type returnType = it.value();
286  if (!returnType.isa<TensorType>()) {
287  // Non-tensor values are returned.
288  retValMapping[returnValIdx] = resultTypes.size();
289  resultTypes.push_back(returnType);
290  continue;
291  }
292 
293  // Returning a memref.
294  retValMapping[returnValIdx] = resultTypes.size();
295  resultTypes.push_back(funcType.getResult(resultTypes.size()));
296  }
297 
298  // 2. Rewrite tensor operands as memrefs based on `bufferizedFuncType`.
299  for (OpOperand &opOperand : callOp->getOpOperands()) {
300  unsigned idx = opOperand.getOperandNumber();
301  Value tensorOperand = opOperand.get();
302 
303  // Non-tensor operands are just copied.
304  if (!tensorOperand.getType().isa<TensorType>()) {
305  newOperands[idx] = tensorOperand;
306  continue;
307  }
308 
309  // Retrieve buffers for tensor operands.
310  Value buffer = newOperands[idx];
311  if (!buffer) {
312  FailureOr<Value> maybeBuffer =
313  getBuffer(rewriter, opOperand.get(), options);
314  if (failed(maybeBuffer))
315  return failure();
316  buffer = *maybeBuffer;
317  }
318 
319  // Caller / callee type mismatch is handled with a CastOp.
320  auto memRefType = funcType.getInput(idx);
321  // Since we don't yet have a clear layout story, to_memref may
322  // conservatively turn tensors into more dynamic memref than necessary.
323  // If the memref type of the callee fails, introduce an extra memref.cast
324  // that will either canonicalize away or fail compilation until we can do
325  // something better.
326  if (buffer.getType() != memRefType) {
327  assert(
328  memref::CastOp::areCastCompatible(buffer.getType(), memRefType) &&
329  "CallOp::bufferize: cast incompatible");
330  Value castBuffer = rewriter.create<memref::CastOp>(callOp.getLoc(),
331  memRefType, buffer);
332  buffer = castBuffer;
333  }
334  newOperands[idx] = buffer;
335  }
336 
337  // 3. Create the new CallOp.
338  Operation *newCallOp = rewriter.create<func::CallOp>(
339  callOp.getLoc(), funcOp.getSymName(), resultTypes, newOperands);
340  newCallOp->setAttrs(callOp->getAttrs());
341  // Get replacement values.
342  for (unsigned i = 0; i < replacementValues.size(); ++i) {
343  if (replacementValues[i])
344  continue;
345  replacementValues[i] = newCallOp->getResult(*retValMapping[i]);
346  }
347 
348  // 4. Replace the old op with the new op.
349  replaceOpWithBufferizedValues(rewriter, callOp, replacementValues);
350 
351  return success();
352  }
353 };
354 
356  : public BufferizableOpInterface::ExternalModel<ReturnOpInterface,
357  func::ReturnOp> {
359  const AnalysisState &state) const {
360  return true;
361  }
362 
364  const AnalysisState &state) const {
365  return false;
366  }
367 
369  const AnalysisState &state) const {
370  return {};
371  }
372 
374  const BufferizationOptions &options) const {
375 #ifndef NDEBUG
376  auto returnOp = cast<func::ReturnOp>(op);
377  assert(isa<FuncOp>(returnOp->getParentOp()) &&
378  "only support FuncOp parent for ReturnOp");
379 #endif // NDEBUG
380 
381  // ReturnOps are bufferized as part of FuncOps.
382  return success();
383  }
384 };
385 
387  : public BufferizableOpInterface::ExternalModel<FuncOpInterface, FuncOp> {
388  /// Rewrite function bbArgs and return values into buffer form. This function
389  /// bufferizes the function signature and the ReturnOp. When the entire
390  /// function body has been bufferized, function return types can be switched
391  /// to more concise memref types as part of `foldMemRefCasts`.
392  ///
393  /// All function bbArgs are writable unless they are explicitly marked as
394  /// read-only. Callers must insert copies when needed.
396  const BufferizationOptions &options) const {
397  auto funcOp = cast<FuncOp>(op);
398  FunctionType funcType = funcOp.getFunctionType();
399 
400  // Construct the bufferized function type.
401  SmallVector<Type> argTypes;
402  for (const auto &it : llvm::enumerate(funcType.getInputs())) {
403  Type argType = it.value();
404  if (auto tensorType = argType.dyn_cast<TensorType>()) {
405  argTypes.push_back(
406  getBufferizedFunctionArgType(funcOp, it.index(), options));
407  continue;
408  }
409  argTypes.push_back(argType);
410  }
411 
412  // Bodiless functions are assumed opaque and we cannot know the
413  // bufferization contract they want to enforce. As a consequence, only
414  // support functions that don't return any tensors atm.
415  if (funcOp.getBody().empty()) {
416  SmallVector<Type> retTypes;
417  for (Type resultType : funcType.getResults()) {
418  if (resultType.isa<TensorType>())
419  return funcOp->emitError() << "cannot bufferize bodiless function "
420  << "that returns a tensor";
421  retTypes.push_back(resultType);
422  }
423  funcOp.setType(FunctionType::get(op->getContext(), argTypes, retTypes));
424  return success();
425  }
426 
427  // TODO: Support functions with multiple returns.
428  func::ReturnOp returnOp = getAssumedUniqueReturnOp(funcOp);
429  assert(returnOp && "expected func with single return op");
430  Location loc = returnOp.getLoc();
431 
432  // 1. Rewrite the bbArgs. Turn every tensor bbArg into a memref bbArg.
433  Block &frontBlock = funcOp.getBody().front();
434  for (BlockArgument &bbArg : frontBlock.getArguments()) {
435  auto tensorType = bbArg.getType().dyn_cast<TensorType>();
436  // Non-tensor types stay the same.
437  if (!tensorType)
438  continue;
439 
440  // Collect all uses of the bbArg.
441  SmallVector<OpOperand *> bbArgUses;
442  for (OpOperand &use : bbArg.getUses())
443  bbArgUses.push_back(&use);
444 
445  // Change the bbArg type to memref.
446  Type memrefType =
447  getBufferizedFunctionArgType(funcOp, bbArg.getArgNumber(), options);
448  bbArg.setType(memrefType);
449 
450  // Replace all uses of the original tensor bbArg.
451  rewriter.setInsertionPointToStart(&frontBlock);
452  if (!bbArgUses.empty()) {
453  // Insert to_tensor because the remaining function body has not been
454  // bufferized yet.
455  Value toTensorOp =
456  rewriter.create<bufferization::ToTensorOp>(funcOp.getLoc(), bbArg);
457  for (OpOperand *use : bbArgUses)
458  use->set(toTensorOp);
459  }
460  }
461 
462  // 2. For each result, keep track of which inplace argument it reuses.
463  SmallVector<Value> returnValues;
464  for (OpOperand &returnOperand : returnOp->getOpOperands()) {
465  Value returnVal = returnOperand.get();
466  auto tensorType = returnVal.getType().dyn_cast<TensorType>();
467  rewriter.setInsertionPoint(returnOp);
468 
469  // If not a tensor type just forward it.
470  if (!tensorType) {
471  returnValues.push_back(returnVal);
472  continue;
473  }
474 
475  BaseMemRefType resultType;
476  if (options.functionBoundaryTypeConversion ==
477  LayoutMapOption::IdentityLayoutMap) {
478  resultType = getMemRefTypeWithStaticIdentityLayout(tensorType);
479  } else {
480  // Note: If `InferLayoutMap`, cast are later folded away.
481  resultType = getMemRefTypeWithFullyDynamicLayout(tensorType);
482  }
483  Value toMemrefOp = rewriter.create<bufferization::ToMemrefOp>(
484  loc, resultType, returnVal);
485  returnValues.push_back(toMemrefOp);
486  }
487 
488  // 3. Rewrite the terminator without the in-place bufferizable values.
489  returnOp.getOperandsMutable().assign(returnValues);
490 
491  // 4. Rewrite the FuncOp type to buffer form.
492  funcOp.setType(FunctionType::get(op->getContext(), argTypes,
493  ValueRange(returnValues).getTypes()));
494 
495  return success();
496  }
497 
498  /// Return `true` if the given function argument is writable.
499  bool isWritable(Operation *op, Value value,
500  const AnalysisState &state) const {
501  auto funcOp = cast<FuncOp>(op);
502  BlockArgument bbArg = value.dyn_cast<BlockArgument>();
503  assert(bbArg && "expected BlockArgument");
504 
505  // "bufferization.writable" overrides other writability decisions. This is
506  // currently used for testing only.
507  if (BoolAttr writable = funcOp.getArgAttrOfType<BoolAttr>(
508  bbArg.getArgNumber(), BufferizationDialect::kWritableAttrName))
509  return writable.getValue();
510 
511  // All function arguments are writable by default.
512  return true;
513  }
514 };
515 
516 } // namespace func_ext
517 } // namespace bufferization
518 } // namespace mlir
519 
522  registry.addExtension(+[](MLIRContext *ctx, func::FuncDialect *dialect) {
523  func::CallOp::attachInterface<func_ext::CallOpInterface>(*ctx);
524  func::FuncOp::attachInterface<func_ext::FuncOpInterface>(*ctx);
525  func::ReturnOp::attachInterface<func_ext::ReturnOpInterface>(*ctx);
526  });
527 }
static llvm::ManagedStatic< PassManagerOptions > options
This class provides a shared interface for ranked and unranked memref types.
Definition: BuiltinTypes.h:113
This class represents an argument of a Block.
Definition: Value.h:304
unsigned getArgNumber() const
Returns the number of this argument.
Definition: Value.h:316
Block represents an ordered list of Operations.
Definition: Block.h:30
BlockArgListType getArguments()
Definition: Block.h:76
Operation & front()
Definition: Block.h:142
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.
void addExtension(std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
This class provides support for representing a failure result, or a valid value of type T.
Definition: LogicalResult.h:78
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:56
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition: Builders.h:384
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition: Builders.h:351
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:422
This class represents an operand of an operation.
Definition: Value.h:255
unsigned getOperandNumber()
Return which operand this is in the OpOperand list of the Operation.
Definition: Value.cpp:212
This is a value defined by a result of an operation.
Definition: Value.h:450
unsigned getResultNumber() const
Returns the number of this result.
Definition: Value.h:462
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:75
void setAttrs(DictionaryAttr newAttrs)
Set the attribute dictionary on this operation.
Definition: Operation.h:406
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition: Operation.h:368
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:191
MutableArrayRef< OpOperand > getOpOperands()
Definition: Operation.h:344
result_range getOpResults()
Definition: Operation.h:381
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
Definition: PatternMatch.h:399
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
Definition: BuiltinTypes.h:77
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
U dyn_cast() const
Definition: Types.h:311
bool isa() const
Definition: Types.h:301
This class provides an abstraction over the different types of ranges over Values.
Definition: ValueRange.h:350
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:93
Type getType() const
Return the type of this value.
Definition: Value.h:122
U dyn_cast() const
Definition: Value.h:103
AnalysisState provides a variety of helper functions for dealing with tensor values.
State for analysis-enabled bufferization.
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 FuncOpAnalysisState getFuncOpAnalysisState(const AnalysisState &state, FuncOp funcOp)
Return the state (phase) of analysis of the FuncOp.
static func::ReturnOp getAssumedUniqueReturnOp(FuncOp funcOp)
Return the unique ReturnOp that terminates funcOp.
static const FuncAnalysisState & getFuncAnalysisState(const AnalysisState &state)
Get FuncAnalysisState.
static FuncOp getCalledFunction(CallOpInterface callOp)
Return the FuncOp called by callOp.
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.
BaseMemRefType getMemRefTypeWithStaticIdentityLayout(TensorType tensorType, Attribute memorySpace=nullptr)
Return a MemRef type with a static identity layout (i.e., no layout map).
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.
BufferRelation
Specify fine-grain relationship between buffers to enable more analysis.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:223
Include the generated interface declarations.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
Options for BufferizableOpInterface-based bufferization.
SmallVector< OpOperand * > getAliasingOpOperand(Operation *op, OpResult opResult, const AnalysisState &state) const
BufferRelation bufferRelation(Operation *op, OpResult opResult, const AnalysisState &state) const
LogicalResult bufferize(Operation *op, RewriterBase &rewriter, const BufferizationOptions &options) const
All function arguments are writable.
SmallVector< OpResult > getAliasingOpResult(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
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, IndexToIndexListMapping > aliasingFuncArgs
A mapping of ReturnOp OpOperand indices to aliasing FuncOp BBArg 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.
bool isWritable(Operation *op, Value value, const AnalysisState &state) const
Return true if the given function argument is writable.
LogicalResult bufferize(Operation *op, RewriterBase &rewriter, const BufferizationOptions &options) const
Rewrite function bbArgs and return values into buffer form.
SmallVector< OpResult > getAliasingOpResult(Operation *op, OpOperand &opOperand, const AnalysisState &state) const
bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand, const AnalysisState &state) const
LogicalResult bufferize(Operation *op, RewriterBase &rewriter, const BufferizationOptions &options) const
bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand, const AnalysisState &state) const