Add lazy compilation pipeline that can work a little bit faster in incremental compiler

This commit is contained in:
Alexey Andreev 2019-01-30 19:00:07 +03:00
parent 5be34dcf44
commit 35ca7fd152
19 changed files with 292 additions and 172 deletions

View File

@ -200,12 +200,12 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
} }
@Override @Override
public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { public void beforeOptimizations(Program program, MethodReader method) {
nullCheckInsertion.transformProgram(program, method.getReference()); nullCheckInsertion.transformProgram(program, method.getReference());
} }
@Override @Override
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { public void afterOptimizations(Program program, MethodReader method) {
clinitInsertionTransformer.apply(method, program); clinitInsertionTransformer.apply(method, program);
classInitializerEliminator.apply(program); classInitializerEliminator.apply(program);
classInitializerTransformer.transform(program); classInitializerTransformer.transform(program);

View File

@ -70,7 +70,6 @@ import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource;
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;
@ -293,11 +292,11 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
} }
@Override @Override
public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { public void beforeOptimizations(Program program, MethodReader method) {
} }
@Override @Override
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { public void afterOptimizations(Program program, MethodReader method) {
clinitInsertionTransformer.apply(method, program); clinitInsertionTransformer.apply(method, program);
} }
@ -628,5 +627,5 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
public ClassReaderSource getClassSource() { public ClassReaderSource getClassSource() {
return classSource; return classSource;
} }
}; }
} }

View File

@ -301,11 +301,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
} }
@Override @Override
public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) { public void beforeOptimizations(Program program, MethodReader method) {
} }
@Override @Override
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classes) { public void afterOptimizations(Program program, MethodReader method) {
clinitInsertionTransformer.apply(method, program); clinitInsertionTransformer.apply(method, program);
classInitializerEliminator.apply(program); classInitializerEliminator.apply(program);
classInitializerTransformer.transform(program); classInitializerTransformer.transform(program);

View File

@ -736,6 +736,15 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
node.transitions = null; node.transitions = null;
node.transitionList = null; node.transitionList = null;
node.method = null; node.method = null;
}
allNodes.clear();
classSource.cleanup();
agent.cleanup();
}
public void cleanupTypes() {
for (DependencyNode node : allNodes) {
if (node.typeSet != null) { if (node.typeSet != null) {
node.typeSet.cleanup(); node.typeSet.cleanup();
} }
@ -747,10 +756,6 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
dependency.variableNodes[i] = null; dependency.variableNodes[i] = null;
} }
} }
allNodes.clear();
classSource.cleanup();
agent.cleanup();
} }
static class ReportEntry { static class ReportEntry {

View File

@ -34,8 +34,13 @@ import org.teavm.model.instructions.PutFieldInstruction;
public class Linker { public class Linker {
private static final MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class); private static final MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
private DependencyInfo dependency;
public void link(DependencyInfo dependency, ClassHolder cls) { public Linker(DependencyInfo dependency) {
this.dependency = dependency;
}
public void link(ClassHolder cls) {
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) { for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
MethodReference methodRef = method.getReference(); MethodReference methodRef = method.getReference();
MethodDependencyInfo methodDep = dependency.getMethod(methodRef); MethodDependencyInfo methodDep = dependency.getMethod(methodRef);
@ -45,7 +50,7 @@ public class Linker {
method.getModifiers().add(ElementModifier.ABSTRACT); method.getModifiers().add(ElementModifier.ABSTRACT);
method.setProgram(null); method.setProgram(null);
} else if (method.getProgram() != null) { } else if (method.getProgram() != null) {
link(dependency, method); link(method.getReference(), method.getProgram());
} }
} }
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) { for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
@ -56,8 +61,7 @@ public class Linker {
} }
} }
private void link(DependencyInfo dependency, MethodHolder method) { public void link(MethodReference method, Program program) {
Program program = method.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);
for (Instruction insn : block) { for (Instruction insn : block) {
@ -96,8 +100,8 @@ public class Linker {
} }
} }
private void insertClinit(DependencyInfo dependency, String className, MethodHolder method, Instruction insn) { private void insertClinit(DependencyInfo dependency, String className, MethodReference method, Instruction insn) {
if (className.equals(method.getOwnerName())) { if (className.equals(method.getClassName())) {
return; return;
} }
ClassReader cls = dependency.getClassSource().get(className); ClassReader cls = dependency.getClassSource().get(className);

View File

@ -15,10 +15,13 @@
*/ */
package org.teavm.model; package org.teavm.model;
import java.util.function.Function;
public class MethodHolder extends MemberHolder implements MethodReader { public class MethodHolder extends MemberHolder implements MethodReader {
private MethodDescriptor descriptor; private MethodDescriptor descriptor;
private ClassHolder owner; private ClassHolder owner;
private Program program; private Program program;
private Function<MethodHolder, Program> programSupplier;
private AnnotationValue annotationDefault; private AnnotationValue annotationDefault;
private AnnotationContainer[] parameterAnnotations; private AnnotationContainer[] parameterAnnotations;
private MethodReference reference; private MethodReference reference;
@ -110,6 +113,13 @@ public class MethodHolder extends MemberHolder implements MethodReader {
@Override @Override
public Program getProgram() { public Program getProgram() {
if (program == null && programSupplier != null) {
program = programSupplier.apply(this);
if (program != null) {
program.setMethod(this);
}
programSupplier = null;
}
return program; return program;
} }
@ -118,11 +128,20 @@ public class MethodHolder extends MemberHolder implements MethodReader {
this.program.setMethod(null); this.program.setMethod(null);
} }
this.program = program; this.program = program;
this.programSupplier = null;
if (this.program != null) { if (this.program != null) {
this.program.setMethod(this); this.program.setMethod(this);
} }
} }
public void setProgramSupplier(Function<MethodHolder, Program> programSupplier) {
if (this.program != null) {
this.program.setMethod(null);
}
this.program = null;
this.programSupplier = programSupplier;
}
@Override @Override
public AnnotationValue getAnnotationDefault() { public AnnotationValue getAnnotationDefault() {
return annotationDefault; return annotationDefault;

View File

@ -25,7 +25,6 @@ import org.teavm.dependency.ValueDependencyInfo;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -35,15 +34,13 @@ import org.teavm.model.instructions.InvokeInstruction;
public class Devirtualization { public class Devirtualization {
private DependencyInfo dependency; private DependencyInfo dependency;
private ClassReaderSource classSource;
private ClassHierarchy hierarchy; private ClassHierarchy hierarchy;
private Set<MethodReference> virtualMethods = new HashSet<>(); private Set<MethodReference> virtualMethods = new HashSet<>();
private Set<? extends MethodReference> readonlyVirtualMethods = Collections.unmodifiableSet(virtualMethods); private Set<? extends MethodReference> readonlyVirtualMethods = Collections.unmodifiableSet(virtualMethods);
public Devirtualization(DependencyInfo dependency, ClassReaderSource classSource) { public Devirtualization(DependencyInfo dependency, ClassHierarchy hierarchy) {
this.dependency = dependency; this.dependency = dependency;
this.classSource = classSource; this.hierarchy = hierarchy;
hierarchy = new ClassHierarchy(classSource);
} }
public void apply(MethodHolder method) { public void apply(MethodHolder method) {
@ -82,7 +79,7 @@ public class Devirtualization {
if (className.startsWith("[")) { if (className.startsWith("[")) {
className = "java.lang.Object"; className = "java.lang.Object";
} }
ClassReader cls = classSource.get(className); ClassReader cls = hierarchy.getClassSource().get(className);
if (cls == null || !isSuperclass.test(cls.getName(), false)) { if (cls == null || !isSuperclass.test(cls.getName(), false)) {
continue; continue;
} }

View File

@ -16,13 +16,10 @@
package org.teavm.model.optimization; package org.teavm.model.optimization;
import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyInfo;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
public interface MethodOptimizationContext { public interface MethodOptimizationContext {
MethodReader getMethod(); MethodReader getMethod();
DependencyInfo getDependencyInfo(); DependencyInfo getDependencyInfo();
ClassReaderSource getClassSource();
} }

View File

@ -56,9 +56,6 @@ public class UnusedVariableElimination implements MethodOptimization {
} }
public boolean optimize(MethodReader method, Program program) { public boolean optimize(MethodReader method, Program program) {
if (method.getProgram() == null) {
return false;
}
Graph graph = VariableUsageGraphBuilder.build(program); Graph graph = VariableUsageGraphBuilder.build(program);
boolean[] escaping = VariableEscapeAnalyzer.findEscapingVariables(program); boolean[] escaping = VariableEscapeAnalyzer.findEscapingVariables(program);
boolean[] used = new boolean[escaping.length]; boolean[] used = new boolean[escaping.length];

View File

@ -30,7 +30,7 @@ public class MissingItemsProcessor {
private ClassHierarchy hierarchy; private ClassHierarchy hierarchy;
private Diagnostics diagnostics; private Diagnostics diagnostics;
private List<Instruction> instructionsToAdd = new ArrayList<>(); private List<Instruction> instructionsToAdd = new ArrayList<>();
private MethodHolder methodHolder; private MethodReference methodRef;
private Program program; private Program program;
private Collection<String> reachableClasses; private Collection<String> reachableClasses;
private Collection<MethodReference> reachableMethods; private Collection<MethodReference> reachableMethods;
@ -54,8 +54,12 @@ public class MissingItemsProcessor {
} }
public void processMethod(MethodHolder method) { public void processMethod(MethodHolder method) {
this.methodHolder = method; processMethod(method.getReference(), method.getProgram());
this.program = method.getProgram(); }
public void processMethod(MethodReference method, Program program) {
this.methodRef = method;
this.program = program;
boolean wasModified = false; boolean wasModified = false;
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);
@ -131,7 +135,7 @@ public class MissingItemsProcessor {
if (!reachableClasses.contains(className) || !dependencyInfo.getClass(className).isMissing()) { if (!reachableClasses.contains(className) || !dependencyInfo.getClass(className).isMissing()) {
return true; return true;
} }
diagnostics.error(new CallLocation(methodHolder.getReference(), location), "Class {{c0}} was not found", diagnostics.error(new CallLocation(methodRef, location), "Class {{c0}} was not found",
className); className);
emitExceptionThrow(location, NoClassDefFoundError.class.getName(), "Class not found: " + className); emitExceptionThrow(location, NoClassDefFoundError.class.getName(), "Class not found: " + className);
return false; return false;
@ -159,7 +163,7 @@ public class MissingItemsProcessor {
return true; return true;
} }
diagnostics.error(new CallLocation(methodHolder.getReference(), location), "Method {{m0}} was not found", diagnostics.error(new CallLocation(methodRef, location), "Method {{m0}} was not found",
method); method);
emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method); emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method);
return true; return true;
@ -177,7 +181,7 @@ public class MissingItemsProcessor {
return true; return true;
} }
diagnostics.error(new CallLocation(methodHolder.getReference(), location), "Method {{m0}} was not found", diagnostics.error(new CallLocation(methodRef, location), "Method {{m0}} was not found",
method); method);
emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method); emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method);
return true; return true;
@ -190,7 +194,7 @@ public class MissingItemsProcessor {
if (!reachableFields.contains(field) || !dependencyInfo.getField(field).isMissing()) { if (!reachableFields.contains(field) || !dependencyInfo.getField(field).isMissing()) {
return true; return true;
} }
diagnostics.error(new CallLocation(methodHolder.getReference(), location), "Field {{f0}} was not found", diagnostics.error(new CallLocation(methodRef, location), "Field {{f0}} was not found",
field); field);
emitExceptionThrow(location, NoSuchFieldError.class.getName(), "Field not found: " + field); emitExceptionThrow(location, NoSuchFieldError.class.getName(), "Field not found: " + field);
return true; return true;

View File

@ -17,19 +17,33 @@ package org.teavm.model.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.teavm.model.*; import org.teavm.model.AnnotationContainer;
import org.teavm.model.AnnotationContainerReader;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReader;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
public final class ModelUtils { public final class ModelUtils {
private ModelUtils() { private ModelUtils() {
} }
public static ClassHolder copyClass(ClassReader original, ClassHolder target) { public static ClassHolder copyClass(ClassReader original, ClassHolder target) {
return copyClass(original, target, true);
}
public static ClassHolder copyClass(ClassReader original, ClassHolder target, boolean withPrograms) {
target.setLevel(original.getLevel()); target.setLevel(original.getLevel());
target.getModifiers().addAll(original.readModifiers()); target.getModifiers().addAll(original.readModifiers());
target.setParent(original.getParent()); target.setParent(original.getParent());
target.getInterfaces().addAll(original.getInterfaces()); target.getInterfaces().addAll(original.getInterfaces());
for (MethodReader method : original.getMethods()) { for (MethodReader method : original.getMethods()) {
target.addMethod(copyMethod(method)); target.addMethod(copyMethod(method, withPrograms));
} }
for (FieldReader field : original.getFields()) { for (FieldReader field : original.getFields()) {
target.addField(copyField(field)); target.addField(copyField(field));
@ -40,14 +54,22 @@ public final class ModelUtils {
} }
public static ClassHolder copyClass(ClassReader original) { public static ClassHolder copyClass(ClassReader original) {
return copyClass(original, new ClassHolder(original.getName())); return copyClass(original, true);
}
public static ClassHolder copyClass(ClassReader original, boolean withPrograms) {
return copyClass(original, new ClassHolder(original.getName()), withPrograms);
} }
public static MethodHolder copyMethod(MethodReader method) { public static MethodHolder copyMethod(MethodReader method) {
return copyMethod(method, true);
}
public static MethodHolder copyMethod(MethodReader method, boolean withProgram) {
MethodHolder copy = new MethodHolder(method.getDescriptor()); MethodHolder copy = new MethodHolder(method.getDescriptor());
copy.setLevel(method.getLevel()); copy.setLevel(method.getLevel());
copy.getModifiers().addAll(method.readModifiers()); copy.getModifiers().addAll(method.readModifiers());
if (method.getProgram() != null) { if (method.getProgram() != null && withProgram) {
copy.setProgram(ProgramUtils.copy(method.getProgram())); copy.setProgram(ProgramUtils.copy(method.getProgram()));
} }
copyAnnotations(method.getAnnotations(), copy.getAnnotations()); copyAnnotations(method.getAnnotations(), copy.getAnnotations());

View File

@ -26,7 +26,6 @@ import org.teavm.common.MutableGraphNode;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.Incoming; import org.teavm.model.Incoming;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Phi; import org.teavm.model.Phi;
import org.teavm.model.Program; import org.teavm.model.Program;
@ -36,7 +35,7 @@ import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.JumpInstruction;
public class RegisterAllocator { public class RegisterAllocator {
public void allocateRegisters(MethodReader method, Program program, boolean debuggerFriendly) { public void allocateRegisters(MethodReference method, Program program, boolean debuggerFriendly) {
insertPhiArgumentsCopies(program); insertPhiArgumentsCopies(program);
InterferenceGraphBuilder interferenceBuilder = new InterferenceGraphBuilder(); InterferenceGraphBuilder interferenceBuilder = new InterferenceGraphBuilder();
LivenessAnalyzer liveness = new LivenessAnalyzer(); LivenessAnalyzer liveness = new LivenessAnalyzer();
@ -60,7 +59,7 @@ public class RegisterAllocator {
for (int cls : classArray) { for (int cls : classArray) {
maxClass = Math.max(maxClass, cls + 1); maxClass = Math.max(maxClass, cls + 1);
} }
int[] categories = getVariableCategories(program, method.getReference()); int[] categories = getVariableCategories(program, method);
String[] names = getVariableNames(program, debuggerFriendly); String[] names = getVariableNames(program, debuggerFriendly);
colorer.colorize(MutableGraphNode.toGraph(interferenceGraph), colors, categories, names); colorer.colorize(MutableGraphNode.toGraph(interferenceGraph), colors, categories, names);

View File

@ -29,6 +29,7 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.teavm.cache.AlwaysStaleCacheStatus; import org.teavm.cache.AlwaysStaleCacheStatus;
@ -44,6 +45,7 @@ import org.teavm.dependency.DependencyListener;
import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.Linker; import org.teavm.dependency.Linker;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.dependency.MethodDependencyInfo;
import org.teavm.diagnostics.AccumulationDiagnostics; import org.teavm.diagnostics.AccumulationDiagnostics;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.diagnostics.ProblemProvider; import org.teavm.diagnostics.ProblemProvider;
@ -52,6 +54,9 @@ import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
@ -141,6 +146,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
private ProgramDependencyExtractor programDependencyExtractor = new ProgramDependencyExtractor(); private ProgramDependencyExtractor programDependencyExtractor = new ProgramDependencyExtractor();
private List<Predicate<MethodReference>> additionalVirtualMethods = new ArrayList<>(); private List<Predicate<MethodReference>> additionalVirtualMethods = new ArrayList<>();
private int lastKnownClasses; private int lastKnownClasses;
private int compileProgressReportStart;
private int compileProgressReportLimit;
private int compileProgressLimit;
private int compileProgressValue;
TeaVM(TeaVMBuilder builder) { TeaVM(TeaVMBuilder builder) {
target = builder.target; target = builder.target;
@ -378,63 +387,97 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass); cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
dependencyAnalyzer.setInterruptor(null); dependencyAnalyzer.setInterruptor(null);
// Link
if (wasCancelled()) {
return;
}
ListableClassHolderSource classSet = link(dependencyAnalyzer);
writtenClasses = classSet;
if (wasCancelled()) { if (wasCancelled()) {
return; return;
} }
// Optimize and allocate registers boolean isLazy = optimizationLevel == TeaVMOptimizationLevel.SIMPLE;
int maxOptimizationProgress = classSet.getClassNames().size(); ListableClassHolderSource classSet;
if (optimizationLevel == TeaVMOptimizationLevel.ADVANCED) { if (isLazy) {
maxOptimizationProgress *= 2; initCompileProgress(1000);
} else if (optimizationLevel == TeaVMOptimizationLevel.FULL) { classSet = lazyPipeline();
maxOptimizationProgress *= 3; } else {
} initCompileProgress(500);
reportPhase(TeaVMPhase.OPTIMIZATION, maxOptimizationProgress); classSet = eagerPipeline();
int progress = 0;
if (optimizationLevel != TeaVMOptimizationLevel.SIMPLE) {
progress = devirtualize(progress, classSet, dependencyAnalyzer);
if (wasCancelled()) { if (wasCancelled()) {
return; return;
} }
} }
dependencyAnalyzer.cleanup();
progress = inline(progress, classSet, dependencyAnalyzer);
if (wasCancelled()) {
return;
}
optimize(progress, classSet);
if (wasCancelled()) { if (wasCancelled()) {
return; return;
} }
// Render // Render
try { try {
reportPhase(TeaVMPhase.RENDERING, 1000); if (!isLazy) {
compileProgressReportStart = 500;
compileProgressReportLimit = 1000;
}
target.emit(classSet, buildTarget, outputName); target.emit(classSet, buildTarget, outputName);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Error generating output files", e); throw new RuntimeException("Error generating output files", e);
} }
} }
private void initCompileProgress(int limit) {
reportPhase(TeaVMPhase.COMPILING, 1000);
compileProgressReportStart = 0;
compileProgressReportLimit = limit;
}
private ListableClassHolderSource eagerPipeline() {
compileProgressValue = 0;
compileProgressLimit = dependencyAnalyzer.getReachableClasses().size();
if (optimizationLevel == TeaVMOptimizationLevel.ADVANCED) {
compileProgressLimit *= 3;
} else if (optimizationLevel == TeaVMOptimizationLevel.FULL) {
compileProgressLimit *= 4;
} else {
compileProgressLimit *= 2;
}
ListableClassHolderSource classSet = link(dependencyAnalyzer);
writtenClasses = classSet;
if (wasCancelled()) {
return null;
}
if (optimizationLevel != TeaVMOptimizationLevel.SIMPLE) {
devirtualize(classSet);
if (wasCancelled()) {
return null;
}
}
dependencyAnalyzer.cleanupTypes();
inline(classSet);
if (wasCancelled()) {
return null;
}
// Optimize and allocate registers
optimize(classSet);
if (wasCancelled()) {
return null;
}
return classSet;
}
private ListableClassHolderSource lazyPipeline() {
return new PostProcessingClassHolderSource();
}
public ListableClassHolderSource link(DependencyAnalyzer dependency) { public ListableClassHolderSource link(DependencyAnalyzer dependency) {
reportPhase(TeaVMPhase.LINKING, dependency.getReachableClasses().size()); Linker linker = new Linker(dependency);
Linker linker = new Linker();
MutableClassHolderSource cutClasses = new MutableClassHolderSource(); MutableClassHolderSource cutClasses = new MutableClassHolderSource();
MissingItemsProcessor missingItemsProcessor = new MissingItemsProcessor(dependency, MissingItemsProcessor missingItemsProcessor = new MissingItemsProcessor(dependency,
dependency.getClassHierarchy(), diagnostics); dependency.getClassHierarchy(), diagnostics);
if (wasCancelled()) { if (wasCancelled()) {
return cutClasses; return cutClasses;
} }
int index = 0;
if (wasCancelled()) { if (wasCancelled()) {
return cutClasses; return cutClasses;
@ -442,14 +485,13 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
for (String className : dependency.getReachableClasses()) { for (String className : dependency.getReachableClasses()) {
ClassReader clsReader = dependency.getClassSource().get(className); ClassReader clsReader = dependency.getClassSource().get(className);
if (clsReader == null) { if (clsReader != null) {
continue; ClassHolder cls = ModelUtils.copyClass(clsReader);
cutClasses.putClassHolder(cls);
missingItemsProcessor.processClass(cls);
linker.link(cls);
} }
ClassHolder cls = ModelUtils.copyClass(clsReader); reportCompileProgress(++compileProgressValue);
cutClasses.putClassHolder(cls);
missingItemsProcessor.processClass(cls);
linker.link(dependency, cls);
reportProgress(++index);
if (wasCancelled()) { if (wasCancelled()) {
break; break;
} }
@ -469,12 +511,17 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
} }
private int devirtualize(int progress, ListableClassHolderSource classes, DependencyInfo dependency) { private void reportCompileProgress(int progress) {
reportProgress(compileProgressReportStart
+ progress * (compileProgressReportLimit - compileProgressReportStart) / compileProgressLimit);
}
private void devirtualize(ListableClassHolderSource classes) {
if (wasCancelled()) { if (wasCancelled()) {
return progress; return;
} }
Devirtualization devirtualization = new Devirtualization(dependency, classes); Devirtualization devirtualization = new Devirtualization(dependencyAnalyzer,
int index = 0; dependencyAnalyzer.getClassHierarchy());
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
ClassHolder cls = classes.get(className); ClassHolder cls = classes.get(className);
for (MethodHolder method : cls.getMethods()) { for (MethodHolder method : cls.getMethods()) {
@ -482,34 +529,33 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
devirtualization.apply(method); devirtualization.apply(method);
} }
} }
reportProgress(++index); reportCompileProgress(++compileProgressValue);
if (wasCancelled()) { if (wasCancelled()) {
break; break;
} }
} }
virtualMethods = devirtualization.getVirtualMethods(); virtualMethods = devirtualization.getVirtualMethods();
return progress;
} }
private int inline(int progress, ListableClassHolderSource classes, DependencyInfo dependencyInfo) { private void inline(ListableClassHolderSource classes) {
if (optimizationLevel != TeaVMOptimizationLevel.FULL) { if (optimizationLevel != TeaVMOptimizationLevel.FULL) {
return progress; return;
} }
Map<MethodReference, Program> inlinedPrograms = new HashMap<>(); Map<MethodReference, Program> inlinedPrograms = new HashMap<>();
Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyInfo); Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer);
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
ClassHolder cls = classes.get(className); ClassHolder cls = classes.get(className);
for (MethodHolder method : cls.getMethods()) { for (MethodHolder method : cls.getMethods()) {
if (method.getProgram() != null) { if (method.getProgram() != null) {
Program program = ProgramUtils.copy(method.getProgram()); Program program = ProgramUtils.copy(method.getProgram());
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method, classes); MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method);
inlining.apply(program, method.getReference()); inlining.apply(program, method.getReference());
new UnusedVariableElimination().optimize(context, program); new UnusedVariableElimination().optimize(context, program);
inlinedPrograms.put(method.getReference(), program); inlinedPrograms.put(method.getReference(), program);
} }
} }
reportProgress(++progress); reportCompileProgress(++compileProgressValue);
if (wasCancelled()) { if (wasCancelled()) {
break; break;
} }
@ -523,25 +569,22 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
} }
} }
return progress;
} }
private int optimize(int progress, ListableClassHolderSource classSource) { private void optimize(ListableClassHolderSource classSource) {
for (String className : classSource.getClassNames()) { for (String className : classSource.getClassNames()) {
ClassHolder cls = classSource.get(className); ClassHolder cls = classSource.get(className);
for (MethodHolder method : cls.getMethods()) { for (MethodHolder method : cls.getMethods()) {
processMethod(method, classSource); optimizeMethod(method);
} }
reportProgress(++progress); reportCompileProgress(++compileProgressValue);
if (wasCancelled()) { if (wasCancelled()) {
break; break;
} }
} }
return progress;
} }
private void processMethod(MethodHolder method, ListableClassReaderSource classSource) { private void optimizeMethod(MethodHolder method) {
if (method.getProgram() == null) { if (method.getProgram() == null) {
return; return;
} }
@ -549,36 +592,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
Program optimizedProgram = !cacheStatus.isStaleMethod(method.getReference()) Program optimizedProgram = !cacheStatus.isStaleMethod(method.getReference())
? programCache.get(method.getReference(), cacheStatus) ? programCache.get(method.getReference(), cacheStatus)
: null; : null;
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method, classSource);
if (optimizedProgram == null) { if (optimizedProgram == null) {
optimizedProgram = ProgramUtils.copy(method.getProgram()); optimizedProgram = optimizeMethodCacheMiss(method, ProgramUtils.copy(method.getProgram()));
target.beforeOptimizations(optimizedProgram, method, classSource);
if (optimizedProgram.basicBlockCount() > 0) {
boolean changed;
do {
changed = false;
for (MethodOptimization optimization : getOptimizations()) {
try {
changed |= optimization.optimize(context, optimizedProgram);
} catch (Exception | AssertionError e) {
ListingBuilder listingBuilder = new ListingBuilder();
String listing = listingBuilder.buildListing(optimizedProgram, "");
System.err.println("Error optimizing program for method " + method.getReference()
+ ":\n" + listing);
throw new RuntimeException(e);
}
}
} while (changed);
target.afterOptimizations(optimizedProgram, method, classSource);
if (target.requiresRegisterAllocation()) {
RegisterAllocator allocator = new RegisterAllocator();
allocator.allocateRegisters(method, optimizedProgram,
optimizationLevel == TeaVMOptimizationLevel.SIMPLE);
}
}
Program finalProgram = optimizedProgram; Program finalProgram = optimizedProgram;
programCache.store(method.getReference(), finalProgram, programCache.store(method.getReference(), finalProgram,
() -> programDependencyExtractor.extractDependencies(finalProgram)); () -> programDependencyExtractor.extractDependencies(finalProgram));
@ -586,13 +601,43 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
method.setProgram(optimizedProgram); method.setProgram(optimizedProgram);
} }
private Program optimizeMethodCacheMiss(MethodHolder method, Program optimizedProgram) {
target.beforeOptimizations(optimizedProgram, method);
if (optimizedProgram.basicBlockCount() > 0) {
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method);
boolean changed;
do {
changed = false;
for (MethodOptimization optimization : getOptimizations()) {
try {
changed |= optimization.optimize(context, optimizedProgram);
} catch (Exception | AssertionError e) {
ListingBuilder listingBuilder = new ListingBuilder();
String listing = listingBuilder.buildListing(optimizedProgram, "");
System.err.println("Error optimizing program for method " + method.getReference()
+ ":\n" + listing);
throw new RuntimeException(e);
}
}
} while (changed);
target.afterOptimizations(optimizedProgram, method);
if (target.requiresRegisterAllocation()) {
RegisterAllocator allocator = new RegisterAllocator();
allocator.allocateRegisters(method.getReference(), optimizedProgram,
optimizationLevel == TeaVMOptimizationLevel.SIMPLE);
}
}
return optimizedProgram;
}
class MethodOptimizationContextImpl implements MethodOptimizationContext { class MethodOptimizationContextImpl implements MethodOptimizationContext {
private MethodReader method; private MethodReader method;
private ClassReaderSource classSource;
MethodOptimizationContextImpl(MethodReader method, ClassReaderSource classSource) { MethodOptimizationContextImpl(MethodReader method) {
this.method = method; this.method = method;
this.classSource = classSource;
} }
@Override @Override
@ -604,11 +649,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
public DependencyInfo getDependencyInfo() { public DependencyInfo getDependencyInfo() {
return dependencyAnalyzer; return dependencyAnalyzer;
} }
@Override
public ClassReaderSource getClassSource() {
return classSource;
}
} }
private List<MethodOptimization> getOptimizations() { private List<MethodOptimization> getOptimizations() {
@ -742,8 +782,72 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
@Override @Override
public TeaVMProgressFeedback reportProgress(int progres) { public TeaVMProgressFeedback reportProgress(int progress) {
return progressListener.progressReached(progres); progress = progress * (compileProgressReportLimit - compileProgressReportStart) / 1000
+ compileProgressReportStart;
return progressListener.progressReached(progress);
} }
}; };
class PostProcessingClassHolderSource implements ListableClassHolderSource {
private Linker linker = new Linker(dependencyAnalyzer);
private MissingItemsProcessor missingItemsProcessor = new MissingItemsProcessor(dependencyAnalyzer,
dependencyAnalyzer.getClassHierarchy(), diagnostics);
private Map<String, ClassHolder> cache = new HashMap<>();
private Set<String> classNames = Collections.unmodifiableSet(new HashSet<>(
dependencyAnalyzer.getReachableClasses()));
@Override
public ClassHolder get(String name) {
return cache.computeIfAbsent(name, className -> {
ClassReader classReader = dependencyAnalyzer.getClassSource().get(className);
if (classReader == null) {
return null;
}
ClassHolder cls = ModelUtils.copyClass(classReader, false);
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
FieldReference fieldRef = new FieldReference(cls.getName(), field.getName());
if (dependencyAnalyzer.getField(fieldRef) == null) {
cls.removeField(field);
}
}
Function<MethodHolder, Program> programSupplier = method -> {
Program program = !cacheStatus.isStaleMethod(method.getReference())
? programCache.get(method.getReference(), cacheStatus)
: null;
if (program == null) {
program = ProgramUtils.copy(classReader.getMethod(method.getDescriptor()).getProgram());
missingItemsProcessor.processMethod(method.getReference(), program);
linker.link(method.getReference(), program);
program = optimizeMethodCacheMiss(method, program);
Program finalProgram = program;
programCache.store(method.getReference(), finalProgram,
() -> programDependencyExtractor.extractDependencies(finalProgram));
}
return program;
};
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
MethodDependencyInfo methodDep = dependencyAnalyzer.getMethod(method.getReference());
if (methodDep == null) {
cls.removeMethod(method);
} else if (!methodDep.isUsed()) {
method.getModifiers().add(ElementModifier.ABSTRACT);
} else {
MethodReader methodReader = classReader.getMethod(method.getDescriptor());
if (methodReader != null && methodReader.getProgram() != null) {
method.setProgramSupplier(programSupplier);
}
}
}
return cls;
});
}
@Override
public Set<String> getClassNames() {
return classNames;
}
}
} }

View File

@ -17,7 +17,5 @@ package org.teavm.vm;
public enum TeaVMPhase { public enum TeaVMPhase {
DEPENDENCY_ANALYSIS, DEPENDENCY_ANALYSIS,
LINKING, COMPILING
OPTIMIZATION,
RENDERING
} }

View File

@ -21,7 +21,6 @@ import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyListener;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.vm.spi.TeaVMHostExtension; import org.teavm.vm.spi.TeaVMHostExtension;
@ -39,9 +38,9 @@ public interface TeaVMTarget {
void contributeDependencies(DependencyAnalyzer dependencyAnalyzer); void contributeDependencies(DependencyAnalyzer dependencyAnalyzer);
void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource); void beforeOptimizations(Program program, MethodReader method);
void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource); void afterOptimizations(Program program, MethodReader method);
void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException; void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException;

View File

@ -21,7 +21,6 @@ import org.junit.Test;
import org.junit.rules.TestName; import org.junit.rules.TestName;
import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyInfo;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ListingParseUtils; import org.teavm.model.ListingParseUtils;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
@ -111,11 +110,6 @@ public class ScalarReplacementTest {
public DependencyInfo getDependencyInfo() { public DependencyInfo getDependencyInfo() {
return null; return null;
} }
@Override
public ClassReaderSource getClassSource() {
return null;
}
}; };
new ScalarReplacement().optimize(context, program); new ScalarReplacement().optimize(context, program);

View File

@ -426,14 +426,8 @@ public final class TeaVMRunner {
case DEPENDENCY_ANALYSIS: case DEPENDENCY_ANALYSIS:
System.out.print("Analyzing classes..."); System.out.print("Analyzing classes...");
break; break;
case LINKING: case COMPILING:
System.out.print("Linking methods..."); System.out.print("Compiling...");
break;
case OPTIMIZATION:
System.out.print("Optimizing code...");
break;
case RENDERING:
System.out.print("Generating output...");
break; break;
} }
currentPhase = phase; currentPhase = phase;

View File

@ -981,18 +981,10 @@ public class CodeServlet extends HttpServlet {
switch (phase) { switch (phase) {
case DEPENDENCY_ANALYSIS: case DEPENDENCY_ANALYSIS:
start = 0; start = 0;
end = 400;
break;
case LINKING:
start = 400;
end = 500; end = 500;
break; break;
case OPTIMIZATION: case COMPILING:
start = 500; start = 500;
end = 750;
break;
case RENDERING:
start = 750;
end = 1000; end = 1000;
break; break;
} }

View File

@ -180,12 +180,8 @@ class TeaVMBuild {
switch (phase) { switch (phase) {
case DEPENDENCY_ANALYSIS: case DEPENDENCY_ANALYSIS:
return "Discovering classes to compile"; return "Discovering classes to compile";
case LINKING: case COMPILING:
return "Resolving method invocations"; return "Compiling";
case OPTIMIZATION:
return "Optimizing code";
case RENDERING:
return "Building JS file";
default: default:
throw new AssertionError(); throw new AssertionError();
} }