mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-27 01:30:35 +08:00
Implementing loop inversion
This commit is contained in:
parent
a57be365b4
commit
3c93b78902
@ -240,8 +240,7 @@ public final class GraphUtils {
|
||||
int[] set = new int[items.length];
|
||||
int sz = 0;
|
||||
int last = -1;
|
||||
for (int i = 0; i < items.length; ++i) {
|
||||
int item = items[i];
|
||||
for (int item : items) {
|
||||
if (item != last) {
|
||||
set[sz++] = item;
|
||||
last = item;
|
||||
@ -252,14 +251,4 @@ 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();
|
||||
}
|
||||
}
|
||||
|
@ -22,14 +22,14 @@ import java.util.*;
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class MutableGraphNode {
|
||||
int tag;
|
||||
Map<MutableGraphNode, MutableGraphEdge> edges = new HashMap<>();
|
||||
private int tag;
|
||||
final Map<MutableGraphNode, MutableGraphEdge> edges = new LinkedHashMap<>();
|
||||
|
||||
public MutableGraphNode(int tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public MutableGraphEdge connect(MutableGraphNode other) {
|
||||
private MutableGraphEdge connect(MutableGraphNode other) {
|
||||
MutableGraphEdge edge = edges.get(other);
|
||||
if (edge == null) {
|
||||
edge = new MutableGraphEdge();
|
||||
|
@ -15,16 +15,43 @@
|
||||
*/
|
||||
package org.teavm.model.util;
|
||||
|
||||
import java.util.function.Function;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.Incoming;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.Phi;
|
||||
import org.teavm.model.TryCatchBlock;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public abstract class InstructionVariableMapper implements InstructionVisitor {
|
||||
protected abstract Variable map(Variable var);
|
||||
public class InstructionVariableMapper implements InstructionVisitor {
|
||||
private final Function<Variable, Variable> f;
|
||||
|
||||
public InstructionVariableMapper(Function<Variable, Variable> f) {
|
||||
this.f = f;
|
||||
}
|
||||
|
||||
public void apply(BasicBlock block) {
|
||||
for (Instruction insn : block.getInstructions()) {
|
||||
insn.acceptVisitor(this);
|
||||
}
|
||||
for (Phi phi : block.getPhis()) {
|
||||
phi.setReceiver(map(phi.getReceiver()));
|
||||
for (Incoming incoming : phi.getIncomings()) {
|
||||
incoming.setValue(map(incoming.getValue()));
|
||||
}
|
||||
}
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
if (tryCatch.getExceptionVariable() != null) {
|
||||
tryCatch.setExceptionVariable(map(tryCatch.getExceptionVariable()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Variable map(Variable var) {
|
||||
return f.apply(var);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(EmptyInstruction insn) {
|
||||
@ -252,8 +279,4 @@ public abstract class InstructionVariableMapper implements InstructionVisitor {
|
||||
public void visit(MonitorExitInstruction insn) {
|
||||
insn.setObjectRef(map(insn.getObjectRef()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -218,26 +218,11 @@ public class RegisterAllocator {
|
||||
}
|
||||
|
||||
private void renameVariables(final Program program, final int[] varMap) {
|
||||
InstructionVariableMapper mapper = new InstructionVariableMapper() {
|
||||
@Override protected Variable map(Variable var) {
|
||||
return program.variableAt(varMap[var.getIndex()]);
|
||||
}
|
||||
};
|
||||
InstructionVariableMapper mapper = new InstructionVariableMapper(var ->
|
||||
program.variableAt(varMap[var.getIndex()]));
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
for (Instruction insn : block.getInstructions()) {
|
||||
insn.acceptVisitor(mapper);
|
||||
}
|
||||
for (Phi phi : block.getPhis()) {
|
||||
phi.setReceiver(program.variableAt(varMap[phi.getReceiver().getIndex()]));
|
||||
for (Incoming incoming : phi.getIncomings()) {
|
||||
incoming.setValue(program.variableAt(varMap[incoming.getValue().getIndex()]));
|
||||
}
|
||||
}
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
tryCatch.setExceptionVariable(program.variableAt(
|
||||
varMap[tryCatch.getExceptionVariable().getIndex()]));
|
||||
}
|
||||
mapper.apply(block);
|
||||
}
|
||||
String[][] originalNames = new String[program.variableCount()][];
|
||||
for (int i = 0; i < program.variableCount(); ++i) {
|
||||
|
@ -29,7 +29,6 @@ import org.teavm.model.util.*;
|
||||
*/
|
||||
public class LoopInvariantMotion implements MethodOptimization {
|
||||
private int[] preheaders;
|
||||
private Instruction[] constantInstructions;
|
||||
private LoopGraph graph;
|
||||
private DominatorTree dom;
|
||||
private Program program;
|
||||
@ -45,7 +44,7 @@ public class LoopInvariantMotion implements MethodOptimization {
|
||||
IntegerStack stack = new IntegerStack(graph.size());
|
||||
int[] defLocation = new int[program.variableCount()];
|
||||
Arrays.fill(defLocation, -1);
|
||||
constantInstructions = new Instruction[program.variableCount()];
|
||||
Instruction[] constantInstructions = new Instruction[program.variableCount()];
|
||||
for (int i = 0; i <= method.parameterCount(); ++i) {
|
||||
defLocation[i] = 0;
|
||||
}
|
||||
@ -126,7 +125,8 @@ public class LoopInvariantMotion implements MethodOptimization {
|
||||
}
|
||||
}
|
||||
if (variableMap != null) {
|
||||
insn.acceptVisitor(new VariableMapperImpl(variableMap));
|
||||
Variable[] currentVariableMap = variableMap;
|
||||
insn.acceptVisitor(new InstructionVariableMapper(var -> currentVariableMap[var.getIndex()]));
|
||||
}
|
||||
newInstructions.add(insn);
|
||||
preheaderInstructions.addAll(preheaderInstructions.size() - 1, newInstructions);
|
||||
@ -208,21 +208,8 @@ public class LoopInvariantMotion implements MethodOptimization {
|
||||
return preheader.getIndex();
|
||||
}
|
||||
|
||||
private static class VariableMapperImpl extends InstructionVariableMapper {
|
||||
private Variable[] map;
|
||||
|
||||
public VariableMapperImpl(Variable[] map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Variable map(Variable var) {
|
||||
return map[var.getIndex()];
|
||||
}
|
||||
}
|
||||
|
||||
private static class InstructionAnalyzer implements InstructionVisitor {
|
||||
public boolean canMove;
|
||||
boolean canMove;
|
||||
public boolean constant;
|
||||
|
||||
@Override
|
||||
|
@ -67,14 +67,18 @@ import org.teavm.model.util.ProgramUtils;
|
||||
* ```
|
||||
*
|
||||
* 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*.
|
||||
* More formally, we define *body start candidate* as a node which 1) dominates all of the "tails" (i.e. nodes
|
||||
* that have edges to loop header), 2) does not dominate loop exits. *Body start* is a body start candidate
|
||||
* that is not dominates by some other body start candidate. If body start does not exits, loop is
|
||||
* not inversible.
|
||||
*
|
||||
* Therefore, *body* is a set of nodes of the loop that are dominated by body start and
|
||||
* all remaining nodes are *condition*.
|
||||
*/
|
||||
class LoopInversionImpl {
|
||||
private Program program;
|
||||
private final Program program;
|
||||
private Graph cfg;
|
||||
private DominatorTree pdom;
|
||||
private DominatorTree dom;
|
||||
private boolean postponed;
|
||||
|
||||
LoopInversionImpl(Program program) {
|
||||
@ -85,7 +89,7 @@ class LoopInversionImpl {
|
||||
do {
|
||||
cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||
LoopGraph loopGraph = new LoopGraph(cfg);
|
||||
pdom = GraphUtils.buildDominatorTree(GraphUtils.invert(cfg));
|
||||
dom = GraphUtils.buildDominatorTree(cfg);
|
||||
List<LoopWithExits> loops = getLoopsWithExits(loopGraph);
|
||||
|
||||
postponed = false;
|
||||
@ -147,11 +151,12 @@ class LoopInversionImpl {
|
||||
final IntSet nodes = new IntOpenHashSet();
|
||||
final IntSet nodesAndCopies = new IntOpenHashSet();
|
||||
final IntSet exits = new IntOpenHashSet();
|
||||
int conditionEnd;
|
||||
int bodyStart;
|
||||
int copyStart;
|
||||
int headCopy;
|
||||
final IntIntMap copiedVars = new IntIntOpenHashMap();
|
||||
final IntIntMap copiedNodes = new IntIntOpenHashMap();
|
||||
final IntIntMap varDefinitionPoints = new IntIntOpenHashMap();
|
||||
boolean shouldSkip;
|
||||
|
||||
LoopWithExits(int head, LoopWithExits parent) {
|
||||
@ -175,8 +180,7 @@ class LoopInversionImpl {
|
||||
return false;
|
||||
}
|
||||
|
||||
findCondition();
|
||||
if (conditionEnd < 0 || !canInvert()) {
|
||||
if (!findCondition() || bodyStart < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -187,37 +191,31 @@ class LoopInversionImpl {
|
||||
removeInternalPhiInputsFromCondition();
|
||||
removeExternalPhiInputsFromConditionCopy();
|
||||
putNewPhis();
|
||||
adjustOutputPhis();
|
||||
|
||||
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);
|
||||
}
|
||||
private boolean findCondition() {
|
||||
IntSet tailNodes = new IntOpenHashSet(program.basicBlockCount());
|
||||
for (int tailCandidate : cfg.incomingEdges(head)) {
|
||||
if (nodes.contains(tailCandidate)) {
|
||||
tailNodes.add(tailCandidate);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
bodyStart = dom.commonDominatorOf(tailNodes.toArray());
|
||||
int candidate = bodyStart;
|
||||
while (bodyStart != head) {
|
||||
int currentCandidate = candidate;
|
||||
if (Arrays.stream(exits.toArray()).anyMatch(exit -> dom.dominates(currentCandidate, exit))) {
|
||||
break;
|
||||
}
|
||||
bodyStart = candidate;
|
||||
candidate = dom.immediateDominatorOf(candidate);
|
||||
}
|
||||
return true;
|
||||
|
||||
return candidate != bodyStart;
|
||||
}
|
||||
|
||||
private void collectNodesToCopy() {
|
||||
@ -225,7 +223,7 @@ class LoopInversionImpl {
|
||||
Arrays.sort(nodes);
|
||||
for (int node : nodes) {
|
||||
nodesAndCopies.add(node);
|
||||
if (pdom.dominates(conditionEnd, node)) {
|
||||
if (node == head || (node != bodyStart && !dom.dominates(bodyStart, node))) {
|
||||
int copy = program.createBasicBlock().getIndex();
|
||||
if (head == node) {
|
||||
headCopy = copy;
|
||||
@ -245,10 +243,12 @@ class LoopInversionImpl {
|
||||
insn.acceptVisitor(definitionExtractor);
|
||||
for (Variable var : definitionExtractor.getDefinedVariables()) {
|
||||
varsToCopy.add(var.getIndex());
|
||||
varDefinitionPoints.put(var.getIndex(), node);
|
||||
}
|
||||
}
|
||||
for (Phi phi : block.getPhis()) {
|
||||
varsToCopy.add(phi.getReceiver().getIndex());
|
||||
varDefinitionPoints.put(phi.getReceiver().getIndex(), node);
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,12 +260,8 @@ class LoopInversionImpl {
|
||||
}
|
||||
|
||||
private void copyCondition() {
|
||||
InstructionVariableMapper variableMapper = new InstructionVariableMapper() {
|
||||
@Override
|
||||
protected Variable map(Variable var) {
|
||||
return program.variableAt(copiedVars.getOrDefault(var.getIndex(), var.getIndex()));
|
||||
}
|
||||
};
|
||||
InstructionVariableMapper variableMapper = new InstructionVariableMapper(var ->
|
||||
program.variableAt(copiedVars.getOrDefault(var.getIndex(), var.getIndex())));
|
||||
BasicBlockMapper blockMapper = new BasicBlockMapper() {
|
||||
@Override
|
||||
protected BasicBlock map(BasicBlock block) {
|
||||
@ -298,7 +294,7 @@ class LoopInversionImpl {
|
||||
incomingCopy.setValue(program.variableAt(copiedVars.getOrDefault(value, value)));
|
||||
phiCopy.getIncomings().add(incomingCopy);
|
||||
}
|
||||
targetBlock.getPhis().add(phi);
|
||||
targetBlock.getPhis().add(phiCopy);
|
||||
}
|
||||
|
||||
for (TryCatchBlock tryCatch : sourceBlock.getTryCatchBlocks()) {
|
||||
@ -358,55 +354,137 @@ class LoopInversionImpl {
|
||||
BasicBlock block = program.basicBlockAt(headCopy);
|
||||
for (Phi phi : block.getPhis()) {
|
||||
List<Incoming> 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--);
|
||||
List<Incoming> newIncomings = new ArrayList<>(incomings.size());
|
||||
for (Incoming incoming : incomings) {
|
||||
if (nodesAndCopies.contains(incoming.getSource().getIndex())) {
|
||||
newIncomings.add(incoming);
|
||||
} else {
|
||||
for (int exit : exits.toArray()) {
|
||||
Incoming newIncoming = new Incoming();
|
||||
newIncoming.setValue(incoming.getValue());
|
||||
newIncoming.setSource(program.basicBlockAt(exit));
|
||||
newIncomings.add(newIncoming);
|
||||
}
|
||||
}
|
||||
}
|
||||
incomings.clear();
|
||||
incomings.addAll(newIncomings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables defined in condition should be converted to phis in a new loop head (i.e. condition end).
|
||||
* Variables defined in condition should be converted to phis in a new loop head (i.e. body start).
|
||||
* Every reference to variable from old condition must be replaced by reference to corresponding phi.
|
||||
*/
|
||||
private void putNewPhis() {
|
||||
BasicBlock head = program.basicBlockAt(conditionEnd);
|
||||
BasicBlock head = program.basicBlockAt(bodyStart);
|
||||
IntIntMap phiMap = new IntIntOpenHashMap();
|
||||
|
||||
int[] vars = copiedVars.keys().toArray();
|
||||
Arrays.sort(vars);
|
||||
List<Phi> phisToAdd = new ArrayList<>();
|
||||
for (int var : vars) {
|
||||
int varCopy = copiedVars.get(var);
|
||||
|
||||
Phi phi = new Phi();
|
||||
phi.setReceiver(program.createVariable());
|
||||
phiMap.put(var, phi.getReceiver().getIndex());
|
||||
head.getPhis().add(phi);
|
||||
phisToAdd.add(phi);
|
||||
|
||||
for (int source : cfg.incomingEdges(bodyStart)) {
|
||||
if (!nodes.contains(source)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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.setValue(program.variableAt(var));
|
||||
incoming.setSource(program.basicBlockAt(source));
|
||||
phi.getIncomings().add(incoming);
|
||||
|
||||
incoming = new Incoming();
|
||||
incoming.setValue(program.variableAt(varCopy));
|
||||
incoming.setSource(program.basicBlockAt(copiedNodes.get(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));
|
||||
}
|
||||
};
|
||||
InstructionVariableMapper mapper = new InstructionVariableMapper(var -> {
|
||||
int index = var.getIndex();
|
||||
return program.variableAt(phiMap.getOrDefault(index, index));
|
||||
});
|
||||
for (int node : nodes.toArray()) {
|
||||
if (copiedNodes.containsKey(node)) {
|
||||
if (!copiedNodes.containsKey(node)) {
|
||||
BasicBlock block = program.basicBlockAt(node);
|
||||
for (Instruction instruction : block.getInstructions()) {
|
||||
instruction.acceptVisitor(mapper);
|
||||
mapper.apply(block);
|
||||
}
|
||||
}
|
||||
|
||||
head.getPhis().addAll(phisToAdd);
|
||||
}
|
||||
|
||||
private void adjustOutputPhis() {
|
||||
IntIntMap phiMap = new IntIntOpenHashMap();
|
||||
class PhiToAdd {
|
||||
private final Phi phi;
|
||||
private final BasicBlock target;
|
||||
private PhiToAdd(Phi phi, BasicBlock target) {
|
||||
this.phi = phi;
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
List<PhiToAdd> phis = new ArrayList<>();
|
||||
|
||||
int[] vars = copiedVars.keys().toArray();
|
||||
Arrays.sort(vars);
|
||||
int[] exits = this.exits.toArray();
|
||||
Arrays.sort(exits);
|
||||
|
||||
for (int exit : exits) {
|
||||
for (int var : vars) {
|
||||
int definedAt = varDefinitionPoints.get(var);
|
||||
if (!dom.dominates(definedAt, exit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int varCopy = copiedVars.get(var);
|
||||
int copiedAt = copiedNodes.get(definedAt);
|
||||
for (int successor : cfg.outgoingEdges(exit)) {
|
||||
if (nodes.contains(successor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Phi phi = new Phi();
|
||||
phi.setReceiver(program.createVariable());
|
||||
|
||||
Incoming originalInput = new Incoming();
|
||||
originalInput.setSource(program.basicBlockAt(definedAt));
|
||||
originalInput.setValue(program.variableAt(var));
|
||||
phi.getIncomings().add(originalInput);
|
||||
|
||||
Incoming copyInput = new Incoming();
|
||||
copyInput.setSource(program.basicBlockAt(copiedAt));
|
||||
copyInput.setValue(program.variableAt(varCopy));
|
||||
phi.getIncomings().add(copyInput);
|
||||
|
||||
phis.add(new PhiToAdd(phi, program.basicBlockAt(successor)));
|
||||
phiMap.put(var, phi.getReceiver().getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InstructionVariableMapper mapper = new InstructionVariableMapper(var -> {
|
||||
int index = var.getIndex();
|
||||
return program.variableAt(phiMap.getOrDefault(index, index));
|
||||
});
|
||||
for (int i = 0; i < cfg.size(); ++i) {
|
||||
if (!nodes.contains(i)) {
|
||||
mapper.apply(program.basicBlockAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (PhiToAdd phiToAdd : phis) {
|
||||
phiToAdd.target.getPhis().add(phiToAdd.phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,16 +54,13 @@ import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.Incoming;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.InstructionLocation;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Phi;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.TryCatchBlock;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
@ -82,15 +79,15 @@ import org.teavm.model.util.ProgramUtils;
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class JSClassProcessor {
|
||||
private ClassReaderSource classSource;
|
||||
private JSBodyRepository repository;
|
||||
private JavaInvocationProcessor javaInvocationProcessor;
|
||||
private final ClassReaderSource classSource;
|
||||
private final JSBodyRepository repository;
|
||||
private final JavaInvocationProcessor javaInvocationProcessor;
|
||||
private Program program;
|
||||
private List<Instruction> replacement = new ArrayList<>();
|
||||
private JSTypeHelper typeHelper;
|
||||
private Diagnostics diagnostics;
|
||||
private final List<Instruction> replacement = new ArrayList<>();
|
||||
private final JSTypeHelper typeHelper;
|
||||
private final Diagnostics diagnostics;
|
||||
private int methodIndexGenerator;
|
||||
private Map<MethodReference, MethodReader> overridenMethodCache = new HashMap<>();
|
||||
private final Map<MethodReference, MethodReader> overridenMethodCache = new HashMap<>();
|
||||
|
||||
public JSClassProcessor(ClassReaderSource classSource, JSBodyRepository repository, Diagnostics diagnostics) {
|
||||
this.classSource = classSource;
|
||||
@ -170,32 +167,15 @@ class JSClassProcessor {
|
||||
callerMethod.getModifiers().add(ElementModifier.STATIC);
|
||||
final Program program = ProgramUtils.copy(method.getProgram());
|
||||
program.createVariable();
|
||||
InstructionVariableMapper variableMapper = new InstructionVariableMapper() {
|
||||
@Override protected Variable map(Variable var) {
|
||||
return program.variableAt(var.getIndex() + 1);
|
||||
}
|
||||
};
|
||||
InstructionVariableMapper variableMapper = new InstructionVariableMapper(var ->
|
||||
program.variableAt(var.getIndex() + 1));
|
||||
for (int i = program.variableCount() - 1; i > 0; --i) {
|
||||
program.variableAt(i).getDebugNames().addAll(program.variableAt(i - 1).getDebugNames());
|
||||
program.variableAt(i - 1).getDebugNames().clear();
|
||||
}
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
for (Instruction insn : block.getInstructions()) {
|
||||
insn.acceptVisitor(variableMapper);
|
||||
}
|
||||
for (Phi phi : block.getPhis()) {
|
||||
phi.setReceiver(program.variableAt(phi.getReceiver().getIndex() + 1));
|
||||
for (Incoming incoming : phi.getIncomings()) {
|
||||
incoming.setValue(program.variableAt(incoming.getValue().getIndex() + 1));
|
||||
}
|
||||
}
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
if (tryCatch.getExceptionVariable() != null) {
|
||||
tryCatch.setExceptionVariable(program.variableAt(
|
||||
tryCatch.getExceptionVariable().getIndex() + 1));
|
||||
}
|
||||
}
|
||||
variableMapper.apply(block);
|
||||
}
|
||||
callerMethod.setProgram(program);
|
||||
ModelUtils.copyAnnotations(method.getAnnotations(), callerMethod.getAnnotations());
|
||||
@ -277,7 +257,7 @@ class JSClassProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
static ValueType[] getStaticSignature(MethodReference method) {
|
||||
private static ValueType[] getStaticSignature(MethodReference method) {
|
||||
ValueType[] signature = method.getSignature();
|
||||
ValueType[] staticSignature = new ValueType[signature.length + 1];
|
||||
for (int i = 0; i < signature.length; ++i) {
|
||||
@ -287,7 +267,7 @@ class JSClassProcessor {
|
||||
return staticSignature;
|
||||
}
|
||||
|
||||
public void processProgram(MethodHolder methodToProcess) {
|
||||
void processProgram(MethodHolder methodToProcess) {
|
||||
program = methodToProcess.getProgram();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.optimizations;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class LoopInversionTest {
|
||||
@Test
|
||||
public void respectsLoopOutput() {
|
||||
int a = 0;
|
||||
int b = 1;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
int c = a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
assertEquals(55, a);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user