Skip to content

Commit 9756e0a

Browse files
committed
Add basic blocks support
1 parent 4bbee67 commit 9756e0a

File tree

10 files changed

+199
-4
lines changed

10 files changed

+199
-4
lines changed

include/Builder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ class Builder {
1212
public:
1313
Node *newModuleNode();
1414
Node *newFunctionNode();
15+
Node *newBasicBlockNode();
1516

1617
void connectModule(Node *moduleNode, Node *anyNode);
18+
void connectFunction(Node *functionNode, Node *anyNode);
19+
void connectBasicBlocks(Node *successor, Node *predecessor);
1720

1821
const std::vector<std::unique_ptr<Node>> &getNodes() const;
1922
const std::vector<std::unique_ptr<Edge>> &getEdges() const;

include/Edge.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace llvm2graphml {
77

8-
enum class EdgeKind { Module };
8+
enum class EdgeKind { Module, Function, Successor, Predecessor };
99

1010
class Edge {
1111
public:

include/Node.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace llvm2graphml {
77

8-
enum class NodeKind { Module, Function };
8+
enum class NodeKind { Module, Function, BasicBlock };
99

1010
class Node {
1111
public:

src/Builder.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,28 @@ Node *Builder::newFunctionNode() {
3030
return newNode(NodeKind::Function);
3131
}
3232

33+
Node *Builder::newBasicBlockNode() {
34+
return newNode(NodeKind::BasicBlock);
35+
}
36+
3337
/// Edge Connectors
3438

3539
void Builder::connectModule(Node *moduleNode, Node *anyNode) {
3640
assert(moduleNode->getKind() == NodeKind::Module);
37-
Edge *edge = newEdge(anyNode->getID(), moduleNode->getID());
38-
edge->setKind(EdgeKind::Module);
41+
42+
newEdge(anyNode->getID(), moduleNode->getID())->setKind(EdgeKind::Module);
43+
}
44+
45+
void Builder::connectFunction(Node *functionNode, Node *anyNode) {
46+
assert(functionNode->getKind() == NodeKind::Function);
47+
48+
newEdge(anyNode->getID(), functionNode->getID())->setKind(EdgeKind::Function);
49+
}
50+
51+
void Builder::connectBasicBlocks(Node *successor, Node *predecessor) {
52+
assert(successor->getKind() == NodeKind::BasicBlock);
53+
assert(predecessor->getKind() == NodeKind::BasicBlock);
54+
55+
newEdge(predecessor->getID(), successor->getID())->setKind(EdgeKind::Successor);
56+
newEdge(successor->getID(), predecessor->getID())->setKind(EdgeKind::Predecessor);
3957
}

src/Edge.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ static std::string edgeKindToString(EdgeKind kind) {
66
switch (kind) {
77
case EdgeKind::Module:
88
return "module";
9+
case EdgeKind::Function:
10+
return "function";
11+
case EdgeKind::Successor:
12+
return "successor";
13+
case EdgeKind::Predecessor:
14+
return "predecessor";
915
}
1016
}
1117

src/Node.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ static std::string kindToString(NodeKind kind) {
99
return "module";
1010
case NodeKind::Function:
1111
return "function";
12+
case NodeKind::BasicBlock:
13+
return "basicBlock";
1214
}
1315
}
1416

src/llvm2graphmlTool.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
#include "GraphmlWriter.h"
55
#include "Logger.h"
66
#include "Version.h"
7+
#include <llvm/IR/CFG.h>
78
#include <llvm/Support/CommandLine.h>
89
#include <sstream>
910
#include <string>
11+
#include <unordered_map>
1012

1113
llvm::cl::OptionCategory llvm2graphmlCategory("llvm2graphml");
1214

@@ -97,7 +99,28 @@ int main(int argc, char **argv) {
9799
functionNode->setIsIntrinsic(function.isIntrinsic());
98100
functionNode->setInstructionCount(function.getInstructionCount());
99101
functionNode->setBasicBlockCount(function.size());
102+
103+
builder.connectFunction(functionNode, moduleNode);
100104
builder.connectModule(moduleNode, functionNode);
105+
106+
std::unordered_map<llvm::BasicBlock *, Node *> basicBlocks;
107+
for (llvm::BasicBlock &block : function.getBasicBlockList()) {
108+
Node *basicBlockNode = builder.newBasicBlockNode();
109+
basicBlockNode->setName(block.getName());
110+
basicBlockNode->setInstructionCount(block.size());
111+
112+
basicBlocks[&block] = basicBlockNode;
113+
114+
builder.connectFunction(functionNode, basicBlockNode);
115+
builder.connectModule(moduleNode, basicBlockNode);
116+
}
117+
for (llvm::BasicBlock &block : function.getBasicBlockList()) {
118+
Node *blockNode = basicBlocks[&block];
119+
for (llvm::BasicBlock *pred : llvm::predecessors(&block)) {
120+
Node *predNode = basicBlocks[pred];
121+
builder.connectBasicBlocks(blockNode, predNode);
122+
}
123+
}
101124
}
102125
}
103126

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
define i32 @abs(i32 %x) {
2+
%tmp = alloca i32
3+
%cmp = icmp slt i32 %x, 0
4+
br i1 %cmp, label %then, label %else
5+
6+
then:
7+
%tmpSub = sub nsw i32 0, %x
8+
store i32 %tmpSub, i32* %tmp
9+
br label %end
10+
11+
else:
12+
store i32 %x, i32* %tmp
13+
br label %end
14+
15+
end:
16+
%rval = load i32, i32* %tmp
17+
ret i32 %rval
18+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/**
2+
; RUN: cd %p
3+
; RUN: %LLVM2GRAPHML_EXEC --output=%t.xml main.ll
4+
; RUN: %GREMLIN_CONSOLE_EXEC -e %s %t.xml | %FILECHECK_EXEC %s --match-full-lines
5+
*/
6+
7+
/// Prepare
8+
9+
graph = TinkerGraph.open()
10+
g = graph.traversal()
11+
g.io(args[0]).read().iterate()
12+
13+
def dump(tr) {
14+
tr.unfold().sideEffect{println it}.iterate()
15+
}
16+
17+
/// Assertions
18+
19+
dump(g.V().hasLabel('function').valueMap('name'))
20+
// CHECK: name=[abs]
21+
22+
dump(g.V().hasLabel('function').has('name', 'abs').valueMap('basicBlockCount'))
23+
// CHECK-NEXT: basicBlockCount=[4]
24+
25+
dump(g.V().hasLabel('basicBlock').count())
26+
// CHECK-NEXT: 4
27+
28+
dump(g.V().hasLabel('basicBlock').out('module').valueMap('moduleIdentifier').dedup())
29+
// CHECK-NEXT: moduleIdentifier=[main.ll]
30+
31+
dump(g.V().hasLabel('basicBlock').out('function').valueMap('name').dedup())
32+
// CHECK-NEXT: name=[abs]
33+
34+
35+
// Entry
36+
37+
dump(g.V().hasLabel('basicBlock').has('name', '').count())
38+
// CHECK-NEXT: 1
39+
40+
dump(g.V().hasLabel('basicBlock').has('name', '').valueMap('instructionCount'))
41+
// CHECK-NEXT: instructionCount=[3]
42+
43+
dump(g.V().hasLabel('basicBlock').has('name', '').out('predecessor').count())
44+
// CHECK-NEXT: 0
45+
46+
dump(g.V().hasLabel('basicBlock').has('name', '').out('predecessor').valueMap('name'))
47+
// empty
48+
49+
dump(g.V().hasLabel('basicBlock').has('name', '').out('successor').count())
50+
// CHECK-NEXT: 2
51+
52+
dump(g.V().hasLabel('basicBlock').has('name', '').out('successor').order().by('name', asc).valueMap('name'))
53+
// CHECK-NEXT: name=[else]
54+
// CHECK-NEXT: name=[then]
55+
56+
57+
// Then
58+
59+
dump(g.V().hasLabel('basicBlock').has('name', 'then').count())
60+
// CHECK-NEXT: 1
61+
62+
dump(g.V().hasLabel('basicBlock').has('name', 'then').valueMap('instructionCount'))
63+
// CHECK-NEXT: instructionCount=[3]
64+
65+
dump(g.V().hasLabel('basicBlock').has('name', 'then').out('predecessor').count())
66+
// CHECK-NEXT: 1
67+
68+
dump(g.V().hasLabel('basicBlock').has('name', 'then').out('predecessor').valueMap('name'))
69+
// CHECK-NEXT: name=[]
70+
71+
dump(g.V().hasLabel('basicBlock').has('name', 'then').out('successor').count())
72+
// CHECK-NEXT: 1
73+
74+
dump(g.V().hasLabel('basicBlock').has('name', 'then').out('successor').valueMap('name'))
75+
// CHECK-NEXT: name=[end]
76+
77+
78+
// Else
79+
80+
dump(g.V().hasLabel('basicBlock').has('name', 'else').count())
81+
// CHECK-NEXT: 1
82+
83+
dump(g.V().hasLabel('basicBlock').has('name', 'else').valueMap('instructionCount'))
84+
// CHECK-NEXT: instructionCount=[2]
85+
86+
dump(g.V().hasLabel('basicBlock').has('name', 'else').out('predecessor').count())
87+
// CHECK-NEXT: 1
88+
89+
dump(g.V().hasLabel('basicBlock').has('name', 'else').out('predecessor').valueMap('name'))
90+
// CHECK-NEXT: name=[]
91+
92+
dump(g.V().hasLabel('basicBlock').has('name', 'else').out('successor').count())
93+
// CHECK-NEXT: 1
94+
95+
dump(g.V().hasLabel('basicBlock').has('name', 'else').out('successor').valueMap('name'))
96+
// CHECK-NEXT: name=[end]
97+
98+
99+
// End
100+
101+
dump(g.V().hasLabel('basicBlock').has('name', 'end').count())
102+
// CHECK-NEXT: 1
103+
104+
dump(g.V().hasLabel('basicBlock').has('name', 'end').valueMap('instructionCount'))
105+
// CHECK-NEXT: instructionCount=[2]
106+
107+
dump(g.V().hasLabel('basicBlock').has('name', 'end').out('predecessor').count())
108+
// CHECK-NEXT: 2
109+
110+
dump(g.V().hasLabel('basicBlock').has('name', 'end').out('predecessor').order().by('name', asc).valueMap('name'))
111+
// CHECK-NEXT: name=[else]
112+
// CHECK-NEXT: name=[then]
113+
114+
dump(g.V().hasLabel('basicBlock').has('name', 'end').out('successor').count())
115+
// CHECK-NEXT: 0
116+
117+
dump(g.V().hasLabel('basicBlock').has('name', 'end').out('successor').valueMap('name'))
118+
// empty
119+
120+
121+
:exit
122+
// CHECK-EMPTY:

tests/integration-tests/module/test.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ def dump(tr) {
1919
dump(g.V().hasLabel('module').valueMap('moduleIdentifier'))
2020
// CHECK: moduleIdentifier=[main.ll]
2121

22+
dump(g.V().hasLabel('module').out('function').valueMap('name'))
23+
// CHECK-NEXT: name=[helloWorld]
24+
2225
dump(g.V().hasLabel('function').valueMap('name'))
2326
// CHECK-NEXT: name=[helloWorld]
2427

0 commit comments

Comments
 (0)