-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathControlFlowGraph.java
More file actions
201 lines (174 loc) · 7.39 KB
/
Copy pathControlFlowGraph.java
File metadata and controls
201 lines (174 loc) · 7.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package de.peeeq.wurstscript.intermediatelang.optimizer;
import de.peeeq.wurstscript.attributes.CompileError;
import de.peeeq.wurstscript.jassIm.*;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import org.eclipse.jdt.annotation.Nullable;
import java.util.List;
public class ControlFlowGraph {
public static final class Node {
private @Nullable ImStmt stmt;
private @Nullable String name = null;
// Use fastutil lists; far less overhead than ArrayList for small lists.
private final ObjectArrayList<Node> predecessors = new ObjectArrayList<>(2);
private final ObjectArrayList<Node> successors = new ObjectArrayList<>(2);
Node(@Nullable ImStmt stmt) { this.stmt = stmt; }
public @Nullable ImStmt getStmt() { return stmt; }
public ObjectArrayList<Node> getPredecessors() { return predecessors; }
public ObjectArrayList<Node> getSuccessors() { return successors; }
@Override public String toString() { return name != null ? name : String.valueOf(stmt); }
Node setName(String name) { this.name = name; return this; }
}
// Identity maps: ImStmt/ImIf/ImLoop are AST nodes; identity semantics are correct & faster.
private final Reference2ObjectOpenHashMap<ImStmt, Node> nodes = new Reference2ObjectOpenHashMap<>();
private final Reference2ObjectOpenHashMap<ImIf, Node> ifEnd = new Reference2ObjectOpenHashMap<>();
private final Reference2ObjectOpenHashMap<ImLoop, Node> loopEnd = new Reference2ObjectOpenHashMap<>();
private final Reference2ObjectOpenHashMap<ImVarargLoop, Node> varargLoopEnd = new Reference2ObjectOpenHashMap<>();
private final ObjectArrayList<Node> nodeList = new ObjectArrayList<>();
public ControlFlowGraph(ImStmts stmts) {
// a light hint helps the first growth step avoid rehash
nodes.trim(0);
buildCfg(stmts);
}
private void buildCfg(ImStmts stmts) {
final int n = stmts.size();
for (int i = 0; i < n; i++) {
ImStmt s = stmts.get(i);
Node current = getNode(s);
nodeList.add(current);
if (s instanceof ImLoop) {
ImLoop imLoop = (ImLoop) s;
ImStmts body = imLoop.getBody();
buildCfg(body);
if (!body.isEmpty()) addSuccessor(current, getNode(body.get(0)));
Node endloopNode = getEndloopNode(imLoop);
nodeList.add(endloopNode);
addAllSuccessors(endloopNode, getSuccessorList(imLoop, i));
} else if (s instanceof ImVarargLoop) {
ImVarargLoop l = (ImVarargLoop) s;
ImStmts body = l.getBody();
buildCfg(body);
if (!body.isEmpty()) addSuccessor(current, getNode(body.get(0)));
Node end = getEndVarargLoopNode(l);
addSuccessor(current, end);
nodeList.add(end);
addAllSuccessors(end, getSuccessorList(l, i));
} else if (s instanceof ImIf) {
ImIf imIf = (ImIf) s;
ImStmts thenBlock = imIf.getThenBlock();
ImStmts elseBlock = imIf.getElseBlock();
buildCfg(thenBlock);
buildCfg(elseBlock);
if (thenBlock.isEmpty()) {
addSuccessor(current, getEndIfNode(imIf));
} else {
addSuccessor(current, getNode(thenBlock.get(0)));
}
if (elseBlock.isEmpty()) {
if (!thenBlock.isEmpty()) addSuccessor(current, getEndIfNode(imIf));
} else {
addSuccessor(current, getNode(elseBlock.get(0)));
}
Node endifNode = getEndIfNode(imIf);
nodeList.add(endifNode);
addAllSuccessors(endifNode, getSuccessorList(imIf, i));
} else {
addAllSuccessors(current, getSuccessorList(s, i));
}
}
}
private static void addAllSuccessors(Node from, List<Node> succs) {
// small, tight loop avoids Stream allocs
for (int j = 0, m = succs.size(); j < m; j++) {
Node succ = succs.get(j);
from.successors.add(succ);
succ.predecessors.add(from);
}
}
private void addSuccessor(Node current, Node succ) {
current.successors.add(succ);
succ.predecessors.add(current);
}
private List<Node> getSuccessorList(ImStmt s, int i) {
// Reuse an ObjectArrayList with tiny expected size (0-2)
final ObjectArrayList<Node> result = new ObjectArrayList<>(2);
if (s instanceof ImReturn) {
return result; // empty
}
if (s instanceof ImExitwhen) {
Element e = s;
while (true) {
if (e instanceof ImLoop) {
result.add(getEndloopNode((ImLoop) e));
break;
}
e = e.getParent();
if (e == null) throw new CompileError(s, "exitwhen outside of loop");
}
}
if (s.getParent() instanceof ImStmts) {
ImStmts stmts = (ImStmts) s.getParent();
if (i + 1 < stmts.size()) {
result.add(getNode(stmts.get(i + 1)));
} else if (stmts.getParent() instanceof ImStmt) {
ImStmt par = (ImStmt) stmts.getParent();
// Successor depends on block container:
if (par instanceof ImLoop || par instanceof ImVarargLoop) {
result.add(getNode(par)); // back-edge to loop header
} else if (par instanceof ImIf) {
result.add(getEndIfNode((ImIf) par));
} else {
throw new Error("unhandled parent block: " + par);
}
}
return result;
}
throw new Error("unexpected CFG shape");
}
private Node getNode(ImStmt s) {
Node result = nodes.get(s);
if (result == null) {
result = new Node(s);
nodes.put(s, result);
// assign display / stmt view for compound statements
if (s instanceof ImIf) {
ImIf imIf = (ImIf) s;
result.setName("if " + imIf.getCondition());
result.stmt = imIf.getCondition(); // condition is the node "stmt"
} else if (s instanceof ImLoop) {
result.setName("loop");
result.stmt = null;
} else if (s instanceof ImVarargLoop) {
result.setName("vararg loop");
}
}
return result;
}
private Node getEndloopNode(ImLoop e) {
Node n = loopEnd.get(e);
if (n == null) {
n = new Node(null).setName("endloop");
loopEnd.put(e, n);
}
return n;
}
private Node getEndVarargLoopNode(ImVarargLoop e) {
Node n = varargLoopEnd.get(e);
if (n == null) {
n = new Node(null).setName("endvarargloop");
varargLoopEnd.put(e, n);
}
return n;
}
private Node getEndIfNode(ImIf e) {
Node n = ifEnd.get(e);
if (n == null) {
n = new Node(null).setName("endif");
ifEnd.put(e, n);
}
return n;
}
public List<Node> getNodes() {
return nodeList;
}
}