Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[mlir] Add function for checking if a block is inside a loop
This function returns whether a block is nested inside of a loop. There can be three kinds of loop: 1) The block is nested inside of a LoopLikeOpInterface 2) The block is nested inside another block which is in a loop 3) 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. Differential Revision: https://reviews.llvm.org/D141401
- Loading branch information
Showing
8 changed files
with
251 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
mlir/test/Interfaces/LoopLikeInterface/test-block-loop.mlir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// RUN: mlir-opt %s --mlir-disable-threading -test-block-is-in-loop 2>&1 | FileCheck %s | ||
|
||
module { | ||
// Test function with only one bb | ||
func.func @simple() { | ||
func.return | ||
} | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb0: | ||
|
||
// Test simple loop bb0 -> bb0 | ||
func.func @loopForever() { | ||
^bb0: | ||
cf.br ^bb1 | ||
^bb1: | ||
cf.br ^bb1 | ||
} | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb0: | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb1: | ||
|
||
// Test bb0 -> bb1 -> bb2 -> bb1 | ||
func.func @loopForever2() { | ||
^bb0: | ||
cf.br ^bb1 | ||
^bb1: | ||
cf.br ^bb2 | ||
^bb2: | ||
cf.br ^bb1 | ||
} | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb0: | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb1: | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb2: | ||
|
||
// Test conditional branch without loop | ||
// bb0 -> bb1 -> {bb2, bb3} | ||
func.func @noLoop(%arg0: i1) { | ||
cf.br ^bb1 | ||
^bb1: | ||
cf.cond_br %arg0, ^bb2, ^bb3 | ||
^bb2: | ||
func.return | ||
^bb3: | ||
func.return | ||
} | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb0(%arg0: i1) | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb1: | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb2: | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb3: | ||
|
||
// test multiple loops | ||
// bb0 -> bb1 -> bb2 -> bb3 { -> bb2} -> bb4 { -> bb1 } -> bb5 | ||
func.func @multipleLoops(%arg0: i1, %arg1: i1) { | ||
cf.br ^bb1 | ||
^bb1: | ||
cf.br ^bb2 | ||
^bb2: | ||
cf.br ^bb3 | ||
^bb3: | ||
cf.cond_br %arg0, ^bb4, ^bb2 | ||
^bb4: | ||
cf.cond_br %arg1, ^bb1, ^bb5 | ||
^bb5: | ||
return | ||
} | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb0(%arg0: i1, %arg1: i1) | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb1: | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb2: | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb3: | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb4: | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb5: | ||
|
||
// test derived from real Flang output | ||
func.func @_QPblockTest0(%arg0: i1, %arg1: i1) { | ||
cf.br ^bb1 | ||
^bb1: // 2 preds: ^bb0, ^bb4 | ||
cf.cond_br %arg0, ^bb2, ^bb5 | ||
^bb2: // pred: ^bb1 | ||
cf.cond_br %arg1, ^bb3, ^bb4 | ||
^bb3: // pred: ^bb2 | ||
return | ||
^bb4: // pred: ^bb2 | ||
cf.br ^bb1 | ||
^bb5: // pred: ^bb1 | ||
return | ||
} | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb0(%arg0: i1, %arg1: i1) | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb1: | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb2: | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb3: | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb4: | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb5: | ||
|
||
// check nested blocks | ||
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 -> () { | ||
%c1 = arith.constant 1 : i64 | ||
scf.yield | ||
} | ||
cf.br ^bb1(%rem: i64) | ||
^bb3: | ||
return | ||
} | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb0(%arg0: i64): | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb1(%0: i64) | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb0: | ||
// CHECK-NEXT: %c1_i64 | ||
// CHECK: Block is in a loop | ||
// CHECK-NEXT: ^bb2: | ||
// CHECK: Block is not in a loop | ||
// CHECK-NEXT: ^bb3: | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
add_subdirectory(LoopLikeInterface) | ||
add_subdirectory(TilingInterface) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
add_mlir_library(MLIRLoopLikeInterfaceTestPasses | ||
TestBlockInLoop.cpp | ||
|
||
EXCLUDE_FROM_LIBMLIR | ||
|
||
LINK_LIBS PUBLIC | ||
MLIRPass | ||
MLIRLoopLikeInterface | ||
) |
47 changes: 47 additions & 0 deletions
47
mlir/test/lib/Interfaces/LoopLikeInterface/TestBlockInLoop.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
//===- TestBlockInLoop.cpp - Pass to test mlir::blockIsInLoop -------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "mlir/Dialect/Func/IR/FuncOps.h" | ||
#include "mlir/IR/BuiltinOps.h" | ||
#include "mlir/Interfaces/LoopLikeInterface.h" | ||
#include "mlir/Pass/Pass.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
|
||
using namespace mlir; | ||
|
||
namespace { | ||
/// This is a test pass that tests Blocks's isInLoop method by checking if each | ||
/// block in a function is in a loop and outputing if it is | ||
struct IsInLoopPass | ||
: public PassWrapper<IsInLoopPass, OperationPass<func::FuncOp>> { | ||
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(IsInLoopPass) | ||
|
||
StringRef getArgument() const final { return "test-block-is-in-loop"; } | ||
StringRef getDescription() const final { | ||
return "Test mlir::blockIsInLoop()"; | ||
} | ||
|
||
void runOnOperation() override { | ||
mlir::func::FuncOp func = getOperation(); | ||
func.walk([](mlir::Block *block) { | ||
llvm::outs() << "Block is "; | ||
if (LoopLikeOpInterface::blockIsInLoop(block)) | ||
llvm::outs() << "in a loop\n"; | ||
else | ||
llvm::outs() << "not in a loop\n"; | ||
block->print(llvm::outs()); | ||
llvm::outs() << "\n"; | ||
}); | ||
} | ||
}; | ||
|
||
} // namespace | ||
|
||
namespace mlir { | ||
void registerLoopLikeInterfaceTestPasses() { PassRegistration<IsInLoopPass>(); } | ||
} // namespace mlir |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters