552 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements patterns to convert `bufferization.dealloc` operations10// to the MemRef dialect.11//12//===----------------------------------------------------------------------===//13 14#include "mlir/Dialect/Arith/IR/Arith.h"15#include "mlir/Dialect/Bufferization/IR/Bufferization.h"16#include "mlir/Dialect/Bufferization/Transforms/Passes.h"17#include "mlir/Dialect/Func/IR/FuncOps.h"18#include "mlir/Dialect/MemRef/IR/MemRef.h"19#include "mlir/Dialect/SCF/IR/SCF.h"20#include "mlir/IR/BuiltinTypes.h"21#include "mlir/Transforms/DialectConversion.h"22 23namespace mlir {24namespace bufferization {25#define GEN_PASS_DEF_LOWERDEALLOCATIONSPASS26#include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"27} // namespace bufferization28} // namespace mlir29 30using namespace mlir;31 32namespace {33/// The DeallocOpConversion transforms all bufferization dealloc operations into34/// memref dealloc operations potentially guarded by scf if operations.35/// Additionally, memref extract_aligned_pointer_as_index and arith operations36/// are inserted to compute the guard conditions. We distinguish multiple cases37/// to provide an overall more efficient lowering. In the general case, a helper38/// func is created to avoid quadratic code size explosion (relative to the39/// number of operands of the dealloc operation). For examples of each case,40/// refer to the documentation of the member functions of this class.41class DeallocOpConversion42 : public OpConversionPattern<bufferization::DeallocOp> {43 44 /// Lower a simple case without any retained values and a single memref to45 /// avoiding the helper function. Ideally, static analysis can provide enough46 /// aliasing information to split the dealloc operations up into this simple47 /// 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 to54 /// ```55 /// scf.if %arg1 {56 /// memref.dealloc %arg0 : memref<2xf32>57 /// }58 /// ```59 LogicalResult60 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 memref::DeallocOp::create(builder, loc, adaptor.getMemrefs()[0]);68 scf::YieldOp::create(builder, loc);69 });70 return success();71 }72 73 /// A special case lowering for the deallocation operation with exactly one74 /// memref, but arbitrary number of retained values. This avoids the helper75 /// function that the general case needs and thus also avoids storing indices76 /// to specifically allocated memrefs. The size of the code produced by this77 /// lowering is linear to the number of retained values.78 ///79 /// Example:80 /// ```mlir81 /// %0:2 = bufferization.dealloc (%m : memref<2xf32>) if (%cond)82 // retain (%r0, %r1 : memref<1xf32>, memref<2xf32>)83 /// return %0#0, %0#1 : i1, i184 /// ```85 /// ```mlir86 /// %m_base_pointer = memref.extract_aligned_pointer_as_index %m87 /// %r0_base_pointer = memref.extract_aligned_pointer_as_index %r088 /// %r0_does_not_alias = arith.cmpi ne, %m_base_pointer, %r0_base_pointer89 /// %r1_base_pointer = memref.extract_aligned_pointer_as_index %r190 /// %r1_does_not_alias = arith.cmpi ne, %m_base_pointer, %r1_base_pointer91 /// %not_retained = arith.andi %r0_does_not_alias, %r1_does_not_alias : i192 /// %should_dealloc = arith.andi %not_retained, %cond : i193 /// scf.if %should_dealloc {94 /// memref.dealloc %m : memref<2xf32>95 /// }96 /// %true = arith.constant true97 /// %r0_does_alias = arith.xori %r0_does_not_alias, %true : i198 /// %r0_ownership = arith.andi %r0_does_alias, %cond : i199 /// %r1_does_alias = arith.xori %r1_does_not_alias, %true : i1100 /// %r1_ownership = arith.andi %r1_does_alias, %cond : i1101 /// return %r0_ownership, %r1_ownership : i1, i1102 /// ```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 the109 // memref index to check if they alias.110 SmallVector<Value> doesNotAliasList;111 Value memrefAsIdx = memref::ExtractAlignedPointerAsIndexOp::create(112 rewriter, op->getLoc(), adaptor.getMemrefs()[0]);113 for (Value retained : adaptor.getRetained()) {114 Value retainedAsIdx = memref::ExtractAlignedPointerAsIndexOp::create(115 rewriter, op->getLoc(), retained);116 Value doesNotAlias = arith::CmpIOp::create(rewriter, op->getLoc(),117 arith::CmpIPredicate::ne,118 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 = arith::AndIOp::create(rewriter, op->getLoc(), prev, doesNotAlias);126 127 // Also consider the condition given by the dealloc operation and perform a128 // conditional deallocation guarded by that value.129 Value shouldDealloc = arith::AndIOp::create(rewriter, op->getLoc(), prev,130 adaptor.getConditions()[0]);131 132 scf::IfOp::create(rewriter, op.getLoc(), shouldDealloc,133 [&](OpBuilder &builder, Location loc) {134 memref::DeallocOp::create(builder, loc,135 adaptor.getMemrefs()[0]);136 scf::YieldOp::create(builder, loc);137 });138 139 // Compute the replacement values for the dealloc operation results. This140 // inserts an already canonicalized form of141 // `select(does_alias_with_memref(r), memref_cond, false)` for each retained142 // value r.143 SmallVector<Value> replacements;144 Value trueVal = arith::ConstantOp::create(rewriter, op->getLoc(),145 rewriter.getBoolAttr(true));146 for (Value doesNotAlias : doesNotAliasList) {147 Value aliases =148 arith::XOrIOp::create(rewriter, op->getLoc(), doesNotAlias, trueVal);149 Value result = arith::AndIOp::create(rewriter, op->getLoc(), aliases,150 adaptor.getConditions()[0]);151 replacements.push_back(result);152 }153 154 rewriter.replaceOp(op, replacements);155 156 return success();157 }158 159 /// Lowering that supports all features the dealloc operation has to offer. It160 /// computes the base pointer of each memref (as an index), stores it in a161 /// new memref helper structure and passes it to the helper function generated162 /// in 'buildDeallocationHelperFunction'. The results are stored in two lists163 /// (represented as memrefs) of booleans passed as arguments. The first list164 /// stores whether the corresponding condition should be deallocated, the165 /// second list stores the ownership of the retained values which can be used166 /// to replace the result values of the `bufferization.dealloc` operation.167 ///168 /// Example:169 /// ```170 /// %0:2 = bufferization.dealloc (%m0, %m1 : memref<2xf32>, memref<5xf32>)171 /// if (%cond0, %cond1)172 /// retain (%r0, %r1 : memref<1xf32>, memref<2xf32>)173 /// ```174 /// lowers to (simplified):175 /// ```176 /// %c0 = arith.constant 0 : index177 /// %c1 = arith.constant 1 : index178 /// %dealloc_base_pointer_list = memref.alloc() : memref<2xindex>179 /// %cond_list = memref.alloc() : memref<2xi1>180 /// %retain_base_pointer_list = memref.alloc() : memref<2xindex>181 /// %m0_base_pointer = memref.extract_aligned_pointer_as_index %m0182 /// memref.store %m0_base_pointer, %dealloc_base_pointer_list[%c0]183 /// %m1_base_pointer = memref.extract_aligned_pointer_as_index %m1184 /// memref.store %m1_base_pointer, %dealloc_base_pointer_list[%c1]185 /// memref.store %cond0, %cond_list[%c0]186 /// memref.store %cond1, %cond_list[%c1]187 /// %r0_base_pointer = memref.extract_aligned_pointer_as_index %r0188 /// memref.store %r0_base_pointer, %retain_base_pointer_list[%c0]189 /// %r1_base_pointer = memref.extract_aligned_pointer_as_index %r1190 /// memref.store %r1_base_pointer, %retain_base_pointer_list[%c1]191 /// %dyn_dealloc_base_pointer_list = memref.cast %dealloc_base_pointer_list :192 /// memref<2xindex> to memref<?xindex>193 /// %dyn_cond_list = memref.cast %cond_list : memref<2xi1> to memref<?xi1>194 /// %dyn_retain_base_pointer_list = memref.cast %retain_base_pointer_list :195 /// memref<2xindex> to memref<?xindex>196 /// %dealloc_cond_out = memref.alloc() : memref<2xi1>197 /// %ownership_out = memref.alloc() : memref<2xi1>198 /// %dyn_dealloc_cond_out = memref.cast %dealloc_cond_out :199 /// memref<2xi1> to memref<?xi1>200 /// %dyn_ownership_out = memref.cast %ownership_out :201 /// memref<2xi1> to memref<?xi1>202 /// call @dealloc_helper(%dyn_dealloc_base_pointer_list,203 /// %dyn_retain_base_pointer_list,204 /// %dyn_cond_list,205 /// %dyn_dealloc_cond_out,206 /// %dyn_ownership_out) : (...)207 /// %m0_dealloc_cond = memref.load %dyn_dealloc_cond_out[%c0] : memref<2xi1>208 /// scf.if %m0_dealloc_cond {209 /// memref.dealloc %m0 : memref<2xf32>210 /// }211 /// %m1_dealloc_cond = memref.load %dyn_dealloc_cond_out[%c1] : memref<2xi1>212 /// scf.if %m1_dealloc_cond {213 /// memref.dealloc %m1 : memref<5xf32>214 /// }215 /// %r0_ownership = memref.load %dyn_ownership_out[%c0] : memref<2xi1>216 /// %r1_ownership = memref.load %dyn_ownership_out[%c1] : memref<2xi1>217 /// memref.dealloc %dealloc_base_pointer_list : memref<2xindex>218 /// memref.dealloc %retain_base_pointer_list : memref<2xindex>219 /// memref.dealloc %cond_list : memref<2xi1>220 /// memref.dealloc %dealloc_cond_out : memref<2xi1>221 /// memref.dealloc %ownership_out : memref<2xi1>222 /// // replace %0#0 with %r0_ownership223 /// // replace %0#1 with %r1_ownership224 /// ```225 LogicalResult rewriteGeneralCase(bufferization::DeallocOp op,226 OpAdaptor adaptor,227 ConversionPatternRewriter &rewriter) const {228 // Allocate two memrefs holding the base pointer indices of the list of229 // memrefs to be deallocated and the ones to be retained. These can then be230 // passed to the helper function and the for-loops can iterate over them.231 // Without storing them to memrefs, we could not use for-loops but only a232 // completely unrolled version of it, potentially leading to code-size233 // blow-up.234 Value toDeallocMemref = memref::AllocOp::create(235 rewriter, op.getLoc(),236 MemRefType::get({(int64_t)adaptor.getMemrefs().size()},237 rewriter.getIndexType()));238 Value conditionMemref = memref::AllocOp::create(239 rewriter, op.getLoc(),240 MemRefType::get({(int64_t)adaptor.getConditions().size()},241 rewriter.getI1Type()));242 Value toRetainMemref = memref::AllocOp::create(243 rewriter, op.getLoc(),244 MemRefType::get({(int64_t)adaptor.getRetained().size()},245 rewriter.getIndexType()));246 247 auto getConstValue = [&](uint64_t value) -> Value {248 return arith::ConstantOp::create(rewriter, op.getLoc(),249 rewriter.getIndexAttr(value));250 };251 252 // Extract the base pointers of the memrefs as indices to check for aliasing253 // at runtime.254 for (auto [i, toDealloc] : llvm::enumerate(adaptor.getMemrefs())) {255 Value memrefAsIdx = memref::ExtractAlignedPointerAsIndexOp::create(256 rewriter, op.getLoc(), toDealloc);257 memref::StoreOp::create(rewriter, op.getLoc(), memrefAsIdx,258 toDeallocMemref, getConstValue(i));259 }260 261 for (auto [i, cond] : llvm::enumerate(adaptor.getConditions()))262 memref::StoreOp::create(rewriter, op.getLoc(), cond, conditionMemref,263 getConstValue(i));264 265 for (auto [i, toRetain] : llvm::enumerate(adaptor.getRetained())) {266 Value memrefAsIdx = memref::ExtractAlignedPointerAsIndexOp::create(267 rewriter, op.getLoc(), toRetain);268 memref::StoreOp::create(rewriter, op.getLoc(), memrefAsIdx,269 toRetainMemref, getConstValue(i));270 }271 272 // Cast the allocated memrefs to dynamic shape because we want only one273 // helper function no matter how many operands the bufferization.dealloc274 // has.275 Value castedDeallocMemref = memref::CastOp::create(276 rewriter, op->getLoc(),277 MemRefType::get({ShapedType::kDynamic}, rewriter.getIndexType()),278 toDeallocMemref);279 Value castedCondsMemref = memref::CastOp::create(280 rewriter, op->getLoc(),281 MemRefType::get({ShapedType::kDynamic}, rewriter.getI1Type()),282 conditionMemref);283 Value castedRetainMemref = memref::CastOp::create(284 rewriter, op->getLoc(),285 MemRefType::get({ShapedType::kDynamic}, rewriter.getIndexType()),286 toRetainMemref);287 288 Value deallocCondsMemref = memref::AllocOp::create(289 rewriter, op.getLoc(),290 MemRefType::get({(int64_t)adaptor.getMemrefs().size()},291 rewriter.getI1Type()));292 Value retainCondsMemref = memref::AllocOp::create(293 rewriter, op.getLoc(),294 MemRefType::get({(int64_t)adaptor.getRetained().size()},295 rewriter.getI1Type()));296 297 Value castedDeallocCondsMemref = memref::CastOp::create(298 rewriter, op->getLoc(),299 MemRefType::get({ShapedType::kDynamic}, rewriter.getI1Type()),300 deallocCondsMemref);301 Value castedRetainCondsMemref = memref::CastOp::create(302 rewriter, op->getLoc(),303 MemRefType::get({ShapedType::kDynamic}, rewriter.getI1Type()),304 retainCondsMemref);305 306 Operation *symtableOp = op->getParentWithTrait<OpTrait::SymbolTable>();307 func::CallOp::create(308 rewriter, op.getLoc(), deallocHelperFuncMap.lookup(symtableOp),309 SmallVector<Value>{castedDeallocMemref, castedRetainMemref,310 castedCondsMemref, castedDeallocCondsMemref,311 castedRetainCondsMemref});312 313 for (unsigned i = 0, e = adaptor.getMemrefs().size(); i < e; ++i) {314 Value idxValue = getConstValue(i);315 Value shouldDealloc = memref::LoadOp::create(316 rewriter, op.getLoc(), deallocCondsMemref, idxValue);317 scf::IfOp::create(rewriter, op.getLoc(), shouldDealloc,318 [&](OpBuilder &builder, Location loc) {319 memref::DeallocOp::create(builder, loc,320 adaptor.getMemrefs()[i]);321 scf::YieldOp::create(builder, loc);322 });323 }324 325 SmallVector<Value> replacements;326 for (unsigned i = 0, e = adaptor.getRetained().size(); i < e; ++i) {327 Value idxValue = getConstValue(i);328 Value ownership = memref::LoadOp::create(rewriter, op.getLoc(),329 retainCondsMemref, idxValue);330 replacements.push_back(ownership);331 }332 333 // Deallocate above allocated memrefs again to avoid memory leaks.334 // Deallocation will not be run on code after this stage.335 memref::DeallocOp::create(rewriter, op.getLoc(), toDeallocMemref);336 memref::DeallocOp::create(rewriter, op.getLoc(), toRetainMemref);337 memref::DeallocOp::create(rewriter, op.getLoc(), conditionMemref);338 memref::DeallocOp::create(rewriter, op.getLoc(), deallocCondsMemref);339 memref::DeallocOp::create(rewriter, op.getLoc(), retainCondsMemref);340 341 rewriter.replaceOp(op, replacements);342 return success();343 }344 345public:346 DeallocOpConversion(347 MLIRContext *context,348 const bufferization::DeallocHelperMap &deallocHelperFuncMap)349 : OpConversionPattern<bufferization::DeallocOp>(context),350 deallocHelperFuncMap(deallocHelperFuncMap) {}351 352 LogicalResult353 matchAndRewrite(bufferization::DeallocOp op, OpAdaptor adaptor,354 ConversionPatternRewriter &rewriter) const override {355 // Lower the trivial case.356 if (adaptor.getMemrefs().empty()) {357 Value falseVal = arith::ConstantOp::create(rewriter, op.getLoc(),358 rewriter.getBoolAttr(false));359 rewriter.replaceOp(360 op, SmallVector<Value>(adaptor.getRetained().size(), falseVal));361 return success();362 }363 364 if (adaptor.getMemrefs().size() == 1 && adaptor.getRetained().empty())365 return rewriteOneMemrefNoRetainCase(op, adaptor, rewriter);366 367 if (adaptor.getMemrefs().size() == 1)368 return rewriteOneMemrefMultipleRetainCase(op, adaptor, rewriter);369 370 Operation *symtableOp = op->getParentWithTrait<OpTrait::SymbolTable>();371 if (!deallocHelperFuncMap.contains(symtableOp))372 return op->emitError(373 "library function required for generic lowering, but cannot be "374 "automatically inserted when operating on functions");375 376 return rewriteGeneralCase(op, adaptor, rewriter);377 }378 379private:380 const bufferization::DeallocHelperMap &deallocHelperFuncMap;381};382} // namespace383 384namespace {385struct LowerDeallocationsPass386 : public bufferization::impl::LowerDeallocationsPassBase<387 LowerDeallocationsPass> {388 void runOnOperation() override {389 if (!isa<ModuleOp, FunctionOpInterface>(getOperation())) {390 emitError(getOperation()->getLoc(),391 "root operation must be a builtin.module or a function");392 signalPassFailure();393 return;394 }395 396 bufferization::DeallocHelperMap deallocHelperFuncMap;397 if (auto module = dyn_cast<ModuleOp>(getOperation())) {398 OpBuilder builder = OpBuilder::atBlockBegin(module.getBody());399 400 // Build dealloc helper function if there are deallocs.401 getOperation()->walk([&](bufferization::DeallocOp deallocOp) {402 Operation *symtableOp =403 deallocOp->getParentWithTrait<OpTrait::SymbolTable>();404 if (deallocOp.getMemrefs().size() > 1 &&405 !deallocHelperFuncMap.contains(symtableOp)) {406 SymbolTable symbolTable(symtableOp);407 func::FuncOp helperFuncOp =408 bufferization::buildDeallocationLibraryFunction(409 builder, getOperation()->getLoc(), symbolTable);410 deallocHelperFuncMap[symtableOp] = helperFuncOp;411 }412 });413 }414 415 RewritePatternSet patterns(&getContext());416 bufferization::populateBufferizationDeallocLoweringPattern(417 patterns, deallocHelperFuncMap);418 419 ConversionTarget target(getContext());420 target.addLegalDialect<memref::MemRefDialect, arith::ArithDialect,421 scf::SCFDialect, func::FuncDialect>();422 target.addIllegalOp<bufferization::DeallocOp>();423 424 if (failed(applyPartialConversion(getOperation(), target,425 std::move(patterns))))426 signalPassFailure();427 }428};429} // namespace430 431func::FuncOp mlir::bufferization::buildDeallocationLibraryFunction(432 OpBuilder &builder, Location loc, SymbolTable &symbolTable) {433 Type indexMemrefType =434 MemRefType::get({ShapedType::kDynamic}, builder.getIndexType());435 Type boolMemrefType =436 MemRefType::get({ShapedType::kDynamic}, builder.getI1Type());437 SmallVector<Type> argTypes{indexMemrefType, indexMemrefType, boolMemrefType,438 boolMemrefType, boolMemrefType};439 builder.clearInsertionPoint();440 441 // Generate the func operation itself.442 auto helperFuncOp = func::FuncOp::create(443 loc, "dealloc_helper", builder.getFunctionType(argTypes, {}));444 helperFuncOp.setVisibility(SymbolTable::Visibility::Private);445 symbolTable.insert(helperFuncOp);446 auto &block = helperFuncOp.getFunctionBody().emplaceBlock();447 block.addArguments(argTypes, SmallVector<Location>(argTypes.size(), loc));448 449 builder.setInsertionPointToStart(&block);450 Value toDeallocMemref = helperFuncOp.getArguments()[0];451 Value toRetainMemref = helperFuncOp.getArguments()[1];452 Value conditionMemref = helperFuncOp.getArguments()[2];453 Value deallocCondsMemref = helperFuncOp.getArguments()[3];454 Value retainCondsMemref = helperFuncOp.getArguments()[4];455 456 // Insert some prerequisites.457 Value c0 = arith::ConstantOp::create(builder, loc, builder.getIndexAttr(0));458 Value c1 = arith::ConstantOp::create(builder, loc, builder.getIndexAttr(1));459 Value trueValue =460 arith::ConstantOp::create(builder, loc, builder.getBoolAttr(true));461 Value falseValue =462 arith::ConstantOp::create(builder, loc, builder.getBoolAttr(false));463 Value toDeallocSize =464 memref::DimOp::create(builder, loc, toDeallocMemref, c0);465 Value toRetainSize = memref::DimOp::create(builder, loc, toRetainMemref, c0);466 467 scf::ForOp::create(468 builder, loc, c0, toRetainSize, c1, ValueRange(),469 [&](OpBuilder &builder, Location loc, Value i, ValueRange iterArgs) {470 memref::StoreOp::create(builder, loc, falseValue, retainCondsMemref, i);471 scf::YieldOp::create(builder, loc);472 });473 474 scf::ForOp::create(475 builder, loc, c0, toDeallocSize, c1, ValueRange(),476 [&](OpBuilder &builder, Location loc, Value outerIter,477 ValueRange iterArgs) {478 Value toDealloc =479 memref::LoadOp::create(builder, loc, toDeallocMemref, outerIter);480 Value cond =481 memref::LoadOp::create(builder, loc, conditionMemref, outerIter);482 483 // Build the first for loop that computes aliasing with retained484 // memrefs.485 Value486 noRetainAlias =487 scf::ForOp::create(488 builder, loc, c0, toRetainSize, c1, trueValue,489 [&](OpBuilder &builder, Location loc, Value i,490 ValueRange iterArgs) {491 Value retainValue = memref::LoadOp::create(492 builder, loc, toRetainMemref, i);493 Value doesAlias = arith::CmpIOp::create(494 builder, loc, arith::CmpIPredicate::eq, retainValue,495 toDealloc);496 scf::IfOp::create(497 builder, loc, doesAlias,498 [&](OpBuilder &builder, Location loc) {499 Value retainCondValue = memref::LoadOp::create(500 builder, loc, retainCondsMemref, i);501 Value aggregatedRetainCond = arith::OrIOp::create(502 builder, loc, retainCondValue, cond);503 memref::StoreOp::create(builder, loc,504 aggregatedRetainCond,505 retainCondsMemref, i);506 scf::YieldOp::create(builder, loc);507 });508 Value doesntAlias = arith::CmpIOp::create(509 builder, loc, arith::CmpIPredicate::ne, retainValue,510 toDealloc);511 Value yieldValue = arith::AndIOp::create(512 builder, loc, iterArgs[0], doesntAlias);513 scf::YieldOp::create(builder, loc, yieldValue);514 })515 .getResult(0);516 517 // Build the second for loop that adds aliasing with previously518 // deallocated memrefs.519 Value520 noAlias =521 scf::ForOp::create(522 builder, loc, c0, outerIter, c1, noRetainAlias,523 [&](OpBuilder &builder, Location loc, Value i,524 ValueRange iterArgs) {525 Value prevDeallocValue = memref::LoadOp::create(526 builder, loc, toDeallocMemref, i);527 Value doesntAlias = arith::CmpIOp::create(528 builder, loc, arith::CmpIPredicate::ne,529 prevDeallocValue, toDealloc);530 Value yieldValue = arith::AndIOp::create(531 builder, loc, iterArgs[0], doesntAlias);532 scf::YieldOp::create(builder, loc, yieldValue);533 })534 .getResult(0);535 536 Value shouldDealoc = arith::AndIOp::create(builder, loc, noAlias, cond);537 memref::StoreOp::create(builder, loc, shouldDealoc, deallocCondsMemref,538 outerIter);539 scf::YieldOp::create(builder, loc);540 });541 542 func::ReturnOp::create(builder, loc);543 return helperFuncOp;544}545 546void mlir::bufferization::populateBufferizationDeallocLoweringPattern(547 RewritePatternSet &patterns,548 const bufferization::DeallocHelperMap &deallocHelperFuncMap) {549 patterns.add<DeallocOpConversion>(patterns.getContext(),550 deallocHelperFuncMap);551}552