brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.0 KiB · e9ad13f Raw
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