From a57be365b4ff948954478ebab4aa10a415d98260 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 7 May 2016 17:36:38 +0300 Subject: [PATCH] Implementing loop inversion --- .../java/org/teavm/common/DominatorTree.java | 12 + .../java/org/teavm/common/GraphUtils.java | 10 + .../teavm/javascript/StatementGenerator.java | 3 + .../model/util/InstructionCopyReader.java | 482 ++++++++++++++++++ .../org/teavm/model/util/ProgramUtils.java | 422 +-------------- .../teavm/optimization/ClassSetOptimizer.java | 50 -- .../org/teavm/optimization/LoopInversion.java | 26 + .../teavm/optimization/LoopInversionImpl.java | 412 +++++++++++++++ core/src/main/java/org/teavm/vm/TeaVM.java | 2 +- 9 files changed, 964 insertions(+), 455 deletions(-) create mode 100644 core/src/main/java/org/teavm/model/util/InstructionCopyReader.java delete mode 100644 core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java create mode 100644 core/src/main/java/org/teavm/optimization/LoopInversion.java create mode 100644 core/src/main/java/org/teavm/optimization/LoopInversionImpl.java diff --git a/core/src/main/java/org/teavm/common/DominatorTree.java b/core/src/main/java/org/teavm/common/DominatorTree.java index fd0ea7736..265e849b6 100644 --- a/core/src/main/java/org/teavm/common/DominatorTree.java +++ b/core/src/main/java/org/teavm/common/DominatorTree.java @@ -29,4 +29,16 @@ public interface DominatorTree { int immediateDominatorOf(int a); int levelOf(int a); + + default int commonDominatorOf(int[] nodes) { + if (nodes.length == 0) { + return -1; + } + + int result = nodes[0]; + for (int i = 1; i < nodes.length; ++i) { + result = commonDominatorOf(result, nodes[i]); + } + return result; + } } diff --git a/core/src/main/java/org/teavm/common/GraphUtils.java b/core/src/main/java/org/teavm/common/GraphUtils.java index 865255fce..67bbbaa02 100644 --- a/core/src/main/java/org/teavm/common/GraphUtils.java +++ b/core/src/main/java/org/teavm/common/GraphUtils.java @@ -252,4 +252,14 @@ public final class GraphUtils { } return set; } + + public static Graph invert(Graph graph) { + GraphBuilder graphBuilder = new GraphBuilder(graph.size()); + for (int node = 0; node < graph.size(); ++node) { + for (int pred : graph.incomingEdges(node)) { + graphBuilder.addEdge(node, pred); + } + } + return graphBuilder.build(); + } } diff --git a/core/src/main/java/org/teavm/javascript/StatementGenerator.java b/core/src/main/java/org/teavm/javascript/StatementGenerator.java index a95b01914..7dcb4c314 100644 --- a/core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -608,6 +608,9 @@ class StatementGenerator implements InstructionVisitor { return null; } Decompiler.Block block = blockMap[target.getIndex()]; + if (block == null) { + throw new IllegalStateException("Could not find block for basic block $" + target.getIndex()); + } if (target.getIndex() == indexer.nodeAt(block.end)) { BreakStatement breakStmt = new BreakStatement(); breakStmt.setLocation(currentLocation); diff --git a/core/src/main/java/org/teavm/model/util/InstructionCopyReader.java b/core/src/main/java/org/teavm/model/util/InstructionCopyReader.java new file mode 100644 index 000000000..df0df1816 --- /dev/null +++ b/core/src/main/java/org/teavm/model/util/InstructionCopyReader.java @@ -0,0 +1,482 @@ +/* + * Copyright 2016 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.model.util; + +import java.util.List; +import java.util.stream.Collectors; +import org.teavm.model.BasicBlock; +import org.teavm.model.BasicBlockReader; +import org.teavm.model.FieldReference; +import org.teavm.model.Instruction; +import org.teavm.model.InstructionLocation; +import org.teavm.model.InvokeDynamicInstruction; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodHandle; +import org.teavm.model.MethodReference; +import org.teavm.model.Program; +import org.teavm.model.RuntimeConstant; +import org.teavm.model.ValueType; +import org.teavm.model.Variable; +import org.teavm.model.VariableReader; +import org.teavm.model.instructions.ArrayElementType; +import org.teavm.model.instructions.ArrayLengthInstruction; +import org.teavm.model.instructions.AssignInstruction; +import org.teavm.model.instructions.BinaryBranchingCondition; +import org.teavm.model.instructions.BinaryBranchingInstruction; +import org.teavm.model.instructions.BinaryInstruction; +import org.teavm.model.instructions.BinaryOperation; +import org.teavm.model.instructions.BranchingCondition; +import org.teavm.model.instructions.BranchingInstruction; +import org.teavm.model.instructions.CastInstruction; +import org.teavm.model.instructions.CastIntegerDirection; +import org.teavm.model.instructions.CastIntegerInstruction; +import org.teavm.model.instructions.CastNumberInstruction; +import org.teavm.model.instructions.ClassConstantInstruction; +import org.teavm.model.instructions.CloneArrayInstruction; +import org.teavm.model.instructions.ConstructArrayInstruction; +import org.teavm.model.instructions.ConstructInstruction; +import org.teavm.model.instructions.ConstructMultiArrayInstruction; +import org.teavm.model.instructions.DoubleConstantInstruction; +import org.teavm.model.instructions.EmptyInstruction; +import org.teavm.model.instructions.ExitInstruction; +import org.teavm.model.instructions.FloatConstantInstruction; +import org.teavm.model.instructions.GetElementInstruction; +import org.teavm.model.instructions.GetFieldInstruction; +import org.teavm.model.instructions.InitClassInstruction; +import org.teavm.model.instructions.InstructionReader; +import org.teavm.model.instructions.IntegerConstantInstruction; +import org.teavm.model.instructions.IntegerSubtype; +import org.teavm.model.instructions.InvocationType; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.IsInstanceInstruction; +import org.teavm.model.instructions.JumpInstruction; +import org.teavm.model.instructions.LongConstantInstruction; +import org.teavm.model.instructions.MonitorEnterInstruction; +import org.teavm.model.instructions.MonitorExitInstruction; +import org.teavm.model.instructions.NegateInstruction; +import org.teavm.model.instructions.NullCheckInstruction; +import org.teavm.model.instructions.NullConstantInstruction; +import org.teavm.model.instructions.NumericOperandType; +import org.teavm.model.instructions.PutElementInstruction; +import org.teavm.model.instructions.PutFieldInstruction; +import org.teavm.model.instructions.RaiseInstruction; +import org.teavm.model.instructions.StringConstantInstruction; +import org.teavm.model.instructions.SwitchInstruction; +import org.teavm.model.instructions.SwitchTableEntry; +import org.teavm.model.instructions.SwitchTableEntryReader; +import org.teavm.model.instructions.UnwrapArrayInstruction; + +public class InstructionCopyReader implements InstructionReader { + private Instruction copy; + private Program programCopy; + private InstructionLocation location; + + public InstructionCopyReader(Program programCopy) { + this.programCopy = programCopy; + } + + public Instruction getCopy() { + return copy; + } + + public void resetLocation() { + location = null; + } + + @Override + public void location(InstructionLocation location) { + this.location = location; + } + + private Variable copyVar(VariableReader var) { + if (var == null) { + throw new NullPointerException(); + } + return programCopy.variableAt(var.getIndex()); + } + + private BasicBlock copyBlock(BasicBlockReader block) { + return programCopy.basicBlockAt(block.getIndex()); + } + + @Override + public void nop() { + copy = new EmptyInstruction(); + copy.setLocation(location); + } + + @Override + public void classConstant(VariableReader receiver, ValueType cst) { + ClassConstantInstruction insnCopy = new ClassConstantInstruction(); + insnCopy.setConstant(cst); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void nullConstant(VariableReader receiver) { + NullConstantInstruction insnCopy = new NullConstantInstruction(); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void integerConstant(VariableReader receiver, int cst) { + IntegerConstantInstruction insnCopy = new IntegerConstantInstruction(); + insnCopy.setConstant(cst); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void longConstant(VariableReader receiver, long cst) { + LongConstantInstruction insnCopy = new LongConstantInstruction(); + insnCopy.setConstant(cst); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void floatConstant(VariableReader receiver, float cst) { + FloatConstantInstruction insnCopy = new FloatConstantInstruction(); + insnCopy.setConstant(cst); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void doubleConstant(VariableReader receiver, double cst) { + DoubleConstantInstruction insnCopy = new DoubleConstantInstruction(); + insnCopy.setConstant(cst); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void stringConstant(VariableReader receiver, String cst) { + StringConstantInstruction insnCopy = new StringConstantInstruction(); + insnCopy.setConstant(cst); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second, + NumericOperandType type) { + BinaryInstruction insnCopy = new BinaryInstruction(op, type); + insnCopy.setFirstOperand(copyVar(first)); + insnCopy.setSecondOperand(copyVar(second)); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) { + NegateInstruction insnCopy = new NegateInstruction(type); + insnCopy.setOperand(copyVar(operand)); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void assign(VariableReader receiver, VariableReader assignee) { + AssignInstruction insnCopy = new AssignInstruction(); + insnCopy.setAssignee(copyVar(assignee)); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void cast(VariableReader receiver, VariableReader value, ValueType targetType) { + CastInstruction insnCopy = new CastInstruction(); + insnCopy.setValue(copyVar(value)); + insnCopy.setReceiver(copyVar(receiver)); + insnCopy.setTargetType(targetType); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType, + NumericOperandType targetType) { + CastNumberInstruction insnCopy = new CastNumberInstruction(sourceType, targetType); + insnCopy.setValue(copyVar(value)); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type, + CastIntegerDirection dir) { + CastIntegerInstruction insnCopy = new CastIntegerInstruction(type, dir); + insnCopy.setValue(copyVar(value)); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent, + BasicBlockReader alternative) { + BranchingInstruction insnCopy = new BranchingInstruction(cond); + insnCopy.setOperand(copyVar(operand)); + insnCopy.setConsequent(copyBlock(consequent)); + insnCopy.setAlternative(copyBlock(alternative)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second, + BasicBlockReader consequent, BasicBlockReader alternative) { + BinaryBranchingInstruction insnCopy = new BinaryBranchingInstruction(cond); + insnCopy.setFirstOperand(copyVar(first)); + insnCopy.setSecondOperand(copyVar(second)); + insnCopy.setConsequent(copyBlock(consequent)); + insnCopy.setAlternative(copyBlock(alternative)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void jump(BasicBlockReader target) { + JumpInstruction insnCopy = new JumpInstruction(); + insnCopy.setTarget(copyBlock(target)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void choose(VariableReader condition, List table, + BasicBlockReader defaultTarget) { + SwitchInstruction insnCopy = new SwitchInstruction(); + insnCopy.setCondition(copyVar(condition)); + insnCopy.setDefaultTarget(copyBlock(defaultTarget)); + for (SwitchTableEntryReader entry : table) { + SwitchTableEntry entryCopy = new SwitchTableEntry(); + entryCopy.setCondition(entry.getCondition()); + entryCopy.setTarget(copyBlock(entry.getTarget())); + insnCopy.getEntries().add(entryCopy); + } + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void exit(VariableReader valueToReturn) { + ExitInstruction insnCopy = new ExitInstruction(); + insnCopy.setValueToReturn(valueToReturn != null ? copyVar(valueToReturn) : null); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void raise(VariableReader exception) { + RaiseInstruction insnCopy = new RaiseInstruction(); + insnCopy.setException(copyVar(exception)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) { + ConstructArrayInstruction insnCopy = new ConstructArrayInstruction(); + insnCopy.setItemType(itemType); + insnCopy.setSize(copyVar(size)); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void createArray(VariableReader receiver, ValueType itemType, + List dimensions) { + ConstructMultiArrayInstruction insnCopy = new ConstructMultiArrayInstruction(); + insnCopy.setItemType(itemType); + insnCopy.setReceiver(copyVar(receiver)); + for (VariableReader dim : dimensions) { + insnCopy.getDimensions().add(copyVar(dim)); + } + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void create(VariableReader receiver, String type) { + ConstructInstruction insnCopy = new ConstructInstruction(); + insnCopy.setType(type); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void getField(VariableReader receiver, VariableReader instance, FieldReference field, + ValueType fieldType) { + GetFieldInstruction insnCopy = new GetFieldInstruction(); + insnCopy.setField(field); + insnCopy.setFieldType(fieldType); + insnCopy.setInstance(instance != null ? copyVar(instance) : null); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void putField(VariableReader instance, FieldReference field, VariableReader value, + ValueType fieldType) { + PutFieldInstruction insnCopy = new PutFieldInstruction(); + insnCopy.setField(field); + insnCopy.setInstance(instance != null ? copyVar(instance) : null); + insnCopy.setValue(copyVar(value)); + insnCopy.setFieldType(fieldType); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void arrayLength(VariableReader receiver, VariableReader array) { + ArrayLengthInstruction insnCopy = new ArrayLengthInstruction(); + insnCopy.setArray(copyVar(array)); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void cloneArray(VariableReader receiver, VariableReader array) { + CloneArrayInstruction insnCopy = new CloneArrayInstruction(); + insnCopy.setArray(copyVar(array)); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) { + UnwrapArrayInstruction insnCopy = new UnwrapArrayInstruction(elementType); + insnCopy.setArray(copyVar(array)); + insnCopy.setReceiver(copyVar(receiver)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void getElement(VariableReader receiver, VariableReader array, VariableReader index) { + GetElementInstruction insnCopy = new GetElementInstruction(); + insnCopy.setArray(copyVar(array)); + insnCopy.setReceiver(copyVar(receiver)); + insnCopy.setIndex(copyVar(index)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void putElement(VariableReader array, VariableReader index, VariableReader value) { + PutElementInstruction insnCopy = new PutElementInstruction(); + insnCopy.setArray(copyVar(array)); + insnCopy.setValue(copyVar(value)); + insnCopy.setIndex(copyVar(index)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void invoke(VariableReader receiver, VariableReader instance, MethodReference method, + List arguments, InvocationType type) { + InvokeInstruction insnCopy = new InvokeInstruction(); + insnCopy.setMethod(method); + insnCopy.setType(type); + insnCopy.setInstance(instance != null ? copyVar(instance) : null); + insnCopy.setReceiver(receiver != null ? copyVar(receiver) : null); + for (VariableReader arg : arguments) { + insnCopy.getArguments().add(copyVar(arg)); + } + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method, + List arguments, MethodHandle bootstrapMethod, + List bootstrapArguments) { + InvokeDynamicInstruction insnCopy = new InvokeDynamicInstruction(); + insnCopy.setMethod(method); + insnCopy.setBootstrapMethod(bootstrapMethod); + insnCopy.getBootstrapArguments().addAll(bootstrapArguments); + if (instance != null) { + insnCopy.setInstance(copyVar(instance)); + } + insnCopy.getArguments().addAll(arguments.stream().map(this::copyVar).collect(Collectors.toList())); + insnCopy.setReceiver(receiver != null ? copyVar(receiver) : null); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void isInstance(VariableReader receiver, VariableReader value, ValueType type) { + IsInstanceInstruction insnCopy = new IsInstanceInstruction(); + insnCopy.setValue(copyVar(value)); + insnCopy.setReceiver(copyVar(receiver)); + insnCopy.setType(type); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void initClass(String className) { + InitClassInstruction insnCopy = new InitClassInstruction(); + insnCopy.setClassName(className); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void nullCheck(VariableReader receiver, VariableReader value) { + NullCheckInstruction insnCopy = new NullCheckInstruction(); + insnCopy.setReceiver(copyVar(receiver)); + insnCopy.setValue(copyVar(value)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void monitorEnter(VariableReader objectRef) { + MonitorEnterInstruction insnCopy = new MonitorEnterInstruction(); + insnCopy.setObjectRef(copyVar(objectRef)); + copy = insnCopy; + copy.setLocation(location); + } + + @Override + public void monitorExit(VariableReader objectRef) { + MonitorExitInstruction insnCopy = new MonitorExitInstruction(); + insnCopy.setObjectRef(copyVar(objectRef)); + copy = insnCopy; + copy.setLocation(location); + } +} diff --git a/core/src/main/java/org/teavm/model/util/ProgramUtils.java b/core/src/main/java/org/teavm/model/util/ProgramUtils.java index 57ab4b9f1..383fedf80 100644 --- a/core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -15,17 +15,25 @@ */ package org.teavm.model.util; -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import org.teavm.common.Graph; import org.teavm.common.GraphBuilder; -import org.teavm.model.*; -import org.teavm.model.instructions.*; +import org.teavm.model.BasicBlock; +import org.teavm.model.BasicBlockReader; +import org.teavm.model.Incoming; +import org.teavm.model.IncomingReader; +import org.teavm.model.Instruction; +import org.teavm.model.InstructionLocation; +import org.teavm.model.Phi; +import org.teavm.model.PhiReader; +import org.teavm.model.Program; +import org.teavm.model.ProgramReader; +import org.teavm.model.TryCatchBlock; +import org.teavm.model.TryCatchBlockReader; +import org.teavm.model.Variable; -/** - * - * @author Alexey Andreev - */ public final class ProgramUtils { private ProgramUtils() { } @@ -81,8 +89,6 @@ public final class ProgramUtils { public static Program copy(ProgramReader program) { Program copy = new Program(); - InstructionCopyReader insnCopier = new InstructionCopyReader(); - insnCopier.programCopy = copy; for (int i = 0; i < program.variableCount(); ++i) { Variable var = copy.createVariable(); var.getDebugNames().addAll(program.variableAt(i).readDebugNames()); @@ -108,12 +114,10 @@ public final class ProgramUtils { public static List copyInstructions(BasicBlockReader block, int from, int to, Program target) { List result = new ArrayList<>(); - InstructionCopyReader copyReader = new InstructionCopyReader(); - copyReader.programCopy = target; + InstructionCopyReader copyReader = new InstructionCopyReader(target); for (int i = from; i < to; ++i) { block.readInstruction(i, copyReader); - copyReader.copy.setLocation(copyReader.location); - result.add(copyReader.copy); + result.add(copyReader.getCopy()); } return result; } @@ -145,394 +149,4 @@ public final class ProgramUtils { } return result; } - - private static class InstructionCopyReader implements InstructionReader { - Instruction copy; - Program programCopy; - InstructionLocation location; - - @Override - public void location(InstructionLocation location) { - this.location = location; - } - - private Variable copyVar(VariableReader var) { - if (var == null) { - throw new NullPointerException(); - } - return programCopy.variableAt(var.getIndex()); - } - - private BasicBlock copyBlock(BasicBlockReader block) { - return programCopy.basicBlockAt(block.getIndex()); - } - - @Override - public void nop() { - copy = new EmptyInstruction(); - copy.setLocation(location); - } - - @Override - public void classConstant(VariableReader receiver, ValueType cst) { - ClassConstantInstruction insnCopy = new ClassConstantInstruction(); - insnCopy.setConstant(cst); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void nullConstant(VariableReader receiver) { - NullConstantInstruction insnCopy = new NullConstantInstruction(); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void integerConstant(VariableReader receiver, int cst) { - IntegerConstantInstruction insnCopy = new IntegerConstantInstruction(); - insnCopy.setConstant(cst); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void longConstant(VariableReader receiver, long cst) { - LongConstantInstruction insnCopy = new LongConstantInstruction(); - insnCopy.setConstant(cst); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void floatConstant(VariableReader receiver, float cst) { - FloatConstantInstruction insnCopy = new FloatConstantInstruction(); - insnCopy.setConstant(cst); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void doubleConstant(VariableReader receiver, double cst) { - DoubleConstantInstruction insnCopy = new DoubleConstantInstruction(); - insnCopy.setConstant(cst); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void stringConstant(VariableReader receiver, String cst) { - StringConstantInstruction insnCopy = new StringConstantInstruction(); - insnCopy.setConstant(cst); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second, - NumericOperandType type) { - BinaryInstruction insnCopy = new BinaryInstruction(op, type); - insnCopy.setFirstOperand(copyVar(first)); - insnCopy.setSecondOperand(copyVar(second)); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) { - NegateInstruction insnCopy = new NegateInstruction(type); - insnCopy.setOperand(copyVar(operand)); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void assign(VariableReader receiver, VariableReader assignee) { - AssignInstruction insnCopy = new AssignInstruction(); - insnCopy.setAssignee(copyVar(assignee)); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void cast(VariableReader receiver, VariableReader value, ValueType targetType) { - CastInstruction insnCopy = new CastInstruction(); - insnCopy.setValue(copyVar(value)); - insnCopy.setReceiver(copyVar(receiver)); - insnCopy.setTargetType(targetType); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType, - NumericOperandType targetType) { - CastNumberInstruction insnCopy = new CastNumberInstruction(sourceType, targetType); - insnCopy.setValue(copyVar(value)); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type, - CastIntegerDirection dir) { - CastIntegerInstruction insnCopy = new CastIntegerInstruction(type, dir); - insnCopy.setValue(copyVar(value)); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent, - BasicBlockReader alternative) { - BranchingInstruction insnCopy = new BranchingInstruction(cond); - insnCopy.setOperand(copyVar(operand)); - insnCopy.setConsequent(copyBlock(consequent)); - insnCopy.setAlternative(copyBlock(alternative)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second, - BasicBlockReader consequent, BasicBlockReader alternative) { - BinaryBranchingInstruction insnCopy = new BinaryBranchingInstruction(cond); - insnCopy.setFirstOperand(copyVar(first)); - insnCopy.setSecondOperand(copyVar(second)); - insnCopy.setConsequent(copyBlock(consequent)); - insnCopy.setAlternative(copyBlock(alternative)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void jump(BasicBlockReader target) { - JumpInstruction insnCopy = new JumpInstruction(); - insnCopy.setTarget(copyBlock(target)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void choose(VariableReader condition, List table, - BasicBlockReader defaultTarget) { - SwitchInstruction insnCopy = new SwitchInstruction(); - insnCopy.setCondition(copyVar(condition)); - insnCopy.setDefaultTarget(copyBlock(defaultTarget)); - for (SwitchTableEntryReader entry : table) { - SwitchTableEntry entryCopy = new SwitchTableEntry(); - entryCopy.setCondition(entry.getCondition()); - entryCopy.setTarget(copyBlock(entry.getTarget())); - insnCopy.getEntries().add(entryCopy); - } - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void exit(VariableReader valueToReturn) { - ExitInstruction insnCopy = new ExitInstruction(); - insnCopy.setValueToReturn(valueToReturn != null ? copyVar(valueToReturn) : null); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void raise(VariableReader exception) { - RaiseInstruction insnCopy = new RaiseInstruction(); - insnCopy.setException(copyVar(exception)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) { - ConstructArrayInstruction insnCopy = new ConstructArrayInstruction(); - insnCopy.setItemType(itemType); - insnCopy.setSize(copyVar(size)); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void createArray(VariableReader receiver, ValueType itemType, - List dimensions) { - ConstructMultiArrayInstruction insnCopy = new ConstructMultiArrayInstruction(); - insnCopy.setItemType(itemType); - insnCopy.setReceiver(copyVar(receiver)); - for (VariableReader dim : dimensions) { - insnCopy.getDimensions().add(copyVar(dim)); - } - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void create(VariableReader receiver, String type) { - ConstructInstruction insnCopy = new ConstructInstruction(); - insnCopy.setType(type); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void getField(VariableReader receiver, VariableReader instance, FieldReference field, - ValueType fieldType) { - GetFieldInstruction insnCopy = new GetFieldInstruction(); - insnCopy.setField(field); - insnCopy.setFieldType(fieldType); - insnCopy.setInstance(instance != null ? copyVar(instance) : null); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void putField(VariableReader instance, FieldReference field, VariableReader value, - ValueType fieldType) { - PutFieldInstruction insnCopy = new PutFieldInstruction(); - insnCopy.setField(field); - insnCopy.setInstance(instance != null ? copyVar(instance) : null); - insnCopy.setValue(copyVar(value)); - insnCopy.setFieldType(fieldType); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void arrayLength(VariableReader receiver, VariableReader array) { - ArrayLengthInstruction insnCopy = new ArrayLengthInstruction(); - insnCopy.setArray(copyVar(array)); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void cloneArray(VariableReader receiver, VariableReader array) { - CloneArrayInstruction insnCopy = new CloneArrayInstruction(); - insnCopy.setArray(copyVar(array)); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) { - UnwrapArrayInstruction insnCopy = new UnwrapArrayInstruction(elementType); - insnCopy.setArray(copyVar(array)); - insnCopy.setReceiver(copyVar(receiver)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void getElement(VariableReader receiver, VariableReader array, VariableReader index) { - GetElementInstruction insnCopy = new GetElementInstruction(); - insnCopy.setArray(copyVar(array)); - insnCopy.setReceiver(copyVar(receiver)); - insnCopy.setIndex(copyVar(index)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void putElement(VariableReader array, VariableReader index, VariableReader value) { - PutElementInstruction insnCopy = new PutElementInstruction(); - insnCopy.setArray(copyVar(array)); - insnCopy.setValue(copyVar(value)); - insnCopy.setIndex(copyVar(index)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void invoke(VariableReader receiver, VariableReader instance, MethodReference method, - List arguments, InvocationType type) { - InvokeInstruction insnCopy = new InvokeInstruction(); - insnCopy.setMethod(method); - insnCopy.setType(type); - insnCopy.setInstance(instance != null ? copyVar(instance) : null); - insnCopy.setReceiver(receiver != null ? copyVar(receiver) : null); - for (VariableReader arg : arguments) { - insnCopy.getArguments().add(copyVar(arg)); - } - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method, - List arguments, MethodHandle bootstrapMethod, - List bootstrapArguments) { - InvokeDynamicInstruction insnCopy = new InvokeDynamicInstruction(); - insnCopy.setMethod(method); - insnCopy.setBootstrapMethod(bootstrapMethod); - insnCopy.getBootstrapArguments().addAll(bootstrapArguments); - if (instance != null) { - insnCopy.setInstance(copyVar(instance)); - } - insnCopy.getArguments().addAll(arguments.stream().map(this::copyVar).collect(Collectors.toList())); - insnCopy.setReceiver(receiver != null ? copyVar(receiver) : null); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void isInstance(VariableReader receiver, VariableReader value, ValueType type) { - IsInstanceInstruction insnCopy = new IsInstanceInstruction(); - insnCopy.setValue(copyVar(value)); - insnCopy.setReceiver(copyVar(receiver)); - insnCopy.setType(type); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void initClass(String className) { - InitClassInstruction insnCopy = new InitClassInstruction(); - insnCopy.setClassName(className); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void nullCheck(VariableReader receiver, VariableReader value) { - NullCheckInstruction insnCopy = new NullCheckInstruction(); - insnCopy.setReceiver(copyVar(receiver)); - insnCopy.setValue(copyVar(value)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void monitorEnter(VariableReader objectRef) { - MonitorEnterInstruction insnCopy = new MonitorEnterInstruction(); - insnCopy.setObjectRef(copyVar(objectRef)); - copy = insnCopy; - copy.setLocation(location); - } - - @Override - public void monitorExit(VariableReader objectRef) { - MonitorExitInstruction insnCopy = new MonitorExitInstruction(); - insnCopy.setObjectRef(copyVar(objectRef)); - copy = insnCopy; - copy.setLocation(location); - } - } } diff --git a/core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java b/core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java deleted file mode 100644 index 6a81fff45..000000000 --- a/core/src/main/java/org/teavm/optimization/ClassSetOptimizer.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013 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.optimization; - -import java.util.Arrays; -import java.util.List; -import org.teavm.model.ClassHolder; -import org.teavm.model.ListableClassHolderSource; -import org.teavm.model.MethodHolder; -import org.teavm.model.Program; -import org.teavm.model.util.ProgramUtils; - -/** - * - * @author Alexey Andreev - */ -public class ClassSetOptimizer { - private List getOptimizations() { - return Arrays.asList(new ArrayUnwrapMotion(), new LoopInvariantMotion(), - new GlobalValueNumbering(), new UnusedVariableElimination()); - } - - public void optimizeAll(ListableClassHolderSource classSource) { - for (String className : classSource.getClassNames()) { - ClassHolder cls = classSource.get(className); - for (MethodHolder method : cls.getMethods()) { - if (method.getProgram() != null && method.getProgram().basicBlockCount() > 0) { - Program program = ProgramUtils.copy(method.getProgram()); - for (MethodOptimization optimization : getOptimizations()) { - optimization.optimize(method, program); - } - method.setProgram(program); - } - } - } - } -} diff --git a/core/src/main/java/org/teavm/optimization/LoopInversion.java b/core/src/main/java/org/teavm/optimization/LoopInversion.java new file mode 100644 index 000000000..5db844e4f --- /dev/null +++ b/core/src/main/java/org/teavm/optimization/LoopInversion.java @@ -0,0 +1,26 @@ +/* + * Copyright 2016 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.optimization; + +import org.teavm.model.MethodReader; +import org.teavm.model.Program; + +public class LoopInversion implements MethodOptimization { + @Override + public void optimize(MethodReader method, Program program) { + new LoopInversionImpl(program).apply(); + } +} diff --git a/core/src/main/java/org/teavm/optimization/LoopInversionImpl.java b/core/src/main/java/org/teavm/optimization/LoopInversionImpl.java new file mode 100644 index 000000000..7211dbd65 --- /dev/null +++ b/core/src/main/java/org/teavm/optimization/LoopInversionImpl.java @@ -0,0 +1,412 @@ +/* + * Copyright 2016 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.optimization; + +import com.carrotsearch.hppc.IntIntMap; +import com.carrotsearch.hppc.IntIntOpenHashMap; +import com.carrotsearch.hppc.IntOpenHashSet; +import com.carrotsearch.hppc.IntSet; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.teavm.common.DominatorTree; +import org.teavm.common.Graph; +import org.teavm.common.GraphUtils; +import org.teavm.common.Loop; +import org.teavm.common.LoopGraph; +import org.teavm.model.BasicBlock; +import org.teavm.model.Incoming; +import org.teavm.model.Instruction; +import org.teavm.model.Phi; +import org.teavm.model.Program; +import org.teavm.model.TryCatchBlock; +import org.teavm.model.Variable; +import org.teavm.model.util.BasicBlockMapper; +import org.teavm.model.util.DefinitionExtractor; +import org.teavm.model.util.InstructionCopyReader; +import org.teavm.model.util.InstructionVariableMapper; +import org.teavm.model.util.ProgramUtils; + +/** + * Transforms loop in form: + * + * ``` + * while (true) { + * condition; + * body; + * } + * ``` + * + * to form: + * + * ``` + * if (condition) { + * while (true) { + * body; + * condition; // copy + * } + * } + * ``` + * + * where `condition` is a part of loop that has exits and `body` has no exits. + * More formally, we define *condition end* as a node that postdominates all loop exits. + * Therefore, *condition* is a set of nodes of the loop that are postdominated by condition end and + * all remaining nodes are *body*. + */ +class LoopInversionImpl { + private Program program; + private Graph cfg; + private DominatorTree pdom; + private boolean postponed; + + LoopInversionImpl(Program program) { + this.program = program; + } + + void apply() { + do { + cfg = ProgramUtils.buildControlFlowGraph(program); + LoopGraph loopGraph = new LoopGraph(cfg); + pdom = GraphUtils.buildDominatorTree(GraphUtils.invert(cfg)); + List loops = getLoopsWithExits(loopGraph); + + postponed = false; + for (LoopWithExits loop : loops) { + loop.invert(); + } + } while (postponed); + } + + private List getLoopsWithExits(LoopGraph cfg) { + Map loops = new HashMap<>(); + + for (int node = 0; node < cfg.size(); ++node) { + Loop loop = cfg.loopAt(node); + while (loop != null) { + LoopWithExits loopWithExits = getLoopWithExits(loops, loop); + loopWithExits.nodes.add(node); + for (int successor : cfg.outgoingEdges(node)) { + Loop successorLoop = cfg.loopAt(successor); + if (successorLoop == null || !successorLoop.isChildOf(loop)) { + loopWithExits.exits.add(node); + break; + } + } + loop = loop.getParent(); + } + } + + List resultList = new ArrayList<>(); + Set visited = new HashSet<>(); + for (LoopWithExits loop : loops.values()) { + sortLoops(loop, visited, resultList); + } + Collections.reverse(resultList); + + return resultList; + } + + private LoopWithExits getLoopWithExits(Map cache, Loop loop) { + return cache.computeIfAbsent(loop, key -> + new LoopWithExits(key.getHead(), key.getParent() != null + ? getLoopWithExits(cache, key.getParent()) + : null)); + } + + private void sortLoops(LoopWithExits loop, Set visited, List target) { + if (!visited.add(loop)) { + return; + } + if (loop.parent != null) { + sortLoops(loop.parent, visited, target); + } + target.add(loop); + } + + private class LoopWithExits { + final int head; + final LoopWithExits parent; + final IntSet nodes = new IntOpenHashSet(); + final IntSet nodesAndCopies = new IntOpenHashSet(); + final IntSet exits = new IntOpenHashSet(); + int conditionEnd; + int copyStart; + int headCopy; + final IntIntMap copiedVars = new IntIntOpenHashMap(); + final IntIntMap copiedNodes = new IntIntOpenHashMap(); + boolean shouldSkip; + + LoopWithExits(int head, LoopWithExits parent) { + this.head = head; + this.parent = parent; + } + + void invert() { + if (tryInvert()) { + LoopWithExits ancestor = parent; + while (ancestor != null) { + ancestor.shouldSkip = true; + ancestor = ancestor.parent; + } + } + } + + private boolean tryInvert() { + if (shouldSkip) { + postponed = true; + return false; + } + + findCondition(); + if (conditionEnd < 0 || !canInvert()) { + return false; + } + + collectNodesToCopy(); + collectVariablesToCopy(); + copyCondition(); + moveBackEdges(); + removeInternalPhiInputsFromCondition(); + removeExternalPhiInputsFromConditionCopy(); + putNewPhis(); + + return true; + } + + private void findCondition() { + IntSet endNodes = new IntOpenHashSet(program.basicBlockCount()); + for (int exit : exits.toArray()) { + for (int successor : cfg.outgoingEdges(exit)) { + if (nodes.contains(successor) && successor != head) { + endNodes.add(successor); + } + } + } + conditionEnd = pdom.commonDominatorOf(endNodes.toArray()); + } + + /** + * We can't invert loop if condition has back edges. Indeed, back edges from `if` statement + * must point inside loop, which makes CFG irreducible. + */ + private boolean canInvert() { + for (int node : nodes.toArray()) { + if (pdom.dominates(conditionEnd, node)) { + for (int successor : cfg.outgoingEdges(node)) { + if (successor == head) { + return false; + } + } + } + } + return true; + } + + private void collectNodesToCopy() { + int[] nodes = this.nodes.toArray(); + Arrays.sort(nodes); + for (int node : nodes) { + nodesAndCopies.add(node); + if (pdom.dominates(conditionEnd, node)) { + int copy = program.createBasicBlock().getIndex(); + if (head == node) { + headCopy = copy; + } + copiedNodes.put(node, copy); + nodesAndCopies.add(copy); + } + } + } + + private void collectVariablesToCopy() { + DefinitionExtractor definitionExtractor = new DefinitionExtractor(); + IntSet varsToCopy = new IntOpenHashSet(); + for (int node : copiedNodes.keys().toArray()) { + BasicBlock block = program.basicBlockAt(node); + for (Instruction insn : block.getInstructions()) { + insn.acceptVisitor(definitionExtractor); + for (Variable var : definitionExtractor.getDefinedVariables()) { + varsToCopy.add(var.getIndex()); + } + } + for (Phi phi : block.getPhis()) { + varsToCopy.add(phi.getReceiver().getIndex()); + } + } + + int[] orderedVarsToCopy = varsToCopy.toArray(); + Arrays.sort(orderedVarsToCopy); + for (int var : orderedVarsToCopy) { + copiedVars.put(var, program.createVariable().getIndex()); + } + } + + private void copyCondition() { + InstructionVariableMapper variableMapper = new InstructionVariableMapper() { + @Override + protected Variable map(Variable var) { + return program.variableAt(copiedVars.getOrDefault(var.getIndex(), var.getIndex())); + } + }; + BasicBlockMapper blockMapper = new BasicBlockMapper() { + @Override + protected BasicBlock map(BasicBlock block) { + return program.basicBlockAt(copiedNodes.getOrDefault(block.getIndex(), block.getIndex())); + } + }; + + InstructionCopyReader copier = new InstructionCopyReader(program); + for (int node : copiedNodes.keys().toArray()) { + BasicBlock sourceBlock = program.basicBlockAt(node); + BasicBlock targetBlock = program.basicBlockAt(copiedNodes.get(node)); + copier.resetLocation(); + for (int i = 0; i < sourceBlock.instructionCount(); ++i) { + sourceBlock.readInstruction(i, copier); + Instruction insn = copier.getCopy(); + insn.acceptVisitor(variableMapper); + insn.acceptVisitor(blockMapper); + targetBlock.getInstructions().add(insn); + } + + for (Phi phi : sourceBlock.getPhis()) { + Phi phiCopy = new Phi(); + int receiver = phi.getReceiver().getIndex(); + phiCopy.setReceiver(program.variableAt(copiedVars.getOrDefault(receiver, receiver))); + for (Incoming incoming : phi.getIncomings()) { + Incoming incomingCopy = new Incoming(); + int source = incoming.getSource().getIndex(); + int value = incoming.getValue().getIndex(); + incomingCopy.setSource(program.basicBlockAt(copiedNodes.getOrDefault(source, source))); + incomingCopy.setValue(program.variableAt(copiedVars.getOrDefault(value, value))); + phiCopy.getIncomings().add(incomingCopy); + } + targetBlock.getPhis().add(phi); + } + + for (TryCatchBlock tryCatch : sourceBlock.getTryCatchBlocks()) { + TryCatchBlock tryCatchCopy = new TryCatchBlock(); + int var = tryCatch.getExceptionVariable().getIndex(); + int handler = tryCatch.getHandler().getIndex(); + tryCatchCopy.setExceptionType(tryCatch.getExceptionType()); + tryCatchCopy.setExceptionVariable(program.variableAt(copiedVars.getOrDefault(var, var))); + tryCatchCopy.setHandler(program.basicBlockAt(copiedNodes.getOrDefault(handler, handler))); + targetBlock.getTryCatchBlocks().add(tryCatchCopy); + } + } + } + + /** + * Back edges from body are not back edges anymore, instead they point to a copied condition. + */ + private void moveBackEdges() { + BasicBlockMapper mapper = new BasicBlockMapper() { + @Override + protected BasicBlock map(BasicBlock block) { + return block.getIndex() == head ? program.basicBlockAt(headCopy) : block; + } + }; + + for (int node : nodes.toArray()) { + BasicBlock block = program.basicBlockAt(node); + Instruction last = block.getLastInstruction(); + if (last != null) { + last.acceptVisitor(mapper); + } + } + } + + /** + * Original head becomes start of `if (condition)`, it's not loop head anymore. + * Hence we don't need phi inputs that come from back edges. + */ + private void removeInternalPhiInputsFromCondition() { + BasicBlock block = program.basicBlockAt(head); + for (Phi phi : block.getPhis()) { + List incomings = phi.getIncomings(); + for (int i = 0; i < incomings.size(); ++i) { + Incoming incoming = incomings.get(i); + if (nodes.contains(incoming.getSource().getIndex())) { + incomings.remove(i--); + } + } + } + } + + /** + * Head copy is not a loop head anymore and there aren't transition from outside of former loop, + * therefore delete all external phi inputs. + */ + private void removeExternalPhiInputsFromConditionCopy() { + BasicBlock block = program.basicBlockAt(headCopy); + for (Phi phi : block.getPhis()) { + List incomings = phi.getIncomings(); + for (int i = 0; i < incomings.size(); ++i) { + Incoming incoming = incomings.get(i); + if (!nodesAndCopies.contains(incoming.getSource().getIndex())) { + incomings.remove(i--); + } + } + } + } + + /** + * Variables defined in condition should be converted to phis in a new loop head (i.e. condition end). + * Every reference to variable from old condition must be replaced by reference to corresponding phi. + */ + private void putNewPhis() { + BasicBlock head = program.basicBlockAt(conditionEnd); + IntIntMap phiMap = new IntIntOpenHashMap(); + + int[] vars = copiedVars.keys().toArray(); + Arrays.sort(vars); + for (int var : vars) { + Phi phi = new Phi(); + phi.setReceiver(program.createVariable()); + phiMap.put(var, phi.getReceiver().getIndex()); + head.getPhis().add(phi); + + for (int source : cfg.incomingEdges(conditionEnd)) { + int inputVar = copiedNodes.containsKey(source) ? var : copiedVars.get(var); + Incoming incoming = new Incoming(); + incoming.setValue(program.variableAt(inputVar)); + incoming.setSource(program.basicBlockAt(source)); + phi.getIncomings().add(incoming); + } + } + + InstructionVariableMapper mapper = new InstructionVariableMapper() { + @Override + protected Variable map(Variable var) { + int index = var.getIndex(); + return program.variableAt(phiMap.getOrDefault(index, index)); + } + }; + for (int node : nodes.toArray()) { + if (copiedNodes.containsKey(node)) { + BasicBlock block = program.basicBlockAt(node); + for (Instruction instruction : block.getInstructions()) { + instruction.acceptVisitor(mapper); + } + } + } + } + } +} diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 502b73fcd..5c0a5ed80 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -649,7 +649,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } private List getOptimizations() { - return Arrays.asList(new ArrayUnwrapMotion(), new LoopInvariantMotion(), + return Arrays.asList(new ArrayUnwrapMotion(), new LoopInversion(), new LoopInvariantMotion(), new GlobalValueNumbering(), new UnusedVariableElimination()); }