MLIR  22.0.0git
LowerDeallocations.cpp
Go to the documentation of this file.
1 //===- LowerDeallocations.cpp - Bufferization Deallocs to MemRef pass -----===//
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 // This file implements patterns to convert `bufferization.dealloc` operations
10 // to the MemRef dialect.
11 //
12 //===----------------------------------------------------------------------===//
13 
20 #include "mlir/IR/BuiltinTypes.h"
22 
23 namespace mlir {
24 namespace bufferization {
25 #define GEN_PASS_DEF_LOWERDEALLOCATIONSPASS
26 #include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
27 } // namespace bufferization
28 } // namespace mlir
29 
30 using namespace mlir;
31 
32 namespace {
33 /// The DeallocOpConversion transforms all bufferization dealloc operations into
34 /// memref dealloc operations potentially guarded by scf if operations.
35 /// Additionally, memref extract_aligned_pointer_as_index and arith operations
36 /// are inserted to compute the guard conditions. We distinguish multiple cases
37 /// to provide an overall more efficient lowering. In the general case, a helper
38 /// func is created to avoid quadratic code size explosion (relative to the
39 /// number of operands of the dealloc operation). For examples of each case,
40 /// refer to the documentation of the member functions of this class.
41 class DeallocOpConversion
42  : public OpConversionPattern<bufferization::DeallocOp> {
43 
44  /// Lower a simple case without any retained values and a single memref to
45  /// avoiding the helper function. Ideally, static analysis can provide enough
46  /// aliasing information to split the dealloc operations up into this simple
47  /// case as much as possible before running this pass.
48  ///
49  /// Example:
50  /// ```
51  /// bufferization.dealloc (%arg0 : memref<2xf32>) if (%arg1)
52  /// ```
53  /// is lowered to
54  /// ```
55  /// scf.if %arg1 {
56  /// memref.dealloc %arg0 : memref<2xf32>
57  /// }
58  /// ```
59  LogicalResult
60  rewriteOneMemrefNoRetainCase(bufferization::DeallocOp op, OpAdaptor adaptor,
61  ConversionPatternRewriter &rewriter) const {
62  assert(adaptor.getMemrefs().size() == 1 && "expected only one memref");
63  assert(adaptor.getRetained().empty() && "expected no retained memrefs");
64 
65  rewriter.replaceOpWithNewOp<scf::IfOp>(
66  op, adaptor.getConditions()[0], [&](OpBuilder &builder, Location loc) {
67  builder.create<memref::DeallocOp>(loc, adaptor.getMemrefs()[0]);
68  builder.create<scf::YieldOp>(loc);
69  });
70  return success();
71  }
72 
73  /// A special case lowering for the deallocation operation with exactly one
74  /// memref, but arbitrary number of retained values. This avoids the helper
75  /// function that the general case needs and thus also avoids storing indices
76  /// to specifically allocated memrefs. The size of the code produced by this
77  /// lowering is linear to the number of retained values.
78  ///
79  /// Example:
80  /// ```mlir
81  /// %0:2 = bufferization.dealloc (%m : memref<2xf32>) if (%cond)
82  // retain (%r0, %r1 : memref<1xf32>, memref<2xf32>)
83  /// return %0#0, %0#1 : i1, i1
84  /// ```
85  /// ```mlir
86  /// %m_base_pointer = memref.extract_aligned_pointer_as_index %m
87  /// %r0_base_pointer = memref.extract_aligned_pointer_as_index %r0
88  /// %r0_does_not_alias = arith.cmpi ne, %m_base_pointer, %r0_base_pointer
89  /// %r1_base_pointer = memref.extract_aligned_pointer_as_index %r1
90  /// %r1_does_not_alias = arith.cmpi ne, %m_base_pointer, %r1_base_pointer
91  /// %not_retained = arith.andi %r0_does_not_alias, %r1_does_not_alias : i1
92  /// %should_dealloc = arith.andi %not_retained, %cond : i1
93  /// scf.if %should_dealloc {
94  /// memref.dealloc %m : memref<2xf32>
95  /// }
96  /// %true = arith.constant true
97  /// %r0_does_alias = arith.xori %r0_does_not_alias, %true : i1
98  /// %r0_ownership = arith.andi %r0_does_alias, %cond : i1
99  /// %r1_does_alias = arith.xori %r1_does_not_alias, %true : i1
100  /// %r1_ownership = arith.andi %r1_does_alias, %cond : i1
101  /// return %r0_ownership, %r1_ownership : i1, i1
102  /// ```
103  LogicalResult rewriteOneMemrefMultipleRetainCase(
104  bufferization::DeallocOp op, OpAdaptor adaptor,
105  ConversionPatternRewriter &rewriter) const {
106  assert(adaptor.getMemrefs().size() == 1 && "expected only one memref");
107 
108  // Compute the base pointer indices, compare all retained indices to the
109  // memref index to check if they alias.
110  SmallVector<Value> doesNotAliasList;
111  Value memrefAsIdx = rewriter.create<memref::ExtractAlignedPointerAsIndexOp>(
112  op->getLoc(), adaptor.getMemrefs()[0]);
113  for (Value retained : adaptor.getRetained()) {
114  Value retainedAsIdx =
115  rewriter.create<memref::ExtractAlignedPointerAsIndexOp>(op->getLoc(),
116  retained);
117  Value doesNotAlias = rewriter.create<arith::CmpIOp>(
118  op->getLoc(), arith::CmpIPredicate::ne, memrefAsIdx, retainedAsIdx);
119  doesNotAliasList.push_back(doesNotAlias);
120  }
121 
122  // AND-reduce the list of booleans from above.
123  Value prev = doesNotAliasList.front();
124  for (Value doesNotAlias : ArrayRef(doesNotAliasList).drop_front())
125  prev = rewriter.create<arith::AndIOp>(op->getLoc(), prev, doesNotAlias);
126 
127  // Also consider the condition given by the dealloc operation and perform a
128  // conditional deallocation guarded by that value.
129  Value shouldDealloc = rewriter.create<arith::AndIOp>(
130  op->getLoc(), prev, adaptor.getConditions()[0]);
131 
132  rewriter.create<scf::IfOp>(
133  op.getLoc(), shouldDealloc, [&](OpBuilder &builder, Location loc) {
134  builder.create<memref::DeallocOp>(loc, adaptor.getMemrefs()[0]);
135  builder.create<scf::YieldOp>(loc);
136  });
137 
138  // Compute the replacement values for the dealloc operation results. This
139  // inserts an already canonicalized form of
140  // `select(does_alias_with_memref(r), memref_cond, false)` for each retained
141  // value r.
142  SmallVector<Value> replacements;
143  Value trueVal = rewriter.create<arith::ConstantOp>(
144  op->getLoc(), rewriter.getBoolAttr(true));
145  for (Value doesNotAlias : doesNotAliasList) {
146  Value aliases =
147  rewriter.create<arith::XOrIOp>(op->getLoc(), doesNotAlias, trueVal);
148  Value result = rewriter.create<arith::AndIOp>(op->getLoc(), aliases,
149  adaptor.getConditions()[0]);
150  replacements.push_back(result);
151  }
152 
153  rewriter.replaceOp(op, replacements);
154 
155  return success();
156  }
157 
158  /// Lowering that supports all features the dealloc operation has to offer. It
159  /// computes the base pointer of each memref (as an index), stores it in a
160  /// new memref helper structure and passes it to the helper function generated
161  /// in 'buildDeallocationHelperFunction'. The results are stored in two lists
162  /// (represented as memrefs) of booleans passed as arguments. The first list
163  /// stores whether the corresponding condition should be deallocated, the
164  /// second list stores the ownership of the retained values which can be used
165  /// to replace the result values of the `bufferization.dealloc` operation.
166  ///
167  /// Example:
168  /// ```
169  /// %0:2 = bufferization.dealloc (%m0, %m1 : memref<2xf32>, memref<5xf32>)
170  /// if (%cond0, %cond1)
171  /// retain (%r0, %r1 : memref<1xf32>, memref<2xf32>)
172  /// ```
173  /// lowers to (simplified):
174  /// ```
175  /// %c0 = arith.constant 0 : index
176  /// %c1 = arith.constant 1 : index
177  /// %dealloc_base_pointer_list = memref.alloc() : memref<2xindex>
178  /// %cond_list = memref.alloc() : memref<2xi1>
179  /// %retain_base_pointer_list = memref.alloc() : memref<2xindex>
180  /// %m0_base_pointer = memref.extract_aligned_pointer_as_index %m0
181  /// memref.store %m0_base_pointer, %dealloc_base_pointer_list[%c0]
182  /// %m1_base_pointer = memref.extract_aligned_pointer_as_index %m1
183  /// memref.store %m1_base_pointer, %dealloc_base_pointer_list[%c1]
184  /// memref.store %cond0, %cond_list[%c0]
185  /// memref.store %cond1, %cond_list[%c1]
186  /// %r0_base_pointer = memref.extract_aligned_pointer_as_index %r0
187  /// memref.store %r0_base_pointer, %retain_base_pointer_list[%c0]
188  /// %r1_base_pointer = memref.extract_aligned_pointer_as_index %r1
189  /// memref.store %r1_base_pointer, %retain_base_pointer_list[%c1]
190  /// %dyn_dealloc_base_pointer_list = memref.cast %dealloc_base_pointer_list :
191  /// memref<2xindex> to memref<?xindex>
192  /// %dyn_cond_list = memref.cast %cond_list : memref<2xi1> to memref<?xi1>
193  /// %dyn_retain_base_pointer_list = memref.cast %retain_base_pointer_list :
194  /// memref<2xindex> to memref<?xindex>
195  /// %dealloc_cond_out = memref.alloc() : memref<2xi1>
196  /// %ownership_out = memref.alloc() : memref<2xi1>
197  /// %dyn_dealloc_cond_out = memref.cast %dealloc_cond_out :
198  /// memref<2xi1> to memref<?xi1>
199  /// %dyn_ownership_out = memref.cast %ownership_out :
200  /// memref<2xi1> to memref<?xi1>
201  /// call @dealloc_helper(%dyn_dealloc_base_pointer_list,
202  /// %dyn_retain_base_pointer_list,
203  /// %dyn_cond_list,
204  /// %dyn_dealloc_cond_out,
205  /// %dyn_ownership_out) : (...)
206  /// %m0_dealloc_cond = memref.load %dyn_dealloc_cond_out[%c0] : memref<2xi1>
207  /// scf.if %m0_dealloc_cond {
208  /// memref.dealloc %m0 : memref<2xf32>
209  /// }
210  /// %m1_dealloc_cond = memref.load %dyn_dealloc_cond_out[%c1] : memref<2xi1>
211  /// scf.if %m1_dealloc_cond {
212  /// memref.dealloc %m1 : memref<5xf32>
213  /// }
214  /// %r0_ownership = memref.load %dyn_ownership_out[%c0] : memref<2xi1>
215  /// %r1_ownership = memref.load %dyn_ownership_out[%c1] : memref<2xi1>
216  /// memref.dealloc %dealloc_base_pointer_list : memref<2xindex>
217  /// memref.dealloc %retain_base_pointer_list : memref<2xindex>
218  /// memref.dealloc %cond_list : memref<2xi1>
219  /// memref.dealloc %dealloc_cond_out : memref<2xi1>
220  /// memref.dealloc %ownership_out : memref<2xi1>
221  /// // replace %0#0 with %r0_ownership
222  /// // replace %0#1 with %r1_ownership
223  /// ```
224  LogicalResult rewriteGeneralCase(bufferization::DeallocOp op,
225  OpAdaptor adaptor,
226  ConversionPatternRewriter &rewriter) const {
227  // Allocate two memrefs holding the base pointer indices of the list of
228  // memrefs to be deallocated and the ones to be retained. These can then be
229  // passed to the helper function and the for-loops can iterate over them.
230  // Without storing them to memrefs, we could not use for-loops but only a
231  // completely unrolled version of it, potentially leading to code-size
232  // blow-up.
233  Value toDeallocMemref = rewriter.create<memref::AllocOp>(
234  op.getLoc(), MemRefType::get({(int64_t)adaptor.getMemrefs().size()},
235  rewriter.getIndexType()));
236  Value conditionMemref = rewriter.create<memref::AllocOp>(
237  op.getLoc(), MemRefType::get({(int64_t)adaptor.getConditions().size()},
238  rewriter.getI1Type()));
239  Value toRetainMemref = rewriter.create<memref::AllocOp>(
240  op.getLoc(), MemRefType::get({(int64_t)adaptor.getRetained().size()},
241  rewriter.getIndexType()));
242 
243  auto getConstValue = [&](uint64_t value) -> Value {
244  return rewriter.create<arith::ConstantOp>(op.getLoc(),
245  rewriter.getIndexAttr(value));
246  };
247 
248  // Extract the base pointers of the memrefs as indices to check for aliasing
249  // at runtime.
250  for (auto [i, toDealloc] : llvm::enumerate(adaptor.getMemrefs())) {
251  Value memrefAsIdx =
252  rewriter.create<memref::ExtractAlignedPointerAsIndexOp>(op.getLoc(),
253  toDealloc);
254  rewriter.create<memref::StoreOp>(op.getLoc(), memrefAsIdx,
255  toDeallocMemref, getConstValue(i));
256  }
257 
258  for (auto [i, cond] : llvm::enumerate(adaptor.getConditions()))
259  rewriter.create<memref::StoreOp>(op.getLoc(), cond, conditionMemref,
260  getConstValue(i));
261 
262  for (auto [i, toRetain] : llvm::enumerate(adaptor.getRetained())) {
263  Value memrefAsIdx =
264  rewriter.create<memref::ExtractAlignedPointerAsIndexOp>(op.getLoc(),
265  toRetain);
266  rewriter.create<memref::StoreOp>(op.getLoc(), memrefAsIdx, toRetainMemref,
267  getConstValue(i));
268  }
269 
270  // Cast the allocated memrefs to dynamic shape because we want only one
271  // helper function no matter how many operands the bufferization.dealloc
272  // has.
273  Value castedDeallocMemref = rewriter.create<memref::CastOp>(
274  op->getLoc(),
275  MemRefType::get({ShapedType::kDynamic}, rewriter.getIndexType()),
276  toDeallocMemref);
277  Value castedCondsMemref = rewriter.create<memref::CastOp>(
278  op->getLoc(),
279  MemRefType::get({ShapedType::kDynamic}, rewriter.getI1Type()),
280  conditionMemref);
281  Value castedRetainMemref = rewriter.create<memref::CastOp>(
282  op->getLoc(),
283  MemRefType::get({ShapedType::kDynamic}, rewriter.getIndexType()),
284  toRetainMemref);
285 
286  Value deallocCondsMemref = rewriter.create<memref::AllocOp>(
287  op.getLoc(), MemRefType::get({(int64_t)adaptor.getMemrefs().size()},
288  rewriter.getI1Type()));
289  Value retainCondsMemref = rewriter.create<memref::AllocOp>(
290  op.getLoc(), MemRefType::get({(int64_t)adaptor.getRetained().size()},
291  rewriter.getI1Type()));
292 
293  Value castedDeallocCondsMemref = rewriter.create<memref::CastOp>(
294  op->getLoc(),
295  MemRefType::get({ShapedType::kDynamic}, rewriter.getI1Type()),
296  deallocCondsMemref);
297  Value castedRetainCondsMemref = rewriter.create<memref::CastOp>(
298  op->getLoc(),
299  MemRefType::get({ShapedType::kDynamic}, rewriter.getI1Type()),
300  retainCondsMemref);
301 
303  rewriter.create<func::CallOp>(
304  op.getLoc(), deallocHelperFuncMap.lookup(symtableOp),
305  SmallVector<Value>{castedDeallocMemref, castedRetainMemref,
306  castedCondsMemref, castedDeallocCondsMemref,
307  castedRetainCondsMemref});
308 
309  for (unsigned i = 0, e = adaptor.getMemrefs().size(); i < e; ++i) {
310  Value idxValue = getConstValue(i);
311  Value shouldDealloc = rewriter.create<memref::LoadOp>(
312  op.getLoc(), deallocCondsMemref, idxValue);
313  rewriter.create<scf::IfOp>(
314  op.getLoc(), shouldDealloc, [&](OpBuilder &builder, Location loc) {
315  builder.create<memref::DeallocOp>(loc, adaptor.getMemrefs()[i]);
316  builder.create<scf::YieldOp>(loc);
317  });
318  }
319 
320  SmallVector<Value> replacements;
321  for (unsigned i = 0, e = adaptor.getRetained().size(); i < e; ++i) {
322  Value idxValue = getConstValue(i);
323  Value ownership = rewriter.create<memref::LoadOp>(
324  op.getLoc(), retainCondsMemref, idxValue);
325  replacements.push_back(ownership);
326  }
327 
328  // Deallocate above allocated memrefs again to avoid memory leaks.
329  // Deallocation will not be run on code after this stage.
330  rewriter.create<memref::DeallocOp>(op.getLoc(), toDeallocMemref);
331  rewriter.create<memref::DeallocOp>(op.getLoc(), toRetainMemref);
332  rewriter.create<memref::DeallocOp>(op.getLoc(), conditionMemref);
333  rewriter.create<memref::DeallocOp>(op.getLoc(), deallocCondsMemref);
334  rewriter.create<memref::DeallocOp>(op.getLoc(), retainCondsMemref);
335 
336  rewriter.replaceOp(op, replacements);
337  return success();
338  }
339 
340 public:
341  DeallocOpConversion(
342  MLIRContext *context,
343  const bufferization::DeallocHelperMap &deallocHelperFuncMap)
344  : OpConversionPattern<bufferization::DeallocOp>(context),
345  deallocHelperFuncMap(deallocHelperFuncMap) {}
346 
347  LogicalResult
348  matchAndRewrite(bufferization::DeallocOp op, OpAdaptor adaptor,
349  ConversionPatternRewriter &rewriter) const override {
350  // Lower the trivial case.
351  if (adaptor.getMemrefs().empty()) {
352  Value falseVal = rewriter.create<arith::ConstantOp>(
353  op.getLoc(), rewriter.getBoolAttr(false));
354  rewriter.replaceOp(
355  op, SmallVector<Value>(adaptor.getRetained().size(), falseVal));
356  return success();
357  }
358 
359  if (adaptor.getMemrefs().size() == 1 && adaptor.getRetained().empty())
360  return rewriteOneMemrefNoRetainCase(op, adaptor, rewriter);
361 
362  if (adaptor.getMemrefs().size() == 1)
363  return rewriteOneMemrefMultipleRetainCase(op, adaptor, rewriter);
364 
366  if (!deallocHelperFuncMap.contains(symtableOp))
367  return op->emitError(
368  "library function required for generic lowering, but cannot be "
369  "automatically inserted when operating on functions");
370 
371  return rewriteGeneralCase(op, adaptor, rewriter);
372  }
373 
374 private:
375  const bufferization::DeallocHelperMap &deallocHelperFuncMap;
376 };
377 } // namespace
378 
379 namespace {
380 struct LowerDeallocationsPass
381  : public bufferization::impl::LowerDeallocationsPassBase<
382  LowerDeallocationsPass> {
383  void runOnOperation() override {
384  if (!isa<ModuleOp, FunctionOpInterface>(getOperation())) {
385  emitError(getOperation()->getLoc(),
386  "root operation must be a builtin.module or a function");
387  signalPassFailure();
388  return;
389  }
390 
391  bufferization::DeallocHelperMap deallocHelperFuncMap;
392  if (auto module = dyn_cast<ModuleOp>(getOperation())) {
393  OpBuilder builder = OpBuilder::atBlockBegin(module.getBody());
394 
395  // Build dealloc helper function if there are deallocs.
396  getOperation()->walk([&](bufferization::DeallocOp deallocOp) {
397  Operation *symtableOp =
399  if (deallocOp.getMemrefs().size() > 1 &&
400  !deallocHelperFuncMap.contains(symtableOp)) {
401  SymbolTable symbolTable(symtableOp);
402  func::FuncOp helperFuncOp =
404  builder, getOperation()->getLoc(), symbolTable);
405  deallocHelperFuncMap[symtableOp] = helperFuncOp;
406  }
407  });
408  }
409 
412  patterns, deallocHelperFuncMap);
413 
414  ConversionTarget target(getContext());
415  target.addLegalDialect<memref::MemRefDialect, arith::ArithDialect,
416  scf::SCFDialect, func::FuncDialect>();
417  target.addIllegalOp<bufferization::DeallocOp>();
418 
419  if (failed(applyPartialConversion(getOperation(), target,
420  std::move(patterns))))
421  signalPassFailure();
422  }
423 };
424 } // namespace
425 
427  OpBuilder &builder, Location loc, SymbolTable &symbolTable) {
428  Type indexMemrefType =
429  MemRefType::get({ShapedType::kDynamic}, builder.getIndexType());
430  Type boolMemrefType =
431  MemRefType::get({ShapedType::kDynamic}, builder.getI1Type());
432  SmallVector<Type> argTypes{indexMemrefType, indexMemrefType, boolMemrefType,
433  boolMemrefType, boolMemrefType};
434  builder.clearInsertionPoint();
435 
436  // Generate the func operation itself.
437  auto helperFuncOp = func::FuncOp::create(
438  loc, "dealloc_helper", builder.getFunctionType(argTypes, {}));
439  helperFuncOp.setVisibility(SymbolTable::Visibility::Private);
440  symbolTable.insert(helperFuncOp);
441  auto &block = helperFuncOp.getFunctionBody().emplaceBlock();
442  block.addArguments(argTypes, SmallVector<Location>(argTypes.size(), loc));
443 
444  builder.setInsertionPointToStart(&block);
445  Value toDeallocMemref = helperFuncOp.getArguments()[0];
446  Value toRetainMemref = helperFuncOp.getArguments()[1];
447  Value conditionMemref = helperFuncOp.getArguments()[2];
448  Value deallocCondsMemref = helperFuncOp.getArguments()[3];
449  Value retainCondsMemref = helperFuncOp.getArguments()[4];
450 
451  // Insert some prerequisites.
452  Value c0 = builder.create<arith::ConstantOp>(loc, builder.getIndexAttr(0));
453  Value c1 = builder.create<arith::ConstantOp>(loc, builder.getIndexAttr(1));
454  Value trueValue =
455  builder.create<arith::ConstantOp>(loc, builder.getBoolAttr(true));
456  Value falseValue =
457  builder.create<arith::ConstantOp>(loc, builder.getBoolAttr(false));
458  Value toDeallocSize = builder.create<memref::DimOp>(loc, toDeallocMemref, c0);
459  Value toRetainSize = builder.create<memref::DimOp>(loc, toRetainMemref, c0);
460 
461  builder.create<scf::ForOp>(
462  loc, c0, toRetainSize, c1, ValueRange(),
463  [&](OpBuilder &builder, Location loc, Value i, ValueRange iterArgs) {
464  builder.create<memref::StoreOp>(loc, falseValue, retainCondsMemref, i);
465  builder.create<scf::YieldOp>(loc);
466  });
467 
468  builder.create<scf::ForOp>(
469  loc, c0, toDeallocSize, c1, ValueRange(),
470  [&](OpBuilder &builder, Location loc, Value outerIter,
471  ValueRange iterArgs) {
472  Value toDealloc =
473  builder.create<memref::LoadOp>(loc, toDeallocMemref, outerIter);
474  Value cond =
475  builder.create<memref::LoadOp>(loc, conditionMemref, outerIter);
476 
477  // Build the first for loop that computes aliasing with retained
478  // memrefs.
479  Value noRetainAlias =
480  builder
481  .create<scf::ForOp>(
482  loc, c0, toRetainSize, c1, trueValue,
483  [&](OpBuilder &builder, Location loc, Value i,
484  ValueRange iterArgs) {
485  Value retainValue = builder.create<memref::LoadOp>(
486  loc, toRetainMemref, i);
487  Value doesAlias = builder.create<arith::CmpIOp>(
488  loc, arith::CmpIPredicate::eq, retainValue,
489  toDealloc);
490  builder.create<scf::IfOp>(
491  loc, doesAlias,
492  [&](OpBuilder &builder, Location loc) {
493  Value retainCondValue =
494  builder.create<memref::LoadOp>(
495  loc, retainCondsMemref, i);
496  Value aggregatedRetainCond =
497  builder.create<arith::OrIOp>(
498  loc, retainCondValue, cond);
499  builder.create<memref::StoreOp>(
500  loc, aggregatedRetainCond, retainCondsMemref,
501  i);
502  builder.create<scf::YieldOp>(loc);
503  });
504  Value doesntAlias = builder.create<arith::CmpIOp>(
505  loc, arith::CmpIPredicate::ne, retainValue,
506  toDealloc);
507  Value yieldValue = builder.create<arith::AndIOp>(
508  loc, iterArgs[0], doesntAlias);
509  builder.create<scf::YieldOp>(loc, yieldValue);
510  })
511  .getResult(0);
512 
513  // Build the second for loop that adds aliasing with previously
514  // deallocated memrefs.
515  Value noAlias =
516  builder
517  .create<scf::ForOp>(
518  loc, c0, outerIter, c1, noRetainAlias,
519  [&](OpBuilder &builder, Location loc, Value i,
520  ValueRange iterArgs) {
521  Value prevDeallocValue = builder.create<memref::LoadOp>(
522  loc, toDeallocMemref, i);
523  Value doesntAlias = builder.create<arith::CmpIOp>(
524  loc, arith::CmpIPredicate::ne, prevDeallocValue,
525  toDealloc);
526  Value yieldValue = builder.create<arith::AndIOp>(
527  loc, iterArgs[0], doesntAlias);
528  builder.create<scf::YieldOp>(loc, yieldValue);
529  })
530  .getResult(0);
531 
532  Value shouldDealoc = builder.create<arith::AndIOp>(loc, noAlias, cond);
533  builder.create<memref::StoreOp>(loc, shouldDealoc, deallocCondsMemref,
534  outerIter);
535  builder.create<scf::YieldOp>(loc);
536  });
537 
538  builder.create<func::ReturnOp>(loc);
539  return helperFuncOp;
540 }
541 
544  const bufferization::DeallocHelperMap &deallocHelperFuncMap) {
545  patterns.add<DeallocOpConversion>(patterns.getContext(),
546  deallocHelperFuncMap);
547 }
static MLIRContext * getContext(OpFoldResult val)
IntegerAttr getIndexAttr(int64_t value)
Definition: Builders.cpp:103
FunctionType getFunctionType(TypeRange inputs, TypeRange results)
Definition: Builders.cpp:75
BoolAttr getBoolAttr(bool value)
Definition: Builders.cpp:95
IntegerType getI1Type()
Definition: Builders.cpp:52
IndexType getIndexType()
Definition: Builders.cpp:50
This class implements a pattern rewriter for use with ConversionPatterns.
void replaceOp(Operation *op, ValueRange newValues) override
Replace the given operation with the new values.
This class describes a specific conversion target.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
This class helps build Operations.
Definition: Builders.h:205
static OpBuilder atBlockBegin(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to before the first operation in the block but still ins...
Definition: Builders.h:238
void clearInsertionPoint()
Reset the insertion point to no location.
Definition: Builders.h:377
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition: Builders.h:429
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:452
OpConversionPattern is a wrapper around ConversionPattern that allows for matching and rewriting agai...
A trait used to provide symbol table functionalities to a region operation.
Definition: SymbolTable.h:452
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
Operation * getParentWithTrait()
Returns the closest surrounding parent operation with trait Trait.
Definition: Operation.h:248
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
Definition: PatternMatch.h:519
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition: SymbolTable.h:24
@ Private
The symbol is private and may only be referenced by SymbolRefAttrs local to the operations within the...
StringAttr insert(Operation *symbol, Block::iterator insertPt={})
Insert a new symbol into the table, and rename it as necessary to avoid collisions.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition: ValueRange.h:387
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
void populateBufferizationDeallocLoweringPattern(RewritePatternSet &patterns, const DeallocHelperMap &deallocHelperFuncMap)
Adds the conversion pattern of the bufferization.dealloc operation to the given pattern set for use i...
func::FuncOp buildDeallocationLibraryFunction(OpBuilder &builder, Location loc, SymbolTable &symbolTable)
Construct the library function needed for the fully generic bufferization.dealloc lowering implemente...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:344
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
const FrozenRewritePatternSet & patterns
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
LogicalResult applyPartialConversion(ArrayRef< Operation * > ops, const ConversionTarget &target, const FrozenRewritePatternSet &patterns, ConversionConfig config=ConversionConfig())
Below we define several entry points for operation conversion.