Improve performance of dependency analysis by filtering out propagated types that don't match types of method parameters and return values

This commit is contained in:
Alexey Andreev 2017-11-12 23:06:15 +03:00
parent d811e7edbb
commit edfbe01a7f
6 changed files with 181 additions and 120 deletions

View File

@ -31,7 +31,6 @@ import java.util.Set;
import org.objectweb.asm.tree.ClassNode;
import org.teavm.callgraph.CallGraph;
import org.teavm.callgraph.DefaultCallGraph;
import org.teavm.callgraph.DefaultCallGraphNode;
import org.teavm.common.CachedMapper;
import org.teavm.common.Mapper;
import org.teavm.common.ServiceRepository;
@ -52,7 +51,6 @@ import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ReferenceCache;
import org.teavm.model.TextLocation;
import org.teavm.model.ValueType;
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
import org.teavm.model.util.ModelUtils;
@ -62,6 +60,8 @@ import org.teavm.parsing.Parser;
public class DependencyChecker implements DependencyInfo {
private static final int PROPAGATION_STACK_THRESHOLD = 50;
static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true");
static final boolean shouldTag = System.getProperty("org.teavm.tagDependencies", "false").equals("true")
|| shouldLog;
private int classNameSuffix;
private DependencyClassSource classSource;
private ClassLoader classLoader;
@ -141,7 +141,11 @@ public class DependencyChecker implements DependencyInfo {
}
public DependencyNode createNode() {
return new DependencyNode(this);
return createNode(null);
}
private DependencyNode createNode(ValueType typeFilter) {
return new DependencyNode(this, typeFilter);
}
@Override
@ -322,12 +326,7 @@ public class DependencyChecker implements DependencyInfo {
}
ClassDependency dep = classCache.map(className);
boolean added = true;
if (callLocation != null && callLocation.getMethod() != null) {
DefaultCallGraphNode callGraphNode = callGraph.getNode(callLocation.getMethod());
if (!addClassAccess(callGraphNode, className, callLocation.getSourceLocation())) {
added = false;
}
} else {
if (callLocation == null || callLocation.getMethod() == null) {
added = classesAddedByRoot.add(className);
}
if (!dep.isMissing() && added) {
@ -336,24 +335,17 @@ public class DependencyChecker implements DependencyInfo {
listener.classReached(agent, className, callLocation);
}
});
}
return dep;
}
private boolean addClassAccess(DefaultCallGraphNode node, String className, TextLocation loc) {
if (!node.addClassAccess(className, loc)) {
return false;
}
ClassReader cls = classSource.get(className);
if (cls != null) {
ClassReader cls = dep.getClassReader();
if (cls.getParent() != null) {
addClassAccess(node, cls.getParent(), loc);
linkClass(cls.getParent(), callLocation);
}
for (String iface : cls.getInterfaces()) {
addClassAccess(node, iface, loc);
linkClass(iface, callLocation);
}
}
return true;
return dep;
}
private ClassDependency createClassDependency(String className) {
@ -414,26 +406,33 @@ public class DependencyChecker implements DependencyInfo {
ValueType[] arguments = methodRef.getParameterTypes();
int paramCount = arguments.length + 1;
DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1];
for (int i = 0; i < parameterNodes.length; ++i) {
parameterNodes[i] = createNode();
parameterNodes[i].method = methodRef;
if (shouldLog) {
parameterNodes[0] = createNode(ValueType.object(methodRef.getClassName()));
parameterNodes[0].method = methodRef;
if (shouldTag) {
parameterNodes[0].setTag(methodRef + ":0");
}
for (int i = 0; i < arguments.length; ++i) {
parameterNodes[i + 1] = createNode(arguments[i]);
parameterNodes[i + 1].method = methodRef;
if (shouldTag) {
parameterNodes[i].setTag(methodRef + ":" + i);
}
}
DependencyNode resultNode;
if (methodRef.getDescriptor().getResultType() == ValueType.VOID) {
resultNode = null;
} else {
resultNode = createNode();
resultNode.method = methodRef;
if (shouldLog) {
if (shouldTag) {
resultNode.setTag(methodRef + ":RESULT");
}
}
DependencyNode thrown = createNode();
thrown.method = methodRef;
if (shouldLog) {
if (shouldTag) {
thrown.setTag(methodRef + ":THROWN");
}
MethodDependency dep = new MethodDependency(this, parameterNodes, paramCount, resultNode, thrown,
@ -504,8 +503,8 @@ public class DependencyChecker implements DependencyInfo {
}
private FieldDependency createFieldNode(FieldReference fieldRef, FieldReader field) {
DependencyNode node = createNode();
if (shouldLog) {
DependencyNode node = createNode(field != null ? field.getType() : null);
if (shouldTag) {
node.setTag(fieldRef.getClassName() + "#" + fieldRef.getFieldName());
}
FieldDependency dep = new FieldDependency(node, field, fieldRef);

View File

@ -112,7 +112,7 @@ class DependencyGraphBuilder {
for (int i = dep.getVariableCount(); i < nodeClasses.length; ++i) {
nodeClasses[i] = dependencyChecker.createNode();
nodeClasses[i].method = ref;
if (DependencyChecker.shouldLog) {
if (DependencyChecker.shouldTag) {
nodeClasses[i].setTag(dep.getMethod().getReference() + ":" + i);
}
}
@ -274,13 +274,13 @@ class DependencyGraphBuilder {
return new ExceptionConsumer(dependencyChecker, exceptions, vars, methodDep);
}
private static class ExceptionConsumer implements DependencyConsumer {
static class ExceptionConsumer implements DependencyConsumer {
private DependencyChecker checker;
private ClassReader[] exceptions;
private DependencyNode[] vars;
private MethodDependency method;
public ExceptionConsumer(DependencyChecker checker, ClassReader[] exceptions, DependencyNode[] vars,
ExceptionConsumer(DependencyChecker checker, ClassReader[] exceptions, DependencyNode[] vars,
MethodDependency method) {
this.checker = checker;
this.exceptions = exceptions;
@ -654,6 +654,11 @@ class DependencyGraphBuilder {
receiver != null ? nodes[receiver.getIndex()] : null, caller, currentLocation,
currentExceptionConsumer);
nodes[instance.getIndex()].addConsumer(listener);
dependencyChecker.getClassSource().overriddenMethods(method).forEach(methodImpl -> {
CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation);
dependencyChecker.linkMethod(methodImpl.getReference(), callLocation);
});
}
@Override

View File

@ -17,6 +17,7 @@ package org.teavm.dependency;
import java.util.*;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class DependencyNode implements ValueDependencyInfo {
private static final int SMALL_TYPES_THRESHOLD = 6;
@ -31,14 +32,17 @@ public class DependencyNode implements ValueDependencyInfo {
private int degree;
boolean locked;
MethodReference method;
private ValueType typeFilter;
private SuperClassFilter cachedTypeFilter;
DependencyNode(DependencyChecker dependencyChecker) {
this(dependencyChecker, 0);
DependencyNode(DependencyChecker dependencyChecker, ValueType typeFilter) {
this(dependencyChecker, typeFilter, 0);
}
private DependencyNode(DependencyChecker dependencyChecker, int degree) {
private DependencyNode(DependencyChecker dependencyChecker, ValueType typeFilter, int degree) {
this.dependencyChecker = dependencyChecker;
this.degree = degree;
this.typeFilter = typeFilter;
}
private boolean addType(DependencyType type) {
@ -88,7 +92,7 @@ public class DependencyNode implements ValueDependencyInfo {
if (degree > 2) {
return;
}
if (addType(type)) {
if (addType(type) && filter(type)) {
if (DependencyChecker.shouldLog) {
System.out.println(tag + " -> " + type.getName());
}
@ -118,7 +122,7 @@ public class DependencyNode implements ValueDependencyInfo {
int j = 0;
for (int i = 0; i < newTypes.length; ++i) {
DependencyType type = newTypes[i];
if (addType(type)) {
if (addType(type) && filter(type)) {
newTypes[j++] = type;
}
}
@ -154,6 +158,24 @@ public class DependencyNode implements ValueDependencyInfo {
}
}
private boolean filter(DependencyType type) {
if (typeFilter == null) {
return true;
}
if (cachedTypeFilter == null) {
String superClass;
if (typeFilter instanceof ValueType.Object) {
superClass = ((ValueType.Object) typeFilter).getClassName();
} else {
superClass = "java.lang.Object";
}
cachedTypeFilter = dependencyChecker.getSuperClassFilter(superClass);
}
return cachedTypeFilter.match(type);
}
public void addConsumer(DependencyConsumer consumer) {
if (followers == null) {
followers = new ArrayList<>(1);
@ -163,7 +185,7 @@ public class DependencyNode implements ValueDependencyInfo {
}
followers.add(consumer);
propagateTypes(consumer, null);
propagateTypes(consumer);
}
public void connect(DependencyNode node, DependencyTypeFilter filter) {
@ -190,30 +212,42 @@ public class DependencyNode implements ValueDependencyInfo {
System.out.println("Connecting " + tag + " to " + node.tag);
}
propagateTypes(transition, filter);
propagateTypes(transition);
}
private void propagateTypes(DependencyConsumer transition, DependencyTypeFilter filter) {
private void propagateTypes(DependencyConsumer transition) {
if (this.types != null) {
List<DependencyType> types = new ArrayList<>();
DependencyType[] types = new DependencyType[this.types.cardinality()];
int j = 0;
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
DependencyType type = dependencyChecker.types.get(index);
if (filter == null || filter.match(type)) {
types.add(type);
}
types[j++] = type;
}
dependencyChecker.schedulePropagation(transition, types.toArray(new DependencyType[types.size()]));
dependencyChecker.schedulePropagation(transition, types);
} else if (this.smallTypes != null) {
DependencyType[] types = new DependencyType[smallTypes.length];
int j = 0;
for (int i = 0; i < types.length; ++i) {
DependencyType type = dependencyChecker.types.get(smallTypes[i]);
if (filter == null || filter.match(type)) {
types[j++] = type;
}
types[i] = type;
}
if (j < types.length) {
types = Arrays.copyOf(types, j);
dependencyChecker.schedulePropagation(transition, types);
}
}
private void propagateTypes(DependencyNodeToNodeTransition transition) {
if (this.types != null) {
DependencyType[] types = new DependencyType[this.types.cardinality()];
int j = 0;
for (int index = this.types.nextSetBit(0); index >= 0; index = this.types.nextSetBit(index + 1)) {
DependencyType type = dependencyChecker.types.get(index);
types[j++] = type;
}
dependencyChecker.schedulePropagation(transition, types);
} else if (this.smallTypes != null) {
DependencyType[] types = new DependencyType[smallTypes.length];
for (int i = 0; i < types.length; ++i) {
DependencyType type = dependencyChecker.types.get(smallTypes[i]);
types[i] = type;
}
dependencyChecker.schedulePropagation(transition, types);
}
@ -226,8 +260,11 @@ public class DependencyNode implements ValueDependencyInfo {
@Override
public DependencyNode getArrayItem() {
if (arrayItemNode == null) {
arrayItemNode = new DependencyNode(dependencyChecker, degree + 1);
if (DependencyChecker.shouldLog) {
ValueType itemTypeFilter = typeFilter instanceof ValueType.Array
? ((ValueType.Array) typeFilter).getItemType()
: null;
arrayItemNode = new DependencyNode(dependencyChecker, itemTypeFilter, degree + 1);
if (DependencyChecker.shouldTag) {
arrayItemNode.tag = tag + "[";
}
}
@ -237,9 +274,9 @@ public class DependencyNode implements ValueDependencyInfo {
@Override
public DependencyNode getClassValueNode() {
if (classValueNode == null) {
classValueNode = new DependencyNode(dependencyChecker, degree);
classValueNode = new DependencyNode(dependencyChecker, null, degree);
classValueNode.classValueNode = classValueNode;
if (DependencyChecker.shouldLog) {
if (DependencyChecker.shouldTag) {
classValueNode.tag = tag + "@";
}
}
@ -272,19 +309,33 @@ public class DependencyNode implements ValueDependencyInfo {
public String[] getTypes() {
if (smallTypes != null) {
String[] result = new String[smallTypes.length];
int j = 0;
for (int i = 0; i < result.length; ++i) {
result[i] = dependencyChecker.types.get(smallTypes[i]).getName();
DependencyType type = dependencyChecker.types.get(smallTypes[i]);
if (filter(type)) {
result[j++] = type.getName();
}
}
if (j < result.length) {
result = Arrays.copyOf(result, j);
}
return result;
}
if (types == null) {
return new String[0];
}
List<String> result = new ArrayList<>();
String[] result = new String[types.cardinality()];
int j = 0;
for (int index = types.nextSetBit(0); index >= 0; index = types.nextSetBit(index + 1)) {
result.add(dependencyChecker.types.get(index).getName());
DependencyType type = dependencyChecker.types.get(index);
if (filter(type)) {
result[j++] = type.getName();
}
}
return result.toArray(new String[result.size()]);
if (j < result.length) {
result = Arrays.copyOf(result, j);
}
return result;
}
public String getTag() {

View File

@ -15,24 +15,15 @@
*/
package org.teavm.dependency;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teavm.model.BasicBlock;
import org.teavm.model.BasicBlockReader;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ProgramReader;
import org.teavm.model.VariableReader;
import org.teavm.model.instructions.AbstractInstructionReader;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.InvocationType;
@ -40,47 +31,12 @@ import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
public class Linker {
private Set<MethodReference> methodsToPreserve = new HashSet<>();
private Set<String> additionalClasses = new HashSet<>();
public void prepare(DependencyInfo dependency, ClassReader cls) {
for (MethodReader method : cls.getMethods().toArray(new MethodReader[0])) {
MethodReference methodRef = new MethodReference(cls.getName(), method.getDescriptor());
MethodDependencyInfo methodDep = dependency.getMethod(methodRef);
if (methodDep != null && method.getProgram() != null) {
collectMethodsToPreserve(method.getProgram());
}
}
}
private void collectMethodsToPreserve(ProgramReader program) {
for (BasicBlockReader block : program.getBasicBlocks()) {
block.readAllInstructions(new AbstractInstructionReader() {
@Override
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments, InvocationType type) {
methodsToPreserve.add(method);
additionalClasses.add(method.getClassName());
}
});
}
}
public Set<String> getAdditionalClasses() {
return additionalClasses;
}
public void link(DependencyInfo dependency, ClassHolder cls) {
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
MethodReference methodRef = method.getReference();
MethodDependencyInfo methodDep = dependency.getMethod(methodRef);
if (methodDep == null) {
if (methodsToPreserve.contains(methodRef)) {
method.getModifiers().add(ElementModifier.ABSTRACT);
method.setProgram(null);
} else {
cls.removeMethod(method);
}
cls.removeMethod(method);
} else if (!methodDep.isUsed()) {
method.getModifiers().add(ElementModifier.ABSTRACT);
method.setProgram(null);

View File

@ -21,9 +21,60 @@ import org.teavm.common.Mapper;
import org.teavm.interop.Remove;
import org.teavm.interop.Rename;
import org.teavm.interop.Superclass;
import org.teavm.model.*;
import org.teavm.model.instructions.*;
import org.teavm.model.util.ModelUtils;
import org.teavm.model.AnnotationContainer;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationValue;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHolder;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.InvokeDynamicInstruction;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHandle;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.RuntimeConstant;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.ValueType;
import org.teavm.model.instructions.ArrayLengthInstruction;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BinaryInstruction;
import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.CastIntegerInstruction;
import org.teavm.model.instructions.CastNumberInstruction;
import org.teavm.model.instructions.ClassConstantInstruction;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.EmptyInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
import org.teavm.model.instructions.GetElementInstruction;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.InstructionVisitor;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.IsInstanceInstruction;
import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.instructions.LongConstantInstruction;
import org.teavm.model.instructions.MonitorEnterInstruction;
import org.teavm.model.instructions.MonitorExitInstruction;
import org.teavm.model.instructions.NegateInstruction;
import org.teavm.model.instructions.NullCheckInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.instructions.PutElementInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.instructions.RaiseInstruction;
import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.instructions.SwitchInstruction;
import org.teavm.model.instructions.UnwrapArrayInstruction;
public class ClassRefsRenamer implements InstructionVisitor {
private Mapper<String, String> classNameMapper;
@ -55,7 +106,7 @@ public class ClassRefsRenamer implements InstructionVisitor {
renamedCls.addMethod(rename(method));
}
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
renamedCls.addField(ModelUtils.copyField(field));
renamedCls.addField(rename(field));
}
if (cls.getOwnerName() != null) {
renamedCls.setOwnerName(classNameMapper.map(cls.getOwnerName()));
@ -89,6 +140,16 @@ public class ClassRefsRenamer implements InstructionVisitor {
return renamedMethod;
}
public FieldHolder rename(FieldHolder field) {
FieldHolder renamedField = new FieldHolder(field.getName());
renamedField.getModifiers().addAll(field.getModifiers());
renamedField.setLevel(field.getLevel());
renamedField.setType(rename(field.getType()));
renamedField.setInitialValue(field.getInitialValue());
rename(field.getAnnotations(), renamedField.getAnnotations());
return renamedField;
}
private ValueType rename(ValueType type) {
if (type instanceof ValueType.Array) {
ValueType itemType = ((ValueType.Array) type).getItemType();

View File

@ -22,12 +22,10 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
import org.teavm.cache.NoCache;
import org.teavm.common.ServiceRepository;
@ -401,20 +399,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
}
int index = 0;
for (String className : dependency.getReachableClasses()) {
ClassReader clsReader = dependency.getClassSource().get(className);
if (clsReader != null) {
linker.prepare(dependency, clsReader);
}
}
if (wasCancelled()) {
return cutClasses;
}
Set<String> allClasses = new LinkedHashSet<>(dependency.getReachableClasses());
allClasses.addAll(linker.getAdditionalClasses());
for (String className : allClasses) {
for (String className : dependency.getReachableClasses()) {
ClassReader clsReader = dependency.getClassSource().get(className);
if (clsReader == null) {
continue;