Add escape analysis

This commit is contained in:
Alexey Andreev 2017-01-16 23:53:30 +03:00 committed by Alexey Andreev
parent 6781dd0abb
commit e4fab2be41
5 changed files with 614 additions and 8 deletions

View File

@ -22,10 +22,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
*
* @author Alexey Andreev
*/
public class GraphBuilder {
private GraphImpl builtGraph;
private List<IntSet> addedEdges = new ArrayList<>();

View File

@ -0,0 +1,367 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model.analysis;
import com.carrotsearch.hppc.IntArrayDeque;
import com.carrotsearch.hppc.IntDeque;
import com.carrotsearch.hppc.IntOpenHashSet;
import com.carrotsearch.hppc.IntSet;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.teavm.common.DisjointSet;
import org.teavm.common.Graph;
import org.teavm.common.GraphBuilder;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference;
import org.teavm.model.Incoming;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.Variable;
import org.teavm.model.instructions.AbstractInstructionVisitor;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.ClassConstantInstruction;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.GetElementInstruction;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.IsInstanceInstruction;
import org.teavm.model.instructions.MonitorEnterInstruction;
import org.teavm.model.instructions.MonitorExitInstruction;
import org.teavm.model.instructions.NullCheckInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
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.UnwrapArrayInstruction;
import org.teavm.model.util.InstructionTransitionExtractor;
import org.teavm.model.util.LivenessAnalyzer;
import org.teavm.model.util.UsageExtractor;
public class EscapeAnalysis {
private ClassReaderSource classSource;
private int[] definitionClasses;
private boolean[] escapingVars;
private FieldReference[][] fields;
public EscapeAnalysis(ClassReaderSource classSource) {
this.classSource = classSource;
}
public void analyze(Program program, MethodReference methodReference) {
InstructionEscapeVisitor visitor = new InstructionEscapeVisitor(program.variableCount(), classSource);
for (int i = 0; i <= methodReference.parameterCount(); ++i) {
visitor.escapingVars[i] = true;
}
for (BasicBlock block : program.getBasicBlocks()) {
for (Instruction insn : block) {
insn.acceptVisitor(visitor);
}
if (block.getExceptionVariable() != null) {
visitor.escapingVars[block.getExceptionVariable().getIndex()] = true;
}
}
definitionClasses = visitor.definitionClasses.pack(program.variableCount());
escapingVars = new boolean[program.variableCount()];
for (int i = 0; i < program.variableCount(); ++i) {
if (visitor.escapingVars[i]) {
escapingVars[definitionClasses[i]] = true;
}
}
analyzePhis(program);
fields = packFields(visitor.fields);
}
public boolean escapes(int var) {
return escapingVars[definitionClasses[var]];
}
public FieldReference[] getFields(int var) {
FieldReference[] varFields = fields[definitionClasses[var]];
return varFields != null ? varFields.clone() : null;
}
private void analyzePhis(Program program) {
LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer();
livenessAnalyzer.analyze(program);
GraphBuilder graphBuilder = new GraphBuilder(program.variableCount());
IntDeque queue = new IntArrayDeque();
for (BasicBlock block : program.getBasicBlocks()) {
IntSet sharedIncomingVars = new IntOpenHashSet();
BitSet usedVars = getUsedVarsInBlock(livenessAnalyzer, block);
for (Phi phi : block.getPhis()) {
for (Incoming incoming : phi.getIncomings()) {
int var = incoming.getValue().getIndex();
graphBuilder.addEdge(var, phi.getReceiver().getIndex());
if (escapingVars[definitionClasses[var]] || !sharedIncomingVars.add(var) || usedVars.get(var)) {
queue.addLast(var);
}
}
}
}
Graph graph = graphBuilder.build();
IntSet visited = new IntOpenHashSet();
while (!queue.isEmpty()) {
int var = queue.removeFirst();
if (visited.add(var)) {
escapingVars[definitionClasses[var]] = true;
for (int successor : graph.outgoingEdges(var)) {
queue.addLast(successor);
}
}
}
}
private BitSet getUsedVarsInBlock(LivenessAnalyzer liveness, BasicBlock block) {
BitSet usedVars = new BitSet();
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
block.getLastInstruction().acceptVisitor(transitionExtractor);
for (BasicBlock successor : transitionExtractor.getTargets()) {
usedVars.or(liveness.liveIn(successor.getIndex()));
}
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
usedVars.or(liveness.liveIn(tryCatch.getHandler().getIndex()));
}
UsageExtractor useExtractor = new UsageExtractor();
for (Instruction instruction : block) {
instruction.acceptVisitor(useExtractor);
for (Variable variable : useExtractor.getUsedVariables()) {
usedVars.set(variable.getIndex());
}
}
return usedVars;
}
private FieldReference[][] packFields(List<Set<FieldReference>> fields) {
List<Set<FieldReference>> joinedFields = new ArrayList<>(Collections.nCopies(fields.size(), null));
for (int i = 0; i < fields.size(); ++i) {
if (fields.get(i) == null) {
continue;
}
int j = definitionClasses[i];
Set<FieldReference> fieldSet = joinedFields.get(j);
if (fieldSet == null) {
fieldSet = new LinkedHashSet<>();
joinedFields.set(j, fieldSet);
}
fieldSet.addAll(fields.get(i));
}
FieldReference[][] packedFields = new FieldReference[fields.size()][];
for (int i = 0; i < packedFields.length; ++i) {
if (joinedFields.get(i) != null) {
packedFields[i] = joinedFields.get(i).toArray(new FieldReference[0]);
}
}
return packedFields;
}
private static class InstructionEscapeVisitor extends AbstractInstructionVisitor {
ClassReaderSource classSource;
DisjointSet definitionClasses;
boolean[] escapingVars;
List<Set<FieldReference>> fields;
public InstructionEscapeVisitor(int variableCount, ClassReaderSource classSource) {
this.classSource = classSource;
fields = new ArrayList<>(Collections.nCopies(variableCount, null));
definitionClasses = new DisjointSet();
for (int i = 0; i < variableCount; ++i) {
definitionClasses.create();
}
escapingVars = new boolean[variableCount];
}
@Override
public void visit(ConstructInstruction insn) {
ClassReader cls = classSource.get(insn.getType());
if (cls == null) {
escapingVars[insn.getReceiver().getIndex()] = true;
}
while (cls != null) {
for (FieldReader field : cls.getFields()) {
addField(insn.getReceiver(), field.getReference());
}
cls = cls.getParent() != null ? classSource.get(cls.getParent()) : null;
}
}
@Override
public void visit(NullConstantInstruction insn) {
escapingVars[insn.getReceiver().getIndex()] = true;
}
@Override
public void visit(ClassConstantInstruction insn) {
escapingVars[insn.getReceiver().getIndex()] = true;
}
@Override
public void visit(StringConstantInstruction insn) {
escapingVars[insn.getReceiver().getIndex()] = true;
}
@Override
public void visit(CloneArrayInstruction insn) {
escapingVars[insn.getArray().getIndex()] = true;
escapingVars[insn.getReceiver().getIndex()] = true;
}
@Override
public void visit(UnwrapArrayInstruction insn) {
definitionClasses.union(insn.getReceiver().getIndex(), insn.getArray().getIndex());
}
@Override
public void visit(AssignInstruction insn) {
definitionClasses.union(insn.getReceiver().getIndex(), insn.getAssignee().getIndex());
}
@Override
public void visit(CastInstruction insn) {
escapingVars[insn.getReceiver().getIndex()] = true;
escapingVars[insn.getValue().getIndex()] = true;
}
@Override
public void visit(ExitInstruction insn) {
if (insn.getValueToReturn() != null) {
escapingVars[insn.getValueToReturn().getIndex()] = true;
}
}
@Override
public void visit(RaiseInstruction insn) {
escapingVars[insn.getException().getIndex()] = true;
}
@Override
public void visit(GetFieldInstruction insn) {
escapingVars[insn.getReceiver().getIndex()] = true;
addField(insn.getInstance(), insn.getField());
}
@Override
public void visit(PutFieldInstruction insn) {
escapingVars[insn.getValue().getIndex()] = true;
addField(insn.getInstance(), insn.getField());
}
private void addField(Variable instance, FieldReference field) {
if (instance == null) {
return;
}
Set<FieldReference> fieldSet = fields.get(instance.getIndex());
if (fieldSet == null) {
fieldSet = new LinkedHashSet<>();
fields.set(instance.getIndex(), fieldSet);
}
fieldSet.add(field);
}
@Override
public void visit(GetElementInstruction insn) {
escapingVars[insn.getReceiver().getIndex()] = true;
}
@Override
public void visit(PutElementInstruction insn) {
escapingVars[insn.getValue().getIndex()] = true;
}
@Override
public void visit(InvokeInstruction insn) {
if (insn.getInstance() != null) {
escapingVars[insn.getInstance().getIndex()] = true;
}
for (Variable arg : insn.getArguments()) {
escapingVars[arg.getIndex()] = true;
}
if (insn.getReceiver() != null) {
escapingVars[insn.getReceiver().getIndex()] = true;
}
}
@Override
public void visit(IsInstanceInstruction insn) {
escapingVars[insn.getValue().getIndex()] = true;
}
@Override
public void visit(NullCheckInstruction insn) {
definitionClasses.union(insn.getValue().getIndex(), insn.getReceiver().getIndex());
}
@Override
public void visit(MonitorEnterInstruction insn) {
escapingVars[insn.getObjectRef().getIndex()] = true;
}
@Override
public void visit(MonitorExitInstruction insn) {
escapingVars[insn.getObjectRef().getIndex()] = true;
}
@Override
public void visit(BranchingInstruction insn) {
switch (insn.getCondition()) {
case NULL:
case NOT_NULL:
escapingVars[insn.getOperand().getIndex()] = true;
break;
default:
break;
}
}
@Override
public void visit(BinaryBranchingInstruction insn) {
switch (insn.getCondition()) {
case REFERENCE_EQUAL:
case REFERENCE_NOT_EQUAL:
escapingVars[insn.getFirstOperand().getIndex()] = true;
escapingVars[insn.getSecondOperand().getIndex()] = true;
break;
default:
break;
}
}
}
}

View File

@ -18,10 +18,6 @@ package org.teavm.model.instructions;
import org.teavm.model.Instruction;
import org.teavm.model.Variable;
/**
*
* @author Alexey Andreev
*/
public class NullConstantInstruction extends Instruction {
private Variable receiver;

View File

@ -0,0 +1,245 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model.optimization;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference;
import org.teavm.model.Incoming;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.analysis.EscapeAnalysis;
import org.teavm.model.instructions.AbstractInstructionVisitor;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.LongConstantInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.util.PhiUpdater;
public class ScalarReplacement implements MethodOptimization {
@Override
public boolean optimize(MethodOptimizationContext context, Program program) {
boolean changed = false;
while (performOnce(context, program)) {
changed = true;
}
return changed;
}
private boolean performOnce(MethodOptimizationContext context, Program program) {
List<Map<FieldReference, Variable>> fieldMappings = new ArrayList<>(
Collections.nCopies(program.variableCount(), null));
MethodReference methodReference = context.getMethod().getReference();
EscapeAnalysis escapeAnalysis = new EscapeAnalysis(context.getClassSource());
escapeAnalysis.analyze(program, methodReference);
boolean canPerform = false;
for (int i = 0; i < fieldMappings.size(); ++i) {
FieldReference[] fields = escapeAnalysis.getFields(i);
if (!escapeAnalysis.escapes(i) && fields != null) {
Map<FieldReference, Variable> fieldMapping = new LinkedHashMap<>();
for (FieldReference field : fields) {
fieldMapping.put(field, program.createVariable());
}
fieldMappings.set(i, fieldMapping);
canPerform = true;
}
}
if (!canPerform) {
return false;
}
ScalarReplacementVisitor visitor = new ScalarReplacementVisitor(escapeAnalysis, context.getClassSource(),
fieldMappings);
for (BasicBlock block : program.getBasicBlocks()) {
for (Instruction instruction : block) {
instruction.acceptVisitor(visitor);
}
List<Phi> additionalPhis = new ArrayList<>();
for (int i = 0; i < block.getPhis().size(); ++i) {
Phi phi = block.getPhis().get(i);
if (escapeAnalysis.escapes(phi.getReceiver().getIndex())) {
continue;
}
FieldReference[] fields = escapeAnalysis.getFields(phi.getReceiver().getIndex());
if (fields == null) {
continue;
}
for (FieldReference field : fields) {
boolean allIncomingsInitialized = true;
for (Incoming incoming : phi.getIncomings()) {
if (fieldMappings.get(incoming.getValue().getIndex()).get(field) == null) {
allIncomingsInitialized = false;
}
}
if (!allIncomingsInitialized) {
continue;
}
Phi phiReplacement = new Phi();
phiReplacement.setReceiver(fieldMappings.get(phi.getReceiver().getIndex()).get(field));
for (Incoming incoming : phi.getIncomings()) {
Incoming incomingReplacement = new Incoming();
incomingReplacement.setSource(incoming.getSource());
incomingReplacement.setValue(fieldMappings.get(incoming.getValue().getIndex()).get(field));
phiReplacement.getIncomings().add(incomingReplacement);
}
additionalPhis.add(phi);
}
block.getPhis().remove(i--);
}
block.getPhis().addAll(additionalPhis);
}
Variable[] arguments = new Variable[methodReference.parameterCount() + 1];
for (int i = 0; i < arguments.length; ++i) {
arguments[i] = program.variableAt(i);
}
new PhiUpdater().updatePhis(program, arguments);
return true;
}
static class ScalarReplacementVisitor extends AbstractInstructionVisitor {
private EscapeAnalysis escapeAnalysis;
private ClassReaderSource classSource;
private List<Map<FieldReference, Variable>> fieldMappings;
public ScalarReplacementVisitor(EscapeAnalysis escapeAnalysis, ClassReaderSource classSource,
List<Map<FieldReference, Variable>> fieldMappings) {
this.escapeAnalysis = escapeAnalysis;
this.classSource = classSource;
this.fieldMappings = fieldMappings;
}
@Override
public void visit(ConstructInstruction insn) {
int var = insn.getReceiver().getIndex();
if (!escapeAnalysis.escapes(var) && escapeAnalysis.getFields(var) != null) {
for (FieldReference fieldRef : escapeAnalysis.getFields(var)) {
FieldReader field = classSource.resolve(fieldRef);
Variable receiver = fieldMappings.get(insn.getReceiver().getIndex()).get(fieldRef);
Instruction initializer = generateDefaultValue(field.getType(), receiver);
initializer.setLocation(initializer.getLocation());
insn.insertPrevious(initializer);
}
insn.delete();
}
}
@Override
public void visit(GetFieldInstruction insn) {
if (insn.getInstance() != null && !escapeAnalysis.escapes(insn.getInstance().getIndex())) {
Variable var = fieldMappings.get(insn.getInstance().getIndex()).get(insn.getField());
AssignInstruction assignment = new AssignInstruction();
assignment.setReceiver(insn.getReceiver());
assignment.setAssignee(var);
assignment.setLocation(insn.getLocation());
insn.replace(assignment);
}
}
@Override
public void visit(PutFieldInstruction insn) {
if (insn.getInstance() != null && !escapeAnalysis.escapes(insn.getInstance().getIndex())) {
Variable var = fieldMappings.get(insn.getInstance().getIndex()).get(insn.getField());
AssignInstruction assignment = new AssignInstruction();
assignment.setReceiver(var);
assignment.setAssignee(insn.getValue());
assignment.setLocation(insn.getLocation());
insn.replace(assignment);
}
}
@Override
public void visit(AssignInstruction insn) {
if (escapeAnalysis.escapes(insn.getAssignee().getIndex())) {
return;
}
FieldReference[] fields = escapeAnalysis.getFields(insn.getReceiver().getIndex());
if (fields == null) {
return;
}
for (FieldReference field : fields) {
Variable assignee = fieldMappings.get(insn.getAssignee().getIndex()).get(field);
if (assignee == null) {
continue;
}
Variable receiver = fieldMappings.get(insn.getReceiver().getIndex()).get(field);
AssignInstruction assignment = new AssignInstruction();
assignment.setReceiver(receiver);
assignment.setAssignee(assignee);
assignment.setLocation(insn.getLocation());
insn.insertPrevious(assignment);
}
insn.delete();
}
private Instruction generateDefaultValue(ValueType type, Variable receiver) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
case BYTE:
case SHORT:
case CHARACTER:
case INTEGER: {
IntegerConstantInstruction insn = new IntegerConstantInstruction();
insn.setReceiver(receiver);
return insn;
}
case LONG: {
LongConstantInstruction insn = new LongConstantInstruction();
insn.setReceiver(receiver);
return insn;
}
case FLOAT: {
FloatConstantInstruction insn = new FloatConstantInstruction();
insn.setReceiver(receiver);
return insn;
}
case DOUBLE: {
DoubleConstantInstruction insn = new DoubleConstantInstruction();
insn.setReceiver(receiver);
return insn;
}
}
}
NullConstantInstruction insn = new NullConstantInstruction();
insn.setReceiver(receiver);
return insn;
}
}
}

View File

@ -60,6 +60,7 @@ import org.teavm.model.optimization.LoopInvariantMotion;
import org.teavm.model.optimization.MethodOptimization;
import org.teavm.model.optimization.MethodOptimizationContext;
import org.teavm.model.optimization.RedundantJumpElimination;
import org.teavm.model.optimization.ScalarReplacement;
import org.teavm.model.optimization.UnreachableBasicBlockElimination;
import org.teavm.model.optimization.UnusedVariableElimination;
import org.teavm.model.text.ListingBuilder;
@ -551,6 +552,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
optimizations.add(new RedundantJumpElimination());
optimizations.add(new ArrayUnwrapMotion());
if (optimizationLevel.ordinal() >= TeaVMOptimizationLevel.ADVANCED.ordinal()) {
optimizations.add(new ScalarReplacement());
//optimizations.add(new LoopInversion());
optimizations.add(new LoopInvariantMotion());
}