diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java index 2aa1324db..b06afd419 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java @@ -27,9 +27,6 @@ public class ObjectNativeGenerator implements Generator, DependencyPlugin { case "hashCode": generateHashCode(context, writer); break; - case "equals": - generateEquals(context, writer); - break; case "clone": generateClone(context, writer); break; @@ -84,11 +81,6 @@ public class ObjectNativeGenerator implements Generator, DependencyPlugin { writer.append("return ").append(context.getParameterName(0)).append(".$id;").newLine(); } - private void generateEquals(GeneratorContext context, SourceWriter writer) { - writer.append("return ").append(context.getParameterName(0)).append(" == ") - .append(context.getParameterName(1)).append(";").newLine(); - } - private void generateClone(GeneratorContext context, SourceWriter writer) { writer.append("var copy = new ").append(context.getParameterName(0)).append(".$class();").newLine(); writer.append("for (var field in obj) {").newLine().indent(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index 4caf3ea0d..adf43112a 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -28,8 +28,9 @@ public class TObject { @GeneratedBy(ObjectNativeGenerator.class) public native int hashCode(); - @GeneratedBy(ObjectNativeGenerator.class) - public native boolean equals(TObject other); + public boolean equals(TObject other) { + return this == other; + } @Rename("toString") public native TString toString0(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java index 53833f742..d00470f31 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java @@ -15,6 +15,7 @@ import org.teavm.javascript.Renderer; import org.teavm.javascript.ast.ClassNode; import org.teavm.model.*; import org.teavm.model.resource.ClasspathClassHolderSource; +import org.teavm.optimization.ClassSetOptimizer; /** * @@ -55,8 +56,10 @@ public class ClasslibTestGenerator { dependencyChecker.addEntryPoint(methodRef); } dependencyChecker.checkDependencies(); - dependencyChecker.cutUnachievableClasses(); - decompileClasses(dependencyChecker.getAchievableClasses()); + ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(); + ClassSetOptimizer optimizer = new ClassSetOptimizer(); + optimizer.optimizeAll(classSet); + decompileClasses(classSet.getClassNames()); renderHead(); ClassLoader classLoader = ClasslibTestGenerator.class.getClassLoader(); try (InputStream input = classLoader.getResourceAsStream("org/teavm/classlib/junit-support.js")) { diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index 94e4568fb..868340fe5 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -289,6 +289,7 @@ public class DependencyChecker { MutableClassHolderSource cutClasses = new MutableClassHolderSource(); for (String className : achievableClasses.keySet()) { ClassHolder classHolder = classSource.getClassHolder(className); + cutClasses.putClassHolder(classHolder); for (MethodHolder method : classHolder.getMethods().toArray(new MethodHolder[0])) { MethodReference methodRef = new MethodReference(className, method.getDescriptor()); if (!methodCache.getCachedPreimages().contains(methodRef)) { 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 5112c4dcc..b88e20cd8 100644 --- a/teavm-core/src/main/java/org/teavm/model/BasicBlock.java +++ b/teavm-core/src/main/java/org/teavm/model/BasicBlock.java @@ -66,7 +66,7 @@ public class BasicBlock { @Override public Instruction remove(int index) { - Instruction insn = super.remove(index); + Instruction insn = instructions.remove(index); insn.setBasicBlock(null); return insn; } diff --git a/teavm-core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java b/teavm-core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java new file mode 100644 index 000000000..d21f4c72b --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java @@ -0,0 +1,27 @@ +package org.teavm.optimization; + +import java.util.Arrays; +import java.util.List; +import org.teavm.model.ClassHolder; +import org.teavm.model.ListableClassHolderSource; +import org.teavm.model.MethodHolder; + +/** + * + * @author Alexey Andreev + */ +public class ClassSetOptimizer { + private List optimizations = Arrays.asList( + 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); + } + } + } + } +} diff --git a/teavm-core/src/main/java/org/teavm/optimization/MethodOptimization.java b/teavm-core/src/main/java/org/teavm/optimization/MethodOptimization.java new file mode 100644 index 000000000..77667d56b --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/optimization/MethodOptimization.java @@ -0,0 +1,11 @@ +package org.teavm.optimization; + +import org.teavm.model.MethodHolder; + +/** + * + * @author Alexey Andreev + */ +public interface MethodOptimization { + void optimize(MethodHolder method); +} diff --git a/teavm-core/src/main/java/org/teavm/optimization/UnusedVariableElimination.java b/teavm-core/src/main/java/org/teavm/optimization/UnusedVariableElimination.java new file mode 100644 index 000000000..85ba8b05c --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/optimization/UnusedVariableElimination.java @@ -0,0 +1,224 @@ +package org.teavm.optimization; + +import org.teavm.common.Graph; +import org.teavm.model.*; +import org.teavm.model.instructions.*; + +/** + * + * @author Alexey Andreev + */ +public class UnusedVariableElimination implements MethodOptimization { + @Override + public void optimize(MethodHolder method) { + if (method.getProgram() == null) { + return; + } + Graph graph = VariableUsageGraphBuilder.build(method.getProgram()); + boolean[] escaping = VariableEscapeAnalyzer.findEscapingVariables(method.getProgram()); + boolean[] used = new boolean[escaping.length]; + + int[] stack = new int[graph.size() * 2]; + int top = 0; + for (int i = 0; i < used.length; ++i) { + if (escaping[i]) { + stack[top++] = i; + } + } + + while (top > 0) { + int var = stack[--top]; + if (used[var]) { + continue; + } + used[var] = true; + for (int arg : graph.incomingEdges(var)) { + if (!used[arg]) { + stack[top++] = arg; + } + } + } + + Program program = method.getProgram(); + InstructionOptimizer insnOptimizer = new InstructionOptimizer(used); + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + for (int j = 0; j < block.getInstructions().size(); ++j) { + insnOptimizer.eliminate = false; + block.getInstructions().get(j).acceptVisitor(insnOptimizer); + if (insnOptimizer.eliminate) { + block.getInstructions().remove(j--); + } + } + for (int j = 0; j < block.getPhis().size(); ++j) { + Phi phi = block.getPhis().get(j); + if (!used[phi.getReceiver().getIndex()]) { + block.getPhis().remove(j--); + } + } + } + } + + private class InstructionOptimizer implements InstructionVisitor { + private boolean[] used; + boolean eliminate; + + public InstructionOptimizer(boolean[] used) { + this.used = used; + } + + private void requestUsage(Variable var) { + if (!used[var.getIndex()]) { + eliminate = true; + } + } + + @Override + public void visit(EmptyInstruction insn) { + } + + @Override + public void visit(ClassConstantInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(NullConstantInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(IntegerConstantInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(LongConstantInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(FloatConstantInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(DoubleConstantInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(StringConstantInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(BinaryInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(NegateInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(AssignInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(CastInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(CastNumberInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(BranchingInstruction insn) { + } + + @Override + public void visit(BinaryBranchingInstruction insn) { + } + + @Override + public void visit(JumpInstruction insn) { + } + + @Override + public void visit(SwitchInstruction insn) { + } + + @Override + public void visit(ExitInstruction insn) { + } + + @Override + public void visit(RaiseInstruction insn) { + } + + @Override + public void visit(ConstructArrayInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(ConstructInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(ConstructMultiArrayInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(GetFieldInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(PutFieldInstruction insn) { + } + + @Override + public void visit(ArrayLengthInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(CloneArrayInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(UnwrapArrayInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(GetElementInstruction insn) { + requestUsage(insn.getReceiver()); + } + + @Override + public void visit(PutElementInstruction insn) { + } + + @Override + public void visit(InvokeInstruction insn) { + if (insn.getReceiver() != null && !used[insn.getReceiver().getIndex()]) { + insn.setReceiver(null); + } + } + + @Override + public void visit(IsInstanceInstruction insn) { + requestUsage(insn.getReceiver()); + } + } +} diff --git a/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java b/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java new file mode 100644 index 000000000..b1923891a --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java @@ -0,0 +1,178 @@ +package org.teavm.optimization; + +import org.teavm.model.BasicBlock; +import org.teavm.model.Instruction; +import org.teavm.model.Program; +import org.teavm.model.Variable; +import org.teavm.model.instructions.*; + +/** + * + * @author Alexey Andreev + */ +public class VariableEscapeAnalyzer { + public static boolean[] findEscapingVariables(Program program) { + boolean[] escaping = new boolean[program.variableCount()]; + InstructionAnalyzer analyzer = new InstructionAnalyzer(escaping); + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + for (Instruction insn : block.getInstructions()) { + insn.acceptVisitor(analyzer); + } + } + return escaping; + } + + private static class InstructionAnalyzer implements InstructionVisitor { + private boolean[] escaping; + + public InstructionAnalyzer(boolean[] escaping) { + this.escaping = escaping; + } + + @Override + public void visit(EmptyInstruction insn) { + } + + @Override + public void visit(ClassConstantInstruction insn) { + } + + @Override + public void visit(NullConstantInstruction insn) { + } + + @Override + public void visit(IntegerConstantInstruction insn) { + } + + @Override + public void visit(LongConstantInstruction insn) { + } + + @Override + public void visit(FloatConstantInstruction insn) { + } + + @Override + public void visit(DoubleConstantInstruction insn) { + } + + @Override + public void visit(StringConstantInstruction insn) { + } + + @Override + public void visit(BinaryInstruction insn) { + } + + @Override + public void visit(NegateInstruction insn) { + } + + @Override + public void visit(AssignInstruction insn) { + } + + @Override + public void visit(CastInstruction insn) { + } + + @Override + public void visit(CastNumberInstruction insn) { + } + + @Override + public void visit(BranchingInstruction insn) { + escaping[insn.getOperand().getIndex()] = true; + } + + @Override + public void visit(BinaryBranchingInstruction insn) { + escaping[insn.getFirstOperand().getIndex()] = true; + escaping[insn.getSecondOperand().getIndex()] = true; + } + + @Override + public void visit(JumpInstruction insn) { + } + + @Override + public void visit(SwitchInstruction insn) { + escaping[insn.getCondition().getIndex()] = true; + } + + @Override + public void visit(ExitInstruction insn) { + if (insn.getValueToReturn() != null) { + escaping[insn.getValueToReturn().getIndex()] = true; + } + } + + @Override + public void visit(RaiseInstruction insn) { + escaping[insn.getException().getIndex()] = true; + } + + @Override + public void visit(ConstructArrayInstruction insn) { + } + + @Override + public void visit(ConstructInstruction insn) { + } + + @Override + public void visit(ConstructMultiArrayInstruction insn) { + } + + @Override + public void visit(GetFieldInstruction insn) { + } + + @Override + public void visit(PutFieldInstruction insn) { + if (insn.getInstance() != null) { + escaping[insn.getInstance().getIndex()] = true; + } + escaping[insn.getValue().getIndex()] = true; + } + + @Override + public void visit(ArrayLengthInstruction insn) { + } + + @Override + public void visit(CloneArrayInstruction insn) { + } + + @Override + public void visit(UnwrapArrayInstruction insn) { + } + + @Override + public void visit(GetElementInstruction insn) { + } + + @Override + public void visit(PutElementInstruction insn) { + escaping[insn.getArray().getIndex()] = true; + escaping[insn.getIndex().getIndex()] = true; + escaping[insn.getValue().getIndex()] = true; + } + + @Override + public void visit(InvokeInstruction insn) { + if (insn.getInstance() != null) { + escaping[insn.getInstance().getIndex()] = true; + } + for (Variable arg : insn.getArguments()) { + escaping[arg.getIndex()] = true; + } + } + + @Override + public void visit(IsInstanceInstruction insn) { + } + } +} diff --git a/teavm-core/src/main/java/org/teavm/optimization/VariableUsageGraphBuilder.java b/teavm-core/src/main/java/org/teavm/optimization/VariableUsageGraphBuilder.java new file mode 100644 index 000000000..31309d0b4 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/optimization/VariableUsageGraphBuilder.java @@ -0,0 +1,179 @@ +package org.teavm.optimization; + +import org.teavm.common.Graph; +import org.teavm.common.GraphBuilder; +import org.teavm.model.*; +import org.teavm.model.instructions.*; + +/** + * + * @author Alexey Andreev + */ +public class VariableUsageGraphBuilder { + public static Graph build(Program program) { + GraphBuilder builder = new GraphBuilder(program.variableCount()); + InstructionAnalyzer analyzer = new InstructionAnalyzer(builder); + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + for (Instruction insn : block.getInstructions()) { + insn.acceptVisitor(analyzer); + } + for (Phi phi : block.getPhis()) { + for (Incoming incoming : phi.getIncomings()) { + builder.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex()); + } + } + } + return builder.build(); + } + + private static class InstructionAnalyzer implements InstructionVisitor { + private GraphBuilder builder; + + public InstructionAnalyzer(GraphBuilder builder) { + this.builder = builder; + } + + private void use(Variable receiver, Variable... arguments) { + for (Variable arg : arguments) { + builder.addEdge(arg.getIndex(), receiver.getIndex()); + } + } + + @Override + public void visit(EmptyInstruction insn) { + } + + @Override + public void visit(ClassConstantInstruction insn) { + } + + @Override + public void visit(NullConstantInstruction insn) { + } + + @Override + public void visit(IntegerConstantInstruction insn) { + } + + @Override + public void visit(LongConstantInstruction insn) { + } + + @Override + public void visit(FloatConstantInstruction insn) { + } + + @Override + public void visit(DoubleConstantInstruction insn) { + } + + @Override + public void visit(StringConstantInstruction insn) { + } + + @Override + public void visit(BinaryInstruction insn) { + use(insn.getReceiver(), insn.getFirstOperand(), insn.getSecondOperand()); + } + + @Override + public void visit(NegateInstruction insn) { + use(insn.getReceiver(), insn.getOperand()); + } + + @Override + public void visit(AssignInstruction insn) { + use(insn.getReceiver(), insn.getAssignee()); + } + + @Override + public void visit(CastInstruction insn) { + use(insn.getReceiver(), insn.getValue()); + } + + @Override + public void visit(CastNumberInstruction insn) { + use(insn.getReceiver(), insn.getValue()); + } + + @Override + public void visit(BranchingInstruction insn) { + } + + @Override + public void visit(BinaryBranchingInstruction insn) { + } + + @Override + public void visit(JumpInstruction insn) { + } + + @Override + public void visit(SwitchInstruction insn) { + } + + @Override + public void visit(ExitInstruction insn) { + } + + @Override + public void visit(RaiseInstruction insn) { + } + + @Override + public void visit(ConstructArrayInstruction insn) { + use(insn.getReceiver(), insn.getSize()); + } + + @Override + public void visit(ConstructInstruction insn) { + } + + @Override + public void visit(ConstructMultiArrayInstruction insn) { + use(insn.getReceiver(), insn.getDimensions().toArray(new Variable[0])); + } + + @Override + public void visit(GetFieldInstruction insn) { + } + + @Override + public void visit(PutFieldInstruction insn) { + } + + @Override + public void visit(ArrayLengthInstruction insn) { + use(insn.getReceiver(), insn.getArray()); + } + + @Override + public void visit(CloneArrayInstruction insn) { + use(insn.getReceiver(), insn.getArray()); + } + + @Override + public void visit(UnwrapArrayInstruction insn) { + use(insn.getReceiver(), insn.getArray()); + } + + @Override + public void visit(GetElementInstruction insn) { + use(insn.getReceiver(), insn.getArray(), insn.getIndex()); + } + + @Override + public void visit(PutElementInstruction insn) { + } + + @Override + public void visit(InvokeInstruction insn) { + } + + @Override + public void visit(IsInstanceInstruction insn) { + use(insn.getReceiver(), insn.getValue()); + } + } +}