Skip to content

Commit 4d72ef1

Browse files
committed
More compiler optimizations
1 parent 20b9bf7 commit 4d72ef1

File tree

9 files changed

+119
-47
lines changed

9 files changed

+119
-47
lines changed

core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/CopyToRedundantVariable.java

+1-34
Original file line numberDiff line numberDiff line change
@@ -25,44 +25,11 @@
2525
import de.mirkosertic.bytecoder.core.ir.Variable;
2626
import de.mirkosertic.bytecoder.core.parser.CompileUnit;
2727

28-
import java.util.ArrayList;
29-
import java.util.HashSet;
3028
import java.util.List;
31-
import java.util.Set;
3229
import java.util.Stack;
3330

3431
public class CopyToRedundantVariable implements Optimizer {
3532

36-
private List<Node> evaluationOrderOf(final Node node) {
37-
final List<Node> result = new ArrayList<>();
38-
39-
// We do a DFS here
40-
final Set<Node> visited = new HashSet<>();
41-
final Stack<Node> workingQueue = new Stack<>();
42-
43-
for (int i = node.incomingDataFlows.length - 1; i >= 0; i--) {
44-
workingQueue.push(node.incomingDataFlows[i]);
45-
}
46-
visited.add(node);
47-
48-
while (!workingQueue.isEmpty()) {
49-
final Node workingItem = workingQueue.pop();
50-
if (visited.add(workingItem)) {
51-
if (!(workingItem instanceof ControlTokenConsumer)) {
52-
if (workingItem.hasSideSideEffect() || workingItem.nodeType == NodeType.Variable) {
53-
result.add(workingItem);
54-
} else {
55-
for (int i = workingItem.incomingDataFlows.length - 1; i >= 0; i--) {
56-
workingQueue.push(workingItem.incomingDataFlows[i]);
57-
}
58-
}
59-
}
60-
}
61-
}
62-
63-
return result;
64-
}
65-
6633
@Override
6734
public boolean optimize(final BackendType backendType, final CompileUnit compileUnit, final ResolvedMethod method) {
6835

@@ -91,7 +58,7 @@ public boolean optimize(final BackendType backendType, final CompileUnit compile
9158
if (variableToCheck.incomingDataFlows.length == 1 && variableToCheck.outgoingDataFlows().length == 1) {
9259
final ControlTokenConsumer successor = workingItem.controlFlowsTo.values().stream().findFirst().get();
9360

94-
final List<Node> evaluationOrder = evaluationOrderOf(successor);
61+
final List<Node> evaluationOrder = Utils.evaluationOrderOf(successor);
9562

9663
if (!evaluationOrder.isEmpty() && evaluationOrder.get(evaluationOrder.size() - 1) == variableToCheck) {
9764
// We found a candidate

core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/InefficientSetFieldOrArray.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class InefficientSetFieldOrArray implements Optimizer {
3232

3333
@Override
3434
public boolean optimize(final BackendType backendType, final CompileUnit compileUnit, final ResolvedMethod method) {
35+
3536
final Graph g = method.methodBody;
3637
boolean changed = false;
3738
for (final Copy copy : g.nodes().stream().filter(t -> t.nodeType == NodeType.Copy).map(t -> (Copy) t).collect(Collectors.toList())) {
@@ -44,7 +45,7 @@ public boolean optimize(final BackendType backendType, final CompileUnit compile
4445
final Node source = copy.incomingDataFlows[0];
4546
// Case one : there is something written to the copyTarget
4647
final Node[] succOutgoing = successor.outgoingDataFlows();
47-
if (source.nodeType == NodeType.Variable && copyTarget.incomingDataFlows.length == 2 && copyTarget.incomingDataFlows[0] == copy && copyTarget.incomingDataFlows[1] == successor && succOutgoing.length == 1 && succOutgoing[0] == copyTarget) {
48+
if (source.nodeType == NodeType.Variable && copyTarget.incomingDataFlows.length == 2 && copyTarget.incomingDataFlows[0] == copy && copyTarget.incomingDataFlows[1] == successor && succOutgoing.length == 1 && succOutgoing[0] == copyTarget && !Utils.evaluationOrderOf(successor).contains(copyTarget)) {
4849

4950
copyTarget.removeFromIncomingData(copy);
5051

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package de.mirkosertic.bytecoder.core.optimizer;
2+
3+
import de.mirkosertic.bytecoder.core.backend.BackendType;
4+
import de.mirkosertic.bytecoder.core.ir.Copy;
5+
import de.mirkosertic.bytecoder.core.ir.Graph;
6+
import de.mirkosertic.bytecoder.core.ir.ResolvedField;
7+
import de.mirkosertic.bytecoder.core.ir.ResolvedMethod;
8+
import de.mirkosertic.bytecoder.core.ir.SetInstanceField;
9+
import de.mirkosertic.bytecoder.core.ir.StandardProjections;
10+
import de.mirkosertic.bytecoder.core.ir.Variable;
11+
import de.mirkosertic.bytecoder.core.parser.CompileUnit;
12+
import de.mirkosertic.bytecoder.core.patternmatcher.Match;
13+
import de.mirkosertic.bytecoder.core.patternmatcher.PatternMatcher;
14+
import org.objectweb.asm.Type;
15+
16+
public class InefficientSetFieldWithPatternMatcher implements Optimizer {
17+
18+
@Override
19+
public boolean optimize(final BackendType backendType, final CompileUnit compileUnit, final ResolvedMethod method) {
20+
21+
final Graph patternToSearch = new Graph(compileUnit.getLogger());
22+
final Variable source = patternToSearch.newVariable(Type.INT_TYPE);
23+
final Variable target = patternToSearch.newVariable(Type.INT_TYPE);
24+
final Copy cp1 = patternToSearch.newCopy();
25+
cp1.addIncomingData(source);
26+
final SetInstanceField sif = patternToSearch.newSetInstanceField(new ResolvedField(null, "field", null, null, 0));
27+
target.addIncomingData(cp1, sif);
28+
cp1.addControlFlowTo(StandardProjections.DEFAULT, sif);
29+
30+
final PatternMatcher matcher = new PatternMatcher(compileUnit.getLogger(), sif);
31+
for (final Match match : matcher.findMatches(method.methodBody)) {
32+
final Variable sourceMatch = match.mappingFor(source);
33+
final Copy copyMatch = match.mappingFor(cp1);
34+
final SetInstanceField sifMatch = match.mappingFor(sif);
35+
final Variable targetMatch = match.mappingFor(target);
36+
37+
sourceMatch.addIncomingData(sifMatch);
38+
copyMatch.deleteFromControlFlow();
39+
method.methodBody.deleteNode(targetMatch);
40+
41+
return true;
42+
}
43+
44+
return false;
45+
}
46+
}

core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Optimizations.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public enum Optimizations implements Optimizer {
3737
new DropUnusedValues(),
3838
new CopyToUnusedPHIOrVariable(),
3939
new SingularPHIOrVariable(),
40-
//new InefficientSetFieldOrArray(),
40+
new InefficientSetFieldOrArray(),
41+
//new InefficientSetField()
4142
}),
4243
ALL(new Optimizer[] {
4344
new DropDebugData(),
@@ -53,7 +54,8 @@ public enum Optimizations implements Optimizer {
5354
new DropUnusedValues(),
5455
new CopyToUnusedPHIOrVariable(),
5556
new SingularPHIOrVariable(),
56-
//new InefficientSetFieldOrArray(),
57+
new InefficientSetFieldOrArray(),
58+
//new InefficientSetField()
5759
}),
5860
;
5961

core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/STATS.md

+5
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,8 @@
7979
JBox2D JS Opt4 1699546 bytes
8080
LUA Wasm Opt4 443106 bytes
8181
LUA JS Opt4 1519864 bytes
82+
83+
JBox2D Wasm Opt4 472281 bytes
84+
JBox2D JS Opt4 1691486 bytes
85+
LUA Wasm Opt4 441364 bytes
86+
LUA JS Opt4 1516822 bytes

core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Utils.java

+37
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,17 @@
1515
*/
1616
package de.mirkosertic.bytecoder.core.optimizer;
1717

18+
import de.mirkosertic.bytecoder.core.ir.ControlTokenConsumer;
1819
import de.mirkosertic.bytecoder.core.ir.Node;
1920
import de.mirkosertic.bytecoder.core.ir.NodeType;
2021
import de.mirkosertic.bytecoder.core.ir.ResolvedMethod;
2122

23+
import java.util.ArrayList;
24+
import java.util.HashSet;
25+
import java.util.List;
26+
import java.util.Set;
27+
import java.util.Stack;
28+
2229
public class Utils {
2330

2431
public static boolean isVariableOrConstant(final Node n) {
@@ -47,6 +54,36 @@ public static long methodSize(final ResolvedMethod rm) {
4754
t.nodeType == NodeType.If).count();
4855
}
4956

57+
public static List<Node> evaluationOrderOf(final Node node) {
58+
final List<Node> result = new ArrayList<>();
59+
60+
// We do a DFS here
61+
final Set<Node> visited = new HashSet<>();
62+
final Stack<Node> workingQueue = new Stack<>();
63+
64+
for (int i = node.incomingDataFlows.length - 1; i >= 0; i--) {
65+
workingQueue.push(node.incomingDataFlows[i]);
66+
}
67+
visited.add(node);
68+
69+
while (!workingQueue.isEmpty()) {
70+
final Node workingItem = workingQueue.pop();
71+
if (visited.add(workingItem)) {
72+
if (!(workingItem instanceof ControlTokenConsumer)) {
73+
if (workingItem.hasSideSideEffect() || workingItem.nodeType == NodeType.Variable) {
74+
result.add(workingItem);
75+
} else {
76+
for (int i = workingItem.incomingDataFlows.length - 1; i >= 0; i--) {
77+
workingQueue.push(workingItem.incomingDataFlows[i]);
78+
}
79+
}
80+
}
81+
}
82+
}
83+
84+
return result;
85+
}
86+
5087
public static int maxInlineSourceSize() {
5188
return Integer.parseInt(System.getProperty("bytecoder.maxInlineSourceSize", "20"));
5289
}

core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/Match.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public Node root() {
3333
return root;
3434
}
3535

36-
public Node mappingFor(final Node node) {
37-
return mappings.get(node);
36+
public <T extends Node> T mappingFor(final T node) {
37+
return (T) mappings.get(node);
3838
}
3939
}

core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/PatternMatcher.java

+10
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,17 @@ private Node parseOutgoingData(final String token, final Node currentNode, final
139139

140140
return node;
141141

142+
} else if (candidates.size() > 1) {
143+
// More than one candidate, maybe we can further strip this down
144+
final List<Node> furtherCheck = candidates.stream().filter(t -> evaluationContext.nodeKnownAt(expectedNodeIndex) && t == evaluationContext.getNodeAt(expectedNodeIndex)).collect(Collectors.toList());
145+
if (furtherCheck.size() == 1) {
146+
return furtherCheck.get(0);
147+
} else {
148+
PatternMatcher.this.logger.debug(" -> Failed, cannot strip down nodes {} to {}", candidates, token);
149+
return null;
150+
}
142151
}
152+
143153
PatternMatcher.this.logger.debug(" -> Failed, matching outgoing nodes are != 1 : {}", candidates);
144154
return null;
145155
}

core/src/test/java/de/mirkosertic/bytecoder/IRExport.java

+12-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import de.mirkosertic.bytecoder.core.ir.Graph;
1111
import de.mirkosertic.bytecoder.core.ir.ResolvedMethod;
1212
import de.mirkosertic.bytecoder.core.loader.BytecoderLoader;
13+
import de.mirkosertic.bytecoder.core.optimizer.InefficientSetFieldWithPatternMatcher;
1314
import de.mirkosertic.bytecoder.core.optimizer.Optimizations;
1415
import de.mirkosertic.bytecoder.core.parser.CompileUnit;
1516
import de.mirkosertic.bytecoder.core.parser.Loader;
@@ -31,15 +32,15 @@ public static void doit(final String[] args) {
3132
public int dosomething(final int value) {
3233
//doit(new String[0]);
3334
//final IRExport dummy = new IRExport();
34-
assert value > 10;
35-
int x = value;
36-
for (int i = 0; i < 100; i++) {
37-
x = x + i + value;
38-
}
35+
//assert value > 10;
36+
//int x = value;
37+
//for (int i = 0; i < 100; i++) {
38+
// x = x + i + value;
39+
// }
3940
IRExport t = new IRExport();
40-
t.target = x;
41-
t.target2 = x + 1;
42-
return x;
41+
t.target = value;
42+
t.target2 = value + 1;
43+
return value;
4344
}
4445

4546
public static void main(final String[] args) throws IOException, ClassNotFoundException {
@@ -76,6 +77,9 @@ public static void main(final String[] args) throws IOException, ClassNotFoundEx
7677
while (Optimizations.ALL.optimize(BackendType.JS, compileUnit, method)) {
7778
}
7879

80+
while (new InefficientSetFieldWithPatternMatcher().optimize(BackendType.JS, compileUnit, method)) {
81+
}
82+
7983
try (final FileOutputStream fos = new FileOutputStream("debug_optimized.dot")) {
8084
g.writeDebugTo(fos);
8185
}

0 commit comments

Comments
 (0)