MLIR  16.0.0git
OneShotModuleBufferize.cpp
Go to the documentation of this file.
1 //===- ModuleBufferization.cpp - Bufferization across Func. Boundaries ----===//
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 //
9 // Module Bufferization is an extension of One-Shot Bufferize that
10 // bufferizes function boundaries. It provides `BufferizableOpInterface`
11 // implementations for FuncOp, CallOp and ReturnOp.
12 //
13 // Module Bufferization is run via `runOneShotModuleBufferize(ModuleOp, ...)`.
14 // This function analyzes the given module and determines the order of analysis
15 // and bufferization: Functions that are called are processed before their
16 // respective callers.
17 //
18 // After analyzing a FuncOp, additional information about its bbArgs is
19 // gathered and stored in `FuncAnalysisState`.
20 //
21 // * `aliasingFuncOpBBArgsAnalysis` determines the equivalent/aliasing bbArgs
22 // for
23 // each tensor return value (if any).
24 // * `funcOpBbArgReadWriteAnalysis` determines whether or not a tensor bbArg is
25 // read/written.
26 //
27 // Module Bufferization implements the following calling convention.
28 //
29 // * In the absence of conflicts within a FuncOp, the FuncOp's bbArgs may always
30 // be written to in-place.
31 // * If a tensor operand of a CallOp is read after the CallOp, the operand of
32 // the CallOp must bufferize out-of-place.
33 //
34 // Example: The tensor.insert op bufferizes in-place because it is allowed to
35 // modify the buffer of `%t1` directly. The CallOp in `caller` must bufferize
36 // out-of-place because `%t0` is modified by the callee but read by the
37 // tensor.extract op. The analysis of CallOps decides whether an OpOperand must
38 // bufferize out-of-place based on results of `funcOpBbArgReadWriteAnalysis`.
39 // ```
40 // func @callee(%t1 : tensor<?xf32>) -> tensor<?xf32> {
41 // %f = ... : f32
42 // %0 = tensor.insert %f into %t1[...] : tensor<?xf32>
43 // return %0 : tensor<?xf32>
44 // }
45 //
46 // func @caller() -> () {
47 // %t0 = ... : tensor<?xf32>
48 // %1 = call @callee(%t0) : (tensor<?xf32>) -> (tensor<?xf32>)
49 // %2 = tensor.extract %1[...] : tensor<?xf32>
50 // }
51 // ```
52 //
53 // Note: If a function is external, `funcOpBbArgReadWriteAnalysis` cannot
54 // analyze the function body. In such a case, the CallOp analysis conservatively
55 // assumes that each tensor OpOperand is both read and written.
56 //
57 // TODO: Add FuncOp attributes so that bbArgs of external FuncOps can be marked
58 // as "not reading" and/or "not writing".
59 
61 
70 #include "mlir/IR/Operation.h"
71 
72 using namespace mlir;
73 using namespace mlir::bufferization;
74 using namespace mlir::bufferization::func_ext;
75 
76 /// A mapping of FuncOps to their callers.
78 
79 /// Get or create FuncAnalysisState.
80 static FuncAnalysisState &
82  auto *result = state.getExtension<FuncAnalysisState>();
83  if (result)
84  return *result;
85  return state.addExtension<FuncAnalysisState>();
86 }
87 
88 /// Return the unique ReturnOp that terminates `funcOp`.
89 /// Return nullptr if there is no such unique ReturnOp.
90 static func::ReturnOp getAssumedUniqueReturnOp(func::FuncOp funcOp) {
91  func::ReturnOp returnOp;
92  for (Block &b : funcOp.getBody()) {
93  if (auto candidateOp = dyn_cast<func::ReturnOp>(b.getTerminator())) {
94  if (returnOp)
95  return nullptr;
96  returnOp = candidateOp;
97  }
98  }
99  return returnOp;
100 }
101 
102 namespace {
103 
104 /// Annotate IR with the results of the analysis. For testing purposes only.
105 static void annotateEquivalentReturnBbArg(OpOperand &returnVal,
106  BlockArgument bbArg) {
107  const char *kEquivalentArgsAttr = "__equivalent_func_args__";
108  Operation *op = returnVal.getOwner();
109 
110  SmallVector<int64_t> equivBbArgs;
111  if (op->hasAttr(kEquivalentArgsAttr)) {
112  auto attr = op->getAttr(kEquivalentArgsAttr).cast<ArrayAttr>();
113  equivBbArgs = llvm::to_vector<4>(llvm::map_range(attr, [](Attribute a) {
114  return a.cast<IntegerAttr>().getValue().getSExtValue();
115  }));
116  } else {
117  equivBbArgs.append(op->getNumOperands(), -1);
118  }
119  equivBbArgs[returnVal.getOperandNumber()] = bbArg.getArgNumber();
120 
121  OpBuilder b(op->getContext());
122  op->setAttr(kEquivalentArgsAttr, b.getI64ArrayAttr(equivBbArgs));
123 }
124 
125 /// Store function BlockArguments that are equivalent to/aliasing a returned
126 /// value in FuncAnalysisState.
127 static LogicalResult
128 aliasingFuncOpBBArgsAnalysis(FuncOp funcOp, OneShotAnalysisState &state,
129  FuncAnalysisState &funcState) {
130  // Support only single return-terminated block in the function.
131  func::ReturnOp returnOp = getAssumedUniqueReturnOp(funcOp);
132  assert(returnOp && "expected func with single return op");
133 
134  for (OpOperand &returnVal : returnOp->getOpOperands())
135  if (returnVal.get().getType().isa<RankedTensorType>())
136  for (BlockArgument bbArg : funcOp.getArguments())
137  if (bbArg.getType().isa<RankedTensorType>()) {
138  int64_t returnIdx = returnVal.getOperandNumber();
139  int64_t bbArgIdx = bbArg.getArgNumber();
140  if (state.areEquivalentBufferizedValues(returnVal.get(), bbArg)) {
141  funcState.equivalentFuncArgs[funcOp][returnIdx] = bbArgIdx;
142  if (state.getOptions().testAnalysisOnly)
143  annotateEquivalentReturnBbArg(returnVal, bbArg);
144  }
145  if (state.areAliasingBufferizedValues(returnVal.get(), bbArg)) {
146  funcState.aliasingFuncArgs[funcOp][returnIdx].push_back(bbArgIdx);
147  funcState.aliasingReturnVals[funcOp][bbArgIdx].push_back(returnIdx);
148  }
149  }
150 
151  return success();
152 }
153 
154 static void annotateFuncArgAccess(func::FuncOp funcOp, BlockArgument bbArg,
155  bool isRead, bool isWritten) {
156  OpBuilder b(funcOp.getContext());
157  Attribute accessType;
158  if (isRead && isWritten) {
159  accessType = b.getStringAttr("read-write");
160  } else if (isRead) {
161  accessType = b.getStringAttr("read");
162  } else if (isWritten) {
163  accessType = b.getStringAttr("write");
164  } else {
165  accessType = b.getStringAttr("none");
166  }
167  funcOp.setArgAttr(bbArg.getArgNumber(), "bufferization.access", accessType);
168 }
169 
170 /// Determine which FuncOp bbArgs are read and which are written. When run on a
171 /// function with unknown ops, we conservatively assume that such ops bufferize
172 /// to a read + write.
173 static LogicalResult
174 funcOpBbArgReadWriteAnalysis(FuncOp funcOp, OneShotAnalysisState &state,
175  FuncAnalysisState &funcState) {
176  // If the function has no body, conservatively assume that all args are
177  // read + written.
178  if (funcOp.getBody().empty()) {
179  for (BlockArgument bbArg : funcOp.getArguments()) {
180  funcState.readBbArgs[funcOp].insert(bbArg.getArgNumber());
181  funcState.writtenBbArgs[funcOp].insert(bbArg.getArgNumber());
182  }
183 
184  return success();
185  }
186 
187  for (BlockArgument bbArg : funcOp.getArguments()) {
188  if (!bbArg.getType().isa<TensorType>())
189  continue;
190  bool isRead = state.isValueRead(bbArg);
191  bool isWritten = state.isValueWritten(bbArg);
192  if (state.getOptions().testAnalysisOnly)
193  annotateFuncArgAccess(funcOp, bbArg, isRead, isWritten);
194  if (isRead)
195  funcState.readBbArgs[funcOp].insert(bbArg.getArgNumber());
196  if (isWritten)
197  funcState.writtenBbArgs[funcOp].insert(bbArg.getArgNumber());
198  }
199 
200  return success();
201 }
202 } // namespace
203 
204 /// Remove bufferization attributes on FuncOp arguments.
206  auto funcOp = cast<func::FuncOp>(bbArg.getOwner()->getParentOp());
207  funcOp.removeArgAttr(bbArg.getArgNumber(),
208  BufferizationDialect::kBufferLayoutAttrName);
209  funcOp.removeArgAttr(bbArg.getArgNumber(),
210  BufferizationDialect::kWritableAttrName);
211 }
212 
213 /// Return the func::FuncOp called by `callOp`.
214 static func::FuncOp getCalledFunction(CallOpInterface callOp) {
215  SymbolRefAttr sym = callOp.getCallableForCallee().dyn_cast<SymbolRefAttr>();
216  if (!sym)
217  return nullptr;
218  return dyn_cast_or_null<func::FuncOp>(
220 }
221 
222 /// Gather equivalence info of CallOps.
223 /// Note: This only adds new equivalence info if the called function was already
224 /// analyzed.
225 // TODO: This does not handle cyclic function call graphs etc.
226 static void equivalenceAnalysis(func::FuncOp funcOp,
227  BufferizationAliasInfo &aliasInfo,
228  OneShotAnalysisState &state,
229  FuncAnalysisState &funcState) {
230  funcOp->walk([&](func::CallOp callOp) {
231  func::FuncOp calledFunction = getCalledFunction(callOp);
232  assert(calledFunction && "could not retrieved called func::FuncOp");
233 
234  // No equivalence info available for the called function.
235  if (!funcState.equivalentFuncArgs.count(calledFunction))
236  return WalkResult::skip();
237 
238  for (auto it : funcState.equivalentFuncArgs[calledFunction]) {
239  int64_t returnIdx = it.first;
240  int64_t bbargIdx = it.second;
241  if (!state.isInPlace(callOp->getOpOperand(bbargIdx)))
242  continue;
243  Value returnVal = callOp.getResult(returnIdx);
244  Value argVal = callOp->getOperand(bbargIdx);
245  aliasInfo.unionEquivalenceClasses(returnVal, argVal);
246  }
247 
248  return WalkResult::advance();
249  });
250 }
251 
252 /// Store all functions of the `moduleOp` in `orderedFuncOps`, sorted by
253 /// callee-caller order (i.e. callees without callers first).
254 /// Store the map of FuncOp to all its callers in `callerMap`.
255 /// Return `failure()` if a cycle of calls is detected or if we are unable to
256 /// retrieve the called FuncOp from any CallOpInterface.
257 static LogicalResult
258 getFuncOpsOrderedByCalls(ModuleOp moduleOp,
259  SmallVectorImpl<func::FuncOp> &orderedFuncOps,
260  FuncCallerMap &callerMap) {
261  // For each FuncOp, the set of functions called by it (i.e. the union of
262  // symbols of all nested CallOpInterfaceOp).
264  // For each FuncOp, the number of CallOpInterface it contains.
265  DenseMap<func::FuncOp, unsigned> numberCallOpsContainedInFuncOp;
266  WalkResult res = moduleOp.walk([&](func::FuncOp funcOp) -> WalkResult {
267  if (!funcOp.getBody().empty()) {
268  func::ReturnOp returnOp = getAssumedUniqueReturnOp(funcOp);
269  if (!returnOp)
270  return funcOp->emitError()
271  << "cannot bufferize a FuncOp with tensors and "
272  "without a unique ReturnOp";
273  }
274 
275  numberCallOpsContainedInFuncOp[funcOp] = 0;
276  return funcOp.walk([&](CallOpInterface callOp) -> WalkResult {
277  // Only support CallOp for now.
278  if (!isa<func::CallOp>(callOp.getOperation()))
279  return callOp->emitError() << "expected a CallOp";
280  func::FuncOp calledFunction = getCalledFunction(callOp);
281  assert(calledFunction && "could not retrieved called func::FuncOp");
282  callerMap[calledFunction].insert(callOp);
283  if (calledBy[calledFunction].insert(funcOp).second) {
284  numberCallOpsContainedInFuncOp[funcOp]++;
285  }
286  return WalkResult::advance();
287  });
288  });
289  if (res.wasInterrupted())
290  return failure();
291  // Iteratively remove function operation that do not call any of the
292  // functions remaining in the callCounter map and add them to the worklist.
293  while (!numberCallOpsContainedInFuncOp.empty()) {
294  auto it = llvm::find_if(numberCallOpsContainedInFuncOp,
295  [](auto entry) { return entry.getSecond() == 0; });
296  if (it == numberCallOpsContainedInFuncOp.end())
297  return moduleOp.emitOpError(
298  "expected callgraph to be free of circular dependencies.");
299  orderedFuncOps.push_back(it->getFirst());
300  for (auto callee : calledBy[it->getFirst()])
301  numberCallOpsContainedInFuncOp[callee]--;
302  numberCallOpsContainedInFuncOp.erase(it);
303  }
304  return success();
305 }
306 
307 /// Fold return values that are memref casts and update function return types.
308 ///
309 /// During FuncOp bufferization, the exact type of the returned memrefs (if any)
310 /// is not known yet. Therefore, the bufferization uses memref types with the
311 /// most generic layout map as function return types. After bufferizing the
312 /// entire function body, a more concise memref type can potentially be used for
313 /// the return type of the function.
314 static void foldMemRefCasts(func::FuncOp funcOp) {
315  if (funcOp.getBody().empty())
316  return;
317 
318  func::ReturnOp returnOp = getAssumedUniqueReturnOp(funcOp);
319  SmallVector<Type> resultTypes;
320 
321  for (OpOperand &operand : returnOp->getOpOperands()) {
322  if (auto castOp = operand.get().getDefiningOp<memref::CastOp>()) {
323  operand.set(castOp.getSource());
324  resultTypes.push_back(castOp.getSource().getType());
325  } else {
326  resultTypes.push_back(operand.get().getType());
327  }
328  }
329 
330  auto newFuncType = FunctionType::get(
331  funcOp.getContext(), funcOp.getFunctionType().getInputs(), resultTypes);
332  funcOp.setType(newFuncType);
333 }
334 
337  OneShotAnalysisState &state) {
338  assert(state.getOptions().bufferizeFunctionBoundaries &&
339  "expected that function boundary bufferization is activated");
341  BufferizationAliasInfo &aliasInfo = state.getAliasInfo();
342 
343  // A list of functions in the order in which they are analyzed + bufferized.
344  SmallVector<func::FuncOp> orderedFuncOps;
345 
346  // A mapping of FuncOps to their callers.
347  FuncCallerMap callerMap;
348 
349  if (failed(getFuncOpsOrderedByCalls(moduleOp, orderedFuncOps, callerMap)))
350  return failure();
351 
352  // Analyze ops.
353  for (func::FuncOp funcOp : orderedFuncOps) {
354  // No body => no analysis.
355  if (funcOp.getBody().empty())
356  continue;
357 
358  // Now analyzing function.
359  funcState.startFunctionAnalysis(funcOp);
360 
361  // Gather equivalence info for CallOps.
362  equivalenceAnalysis(funcOp, aliasInfo, state, funcState);
363 
364  // Analyze funcOp.
365  if (failed(analyzeOp(funcOp, state)))
366  return failure();
367 
368  // Run some extra function analyses.
369  if (failed(aliasingFuncOpBBArgsAnalysis(funcOp, state, funcState)) ||
370  failed(funcOpBbArgReadWriteAnalysis(funcOp, state, funcState)))
371  return failure();
372 
373  // Mark op as fully analyzed.
374  funcState.analyzedFuncOps[funcOp] = FuncOpAnalysisState::Analyzed;
375  }
376 
377  return success();
378 }
379 
381  ModuleOp moduleOp) {
382  moduleOp.walk([&](func::FuncOp op) {
383  for (BlockArgument bbArg : op.getArguments())
385  });
386 }
387 
389  ModuleOp moduleOp, const OneShotBufferizationOptions &options) {
390  assert(options.bufferizeFunctionBoundaries &&
391  "expected that function boundary bufferization is activated");
392  IRRewriter rewriter(moduleOp.getContext());
393 
394  // A list of functions in the order in which they are analyzed + bufferized.
395  SmallVector<func::FuncOp> orderedFuncOps;
396 
397  // A mapping of FuncOps to their callers.
398  FuncCallerMap callerMap;
399 
400  if (failed(getFuncOpsOrderedByCalls(moduleOp, orderedFuncOps, callerMap)))
401  return failure();
402 
403  // Bufferize functions.
404  for (func::FuncOp funcOp : orderedFuncOps) {
405  // Note: It would be good to apply cleanups here but we cannot as aliasInfo
406  // would be invalidated.
407  if (failed(bufferizeOp(funcOp, options, options.copyBeforeWrite)))
408  return failure();
409  // Change buffer return types to more precise layout maps.
410  if (options.functionBoundaryTypeConversion ==
411  LayoutMapOption::InferLayoutMap)
412  foldMemRefCasts(funcOp);
413  }
414 
415  // Post-pass cleanup of function argument attributes.
417 
418  return success();
419 }
420 
422  ModuleOp moduleOp, const OneShotBufferizationOptions &options) {
423  assert(options.bufferizeFunctionBoundaries &&
424  "expected that function boundary bufferization is activated");
425  assert(!(options.copyBeforeWrite && options.testAnalysisOnly) &&
426  "invalid combination of bufferization flags");
427  if (!options.copyBeforeWrite) {
428  OneShotAnalysisState analysisState(moduleOp, options);
429  if (failed(insertTensorCopies(moduleOp, options)))
430  return failure();
431  }
432  if (options.testAnalysisOnly)
433  return success();
434  if (failed(bufferizeModuleOp(moduleOp, options)))
435  return failure();
436  return success();
437 }
static LogicalResult getFuncOpsOrderedByCalls(ModuleOp moduleOp, SmallVectorImpl< func::FuncOp > &orderedFuncOps, FuncCallerMap &callerMap)
Store all functions of the moduleOp in orderedFuncOps, sorted by callee-caller order (i....
static FuncAnalysisState & getOrCreateFuncAnalysisState(OneShotAnalysisState &state)
Get or create FuncAnalysisState.
static void removeBufferizationAttributes(BlockArgument bbArg)
Remove bufferization attributes on FuncOp arguments.
static void foldMemRefCasts(func::FuncOp funcOp)
Fold return values that are memref casts and update function return types.
static void equivalenceAnalysis(func::FuncOp funcOp, BufferizationAliasInfo &aliasInfo, OneShotAnalysisState &state, FuncAnalysisState &funcState)
Gather equivalence info of CallOps.
static func::ReturnOp getAssumedUniqueReturnOp(func::FuncOp funcOp)
Return the unique ReturnOp that terminates funcOp.
static llvm::ManagedStatic< PassManagerOptions > options
Attributes are known-constant values of operations.
Definition: Attributes.h:25
U cast() const
Definition: Attributes.h:137
This class represents an argument of a Block.
Definition: Value.h:296
Block * getOwner() const
Returns the block that owns this argument.
Definition: Value.h:305
unsigned getArgNumber() const
Returns the number of this argument.
Definition: Value.h:308
Block represents an ordered list of Operations.
Definition: Block.h:30
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition: Block.cpp:30
IRValueT get() const
Return the current value being used by this operand.
Definition: UseDefLists.h:137
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
Definition: PatternMatch.h:589
This class helps build Operations.
Definition: Builders.h:198
This class represents an operand of an operation.
Definition: Value.h:247
unsigned getOperandNumber()
Return which operand this is in the OpOperand list of the Operation.
Definition: Value.cpp:212
Operation is a basic unit of execution within MLIR.
Definition: Operation.h:31
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
Definition: Operation.h:371
bool hasAttr(StringAttr name)
Return true if the operation has an attribute with the provided name, false otherwise.
Definition: Operation.h:385
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:147
unsigned getNumOperands()
Definition: Operation.h:263
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Definition: Operation.h:395
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:78
bool isa() const
Definition: Types.h:260
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:85
Type getType() const
Return the type of this value.
Definition: Value.h:114
A utility result that is used to signal how to proceed with an ongoing walk:
Definition: Visitors.h:34
static WalkResult skip()
Definition: Visitors.h:52
static WalkResult advance()
Definition: Visitors.h:51
bool isValueRead(Value value) const
Return true if the given value is read by an op that bufferizes to a memory read.
The BufferizationAliasInfo class maintains a list of buffer aliases and equivalence classes to suppor...
void unionEquivalenceClasses(Value v1, Value v2)
Union the equivalence classes of v1 and v2.
State for analysis-enabled bufferization.
bool isInPlace(OpOperand &opOperand) const override
Return true if the given OpResult has been decided to bufferize inplace.
bool isValueWritten(Value value) const
Return true if the buffer of the given tensor value is written to.
const OneShotBufferizationOptions & getOptions() const
Return a reference to the BufferizationOptions.
Ty & addExtension(Args &&...args)
Adds a new Extension of the type specified as template parameter, constructing it with the arguments ...
bool areEquivalentBufferizedValues(Value v1, Value v2) const override
Return true if v1 and v2 bufferize to equivalent buffers.
BufferizationAliasInfo & getAliasInfo()
Return a reference to the BufferizationAliasInfo.
bool areAliasingBufferizedValues(Value v1, Value v2) const override
Return true if v1 and v2 may bufferize to aliasing buffers.
Ty * getExtension()
Returns the extension of the specified type.
Operation * getOwner() const
Return the owner of this operand.
Definition: UseDefLists.h:40
static func::ReturnOp getAssumedUniqueReturnOp(FuncOp funcOp)
Return the unique ReturnOp that terminates funcOp.
static FuncOp getCalledFunction(CallOpInterface callOp)
Return the FuncOp called by callOp.
LogicalResult runOneShotModuleBufferize(ModuleOp moduleOp, const bufferization::OneShotBufferizationOptions &options)
Run One-Shot Module Bufferization on the given module.
LogicalResult bufferizeOp(Operation *op, const BufferizationOptions &options, bool copyBeforeWrite=true, const OpFilter *opFilter=nullptr)
Bufferize op and its nested ops that implement BufferizableOpInterface.
Definition: Bufferize.cpp:398
LogicalResult analyzeOp(Operation *op, OneShotAnalysisState &state)
Analyze op and its nested ops.
LogicalResult insertTensorCopies(Operation *op, const OneShotBufferizationOptions &options)
Resolve RaW and other conflicts by inserting bufferization.alloc_tensor ops.
LogicalResult bufferizeModuleOp(ModuleOp moduleOp, const OneShotBufferizationOptions &options)
Bufferize op and its nested ops that implement BufferizableOpInterface.
LogicalResult analyzeModuleOp(ModuleOp moduleOp, OneShotAnalysisState &state)
Analyze moduleOp and its nested ops.
void removeBufferizationAttributesInModule(ModuleOp moduleOp)
Remove bufferization attributes on every FuncOp arguments in the ModuleOp.
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
bool testAnalysisOnly
If set to true, does not modify the IR apart from adding attributes (for checking the results of the ...
bool bufferizeFunctionBoundaries
Specifies whether function boundaries (ops in the func dialect) should be bufferized or not.
Options for analysis-enabled bufferization.
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< FuncOp, BbArgIndexSet > readBbArgs
A set of all read BlockArguments of FuncOps.
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.