diff --git a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingDependencyListener.java b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingDependencyListener.java index 4c620ccd5..359a305d6 100644 --- a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingDependencyListener.java +++ b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingDependencyListener.java @@ -93,7 +93,7 @@ public class MetaprogrammingDependencyListener extends AbstractDependencyListene result.returnValue(); } - agent.submitMethod(model.getMethod(), new Optimizations().apply(pe.getProgram())); + agent.submitMethod(model.getMethod(), new Optimizations().apply(pe.getProgram(), model.getMethod())); } private void emitMultipleUsage(MethodModel model, ProgramEmitter pe, DependencyAgent agent, @@ -132,6 +132,6 @@ public class MetaprogrammingDependencyListener extends AbstractDependencyListene } }); - agent.submitMethod(model.getMethod(), new Optimizations().apply(pe.getProgram())); + agent.submitMethod(model.getMethod(), new Optimizations().apply(pe.getProgram(), model.getMethod())); } } diff --git a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java index caa484dc9..595cc0168 100644 --- a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java +++ b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/MetaprogrammingImpl.java @@ -304,7 +304,7 @@ public final class MetaprogrammingImpl { jumpToStart.setTarget(program.basicBlockAt(startBlock.getIndex() + 1)); startBlock.add(jumpToStart); - new Optimizations().apply(program); + new Optimizations().apply(program, new MethodReference(cls.getName(), methodHolder.getDescriptor())); cls.addMethod(methodHolder); } finally { returnType = returnTypeBackup; diff --git a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/optimization/ArrayElimination.java b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/optimization/ArrayElimination.java new file mode 100644 index 000000000..9005c084c --- /dev/null +++ b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/optimization/ArrayElimination.java @@ -0,0 +1,278 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.metaprogramming.impl.optimization; + +import org.teavm.common.DisjointSet; +import org.teavm.model.BasicBlock; +import org.teavm.model.Incoming; +import org.teavm.model.Instruction; +import org.teavm.model.MethodReference; +import org.teavm.model.Phi; +import org.teavm.model.Program; +import org.teavm.model.ValueType; +import org.teavm.model.Variable; +import org.teavm.model.instructions.AssignInstruction; +import org.teavm.model.instructions.ConstructArrayInstruction; +import org.teavm.model.instructions.DoubleConstantInstruction; +import org.teavm.model.instructions.FloatConstantInstruction; +import org.teavm.model.instructions.GetElementInstruction; +import org.teavm.model.instructions.IntegerConstantInstruction; +import org.teavm.model.instructions.LongConstantInstruction; +import org.teavm.model.instructions.NullConstantInstruction; +import org.teavm.model.instructions.PutElementInstruction; +import org.teavm.model.instructions.UnwrapArrayInstruction; +import org.teavm.model.util.DefinitionExtractor; +import org.teavm.model.util.PhiUpdater; +import org.teavm.model.util.UsageExtractor; + +public class ArrayElimination { + private Program program; + private int[] varClasses; + private int[] constantValues; + private boolean[] constants; + private boolean[] unsafeArrays; + private int[] arraySizes; + private boolean hasModifications; + + public void apply(Program program, MethodReference methodReference) { + this.program = program; + findVarClasses(); + findConstantVariables(); + findSafeArrays(); + removeSafeArrays(); + if (hasModifications) { + Variable[] parameters = new Variable[methodReference.parameterCount() + 1]; + for (int i = 0; i < parameters.length; ++i) { + parameters[i] = program.variableAt(i); + } + new PhiUpdater().updatePhis(program, parameters); + } + } + + private void findVarClasses() { + DisjointSet varClasses = new DisjointSet(); + for (int i = 0; i < program.variableCount(); ++i) { + varClasses.create(); + } + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block) { + int receiver; + int assignee; + if (instruction instanceof AssignInstruction) { + AssignInstruction assign = (AssignInstruction) instruction; + receiver = assign.getReceiver().getIndex(); + assignee = assign.getAssignee().getIndex(); + } else if (instruction instanceof UnwrapArrayInstruction) { + UnwrapArrayInstruction unwrap = (UnwrapArrayInstruction) instruction; + receiver = unwrap.getReceiver().getIndex(); + assignee = unwrap.getArray().getIndex(); + } else { + continue; + } + varClasses.union(receiver, assignee); + } + } + this.varClasses = varClasses.pack(program.variableCount()); + } + + private void findConstantVariables() { + int[] constantValuesByClasses = new int[program.variableCount()]; + boolean[] constantsByClasses = new boolean[program.variableCount()]; + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block) { + if (instruction instanceof IntegerConstantInstruction) { + IntegerConstantInstruction constant = (IntegerConstantInstruction) instruction; + int receiver = varClasses[constant.getReceiver().getIndex()]; + constantsByClasses[receiver] = true; + constantValuesByClasses[receiver] = constant.getConstant(); + } + } + } + + constantValues = new int[program.variableCount()]; + constants = new boolean[program.variableCount()]; + for (int i = 0; i < program.variableCount(); ++i) { + constantValues[i] = constantValuesByClasses[varClasses[i]]; + constants[i] = constantsByClasses[varClasses[i]]; + } + } + + private void findSafeArrays() { + boolean[] unsafeArraysByClasses = new boolean[program.variableCount()]; + int[] arraySizesByClasses = new int[program.variableCount()]; + + DefinitionExtractor defExtractor = new DefinitionExtractor(); + UsageExtractor useExtractor = new UsageExtractor(); + for (BasicBlock block : program.getBasicBlocks()) { + for (Phi phi : block.getPhis()) { + unsafeArraysByClasses[varClasses[phi.getReceiver().getIndex()]] = true; + for (Incoming incoming : phi.getIncomings()) { + unsafeArraysByClasses[varClasses[incoming.getValue().getIndex()]] = true; + } + } + for (Instruction instruction : block) { + if (instruction instanceof GetElementInstruction) { + GetElementInstruction getElement = (GetElementInstruction) instruction; + unsafeArraysByClasses[varClasses[getElement.getReceiver().getIndex()]] = true; + } else if (instruction instanceof PutElementInstruction) { + PutElementInstruction putElement = (PutElementInstruction) instruction; + unsafeArraysByClasses[varClasses[putElement.getValue().getIndex()]] = true; + if (!constants[putElement.getIndex().getIndex()]) { + unsafeArraysByClasses[varClasses[putElement.getIndex().getIndex()]] = true; + } + } else if (instruction instanceof ConstructArrayInstruction) { + ConstructArrayInstruction construct = (ConstructArrayInstruction) instruction; + int receiver = varClasses[construct.getReceiver().getIndex()]; + if (!constants[construct.getSize().getIndex()]) { + unsafeArraysByClasses[receiver] = true; + } else { + arraySizesByClasses[receiver] = constantValues[construct.getSize().getIndex()]; + } + } else { + if (instruction instanceof AssignInstruction || instruction instanceof UnwrapArrayInstruction) { + continue; + } + instruction.acceptVisitor(defExtractor); + instruction.acceptVisitor(useExtractor); + for (Variable var : defExtractor.getDefinedVariables()) { + unsafeArraysByClasses[varClasses[var.getIndex()]] = true; + } + for (Variable var : useExtractor.getUsedVariables()) { + unsafeArraysByClasses[varClasses[var.getIndex()]] = true; + } + } + } + } + + unsafeArrays = new boolean[program.variableCount()]; + arraySizes = new int[program.variableCount()]; + for (int i = 0; i < program.variableCount(); ++i) { + unsafeArrays[i] = unsafeArraysByClasses[varClasses[i]]; + arraySizes[i] = arraySizesByClasses[varClasses[i]]; + } + } + + private void removeSafeArrays() { + int[][] arrayItemsAsVariablesByClass = new int[program.variableCount()][]; + for (int i = 0; i < arrayItemsAsVariablesByClass.length; ++i) { + int varClass = varClasses[i]; + if (arrayItemsAsVariablesByClass[varClass] != null || unsafeArrays[i]) { + continue; + } + int size = arraySizes[i]; + arrayItemsAsVariablesByClass[varClass] = new int[size]; + for (int j = 0; j < size; ++j) { + arrayItemsAsVariablesByClass[varClass][j] = program.createVariable().getIndex(); + } + } + + int[][] arrayItemsAsVariables = new int[arrayItemsAsVariablesByClass.length][]; + for (int i = 0; i < arrayItemsAsVariables.length; ++i) { + arrayItemsAsVariables[i] = arrayItemsAsVariablesByClass[varClasses[i]]; + } + + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block) { + if (instruction instanceof GetElementInstruction) { + GetElementInstruction getElement = (GetElementInstruction) instruction; + int array = getElement.getArray().getIndex(); + if (!unsafeArrays[array]) { + int index = constantValues[getElement.getIndex().getIndex()]; + Variable var = program.variableAt(arrayItemsAsVariables[array][index]); + AssignInstruction assign = new AssignInstruction(); + assign.setReceiver(getElement.getReceiver()); + assign.setAssignee(var); + assign.setLocation(getElement.getLocation()); + getElement.replace(assign); + hasModifications = true; + } + } else if (instruction instanceof PutElementInstruction) { + PutElementInstruction putElement = (PutElementInstruction) instruction; + int array = putElement.getArray().getIndex(); + if (!unsafeArrays[array]) { + int index = constantValues[putElement.getIndex().getIndex()]; + Variable var = program.variableAt(arrayItemsAsVariables[array][index]); + AssignInstruction assign = new AssignInstruction(); + assign.setReceiver(var); + assign.setAssignee(putElement.getValue()); + assign.setLocation(putElement.getLocation()); + putElement.replace(assign); + hasModifications = true; + } + } else if (instruction instanceof ConstructArrayInstruction) { + ConstructArrayInstruction construct = (ConstructArrayInstruction) instruction; + if (!unsafeArrays[construct.getReceiver().getIndex()]) { + int[] vars = arrayItemsAsVariables[construct.getReceiver().getIndex()]; + for (int i = 0; i < vars.length; ++i) { + Instruction constantInsn = createDefaultConstantInstruction(construct.getItemType(), + program.variableAt(vars[i])); + constantInsn.setLocation(construct.getLocation()); + construct.insertPrevious(constantInsn); + } + construct.delete(); + hasModifications = true; + } + } else if (instruction instanceof UnwrapArrayInstruction) { + UnwrapArrayInstruction unwrap = (UnwrapArrayInstruction) instruction; + if (!unsafeArrays[unwrap.getArray().getIndex()]) { + unwrap.delete(); + hasModifications = true; + } + } else if (instruction instanceof AssignInstruction) { + AssignInstruction assign = (AssignInstruction) instruction; + if (!unsafeArrays[assign.getAssignee().getIndex()]) { + assign.delete(); + hasModifications = true; + } + } + } + } + } + + private Instruction createDefaultConstantInstruction(ValueType valueType, Variable receiver) { + if (valueType instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) valueType).getKind()) { + case BOOLEAN: + case CHARACTER: + case BYTE: + case SHORT: + case INTEGER: { + IntegerConstantInstruction result = new IntegerConstantInstruction(); + result.setReceiver(receiver); + return result; + } + case LONG: { + LongConstantInstruction result = new LongConstantInstruction(); + result.setReceiver(receiver); + return result; + } + case FLOAT: { + FloatConstantInstruction result = new FloatConstantInstruction(); + result.setReceiver(receiver); + return result; + } + case DOUBLE: { + DoubleConstantInstruction result = new DoubleConstantInstruction(); + result.setReceiver(receiver); + return result; + } + } + } + NullConstantInstruction result = new NullConstantInstruction(); + result.setReceiver(receiver); + return result; + } +} \ No newline at end of file diff --git a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/optimization/Optimizations.java b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/optimization/Optimizations.java index 8a6613afa..373e556c9 100644 --- a/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/optimization/Optimizations.java +++ b/metaprogramming/impl/src/main/java/org/teavm/metaprogramming/impl/optimization/Optimizations.java @@ -15,13 +15,16 @@ */ package org.teavm.metaprogramming.impl.optimization; +import org.teavm.model.MethodReference; import org.teavm.model.Program; public class Optimizations { private BoxingElimination boxingElimination = new BoxingElimination(); + private ArrayElimination arrayElimination = new ArrayElimination(); - public Program apply(Program program) { + public Program apply(Program program, MethodReference methodReference) { boxingElimination.optimize(program); + arrayElimination.apply(program, methodReference); return program; } }