[mlir][Analysis] Introduce LoopInfo in mlir
authorChristian Ulmann <christian.ulmann@nextsilicon.com>
Wed, 5 Apr 2023 12:55:22 +0000 (12:55 +0000)
committerChristian Ulmann <christian.ulmann@nextsilicon.com>
Wed, 5 Apr 2023 12:57:16 +0000 (12:57 +0000)
This commit introduces an instantiation of LLVM's LoopInfo for CFGs in
MLIR. To test the LoopInfo, a test pass is added the checks the analysis
results for a set of CFGs.

Reviewed By: ftynse

Differential Revision: https://reviews.llvm.org/D147323

mlir/include/mlir/Analysis/CFGLoopInfo.h [new file with mode: 0644]
mlir/include/mlir/IR/RegionGraphTraits.h
mlir/lib/Analysis/CFGLoopInfo.cpp [new file with mode: 0644]
mlir/lib/Analysis/CMakeLists.txt
mlir/test/Analysis/test-cfg-loop-info.mlir [new file with mode: 0644]
mlir/test/lib/Analysis/CMakeLists.txt
mlir/test/lib/Analysis/TestCFGLoopInfo.cpp [new file with mode: 0644]
mlir/tools/mlir-opt/mlir-opt.cpp

diff --git a/mlir/include/mlir/Analysis/CFGLoopInfo.h b/mlir/include/mlir/Analysis/CFGLoopInfo.h
new file mode 100644 (file)
index 0000000..4bbae77
--- /dev/null
@@ -0,0 +1,43 @@
+//===- CFGLoopInfo.h - LoopInfo analysis for region bodies ------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the CFGLoopInfo analysis for MLIR. The CFGLoopInfo is used
+// to identify natural loops and determine the loop depth of various nodes of a
+// CFG.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_ANALYSIS_LOOPINFO_H
+#define MLIR_ANALYSIS_LOOPINFO_H
+
+#include "mlir/IR/Dominance.h"
+#include "mlir/IR/RegionGraphTraits.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/LoopInfoImpl.h"
+
+namespace mlir {
+
+/// Representation of a single loop formed by blocks. The inherited LoopBase
+/// class provides accessors to the loop analysis.
+class CFGLoop : public llvm::LoopBase<mlir::Block, mlir::CFGLoop> {
+private:
+  explicit CFGLoop(mlir::Block *block);
+
+  friend class llvm::LoopBase<mlir::Block, CFGLoop>;
+  friend class llvm::LoopInfoBase<mlir::Block, CFGLoop>;
+};
+
+/// An LLVM LoopInfo instantiation for MLIR that provides access to CFG loops
+/// found in the dominator tree.
+class CFGLoopInfo : public llvm::LoopInfoBase<mlir::Block, mlir::CFGLoop> {
+public:
+  CFGLoopInfo(const llvm::DominatorTreeBase<mlir::Block, false> &domTree);
+};
+} // namespace mlir
+
+#endif // MLIR_ANALYSIS_LOOPINFO_H
index 498064e..d7d80d9 100644 (file)
@@ -50,6 +50,40 @@ struct GraphTraits<Inverse<mlir::Block *>> {
 };
 
 template <>
+struct GraphTraits<const mlir::Block *> {
+  using ChildIteratorType = mlir::Block::succ_iterator;
+  using Node = const mlir::Block;
+  using NodeRef = Node *;
+
+  static NodeRef getEntryNode(NodeRef node) { return node; }
+
+  static ChildIteratorType child_begin(NodeRef node) {
+    return const_cast<mlir::Block *>(node)->succ_begin();
+  }
+  static ChildIteratorType child_end(NodeRef node) {
+    return const_cast<mlir::Block *>(node)->succ_end();
+  }
+};
+
+template <>
+struct GraphTraits<Inverse<const mlir::Block *>> {
+  using ChildIteratorType = mlir::Block::pred_iterator;
+  using Node = const mlir::Block;
+  using NodeRef = Node *;
+
+  static NodeRef getEntryNode(Inverse<NodeRef> inverseGraph) {
+    return inverseGraph.Graph;
+  }
+
+  static ChildIteratorType child_begin(NodeRef node) {
+    return const_cast<mlir::Block *>(node)->pred_begin();
+  }
+  static ChildIteratorType child_end(NodeRef node) {
+    return const_cast<mlir::Block *>(node)->pred_end();
+  }
+};
+
+template <>
 struct GraphTraits<mlir::Region *> : public GraphTraits<mlir::Block *> {
   using GraphType = mlir::Region *;
   using NodeRef = mlir::Block *;
diff --git a/mlir/lib/Analysis/CFGLoopInfo.cpp b/mlir/lib/Analysis/CFGLoopInfo.cpp
new file mode 100644 (file)
index 0000000..983643f
--- /dev/null
@@ -0,0 +1,19 @@
+//===- CFGLoopInfo.cpp - LoopInfo analysis for region bodies --------------===//
+//
+// 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/Analysis/CFGLoopInfo.h"
+
+using namespace mlir;
+
+CFGLoop::CFGLoop(mlir::Block *block)
+    : llvm::LoopBase<mlir::Block, CFGLoop>(block) {}
+
+CFGLoopInfo::CFGLoopInfo(
+    const llvm::DominatorTreeBase<mlir::Block, false> &domTree) {
+  analyze(domTree);
+}
index b68e03c..b2fbf70 100644 (file)
@@ -4,6 +4,7 @@ set(LLVM_OPTIONAL_SOURCES
   DataLayoutAnalysis.cpp
   FlatLinearValueConstraints.cpp
   Liveness.cpp
+  CFGLoopInfo.cpp
   SliceAnalysis.cpp
 
   AliasAnalysis/LocalAliasAnalysis.cpp
@@ -24,6 +25,7 @@ add_mlir_library(MLIRAnalysis
   DataLayoutAnalysis.cpp
   FlatLinearValueConstraints.cpp
   Liveness.cpp
+  CFGLoopInfo.cpp
   SliceAnalysis.cpp
 
   AliasAnalysis/LocalAliasAnalysis.cpp
diff --git a/mlir/test/Analysis/test-cfg-loop-info.mlir b/mlir/test/Analysis/test-cfg-loop-info.mlir
new file mode 100644 (file)
index 0000000..cc1a5c3
--- /dev/null
@@ -0,0 +1,115 @@
+// RUN: mlir-opt -pass-pipeline="builtin.module(any(test-cfg-loop-info))" --split-input-file %s 2>&1 | FileCheck %s
+
+// CHECK-LABEL: Testing : "no_loop_single_block"
+// CHECK: no loops
+func.func @no_loop_single_block() {
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "no_loop"
+// CHECK: no loops
+func.func @no_loop() {
+  cf.br ^bb1
+^bb1:
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "simple_loop"
+// CHECK-NEXT: Blocks : ^[[BB0:.*]], ^[[BB1:.*]], ^[[BB2:.*]], ^[[BB3:.*]]
+// CHECK: Loop at depth 1 containing:
+// CHECK-SAME: ^[[BB1]]<header><exiting>
+// CHECK-SAME: ^[[BB2]]<latch>
+func.func @simple_loop(%c: i1) {
+  cf.br ^bb1
+^bb1:
+  cf.cond_br %c, ^bb2, ^bb3
+^bb2:
+  cf.br ^bb1
+^bb3:
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "single_block_loop"
+// CHECK-NEXT: Blocks : ^[[BB0:.*]], ^[[BB1:.*]], ^[[BB2:.*]]
+// CHECK: Loop at depth 1 containing:
+// CHECK-SAME: ^[[BB1]]<header><latch><exiting>
+func.func @single_block_loop(%c: i1) {
+  cf.br ^bb1
+^bb1:
+  cf.cond_br %c, ^bb1, ^bb2
+^bb2:
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "nested_loop"
+// CHECK-NEXT: Blocks : ^[[BB0:.*]], ^[[BB1:.*]], ^[[BB2:.*]], ^[[BB3:.*]], ^[[BB4:.*]]
+// CHECK: Loop at depth 1
+// CHECK-SAME: ^[[BB1]]<header><exiting>
+// CHECK-SAME: ^[[BB2]]<latch>
+// CHECK-SAME: ^[[BB3]]
+// CHECK: Loop at depth 2
+// CHECK-SAME: ^[[BB2]]<header><exiting>
+// CHECK-SAME: ^[[BB3]]<latch>
+func.func @nested_loop(%c: i1) {
+  cf.br ^bb1
+^bb1:
+  cf.cond_br %c, ^bb2, ^bb4
+^bb2:
+  cf.cond_br %c, ^bb1, ^bb3
+^bb3:
+  cf.br ^bb2
+^bb4:
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "multi_latch"
+// CHECK-NEXT: Blocks : ^[[BB0:.*]], ^[[BB1:.*]], ^[[BB2:.*]], ^[[BB3:.*]], ^[[BB4:.*]]
+// CHECK: Loop at depth 1
+// CHECK-SAME: ^[[BB1]]<header><exiting>
+// CHECK-SAME: ^[[BB2]]<latch>
+// CHECK-SAME: ^[[BB3]]<latch>
+func.func @multi_latch(%c: i1) {
+  cf.br ^bb1
+^bb1:
+  cf.cond_br %c, ^bb4, ^bb2
+^bb2:
+  cf.cond_br %c, ^bb1, ^bb3
+^bb3:
+  cf.br ^bb1
+^bb4:
+  return
+}
+
+// -----
+
+// CHECK-LABEL: Testing : "multiple_loops"
+// CHECK-NEXT: Blocks : ^[[BB0:.*]], ^[[BB1:.*]], ^[[BB2:.*]], ^[[BB3:.*]], ^[[BB4:.*]], ^[[BB5:.*]]
+// CHECK: Loop at depth 1
+// CHECK-SAME: ^[[BB3]]<header><exiting>
+// CHECK-SAME: ^[[BB4]]<latch>
+// CHECK: Loop at depth 1
+// CHECK-SAME: ^[[BB1]]<header>
+// CHECK-SAME: ^[[BB2]]<latch><exiting>
+func.func @multiple_loops(%c: i1) {
+  cf.br ^bb1
+^bb1:
+  cf.br ^bb2
+^bb2:
+  cf.cond_br %c, ^bb3, ^bb1
+^bb3:
+  cf.cond_br %c, ^bb5, ^bb4
+^bb4:
+  cf.br ^bb3
+^bb5:
+  return
+}
index d83a8d5..00e77e8 100644 (file)
@@ -4,6 +4,7 @@ add_mlir_library(MLIRTestAnalysis
   TestCallGraph.cpp
   TestDataFlowFramework.cpp
   TestLiveness.cpp
+  TestCFGLoopInfo.cpp
   TestMatchReduction.cpp
   TestMemRefBoundCheck.cpp
   TestMemRefDependenceCheck.cpp
diff --git a/mlir/test/lib/Analysis/TestCFGLoopInfo.cpp b/mlir/test/lib/Analysis/TestCFGLoopInfo.cpp
new file mode 100644 (file)
index 0000000..fbba93d
--- /dev/null
@@ -0,0 +1,75 @@
+//===- TestCFGLoopInfo.cpp - Test CFG loop info analysis ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements logic for testing the CFGLoopInfo analysis.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Analysis/CFGLoopInfo.h"
+#include "mlir/IR/FunctionInterfaces.h"
+#include "mlir/Pass/Pass.h"
+
+using namespace mlir;
+
+namespace {
+/// A testing pass that applies the CFGLoopInfo analysis on a region and prints
+/// the information it collected to llvm::errs().
+struct TestCFGLoopInfo
+    : public PassWrapper<TestCFGLoopInfo, InterfacePass<FunctionOpInterface>> {
+  MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestCFGLoopInfo)
+
+  StringRef getArgument() const final { return "test-cfg-loop-info"; }
+  StringRef getDescription() const final {
+    return "Test the loop info analysis.";
+  }
+
+  void runOnOperation() override;
+};
+} // namespace
+
+void TestCFGLoopInfo::runOnOperation() {
+  auto func = getOperation();
+  DominanceInfo &domInfo = getAnalysis<DominanceInfo>();
+  Region &region = func.getFunctionBody();
+
+  // Prints the label of the test.
+  llvm::errs() << "Testing : " << func.getNameAttr() << "\n";
+  if (region.empty()) {
+    llvm::errs() << "empty region\n";
+    return;
+  }
+
+  // Print all the block identifiers first such that the tests can match them.
+  llvm::errs() << "Blocks : ";
+  region.front().printAsOperand(llvm::errs());
+  for (auto &block : region.getBlocks()) {
+    llvm::errs() << ", ";
+    block.printAsOperand(llvm::errs());
+  }
+  llvm::errs() << "\n";
+
+  if (region.getBlocks().size() == 1) {
+    llvm::errs() << "no loops\n";
+    return;
+  }
+
+  llvm::DominatorTreeBase<mlir::Block, false> &domTree =
+      domInfo.getDomTree(&region);
+  mlir::CFGLoopInfo loopInfo(domTree);
+
+  if (loopInfo.getTopLevelLoops().empty())
+    llvm::errs() << "no loops\n";
+  else
+    loopInfo.print(llvm::errs());
+}
+
+namespace mlir {
+namespace test {
+void registerTestCFGLoopInfoPass() { PassRegistration<TestCFGLoopInfo>(); }
+} // namespace test
+} // namespace mlir
index 855c6a6..2ddd83f 100644 (file)
@@ -99,6 +99,7 @@ void registerTestLinalgGreedyFusion();
 void registerTestLinalgTransforms();
 void registerTestLivenessPass();
 void registerTestLoopFusion();
+void registerTestCFGLoopInfoPass();
 void registerTestLoopMappingPass();
 void registerTestLoopUnrollingPass();
 void registerTestLowerToLLVM();
@@ -211,6 +212,7 @@ void registerTestPasses() {
   mlir::test::registerTestLinalgTransforms();
   mlir::test::registerTestLivenessPass();
   mlir::test::registerTestLoopFusion();
+  mlir::test::registerTestCFGLoopInfoPass();
   mlir::test::registerTestLoopMappingPass();
   mlir::test::registerTestLoopUnrollingPass();
   mlir::test::registerTestLowerToLLVM();