diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java index 0a66d0d8d..c6e0d43c2 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java @@ -48,7 +48,7 @@ public class TString extends TObject implements TSerializable, TComparable= characters.length) { - throw new TStringIndexOutOfBoundsException(null); + throw new TStringIndexOutOfBoundsException(); } return characters[index]; } diff --git a/teavm-core/src/main/java/org/teavm/model/BasicBlock.java b/teavm-core/src/main/java/org/teavm/model/BasicBlock.java index b88e20cd8..c1d1e2d31 100644 --- a/teavm-core/src/main/java/org/teavm/model/BasicBlock.java +++ b/teavm-core/src/main/java/org/teavm/model/BasicBlock.java @@ -47,7 +47,7 @@ public class BasicBlock { @Override public void add(int index, Instruction e) { if (e.getBasicBlock() != null) { - throw new IllegalArgumentException("This instruction is some basic block"); + throw new IllegalArgumentException("This instruction is in some basic block"); } e.setBasicBlock(BasicBlock.this); instructions.add(index, e); @@ -56,7 +56,7 @@ public class BasicBlock { @Override public Instruction set(int index, Instruction element) { if (element.getBasicBlock() != null) { - throw new IllegalArgumentException("This instruction is some basic block"); + throw new IllegalArgumentException("This instruction is in some basic block"); } Instruction oldInsn = instructions.get(index); oldInsn.setBasicBlock(null); diff --git a/teavm-core/src/main/java/org/teavm/model/Program.java b/teavm-core/src/main/java/org/teavm/model/Program.java index e8334ddf1..96e03f41e 100644 --- a/teavm-core/src/main/java/org/teavm/model/Program.java +++ b/teavm-core/src/main/java/org/teavm/model/Program.java @@ -53,6 +53,7 @@ public class Program { variables.set(index, null); variable.setIndex(-1); variable.setProgram(null); + packed = false; } public boolean isPacked() { @@ -75,9 +76,17 @@ public class Program { basicBlocks.remove(basicBlocks.size() - 1); } sz = 0; - for (int i = 0; i < basicBlocks.size(); ++i) { - + for (int i = 0; i < variables.size(); ++i) { + Variable var = variables.get(i); + if (var != null) { + var.setIndex(sz); + variables.set(sz++, var); + } } + while (variables.size() > sz) { + variables.remove(variables.size() - 1); + } + packed = true; } public int variableCount() { diff --git a/teavm-core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java b/teavm-core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java index d21f4c72b..9e2144668 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java +++ b/teavm-core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java @@ -12,14 +12,16 @@ import org.teavm.model.MethodHolder; */ public class ClassSetOptimizer { private List optimizations = Arrays.asList( - new UnusedVariableElimination()); + new CommonSubexpressionElimination(), new UnusedVariableElimination()); public void optimizeAll(ListableClassHolderSource classSource) { for (String className : classSource.getClassNames()) { ClassHolder cls = classSource.getClassHolder(className); for (MethodHolder method : cls.getMethods()) { - for (MethodOptimization optimization : optimizations) { - optimization.optimize(method); + if (method.getProgram() != null && method.getProgram().basicBlockCount() > 0) { + for (MethodOptimization optimization : optimizations) { + optimization.optimize(method); + } } } } diff --git a/teavm-core/src/main/java/org/teavm/optimization/CommonSubexpressionElimination.java b/teavm-core/src/main/java/org/teavm/optimization/CommonSubexpressionElimination.java index 9e2a58623..b0dad1118 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/CommonSubexpressionElimination.java +++ b/teavm-core/src/main/java/org/teavm/optimization/CommonSubexpressionElimination.java @@ -2,12 +2,10 @@ package org.teavm.optimization; import java.util.HashMap; import java.util.Map; +import org.teavm.common.DominatorTree; import org.teavm.common.Graph; import org.teavm.common.GraphUtils; -import org.teavm.model.BasicBlock; -import org.teavm.model.Instruction; -import org.teavm.model.MethodHolder; -import org.teavm.model.Program; +import org.teavm.model.*; import org.teavm.model.instructions.*; import org.teavm.model.util.ProgramUtils; @@ -16,16 +14,25 @@ import org.teavm.model.util.ProgramUtils; * @author Alexey Andreev */ public class CommonSubexpressionElimination implements MethodOptimization { - private Map knownValues = new HashMap<>(); - private Instruction currentInsn; + private Map knownValues = new HashMap<>(); + private boolean eliminate; private int[] map; private Program program; + private int currentBlockIndex; + private DominatorTree domTree; + + private static class KnownValue { + int value; + int location; + } @Override public void optimize(MethodHolder method) { program = method.getProgram(); + knownValues.clear(); Graph cfg = ProgramUtils.buildControlFlowGraph(method.getProgram()); - Graph dom = GraphUtils.buildDominatorGraph(GraphUtils.buildDominatorTree(cfg), cfg.size()); + domTree = GraphUtils.buildDominatorTree(cfg); + Graph dom = GraphUtils.buildDominatorGraph(domTree, cfg.size()); map = new int[program.variableCount()]; for (int i = 0; i < map.length; ++i) { map[i] = i; @@ -40,30 +47,51 @@ public class CommonSubexpressionElimination implements MethodOptimization { } while (top > 0) { int v = stack[--top]; + currentBlockIndex = v; BasicBlock block = program.basicBlockAt(v); for (int i = 0; i < block.getInstructions().size(); ++i) { - currentInsn = block.getInstructions().get(i); + Instruction currentInsn = block.getInstructions().get(i); currentInsn.acceptVisitor(optimizer); - if (currentInsn != null) { - block.getInstructions().set(i, currentInsn); - } else { + if (eliminate) { block.getInstructions().remove(i--); + eliminate = false; } } for (int succ : dom.outgoingEdges(v)) { stack[top++] = succ; } } + for (int v = 0; v < program.basicBlockCount(); ++v) { + BasicBlock block = program.basicBlockAt(v); + for (int i = 0; i < block.getPhis().size(); ++i) { + Phi phi = block.getPhis().get(i); + for (Incoming incoming : phi.getIncomings()) { + int value = map[incoming.getValue().getIndex()]; + incoming.setValue(program.variableAt(value)); + } + } + } + + for (int i = 0; i < map.length; ++i) { + if (map[i] != i) { + program.deleteVariable(i); + } + } + program.pack(); + program = null; } private void bind(int var, String value) { - Integer known = knownValues.get(value); - if (known != null) { - currentInsn = null; - map[var] = known; + KnownValue known = knownValues.get(value); + if (known != null && domTree.dominates(known.location, currentBlockIndex)) { + eliminate = true; + map[var] = known.value; } else { - knownValues.put(value, var); + known = new KnownValue(); + known.location = currentBlockIndex; + known.value = var; + knownValues.put(value, known); } } @@ -160,34 +188,43 @@ public class CommonSubexpressionElimination implements MethodOptimization { @Override public void visit(AssignInstruction insn) { - map[insn.getReceiver().getIndex()] = insn.getAssignee().getIndex(); - currentInsn = null; + map[insn.getReceiver().getIndex()] = map[insn.getAssignee().getIndex()]; + eliminate = true; } @Override public void visit(CastInstruction insn) { - bind(insn.getReceiver().getIndex(), "@" + insn.getValue().getIndex() + " cast " + - insn.getTargetType()); + int a = map[insn.getValue().getIndex()]; + insn.setValue(program.variableAt(a)); + bind(insn.getReceiver().getIndex(), "@" + a + "::" + insn.getTargetType()); } @Override public void visit(CastNumberInstruction insn) { - bind(insn.getReceiver().getIndex(), "@" + insn.getValue().getIndex() + " cast " + - insn.getTargetType()); + int a = map[insn.getValue().getIndex()]; + insn.setValue(program.variableAt(a)); + bind(insn.getReceiver().getIndex(), "@" + a + "::" + insn.getTargetType()); } @Override public void visit(CastIntegerInstruction insn) { - bind(insn.getReceiver().getIndex(), "@" + insn.getValue().getIndex() + " cast " + - insn.getTargetType() + " " + insn.getDirection()); + int a = map[insn.getValue().getIndex()]; + insn.setValue(program.variableAt(a)); + bind(insn.getReceiver().getIndex(), "@" + a + "::" + insn.getTargetType() + " " + insn.getDirection()); } @Override public void visit(BranchingInstruction insn) { + int a = map[insn.getOperand().getIndex()]; + insn.setOperand(program.variableAt(a)); } @Override public void visit(BinaryBranchingInstruction insn) { + int a = map[insn.getFirstOperand().getIndex()]; + int b = map[insn.getSecondOperand().getIndex()]; + insn.setFirstOperand(program.variableAt(a)); + insn.setSecondOperand(program.variableAt(b)); } @Override @@ -196,18 +233,28 @@ public class CommonSubexpressionElimination implements MethodOptimization { @Override public void visit(SwitchInstruction insn) { + int a = map[insn.getCondition().getIndex()]; + insn.setCondition(program.variableAt(a)); } @Override public void visit(ExitInstruction insn) { + if (insn.getValueToReturn() != null) { + int a = map[insn.getValueToReturn().getIndex()]; + insn.setValueToReturn(program.variableAt(a)); + } } @Override public void visit(RaiseInstruction insn) { + int a = map[insn.getException().getIndex()]; + insn.setException(program.variableAt(a)); } @Override public void visit(ConstructArrayInstruction insn) { + int a = map[insn.getSize().getIndex()]; + insn.setSize(program.variableAt(a)); } @Override @@ -216,18 +263,35 @@ public class CommonSubexpressionElimination implements MethodOptimization { @Override public void visit(ConstructMultiArrayInstruction insn) { + for (int i = 0; i < insn.getDimensions().size(); ++i) { + int a = map[insn.getDimensions().get(i).getIndex()]; + insn.getDimensions().set(i, program.variableAt(a)); + } } @Override public void visit(GetFieldInstruction insn) { + if (insn.getInstance() != null) { + int instance = map[insn.getInstance().getIndex()]; + insn.setInstance(program.variableAt(instance)); + } } @Override public void visit(PutFieldInstruction insn) { + if (insn.getInstance() != null) { + int instance = map[insn.getInstance().getIndex()]; + insn.setInstance(program.variableAt(instance)); + } + int val = map[insn.getValue().getIndex()]; + insn.setValue(program.variableAt(val)); } @Override public void visit(ArrayLengthInstruction insn) { + int a = map[insn.getArray().getIndex()]; + insn.setArray(program.variableAt(a)); + bind(insn.getReceiver().getIndex(), "@" + a + ".length"); } @Override @@ -236,22 +300,46 @@ public class CommonSubexpressionElimination implements MethodOptimization { @Override public void visit(UnwrapArrayInstruction insn) { + int a = map[insn.getArray().getIndex()]; + insn.setArray(program.variableAt(a)); + bind(insn.getReceiver().getIndex(), "@" + a + ".data"); } @Override public void visit(GetElementInstruction insn) { + int a = map[insn.getArray().getIndex()]; + insn.setArray(program.variableAt(a)); + int index = map[insn.getIndex().getIndex()]; + insn.setIndex(program.variableAt(index)); } @Override public void visit(PutElementInstruction insn) { + int a = map[insn.getArray().getIndex()]; + insn.setArray(program.variableAt(a)); + int index = map[insn.getIndex().getIndex()]; + insn.setIndex(program.variableAt(index)); + int val = map[insn.getValue().getIndex()]; + insn.setValue(program.variableAt(val)); } @Override public void visit(InvokeInstruction insn) { + if (insn.getInstance() != null) { + int instance = map[insn.getInstance().getIndex()]; + insn.setInstance(program.variableAt(instance)); + } + for (int i = 0; i < insn.getArguments().size(); ++i) { + int arg = map[insn.getArguments().get(i).getIndex()]; + insn.getArguments().set(i, program.variableAt(arg)); + } } @Override public void visit(IsInstanceInstruction insn) { + int val = map[insn.getValue().getIndex()]; + insn.setValue(program.variableAt(val)); + bind(insn.getReceiver().getIndex(), "@" + val + " :? " + insn.getType()); } }; }