Implementing loop inversion

This commit is contained in:
Alexey Andreev 2016-05-09 18:13:00 +03:00
parent a57be365b4
commit 3c93b78902
8 changed files with 229 additions and 151 deletions

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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()));
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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,54 +354,136 @@ 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) {
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);
}
}
}

View File

@ -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);

View File

@ -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);
}
}