mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -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[] set = new int[items.length];
|
||||||
int sz = 0;
|
int sz = 0;
|
||||||
int last = -1;
|
int last = -1;
|
||||||
for (int i = 0; i < items.length; ++i) {
|
for (int item : items) {
|
||||||
int item = items[i];
|
|
||||||
if (item != last) {
|
if (item != last) {
|
||||||
set[sz++] = item;
|
set[sz++] = item;
|
||||||
last = item;
|
last = item;
|
||||||
|
@ -252,14 +251,4 @@ public final class GraphUtils {
|
||||||
}
|
}
|
||||||
return set;
|
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
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
public class MutableGraphNode {
|
public class MutableGraphNode {
|
||||||
int tag;
|
private int tag;
|
||||||
Map<MutableGraphNode, MutableGraphEdge> edges = new HashMap<>();
|
final Map<MutableGraphNode, MutableGraphEdge> edges = new LinkedHashMap<>();
|
||||||
|
|
||||||
public MutableGraphNode(int tag) {
|
public MutableGraphNode(int tag) {
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableGraphEdge connect(MutableGraphNode other) {
|
private MutableGraphEdge connect(MutableGraphNode other) {
|
||||||
MutableGraphEdge edge = edges.get(other);
|
MutableGraphEdge edge = edges.get(other);
|
||||||
if (edge == null) {
|
if (edge == null) {
|
||||||
edge = new MutableGraphEdge();
|
edge = new MutableGraphEdge();
|
||||||
|
|
|
@ -15,16 +15,43 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model.util;
|
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.InvokeDynamicInstruction;
|
||||||
|
import org.teavm.model.Phi;
|
||||||
|
import org.teavm.model.TryCatchBlock;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.*;
|
import org.teavm.model.instructions.*;
|
||||||
|
|
||||||
/**
|
public class InstructionVariableMapper implements InstructionVisitor {
|
||||||
*
|
private final Function<Variable, Variable> f;
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
public InstructionVariableMapper(Function<Variable, Variable> f) {
|
||||||
public abstract class InstructionVariableMapper implements InstructionVisitor {
|
this.f = f;
|
||||||
protected abstract Variable map(Variable var);
|
}
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
public void visit(EmptyInstruction insn) {
|
public void visit(EmptyInstruction insn) {
|
||||||
|
@ -252,8 +279,4 @@ public abstract class InstructionVariableMapper implements InstructionVisitor {
|
||||||
public void visit(MonitorExitInstruction insn) {
|
public void visit(MonitorExitInstruction insn) {
|
||||||
insn.setObjectRef(map(insn.getObjectRef()));
|
insn.setObjectRef(map(insn.getObjectRef()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,26 +218,11 @@ public class RegisterAllocator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renameVariables(final Program program, final int[] varMap) {
|
private void renameVariables(final Program program, final int[] varMap) {
|
||||||
InstructionVariableMapper mapper = new InstructionVariableMapper() {
|
InstructionVariableMapper mapper = new InstructionVariableMapper(var ->
|
||||||
@Override protected Variable map(Variable var) {
|
program.variableAt(varMap[var.getIndex()]));
|
||||||
return program.variableAt(varMap[var.getIndex()]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
for (Instruction insn : block.getInstructions()) {
|
mapper.apply(block);
|
||||||
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()]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
String[][] originalNames = new String[program.variableCount()][];
|
String[][] originalNames = new String[program.variableCount()][];
|
||||||
for (int i = 0; i < program.variableCount(); ++i) {
|
for (int i = 0; i < program.variableCount(); ++i) {
|
||||||
|
|
|
@ -29,7 +29,6 @@ import org.teavm.model.util.*;
|
||||||
*/
|
*/
|
||||||
public class LoopInvariantMotion implements MethodOptimization {
|
public class LoopInvariantMotion implements MethodOptimization {
|
||||||
private int[] preheaders;
|
private int[] preheaders;
|
||||||
private Instruction[] constantInstructions;
|
|
||||||
private LoopGraph graph;
|
private LoopGraph graph;
|
||||||
private DominatorTree dom;
|
private DominatorTree dom;
|
||||||
private Program program;
|
private Program program;
|
||||||
|
@ -45,7 +44,7 @@ public class LoopInvariantMotion implements MethodOptimization {
|
||||||
IntegerStack stack = new IntegerStack(graph.size());
|
IntegerStack stack = new IntegerStack(graph.size());
|
||||||
int[] defLocation = new int[program.variableCount()];
|
int[] defLocation = new int[program.variableCount()];
|
||||||
Arrays.fill(defLocation, -1);
|
Arrays.fill(defLocation, -1);
|
||||||
constantInstructions = new Instruction[program.variableCount()];
|
Instruction[] constantInstructions = new Instruction[program.variableCount()];
|
||||||
for (int i = 0; i <= method.parameterCount(); ++i) {
|
for (int i = 0; i <= method.parameterCount(); ++i) {
|
||||||
defLocation[i] = 0;
|
defLocation[i] = 0;
|
||||||
}
|
}
|
||||||
|
@ -126,7 +125,8 @@ public class LoopInvariantMotion implements MethodOptimization {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (variableMap != null) {
|
if (variableMap != null) {
|
||||||
insn.acceptVisitor(new VariableMapperImpl(variableMap));
|
Variable[] currentVariableMap = variableMap;
|
||||||
|
insn.acceptVisitor(new InstructionVariableMapper(var -> currentVariableMap[var.getIndex()]));
|
||||||
}
|
}
|
||||||
newInstructions.add(insn);
|
newInstructions.add(insn);
|
||||||
preheaderInstructions.addAll(preheaderInstructions.size() - 1, newInstructions);
|
preheaderInstructions.addAll(preheaderInstructions.size() - 1, newInstructions);
|
||||||
|
@ -208,21 +208,8 @@ public class LoopInvariantMotion implements MethodOptimization {
|
||||||
return preheader.getIndex();
|
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 {
|
private static class InstructionAnalyzer implements InstructionVisitor {
|
||||||
public boolean canMove;
|
boolean canMove;
|
||||||
public boolean constant;
|
public boolean constant;
|
||||||
|
|
||||||
@Override
|
@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.
|
* 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.
|
* More formally, we define *body start candidate* as a node which 1) dominates all of the "tails" (i.e. nodes
|
||||||
* Therefore, *condition* is a set of nodes of the loop that are postdominated by condition end and
|
* that have edges to loop header), 2) does not dominate loop exits. *Body start* is a body start candidate
|
||||||
* all remaining nodes are *body*.
|
* 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 {
|
class LoopInversionImpl {
|
||||||
private Program program;
|
private final Program program;
|
||||||
private Graph cfg;
|
private Graph cfg;
|
||||||
private DominatorTree pdom;
|
private DominatorTree dom;
|
||||||
private boolean postponed;
|
private boolean postponed;
|
||||||
|
|
||||||
LoopInversionImpl(Program program) {
|
LoopInversionImpl(Program program) {
|
||||||
|
@ -85,7 +89,7 @@ class LoopInversionImpl {
|
||||||
do {
|
do {
|
||||||
cfg = ProgramUtils.buildControlFlowGraph(program);
|
cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||||
LoopGraph loopGraph = new LoopGraph(cfg);
|
LoopGraph loopGraph = new LoopGraph(cfg);
|
||||||
pdom = GraphUtils.buildDominatorTree(GraphUtils.invert(cfg));
|
dom = GraphUtils.buildDominatorTree(cfg);
|
||||||
List<LoopWithExits> loops = getLoopsWithExits(loopGraph);
|
List<LoopWithExits> loops = getLoopsWithExits(loopGraph);
|
||||||
|
|
||||||
postponed = false;
|
postponed = false;
|
||||||
|
@ -147,11 +151,12 @@ class LoopInversionImpl {
|
||||||
final IntSet nodes = new IntOpenHashSet();
|
final IntSet nodes = new IntOpenHashSet();
|
||||||
final IntSet nodesAndCopies = new IntOpenHashSet();
|
final IntSet nodesAndCopies = new IntOpenHashSet();
|
||||||
final IntSet exits = new IntOpenHashSet();
|
final IntSet exits = new IntOpenHashSet();
|
||||||
int conditionEnd;
|
int bodyStart;
|
||||||
int copyStart;
|
int copyStart;
|
||||||
int headCopy;
|
int headCopy;
|
||||||
final IntIntMap copiedVars = new IntIntOpenHashMap();
|
final IntIntMap copiedVars = new IntIntOpenHashMap();
|
||||||
final IntIntMap copiedNodes = new IntIntOpenHashMap();
|
final IntIntMap copiedNodes = new IntIntOpenHashMap();
|
||||||
|
final IntIntMap varDefinitionPoints = new IntIntOpenHashMap();
|
||||||
boolean shouldSkip;
|
boolean shouldSkip;
|
||||||
|
|
||||||
LoopWithExits(int head, LoopWithExits parent) {
|
LoopWithExits(int head, LoopWithExits parent) {
|
||||||
|
@ -175,8 +180,7 @@ class LoopInversionImpl {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
findCondition();
|
if (!findCondition() || bodyStart < 0) {
|
||||||
if (conditionEnd < 0 || !canInvert()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,37 +191,31 @@ class LoopInversionImpl {
|
||||||
removeInternalPhiInputsFromCondition();
|
removeInternalPhiInputsFromCondition();
|
||||||
removeExternalPhiInputsFromConditionCopy();
|
removeExternalPhiInputsFromConditionCopy();
|
||||||
putNewPhis();
|
putNewPhis();
|
||||||
|
adjustOutputPhis();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findCondition() {
|
private boolean findCondition() {
|
||||||
IntSet endNodes = new IntOpenHashSet(program.basicBlockCount());
|
IntSet tailNodes = new IntOpenHashSet(program.basicBlockCount());
|
||||||
for (int exit : exits.toArray()) {
|
for (int tailCandidate : cfg.incomingEdges(head)) {
|
||||||
for (int successor : cfg.outgoingEdges(exit)) {
|
if (nodes.contains(tailCandidate)) {
|
||||||
if (nodes.contains(successor) && successor != head) {
|
tailNodes.add(tailCandidate);
|
||||||
endNodes.add(successor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
conditionEnd = pdom.commonDominatorOf(endNodes.toArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
bodyStart = dom.commonDominatorOf(tailNodes.toArray());
|
||||||
* We can't invert loop if condition has back edges. Indeed, back edges from `if` statement
|
int candidate = bodyStart;
|
||||||
* must point inside loop, which makes CFG irreducible.
|
while (bodyStart != head) {
|
||||||
*/
|
int currentCandidate = candidate;
|
||||||
private boolean canInvert() {
|
if (Arrays.stream(exits.toArray()).anyMatch(exit -> dom.dominates(currentCandidate, exit))) {
|
||||||
for (int node : nodes.toArray()) {
|
break;
|
||||||
if (pdom.dominates(conditionEnd, node)) {
|
|
||||||
for (int successor : cfg.outgoingEdges(node)) {
|
|
||||||
if (successor == head) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
bodyStart = candidate;
|
||||||
|
candidate = dom.immediateDominatorOf(candidate);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
return candidate != bodyStart;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void collectNodesToCopy() {
|
private void collectNodesToCopy() {
|
||||||
|
@ -225,7 +223,7 @@ class LoopInversionImpl {
|
||||||
Arrays.sort(nodes);
|
Arrays.sort(nodes);
|
||||||
for (int node : nodes) {
|
for (int node : nodes) {
|
||||||
nodesAndCopies.add(node);
|
nodesAndCopies.add(node);
|
||||||
if (pdom.dominates(conditionEnd, node)) {
|
if (node == head || (node != bodyStart && !dom.dominates(bodyStart, node))) {
|
||||||
int copy = program.createBasicBlock().getIndex();
|
int copy = program.createBasicBlock().getIndex();
|
||||||
if (head == node) {
|
if (head == node) {
|
||||||
headCopy = copy;
|
headCopy = copy;
|
||||||
|
@ -245,10 +243,12 @@ class LoopInversionImpl {
|
||||||
insn.acceptVisitor(definitionExtractor);
|
insn.acceptVisitor(definitionExtractor);
|
||||||
for (Variable var : definitionExtractor.getDefinedVariables()) {
|
for (Variable var : definitionExtractor.getDefinedVariables()) {
|
||||||
varsToCopy.add(var.getIndex());
|
varsToCopy.add(var.getIndex());
|
||||||
|
varDefinitionPoints.put(var.getIndex(), node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Phi phi : block.getPhis()) {
|
for (Phi phi : block.getPhis()) {
|
||||||
varsToCopy.add(phi.getReceiver().getIndex());
|
varsToCopy.add(phi.getReceiver().getIndex());
|
||||||
|
varDefinitionPoints.put(phi.getReceiver().getIndex(), node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,12 +260,8 @@ class LoopInversionImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyCondition() {
|
private void copyCondition() {
|
||||||
InstructionVariableMapper variableMapper = new InstructionVariableMapper() {
|
InstructionVariableMapper variableMapper = new InstructionVariableMapper(var ->
|
||||||
@Override
|
program.variableAt(copiedVars.getOrDefault(var.getIndex(), var.getIndex())));
|
||||||
protected Variable map(Variable var) {
|
|
||||||
return program.variableAt(copiedVars.getOrDefault(var.getIndex(), var.getIndex()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
BasicBlockMapper blockMapper = new BasicBlockMapper() {
|
BasicBlockMapper blockMapper = new BasicBlockMapper() {
|
||||||
@Override
|
@Override
|
||||||
protected BasicBlock map(BasicBlock block) {
|
protected BasicBlock map(BasicBlock block) {
|
||||||
|
@ -298,7 +294,7 @@ class LoopInversionImpl {
|
||||||
incomingCopy.setValue(program.variableAt(copiedVars.getOrDefault(value, value)));
|
incomingCopy.setValue(program.variableAt(copiedVars.getOrDefault(value, value)));
|
||||||
phiCopy.getIncomings().add(incomingCopy);
|
phiCopy.getIncomings().add(incomingCopy);
|
||||||
}
|
}
|
||||||
targetBlock.getPhis().add(phi);
|
targetBlock.getPhis().add(phiCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TryCatchBlock tryCatch : sourceBlock.getTryCatchBlocks()) {
|
for (TryCatchBlock tryCatch : sourceBlock.getTryCatchBlocks()) {
|
||||||
|
@ -358,54 +354,136 @@ class LoopInversionImpl {
|
||||||
BasicBlock block = program.basicBlockAt(headCopy);
|
BasicBlock block = program.basicBlockAt(headCopy);
|
||||||
for (Phi phi : block.getPhis()) {
|
for (Phi phi : block.getPhis()) {
|
||||||
List<Incoming> incomings = phi.getIncomings();
|
List<Incoming> incomings = phi.getIncomings();
|
||||||
for (int i = 0; i < incomings.size(); ++i) {
|
List<Incoming> newIncomings = new ArrayList<>(incomings.size());
|
||||||
Incoming incoming = incomings.get(i);
|
for (Incoming incoming : incomings) {
|
||||||
if (!nodesAndCopies.contains(incoming.getSource().getIndex())) {
|
if (nodesAndCopies.contains(incoming.getSource().getIndex())) {
|
||||||
incomings.remove(i--);
|
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.
|
* Every reference to variable from old condition must be replaced by reference to corresponding phi.
|
||||||
*/
|
*/
|
||||||
private void putNewPhis() {
|
private void putNewPhis() {
|
||||||
BasicBlock head = program.basicBlockAt(conditionEnd);
|
BasicBlock head = program.basicBlockAt(bodyStart);
|
||||||
IntIntMap phiMap = new IntIntOpenHashMap();
|
IntIntMap phiMap = new IntIntOpenHashMap();
|
||||||
|
|
||||||
int[] vars = copiedVars.keys().toArray();
|
int[] vars = copiedVars.keys().toArray();
|
||||||
Arrays.sort(vars);
|
Arrays.sort(vars);
|
||||||
|
List<Phi> phisToAdd = new ArrayList<>();
|
||||||
for (int var : vars) {
|
for (int var : vars) {
|
||||||
|
int varCopy = copiedVars.get(var);
|
||||||
|
|
||||||
Phi phi = new Phi();
|
Phi phi = new Phi();
|
||||||
phi.setReceiver(program.createVariable());
|
phi.setReceiver(program.createVariable());
|
||||||
phiMap.put(var, phi.getReceiver().getIndex());
|
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 incoming = new Incoming();
|
||||||
incoming.setValue(program.variableAt(inputVar));
|
incoming.setValue(program.variableAt(var));
|
||||||
incoming.setSource(program.basicBlockAt(source));
|
incoming.setSource(program.basicBlockAt(source));
|
||||||
phi.getIncomings().add(incoming);
|
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() {
|
InstructionVariableMapper mapper = new InstructionVariableMapper(var -> {
|
||||||
@Override
|
|
||||||
protected Variable map(Variable var) {
|
|
||||||
int index = var.getIndex();
|
int index = var.getIndex();
|
||||||
return program.variableAt(phiMap.getOrDefault(index, index));
|
return program.variableAt(phiMap.getOrDefault(index, index));
|
||||||
}
|
});
|
||||||
};
|
|
||||||
for (int node : nodes.toArray()) {
|
for (int node : nodes.toArray()) {
|
||||||
if (copiedNodes.containsKey(node)) {
|
if (!copiedNodes.containsKey(node)) {
|
||||||
BasicBlock block = program.basicBlockAt(node);
|
BasicBlock block = program.basicBlockAt(node);
|
||||||
for (Instruction instruction : block.getInstructions()) {
|
mapper.apply(block);
|
||||||
instruction.acceptVisitor(mapper);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldHolder;
|
import org.teavm.model.FieldHolder;
|
||||||
import org.teavm.model.Incoming;
|
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.InstructionLocation;
|
import org.teavm.model.InstructionLocation;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Phi;
|
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.TryCatchBlock;
|
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
@ -82,15 +79,15 @@ import org.teavm.model.util.ProgramUtils;
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
class JSClassProcessor {
|
class JSClassProcessor {
|
||||||
private ClassReaderSource classSource;
|
private final ClassReaderSource classSource;
|
||||||
private JSBodyRepository repository;
|
private final JSBodyRepository repository;
|
||||||
private JavaInvocationProcessor javaInvocationProcessor;
|
private final JavaInvocationProcessor javaInvocationProcessor;
|
||||||
private Program program;
|
private Program program;
|
||||||
private List<Instruction> replacement = new ArrayList<>();
|
private final List<Instruction> replacement = new ArrayList<>();
|
||||||
private JSTypeHelper typeHelper;
|
private final JSTypeHelper typeHelper;
|
||||||
private Diagnostics diagnostics;
|
private final Diagnostics diagnostics;
|
||||||
private int methodIndexGenerator;
|
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) {
|
public JSClassProcessor(ClassReaderSource classSource, JSBodyRepository repository, Diagnostics diagnostics) {
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
|
@ -170,32 +167,15 @@ class JSClassProcessor {
|
||||||
callerMethod.getModifiers().add(ElementModifier.STATIC);
|
callerMethod.getModifiers().add(ElementModifier.STATIC);
|
||||||
final Program program = ProgramUtils.copy(method.getProgram());
|
final Program program = ProgramUtils.copy(method.getProgram());
|
||||||
program.createVariable();
|
program.createVariable();
|
||||||
InstructionVariableMapper variableMapper = new InstructionVariableMapper() {
|
InstructionVariableMapper variableMapper = new InstructionVariableMapper(var ->
|
||||||
@Override protected Variable map(Variable var) {
|
program.variableAt(var.getIndex() + 1));
|
||||||
return program.variableAt(var.getIndex() + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (int i = program.variableCount() - 1; i > 0; --i) {
|
for (int i = program.variableCount() - 1; i > 0; --i) {
|
||||||
program.variableAt(i).getDebugNames().addAll(program.variableAt(i - 1).getDebugNames());
|
program.variableAt(i).getDebugNames().addAll(program.variableAt(i - 1).getDebugNames());
|
||||||
program.variableAt(i - 1).getDebugNames().clear();
|
program.variableAt(i - 1).getDebugNames().clear();
|
||||||
}
|
}
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
for (Instruction insn : block.getInstructions()) {
|
variableMapper.apply(block);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
callerMethod.setProgram(program);
|
callerMethod.setProgram(program);
|
||||||
ModelUtils.copyAnnotations(method.getAnnotations(), callerMethod.getAnnotations());
|
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[] signature = method.getSignature();
|
||||||
ValueType[] staticSignature = new ValueType[signature.length + 1];
|
ValueType[] staticSignature = new ValueType[signature.length + 1];
|
||||||
for (int i = 0; i < signature.length; ++i) {
|
for (int i = 0; i < signature.length; ++i) {
|
||||||
|
@ -287,7 +267,7 @@ class JSClassProcessor {
|
||||||
return staticSignature;
|
return staticSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processProgram(MethodHolder methodToProcess) {
|
void processProgram(MethodHolder methodToProcess) {
|
||||||
program = methodToProcess.getProgram();
|
program = methodToProcess.getProgram();
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlock block = program.basicBlockAt(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