diff --git a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/CopyToRedundantVariable.java b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/CopyToRedundantVariable.java index 8ccfba2b2..8ad6bc559 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/CopyToRedundantVariable.java +++ b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/CopyToRedundantVariable.java @@ -25,44 +25,11 @@ import de.mirkosertic.bytecoder.core.ir.Variable; import de.mirkosertic.bytecoder.core.parser.CompileUnit; -import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.Stack; public class CopyToRedundantVariable implements Optimizer { - private List evaluationOrderOf(final Node node) { - final List result = new ArrayList<>(); - - // We do a DFS here - final Set visited = new HashSet<>(); - final Stack workingQueue = new Stack<>(); - - for (int i = node.incomingDataFlows.length - 1; i >= 0; i--) { - workingQueue.push(node.incomingDataFlows[i]); - } - visited.add(node); - - while (!workingQueue.isEmpty()) { - final Node workingItem = workingQueue.pop(); - if (visited.add(workingItem)) { - if (!(workingItem instanceof ControlTokenConsumer)) { - if (workingItem.hasSideSideEffect() || workingItem.nodeType == NodeType.Variable) { - result.add(workingItem); - } else { - for (int i = workingItem.incomingDataFlows.length - 1; i >= 0; i--) { - workingQueue.push(workingItem.incomingDataFlows[i]); - } - } - } - } - } - - return result; - } - @Override public boolean optimize(final BackendType backendType, final CompileUnit compileUnit, final ResolvedMethod method) { @@ -91,7 +58,7 @@ public boolean optimize(final BackendType backendType, final CompileUnit compile if (variableToCheck.incomingDataFlows.length == 1 && variableToCheck.outgoingDataFlows().length == 1) { final ControlTokenConsumer successor = workingItem.controlFlowsTo.values().stream().findFirst().get(); - final List evaluationOrder = evaluationOrderOf(successor); + final List evaluationOrder = Utils.evaluationOrderOf(successor); if (!evaluationOrder.isEmpty() && evaluationOrder.get(evaluationOrder.size() - 1) == variableToCheck) { // We found a candidate diff --git a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/InefficientSetFieldOrArray.java b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/InefficientSetFieldOrArray.java index 2fdb1fba1..12aa1c296 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/InefficientSetFieldOrArray.java +++ b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/InefficientSetFieldOrArray.java @@ -32,6 +32,7 @@ public class InefficientSetFieldOrArray implements Optimizer { @Override public boolean optimize(final BackendType backendType, final CompileUnit compileUnit, final ResolvedMethod method) { + final Graph g = method.methodBody; boolean changed = false; 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 final Node source = copy.incomingDataFlows[0]; // Case one : there is something written to the copyTarget final Node[] succOutgoing = successor.outgoingDataFlows(); - if (source.nodeType == NodeType.Variable && copyTarget.incomingDataFlows.length == 2 && copyTarget.incomingDataFlows[0] == copy && copyTarget.incomingDataFlows[1] == successor && succOutgoing.length == 1 && succOutgoing[0] == copyTarget) { + 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)) { copyTarget.removeFromIncomingData(copy); diff --git a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/InefficientSetFieldWithPatternMatcher.java b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/InefficientSetFieldWithPatternMatcher.java new file mode 100644 index 000000000..0b60064b7 --- /dev/null +++ b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/InefficientSetFieldWithPatternMatcher.java @@ -0,0 +1,46 @@ +package de.mirkosertic.bytecoder.core.optimizer; + +import de.mirkosertic.bytecoder.core.backend.BackendType; +import de.mirkosertic.bytecoder.core.ir.Copy; +import de.mirkosertic.bytecoder.core.ir.Graph; +import de.mirkosertic.bytecoder.core.ir.ResolvedField; +import de.mirkosertic.bytecoder.core.ir.ResolvedMethod; +import de.mirkosertic.bytecoder.core.ir.SetInstanceField; +import de.mirkosertic.bytecoder.core.ir.StandardProjections; +import de.mirkosertic.bytecoder.core.ir.Variable; +import de.mirkosertic.bytecoder.core.parser.CompileUnit; +import de.mirkosertic.bytecoder.core.patternmatcher.Match; +import de.mirkosertic.bytecoder.core.patternmatcher.PatternMatcher; +import org.objectweb.asm.Type; + +public class InefficientSetFieldWithPatternMatcher implements Optimizer { + + @Override + public boolean optimize(final BackendType backendType, final CompileUnit compileUnit, final ResolvedMethod method) { + + final Graph patternToSearch = new Graph(compileUnit.getLogger()); + final Variable source = patternToSearch.newVariable(Type.INT_TYPE); + final Variable target = patternToSearch.newVariable(Type.INT_TYPE); + final Copy cp1 = patternToSearch.newCopy(); + cp1.addIncomingData(source); + final SetInstanceField sif = patternToSearch.newSetInstanceField(new ResolvedField(null, "field", null, null, 0)); + target.addIncomingData(cp1, sif); + cp1.addControlFlowTo(StandardProjections.DEFAULT, sif); + + final PatternMatcher matcher = new PatternMatcher(compileUnit.getLogger(), sif); + for (final Match match : matcher.findMatches(method.methodBody)) { + final Variable sourceMatch = match.mappingFor(source); + final Copy copyMatch = match.mappingFor(cp1); + final SetInstanceField sifMatch = match.mappingFor(sif); + final Variable targetMatch = match.mappingFor(target); + + sourceMatch.addIncomingData(sifMatch); + copyMatch.deleteFromControlFlow(); + method.methodBody.deleteNode(targetMatch); + + return true; + } + + return false; + } +} diff --git a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Optimizations.java b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Optimizations.java index 9bbd51e2f..f1f2c42eb 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Optimizations.java +++ b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Optimizations.java @@ -37,7 +37,8 @@ public enum Optimizations implements Optimizer { new DropUnusedValues(), new CopyToUnusedPHIOrVariable(), new SingularPHIOrVariable(), - //new InefficientSetFieldOrArray(), + new InefficientSetFieldOrArray(), + //new InefficientSetField() }), ALL(new Optimizer[] { new DropDebugData(), @@ -53,7 +54,8 @@ public enum Optimizations implements Optimizer { new DropUnusedValues(), new CopyToUnusedPHIOrVariable(), new SingularPHIOrVariable(), - //new InefficientSetFieldOrArray(), + new InefficientSetFieldOrArray(), + //new InefficientSetField() }), ; diff --git a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/STATS.md b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/STATS.md index 04e4045c3..f326885fe 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/STATS.md +++ b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/STATS.md @@ -79,3 +79,8 @@ JBox2D JS Opt4 1699546 bytes LUA Wasm Opt4 443106 bytes LUA JS Opt4 1519864 bytes + + JBox2D Wasm Opt4 472281 bytes + JBox2D JS Opt4 1691486 bytes + LUA Wasm Opt4 441364 bytes + LUA JS Opt4 1516822 bytes diff --git a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Utils.java b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Utils.java index c27095a17..cec384eb5 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Utils.java +++ b/core/src/main/java/de/mirkosertic/bytecoder/core/optimizer/Utils.java @@ -15,10 +15,17 @@ */ package de.mirkosertic.bytecoder.core.optimizer; +import de.mirkosertic.bytecoder.core.ir.ControlTokenConsumer; import de.mirkosertic.bytecoder.core.ir.Node; import de.mirkosertic.bytecoder.core.ir.NodeType; import de.mirkosertic.bytecoder.core.ir.ResolvedMethod; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; + public class Utils { public static boolean isVariableOrConstant(final Node n) { @@ -47,6 +54,36 @@ public static long methodSize(final ResolvedMethod rm) { t.nodeType == NodeType.If).count(); } + public static List evaluationOrderOf(final Node node) { + final List result = new ArrayList<>(); + + // We do a DFS here + final Set visited = new HashSet<>(); + final Stack workingQueue = new Stack<>(); + + for (int i = node.incomingDataFlows.length - 1; i >= 0; i--) { + workingQueue.push(node.incomingDataFlows[i]); + } + visited.add(node); + + while (!workingQueue.isEmpty()) { + final Node workingItem = workingQueue.pop(); + if (visited.add(workingItem)) { + if (!(workingItem instanceof ControlTokenConsumer)) { + if (workingItem.hasSideSideEffect() || workingItem.nodeType == NodeType.Variable) { + result.add(workingItem); + } else { + for (int i = workingItem.incomingDataFlows.length - 1; i >= 0; i--) { + workingQueue.push(workingItem.incomingDataFlows[i]); + } + } + } + } + } + + return result; + } + public static int maxInlineSourceSize() { return Integer.parseInt(System.getProperty("bytecoder.maxInlineSourceSize", "20")); } diff --git a/core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/Match.java b/core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/Match.java index d70d1c9a3..2278bd34a 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/Match.java +++ b/core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/Match.java @@ -33,7 +33,7 @@ public Node root() { return root; } - public Node mappingFor(final Node node) { - return mappings.get(node); + public T mappingFor(final T node) { + return (T) mappings.get(node); } } diff --git a/core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/PatternMatcher.java b/core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/PatternMatcher.java index 86c3f872c..751972333 100644 --- a/core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/PatternMatcher.java +++ b/core/src/main/java/de/mirkosertic/bytecoder/core/patternmatcher/PatternMatcher.java @@ -139,7 +139,17 @@ private Node parseOutgoingData(final String token, final Node currentNode, final return node; + } else if (candidates.size() > 1) { + // More than one candidate, maybe we can further strip this down + final List furtherCheck = candidates.stream().filter(t -> evaluationContext.nodeKnownAt(expectedNodeIndex) && t == evaluationContext.getNodeAt(expectedNodeIndex)).collect(Collectors.toList()); + if (furtherCheck.size() == 1) { + return furtherCheck.get(0); + } else { + PatternMatcher.this.logger.debug(" -> Failed, cannot strip down nodes {} to {}", candidates, token); + return null; + } } + PatternMatcher.this.logger.debug(" -> Failed, matching outgoing nodes are != 1 : {}", candidates); return null; } diff --git a/core/src/test/java/de/mirkosertic/bytecoder/IRExport.java b/core/src/test/java/de/mirkosertic/bytecoder/IRExport.java index b1fa46b1e..fe803047c 100644 --- a/core/src/test/java/de/mirkosertic/bytecoder/IRExport.java +++ b/core/src/test/java/de/mirkosertic/bytecoder/IRExport.java @@ -10,6 +10,7 @@ import de.mirkosertic.bytecoder.core.ir.Graph; import de.mirkosertic.bytecoder.core.ir.ResolvedMethod; import de.mirkosertic.bytecoder.core.loader.BytecoderLoader; +import de.mirkosertic.bytecoder.core.optimizer.InefficientSetFieldWithPatternMatcher; import de.mirkosertic.bytecoder.core.optimizer.Optimizations; import de.mirkosertic.bytecoder.core.parser.CompileUnit; import de.mirkosertic.bytecoder.core.parser.Loader; @@ -31,15 +32,15 @@ public static void doit(final String[] args) { public int dosomething(final int value) { //doit(new String[0]); //final IRExport dummy = new IRExport(); - assert value > 10; - int x = value; - for (int i = 0; i < 100; i++) { - x = x + i + value; - } + //assert value > 10; + //int x = value; + //for (int i = 0; i < 100; i++) { +// x = x + i + value; +// } IRExport t = new IRExport(); - t.target = x; - t.target2 = x + 1; - return x; + t.target = value; + t.target2 = value + 1; + return value; } public static void main(final String[] args) throws IOException, ClassNotFoundException { @@ -76,6 +77,9 @@ public static void main(final String[] args) throws IOException, ClassNotFoundEx while (Optimizations.ALL.optimize(BackendType.JS, compileUnit, method)) { } + while (new InefficientSetFieldWithPatternMatcher().optimize(BackendType.JS, compileUnit, method)) { + } + try (final FileOutputStream fos = new FileOutputStream("debug_optimized.dot")) { g.writeDebugTo(fos); }