Skip to content

Commit 9102a16

Browse files
[mlir] Support drawing control-flow graphs in ViewOpGraph.cpp
* Add new pass option `print-data-flow-edges`, default value `true`. * Add new pass option `print-control-flow-edges`, default value `false`. * Remove `PrintCFGPass`. Same functionality now provided by `PrintOpPass`. Differential Revision: https://reviews.llvm.org/D106342
1 parent 636428c commit 9102a16

File tree

8 files changed

+113
-165
lines changed

8 files changed

+113
-165
lines changed

mlir/include/mlir/Transforms/Passes.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#include "mlir/Pass/Pass.h"
1818
#include "mlir/Transforms/LocationSnapshot.h"
1919
#include "mlir/Transforms/ViewOpGraph.h"
20-
#include "mlir/Transforms/ViewRegionGraph.h"
2120
#include <limits>
2221

2322
namespace mlir {

mlir/include/mlir/Transforms/Passes.td

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -612,11 +612,6 @@ def ParallelLoopCollapsing : Pass<"parallel-loop-collapsing"> {
612612
];
613613
}
614614

615-
def PrintCFG : FunctionPass<"print-cfg-graph"> {
616-
let summary = "Print CFG graph per-Region";
617-
let constructor = "mlir::createPrintCFGGraphPass()";
618-
}
619-
620615
def PrintOpStats : Pass<"print-op-stats"> {
621616
let summary = "Print statistics of operations";
622617
let constructor = "mlir::createPrintOpStatsPass()";
@@ -689,14 +684,17 @@ def SymbolDCE : Pass<"symbol-dce"> {
689684
}
690685

691686
def ViewOpGraphPass : Pass<"view-op-graph"> {
692-
let summary = "Print Graphviz dataflow visualization of an operation";
687+
let summary = "Print Graphviz visualization of an operation";
693688
let description = [{
694-
This pass prints a Graphviz dataflow graph of a module.
689+
This pass prints a Graphviz graph of a module.
695690

696691
- Operations are represented as nodes;
697-
- Uses as edges;
692+
- Uses (data flow) as edges;
693+
- Control flow as dashed edges;
698694
- Regions/blocks as subgraphs.
699695

696+
By default, only data flow edges are printed.
697+
700698
Note: See https://www.graphviz.org/doc/info/lang.html for more information
701699
about the Graphviz DOT language.
702700
}];
@@ -705,6 +703,10 @@ def ViewOpGraphPass : Pass<"view-op-graph"> {
705703
/*default=*/"20", "Limit attribute/type length to number of chars">,
706704
Option<"printAttrs", "print-attrs", "bool",
707705
/*default=*/"true", "Print attributes of operations">,
706+
Option<"printControlFlowEdges", "print-control-flow-edges", "bool",
707+
/*default=*/"false", "Print control flow edges">,
708+
Option<"printDataFlowEdges", "print-data-flow-edges", "bool",
709+
/*default=*/"true", "Print data flow edges">,
708710
Option<"printResultTypes", "print-result-types", "bool",
709711
/*default=*/"true", "Print result types of operations">
710712
];

mlir/include/mlir/Transforms/ViewRegionGraph.h

Lines changed: 0 additions & 41 deletions
This file was deleted.

mlir/lib/Transforms/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ add_mlir_library(MLIRTransforms
2121
StripDebugInfo.cpp
2222
SymbolDCE.cpp
2323
ViewOpGraph.cpp
24-
ViewRegionGraph.cpp
2524

2625
ADDITIONAL_HEADER_DIRS
2726
${MLIR_MAIN_INCLUDE_DIR}/mlir/Transforms

mlir/lib/Transforms/NormalizeMemRefs.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "mlir/Transforms/Passes.h"
1818
#include "mlir/Transforms/Utils.h"
1919
#include "llvm/ADT/SmallSet.h"
20+
#include "llvm/Support/Debug.h"
2021

2122
#define DEBUG_TYPE "normalize-memrefs"
2223

mlir/lib/Transforms/ViewOpGraph.cpp

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
#include "mlir/IR/Operation.h"
1313
#include "mlir/Support/IndentedOstream.h"
1414
#include "llvm/Support/Format.h"
15+
#include "llvm/Support/GraphWriter.h"
1516

1617
using namespace mlir;
1718

19+
static const StringRef kLineStyleControlFlow = "dashed";
1820
static const StringRef kLineStyleDataFlow = "solid";
1921
static const StringRef kShapeNode = "ellipse";
2022
static const StringRef kShapeNone = "plain";
@@ -78,6 +80,13 @@ class PrintOpPass : public ViewOpGraphPassBase<PrintOpPass> {
7880
});
7981
}
8082

83+
/// Create a CFG graph for a region. Used in `Region::viewGraph`.
84+
void emitRegionCFG(Region &region) {
85+
printControlFlowEdges = true;
86+
printDataFlowEdges = false;
87+
emitGraph([&]() { processRegion(region); });
88+
}
89+
8190
private:
8291
/// Emit all edges. This function should be called after all nodes have been
8392
/// emitted.
@@ -151,8 +160,7 @@ class PrintOpPass : public ViewOpGraphPassBase<PrintOpPass> {
151160

152161
/// Append an edge to the list of edges.
153162
/// Note: Edges are written to the output stream via `emitAllEdgeStmts`.
154-
void emitEdgeStmt(Node n1, Node n2, std::string label,
155-
StringRef style = kLineStyleDataFlow) {
163+
void emitEdgeStmt(Node n1, Node n2, std::string label, StringRef style) {
156164
AttributeMap attrs;
157165
attrs["style"] = style.str();
158166
// Do not label edges that start/end at a cluster boundary. Such edges are
@@ -233,14 +241,20 @@ class PrintOpPass : public ViewOpGraphPassBase<PrintOpPass> {
233241
valueToNode[blockArg] = emitNodeStmt(getLabel(blockArg));
234242

235243
// Emit a node for each operation.
236-
for (Operation &op : block)
237-
processOperation(&op);
244+
Optional<Node> prevNode;
245+
for (Operation &op : block) {
246+
Node nextNode = processOperation(&op);
247+
if (printControlFlowEdges && prevNode)
248+
emitEdgeStmt(*prevNode, nextNode, /*label=*/"",
249+
kLineStyleControlFlow);
250+
prevNode = nextNode;
251+
}
238252
});
239253
}
240254

241255
/// Process an operation. If the operation has regions, emit a cluster.
242256
/// Otherwise, emit a node.
243-
void processOperation(Operation *op) {
257+
Node processOperation(Operation *op) {
244258
Node node;
245259
if (op->getNumRegions() > 0) {
246260
// Emit cluster for op with regions.
@@ -254,14 +268,19 @@ class PrintOpPass : public ViewOpGraphPassBase<PrintOpPass> {
254268
node = emitNodeStmt(getLabel(op));
255269
}
256270

257-
// Insert edges originating from each operand.
258-
unsigned numOperands = op->getNumOperands();
259-
for (unsigned i = 0; i < numOperands; i++)
260-
emitEdgeStmt(valueToNode[op->getOperand(i)], node,
261-
/*label=*/numOperands == 1 ? "" : std::to_string(i));
271+
// Insert data flow edges originating from each operand.
272+
if (printDataFlowEdges) {
273+
unsigned numOperands = op->getNumOperands();
274+
for (unsigned i = 0; i < numOperands; i++)
275+
emitEdgeStmt(valueToNode[op->getOperand(i)], node,
276+
/*label=*/numOperands == 1 ? "" : std::to_string(i),
277+
kLineStyleDataFlow);
278+
}
262279

263280
for (Value result : op->getResults())
264281
valueToNode[result] = node;
282+
283+
return node;
265284
}
266285

267286
/// Process a region.
@@ -294,3 +313,25 @@ std::unique_ptr<Pass>
294313
mlir::createPrintOpGraphPass(raw_ostream &os) {
295314
return std::make_unique<PrintOpPass>(os);
296315
}
316+
317+
/// Generate a CFG for a region and show it in a window.
318+
static void llvmViewGraph(Region &region, const Twine &name) {
319+
int fd;
320+
std::string filename = llvm::createGraphFilename(name.str(), fd);
321+
{
322+
llvm::raw_fd_ostream os(fd, /*shouldClose=*/true);
323+
if (fd == -1) {
324+
llvm::errs() << "error opening file '" << filename << "' for writing\n";
325+
return;
326+
}
327+
PrintOpPass pass(os);
328+
pass.emitRegionCFG(region);
329+
}
330+
llvm::DisplayGraph(filename, /*wait=*/false, llvm::GraphProgram::DOT);
331+
}
332+
333+
void mlir::Region::viewGraph(const Twine &regionName) {
334+
llvmViewGraph(*this, regionName);
335+
}
336+
337+
void mlir::Region::viewGraph() { viewGraph("region"); }

mlir/lib/Transforms/ViewRegionGraph.cpp

Lines changed: 0 additions & 82 deletions
This file was deleted.

mlir/test/Transforms/print-op-graph.mlir

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,55 @@
1-
// RUN: mlir-opt -allow-unregistered-dialect -mlir-elide-elementsattrs-if-larger=2 -view-op-graph %s -o %t 2>&1 | FileCheck %s
1+
// RUN: mlir-opt -allow-unregistered-dialect -mlir-elide-elementsattrs-if-larger=2 -view-op-graph %s -o %t 2>&1 | FileCheck -check-prefix=DFG %s
2+
// RUN: mlir-opt -allow-unregistered-dialect -mlir-elide-elementsattrs-if-larger=2 -view-op-graph='print-data-flow-edges=false print-control-flow-edges=true' %s -o %t 2>&1 | FileCheck -check-prefix=CFG %s
3+
4+
// DFG-LABEL: digraph G {
5+
// DFG: subgraph {{.*}} {
6+
// DFG: subgraph {{.*}}
7+
// DFG: label = "builtin.func{{.*}}merge_blocks
8+
// DFG: subgraph {{.*}} {
9+
// DFG: v[[ARG0:.*]] [label = "arg0"
10+
// DFG: v[[CONST10:.*]] [label ={{.*}}10 : i32
11+
// DFG: subgraph [[CLUSTER_MERGE_BLOCKS:.*]] {
12+
// DFG: v[[ANCHOR:.*]] [label = " ", shape = plain]
13+
// DFG: label = "test.merge_blocks
14+
// DFG: subgraph {{.*}} {
15+
// DFG: v[[TEST_BR:.*]] [label = "test.br
16+
// DFG: }
17+
// DFG: subgraph {{.*}} {
18+
// DFG: }
19+
// DFG: }
20+
// DFG: v[[TEST_RET:.*]] [label = "test.return
21+
// DFG: v[[ARG0]] -> v[[TEST_BR]]
22+
// DFG: v[[CONST10]] -> v[[TEST_BR]]
23+
// DFG: v[[ANCHOR]] -> v[[TEST_RET]] [{{.*}}, ltail = [[CLUSTER_MERGE_BLOCKS]]]
24+
// DFG: v[[ANCHOR]] -> v[[TEST_RET]] [{{.*}}, ltail = [[CLUSTER_MERGE_BLOCKS]]]
25+
26+
// CFG-LABEL: digraph G {
27+
// CFG: subgraph {{.*}} {
28+
// CFG: subgraph {{.*}}
29+
// CFG: label = "builtin.func{{.*}}merge_blocks
30+
// CFG: subgraph {{.*}} {
31+
// CFG: v[[C1:.*]] [label = "std.constant
32+
// CFG: v[[C2:.*]] [label = "std.constant
33+
// CFG: v[[C3:.*]] [label = "std.constant
34+
// CFG: v[[C4:.*]] [label = "std.constant
35+
// CFG: v[[TEST_FUNC:.*]] [label = "test.func
36+
// CFG: subgraph [[CLUSTER_MERGE_BLOCKS:.*]] {
37+
// CFG: v[[ANCHOR:.*]] [label = " ", shape = plain]
38+
// CFG: label = "test.merge_blocks
39+
// CFG: subgraph {{.*}} {
40+
// CFG: v[[TEST_BR:.*]] [label = "test.br
41+
// CFG: }
42+
// CFG: subgraph {{.*}} {
43+
// CFG: }
44+
// CFG: }
45+
// CFG: v[[TEST_RET:.*]] [label = "test.return
46+
// CFG: v[[C1]] -> v[[C2]]
47+
// CFG: v[[C2]] -> v[[C3]]
48+
// CFG: v[[C3]] -> v[[C4]]
49+
// CFG: v[[C4]] -> v[[TEST_FUNC]]
50+
// CFG: v[[TEST_FUNC]] -> v[[ANCHOR]] [{{.*}}, lhead = [[CLUSTER_MERGE_BLOCKS]]]
51+
// CFG: v[[ANCHOR]] -> v[[TEST_RET]] [{{.*}}, ltail = [[CLUSTER_MERGE_BLOCKS]]]
252

3-
// CHECK-LABEL: digraph G {
4-
// CHECK: subgraph {{.*}} {
5-
// CHECK: subgraph {{.*}}
6-
// CHECK: label = "builtin.func{{.*}}merge_blocks
7-
// CHECK: subgraph {{.*}} {
8-
// CHECK: v[[ARG0:.*]] [label = "arg0"
9-
// CHECK: v[[CONST10:.*]] [label ={{.*}}10 : i32
10-
// CHECK: subgraph [[CLUSTER_MERGE_BLOCKS:.*]] {
11-
// CHECK: v[[ANCHOR:.*]] [label = " ", shape = plain]
12-
// CHECK: label = "test.merge_blocks
13-
// CHECK: subgraph {{.*}} {
14-
// CHECK: v[[TEST_BR:.*]] [label = "test.br
15-
// CHECK: }
16-
// CHECK: subgraph {{.*}} {
17-
// CHECK: }
18-
// CHECK: }
19-
// CHECK: v[[TEST_RET:.*]] [label = "test.return
20-
// CHECK: v[[ARG0]] -> v[[TEST_BR]]
21-
// CHECK: v[[CONST10]] -> v[[TEST_BR]]
22-
// CHECK: v[[ANCHOR]] -> v[[TEST_RET]] [{{.*}}, ltail = [[CLUSTER_MERGE_BLOCKS]]]
23-
// CHECK: v[[ANCHOR]] -> v[[TEST_RET]] [{{.*}}, ltail = [[CLUSTER_MERGE_BLOCKS]]]
2453
func @merge_blocks(%arg0: i32, %arg1 : i32) -> () {
2554
%0 = constant dense<[[0, 1], [2, 3]]> : tensor<2x2xi32>
2655
%1 = constant dense<1> : tensor<5xi32>

0 commit comments

Comments
 (0)