This is an archive of the discontinued LLVM Phabricator instance.

[mlir] Add `mlir::blockIsInLoop()`
ClosedPublic

Authored by tblah on Jan 10 2023, 9:10 AM.

Details

Summary

This method returns whether a block is nested inside of a loop. There
can be two kinds of loop:

  1. The block is nested inside of a LoopLikeOpInterface
  2. There is a cycle in the control flow graph

This will be useful for Flang's stack arrays pass, which moves array
allocations from the heap to the stack. Special handling is needed when
allocations occur inside of loops to ensure additional stack space is
not allocated on each loop iteration.

Diff Detail

Event Timeline

tblah created this revision.Jan 10 2023, 9:10 AM
Herald added a project: Restricted Project. · View Herald Transcript
tblah requested review of this revision.Jan 10 2023, 9:10 AM
rriddle requested changes to this revision.Jan 10 2023, 11:12 AM

We generally try to keep the core IR API free from things specific to certain interfaces/compilation flows/etc. This utility is something that should generally live near the LoopLikeOpInterface.

This revision now requires changes to proceed.Jan 10 2023, 11:12 AM
tblah updated this revision to Diff 488141.Jan 11 2023, 3:33 AM

Moved to a stand alone function in LoopLikeInterface.h

DavidTruby added inline comments.
mlir/lib/Interfaces/LoopLikeInterface.cpp
31

nit: this could be

return llvm::any_of(current->getSuccessors(), [&](const Block* successor) {
   return isInLoopImpl(target, successor, visited);
});

you can ignore this suggestion though.

mlir/test/lib/Interfaces/LoopLikeInterface/TestBlockInLoop.cpp
25

Nit: these should also say override

tblah updated this revision to Diff 488651.Jan 12 2023, 7:45 AM
tblah marked an inline comment as done.

Updated to use llvm::any_of instead of a for loop and an if.

tblah marked an inline comment as done.Jan 12 2023, 7:46 AM

Thanks for review @DavidTruby

mlir/test/lib/Interfaces/LoopLikeInterface/TestBlockInLoop.cpp
25

clang-tidy tells me that override is already implied by final.

DavidTruby added inline comments.Jan 17 2023, 6:54 AM
mlir/test/lib/Interfaces/LoopLikeInterface/TestBlockInLoop.cpp
25

Interesting, I don't think that's true in the general case. It might be true here though as these functions don't say they're virtual (so the virtualness must come from a base class)
You can think of a case where a virtual function at the very base of the hierarchy is marked final; this function clearly wouldn't imply override as it isn't overriding anything.
Probably just defer to clang-tidy here though!

tblah updated this revision to Diff 490096.Jan 18 2023, 3:12 AM

Updated a comment and documentation string in the test pass to make it clear
the loop detection is no longer a method in mlir::Block

tblah retitled this revision from [mlir] Add `Block::isInLoop` to [mlir] Add `mlir::blockIsInLoop()`.Jan 18 2023, 3:13 AM

Thanks @tblah for this patch. I have a question/comment inline.

mlir/lib/Interfaces/LoopLikeInterface.cpp
40–43

I was thinking of a case where there is no loop in the current region, but there is a cf style loop in the outer-region. A case is presented below. I think we will not currently catch this case. May be we have to do this check at the parent levels all the way to the function-level op.

func.func @check_alloc_in_loop(%counter : i64) {
  cf.br ^bb1(%counter: i64)
  ^bb1(%lv : i64):
    %cm1 = arith.constant -1 : i64
    %rem = arith.addi %lv, %cm1 : i64
    %zero = arith.constant 0 : i64
    %p = arith.cmpi eq, %rem, %zero : i64
    cf.cond_br %p, ^bb3, ^bb2
  ^bb2:
    scf.execute_region -> () {
      //Assume alloc is here
      %c1 = arith.constant 1 : i64
      scf.yield
    }
    cf.br ^bb1(%rem: i64)
  ^bb3:
    return
}
tblah updated this revision to Diff 490139.Jan 18 2023, 6:56 AM

Handle case where blocks are nested inside of eachother

tblah marked an inline comment as done.Jan 18 2023, 6:57 AM
tblah added inline comments.
mlir/lib/Interfaces/LoopLikeInterface.cpp
40–43

Thanks for pointing out this case. I've updated the patch and added this to the tests.

LGTM. I have not accepted patches in this area before. If @rriddle is busy, it will be great to get another approval from one of the other reviewers on the list before submitting.

mlir/lib/Interfaces/LoopLikeInterface.cpp
37–41
45

Nit: Now that we are recursing to all the parents, I guess the getParentOfType<LoopLikeOpInterface> is not required.

tblah marked an inline comment as done.Jan 24 2023, 3:45 AM

Ping for any MLIR reviewers

mehdi_amini added inline comments.Jan 24 2023, 9:21 AM
mlir/lib/Interfaces/LoopLikeInterface.cpp
29

Can you please remove the recursion and use an iterative algorithm?
(Guideline in https://mlir.llvm.org/getting_started/DeveloperGuide/)

mehdi_amini added inline comments.Jan 24 2023, 10:43 AM
mlir/lib/Interfaces/LoopLikeInterface.cpp
41

I'm not sure why we should hardcode func::FuncOp, for example this wouldn't work with a llvm::FuncOp. But also what is this supposed to handle that isn't handled later?

I suspect you could try to first filter out if there is a parent implementing the interface, before looking for a loop in the CFG at every level in parent regions:

 if (parent && getParentOfType<LoopLikeOpInterface>(parent))
   return true;

// Or the block could be inside a control flow graph loop:
 llvm::DenseSet<Block *> visited;
 while (block) {
   visited.clear();
   if ( isInLoopImpl(block, block, visited)) return true;
   if (Operation *parent = block->getParentOp()) block = parent->getBlock();
   else break;
 }
 return false;
tblah updated this revision to Diff 492040.Jan 25 2023, 2:21 AM
  • Check for a parent implementing LoopLikeOpInterface before searching for a CFG loop
  • Don't use recursion when searching for CFG cycles
  • Stop hierarchical search at anything implementing FunctionOpInterface, not just func.func
tblah marked 3 inline comments as done.Jan 25 2023, 2:26 AM
tblah added inline comments.
mlir/lib/Interfaces/LoopLikeInterface.cpp
40–43

I was thinking of a case where there is no loop in the current region, but there is a cf style loop in the outer-region. A case is presented below. I think we will not currently catch this case. May be we have to do this check at the parent levels all the way to the function-level op.

func.func @check_alloc_in_loop(%counter : i64) {
  cf.br ^bb1(%counter: i64)
  ^bb1(%lv : i64):
    %cm1 = arith.constant -1 : i64
    %rem = arith.addi %lv, %cm1 : i64
    %zero = arith.constant 0 : i64
    %p = arith.cmpi eq, %rem, %zero : i64
    cf.cond_br %p, ^bb3, ^bb2
  ^bb2:
    scf.execute_region -> () {
      //Assume alloc is here
      %c1 = arith.constant 1 : i64
      scf.yield
    }
    cf.br ^bb1(%rem: i64)
  ^bb3:
    return
}
41

Thanks for reviewing.

I have changed to check to see if the parent implements FunctionOpInterface and now check for a parent implementing LoopLikeOpInterface first.

This condition is supposed to handle where there is no loop in the region that the current block belongs to, but that whole region is inside a loop, for example in @kiranchandramohan's example:

func.func @check_alloc_in_loop(%counter : i64) {
  cf.br ^bb1(%counter: i64)
  ^bb1(%lv : i64):
    %cm1 = arith.constant -1 : i64
    %rem = arith.addi %lv, %cm1 : i64
    %zero = arith.constant 0 : i64
    %p = arith.cmpi eq, %rem, %zero : i64
    cf.cond_br %p, ^bb3, ^bb2
  ^bb2:
    scf.execute_region -> () {
      //Assume alloc is here
      %c1 = arith.constant 1 : i64
      scf.yield
    }
    cf.br ^bb1(%rem: i64)
  ^bb3:
    return
}
rriddle accepted this revision.Feb 8 2023, 10:59 AM
rriddle added inline comments.
mlir/include/mlir/Interfaces/LoopLikeInterface.h
26

I'm slightly nervous about how generally this function is named. Could we make it slightly more specific to LoopLikeInterface, or even make it a static method on the interface class?

mlir/lib/Interfaces/LoopLikeInterface.cpp
20
37–38

Do you need the llvm:: on these? We re-export a bunch of stuff from llvm into the mlir namespace.

This revision is now accepted and ready to land.Feb 8 2023, 10:59 AM
tblah updated this revision to Diff 496056.Feb 9 2023, 2:17 AM
tblah marked 3 inline comments as done.

Thanks for the review.

Changes:

  • Move to a static method in LoopLikeOpInterface
  • Remove redundant namespace specification on re-exported LLVM clases
tblah marked an inline comment as done.Feb 9 2023, 2:19 AM
This revision was automatically updated to reflect the committed changes.
tblah added a comment.Feb 9 2023, 8:20 AM

Fix for failed shared library builds is at https://reviews.llvm.org/D143658

Is there a link to a log for the failure? I can't repro on MacOS

Actually that may be it:

: && /Library/Developer/CommandLineTools/usr/bin/c++ -fPIC -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -Werror=mismatched-tags -O3 -DNDEBUG -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX13.1.sdk -dynamiclib -Wl,-headerpad_max_install_names -Wl,-dead_strip -o lib/libMLIRLoopLikeInterfaceTestPasses.dylib -install_name @rpath/libMLIRLoopLikeInterfaceTestPasses.dylib tools/mlir/test/lib/Interfaces/LoopLikeInterface/CMakeFiles/MLIRLoopLikeInterfaceTestPasses.dir/TestBlockInLoop.cpp.o  -Wl,-rpath,@loader_path/../lib  lib/libMLIRPass.dylib  lib/libMLIRAnalysis.dylib  lib/libMLIRLoopLikeInterface.dylib  lib/libMLIRCallInterfaces.dylib  lib/libMLIRControlFlowInterfaces.dylib  lib/libMLIRDataLayoutInterfaces.dylib  lib/libMLIRInferIntRangeInterface.dylib  lib/libMLIRInferTypeOpInterface.dylib  lib/libMLIRSideEffectInterfaces.dylib  lib/libMLIRViewLikeInterface.dylib  lib/libMLIRIR.dylib  lib/libMLIRSupport.dylib  lib/libLLVMSupport.dylib && :
Undefined symbols for architecture arm64:
  "mlir::detail::TypeIDResolver<mlir::func::FuncOp, void>::id", referenced from:
      (anonymous namespace)::IsInLoopPass::runOnOperation() in TestBlockInLoop.cpp.o
ld: symbol(s) not found for architecture arm64
mlir/test/lib/Interfaces/LoopLikeInterface/CMakeLists.txt
9

The missing dependency on the FuncDialect is here, and not on the interface I believe.

rkayaith added inline comments.
mlir/include/mlir/Interfaces/LoopLikeInterface.td
102–105 ↗(On Diff #496118)
mlir/lib/Interfaces/LoopLikeInterface.cpp
10

this include doesn't seem necessary

tblah reopened this revision.Feb 10 2023, 2:37 AM
tblah marked 3 inline comments as done.
tblah added inline comments.
mlir/include/mlir/Interfaces/LoopLikeInterface.td
102–105 ↗(On Diff #496118)

Thanks!

mlir/lib/Interfaces/LoopLikeInterface.cpp
10

It was for a reference to FunctionOpInterface, I have changed it to the more specific mlir/IR/FunctionInterfaces.h

mlir/test/lib/Interfaces/LoopLikeInterface/CMakeLists.txt
9

Thanks!

This revision is now accepted and ready to land.Feb 10 2023, 2:37 AM
tblah updated this revision to Diff 496391.Feb 10 2023, 2:37 AM
tblah marked 3 inline comments as done.

Updated to remove dependency upon func dialect plus fix comment.

tblah added a comment.Feb 10 2023, 6:56 AM

@rkayaith, @mehdi_amini thanks for taking a look. Am I okay to commit this with the fixes?

@rkayaith, @mehdi_amini thanks for taking a look. Am I okay to commit this with the fixes?

Looks good to me, assuming the build issue is fixed.