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
public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
public void beforeOptimizations(Program program, MethodReader method) {
nullCheckInsertion.transformProgram(program, method.getReference());
}
@Override
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
public void afterOptimizations(Program program, MethodReader method) {
clinitInsertionTransformer.apply(method, program);
classInitializerEliminator.apply(program);
classInitializerTransformer.transform(program);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,19 +17,33 @@ package org.teavm.model.util;
import java.util.ArrayList;
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 {
private ModelUtils() {
}
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.getModifiers().addAll(original.readModifiers());
target.setParent(original.getParent());
target.getInterfaces().addAll(original.getInterfaces());
for (MethodReader method : original.getMethods()) {
target.addMethod(copyMethod(method));
target.addMethod(copyMethod(method, withPrograms));
}
for (FieldReader field : original.getFields()) {
target.addField(copyField(field));
@ -40,14 +54,22 @@ public final class ModelUtils {
}
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) {
return copyMethod(method, true);
}
public static MethodHolder copyMethod(MethodReader method, boolean withProgram) {
MethodHolder copy = new MethodHolder(method.getDescriptor());
copy.setLevel(method.getLevel());
copy.getModifiers().addAll(method.readModifiers());
if (method.getProgram() != null) {
if (method.getProgram() != null && withProgram) {
copy.setProgram(ProgramUtils.copy(method.getProgram()));
}
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.Incoming;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program;
@ -36,7 +35,7 @@ import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.JumpInstruction;
public class RegisterAllocator {
public void allocateRegisters(MethodReader method, Program program, boolean debuggerFriendly) {
public void allocateRegisters(MethodReference method, Program program, boolean debuggerFriendly) {
insertPhiArgumentsCopies(program);
InterferenceGraphBuilder interferenceBuilder = new InterferenceGraphBuilder();
LivenessAnalyzer liveness = new LivenessAnalyzer();
@ -60,7 +59,7 @@ public class RegisterAllocator {
for (int cls : classArray) {
maxClass = Math.max(maxClass, cls + 1);
}
int[] categories = getVariableCategories(program, method.getReference());
int[] categories = getVariableCategories(program, method);
String[] names = getVariableNames(program, debuggerFriendly);
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.ServiceLoader;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.teavm.cache.AlwaysStaleCacheStatus;
@ -44,6 +45,7 @@ import org.teavm.dependency.DependencyListener;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.Linker;
import org.teavm.dependency.MethodDependency;
import org.teavm.dependency.MethodDependencyInfo;
import org.teavm.diagnostics.AccumulationDiagnostics;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.diagnostics.ProblemProvider;
@ -52,6 +54,9 @@ import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader;
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.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor;
@ -141,6 +146,10 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
private ProgramDependencyExtractor programDependencyExtractor = new ProgramDependencyExtractor();
private List<Predicate<MethodReference>> additionalVirtualMethods = new ArrayList<>();
private int lastKnownClasses;
private int compileProgressReportStart;
private int compileProgressReportLimit;
private int compileProgressLimit;
private int compileProgressValue;
TeaVM(TeaVMBuilder builder) {
target = builder.target;
@ -378,63 +387,97 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
dependencyAnalyzer.setInterruptor(null);
// Link
if (wasCancelled()) {
return;
}
ListableClassHolderSource classSet = link(dependencyAnalyzer);
writtenClasses = classSet;
if (wasCancelled()) {
return;
}
// Optimize and allocate registers
int maxOptimizationProgress = classSet.getClassNames().size();
if (optimizationLevel == TeaVMOptimizationLevel.ADVANCED) {
maxOptimizationProgress *= 2;
} else if (optimizationLevel == TeaVMOptimizationLevel.FULL) {
maxOptimizationProgress *= 3;
}
reportPhase(TeaVMPhase.OPTIMIZATION, maxOptimizationProgress);
int progress = 0;
if (optimizationLevel != TeaVMOptimizationLevel.SIMPLE) {
progress = devirtualize(progress, classSet, dependencyAnalyzer);
boolean isLazy = optimizationLevel == TeaVMOptimizationLevel.SIMPLE;
ListableClassHolderSource classSet;
if (isLazy) {
initCompileProgress(1000);
classSet = lazyPipeline();
} else {
initCompileProgress(500);
classSet = eagerPipeline();
if (wasCancelled()) {
return;
}
}
dependencyAnalyzer.cleanup();
progress = inline(progress, classSet, dependencyAnalyzer);
if (wasCancelled()) {
return;
}
optimize(progress, classSet);
if (wasCancelled()) {
return;
}
// Render
try {
reportPhase(TeaVMPhase.RENDERING, 1000);
if (!isLazy) {
compileProgressReportStart = 500;
compileProgressReportLimit = 1000;
}
target.emit(classSet, buildTarget, outputName);
} catch (IOException 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) {
reportPhase(TeaVMPhase.LINKING, dependency.getReachableClasses().size());
Linker linker = new Linker();
Linker linker = new Linker(dependency);
MutableClassHolderSource cutClasses = new MutableClassHolderSource();
MissingItemsProcessor missingItemsProcessor = new MissingItemsProcessor(dependency,
dependency.getClassHierarchy(), diagnostics);
if (wasCancelled()) {
return cutClasses;
}
int index = 0;
if (wasCancelled()) {
return cutClasses;
@ -442,14 +485,13 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
for (String className : dependency.getReachableClasses()) {
ClassReader clsReader = dependency.getClassSource().get(className);
if (clsReader == null) {
continue;
if (clsReader != null) {
ClassHolder cls = ModelUtils.copyClass(clsReader);
cutClasses.putClassHolder(cls);
missingItemsProcessor.processClass(cls);
linker.link(cls);
}
ClassHolder cls = ModelUtils.copyClass(clsReader);
cutClasses.putClassHolder(cls);
missingItemsProcessor.processClass(cls);
linker.link(dependency, cls);
reportProgress(++index);
reportCompileProgress(++compileProgressValue);
if (wasCancelled()) {
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()) {
return progress;
return;
}
Devirtualization devirtualization = new Devirtualization(dependency, classes);
int index = 0;
Devirtualization devirtualization = new Devirtualization(dependencyAnalyzer,
dependencyAnalyzer.getClassHierarchy());
for (String className : classes.getClassNames()) {
ClassHolder cls = classes.get(className);
for (MethodHolder method : cls.getMethods()) {
@ -482,34 +529,33 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
devirtualization.apply(method);
}
}
reportProgress(++index);
reportCompileProgress(++compileProgressValue);
if (wasCancelled()) {
break;
}
}
virtualMethods = devirtualization.getVirtualMethods();
return progress;
}
private int inline(int progress, ListableClassHolderSource classes, DependencyInfo dependencyInfo) {
private void inline(ListableClassHolderSource classes) {
if (optimizationLevel != TeaVMOptimizationLevel.FULL) {
return progress;
return;
}
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()) {
ClassHolder cls = classes.get(className);
for (MethodHolder method : cls.getMethods()) {
if (method.getProgram() != null) {
Program program = ProgramUtils.copy(method.getProgram());
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method, classes);
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method);
inlining.apply(program, method.getReference());
new UnusedVariableElimination().optimize(context, program);
inlinedPrograms.put(method.getReference(), program);
}
}
reportProgress(++progress);
reportCompileProgress(++compileProgressValue);
if (wasCancelled()) {
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()) {
ClassHolder cls = classSource.get(className);
for (MethodHolder method : cls.getMethods()) {
processMethod(method, classSource);
optimizeMethod(method);
}
reportProgress(++progress);
reportCompileProgress(++compileProgressValue);
if (wasCancelled()) {
break;
}
}
return progress;
}
private void processMethod(MethodHolder method, ListableClassReaderSource classSource) {
private void optimizeMethod(MethodHolder method) {
if (method.getProgram() == null) {
return;
}
@ -549,36 +592,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
Program optimizedProgram = !cacheStatus.isStaleMethod(method.getReference())
? programCache.get(method.getReference(), cacheStatus)
: null;
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method, classSource);
if (optimizedProgram == null) {
optimizedProgram = 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);
}
}
optimizedProgram = optimizeMethodCacheMiss(method, ProgramUtils.copy(method.getProgram()));
Program finalProgram = optimizedProgram;
programCache.store(method.getReference(), finalProgram,
() -> programDependencyExtractor.extractDependencies(finalProgram));
@ -586,13 +601,43 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
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 {
private MethodReader method;
private ClassReaderSource classSource;
MethodOptimizationContextImpl(MethodReader method, ClassReaderSource classSource) {
MethodOptimizationContextImpl(MethodReader method) {
this.method = method;
this.classSource = classSource;
}
@Override
@ -604,11 +649,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
public DependencyInfo getDependencyInfo() {
return dependencyAnalyzer;
}
@Override
public ClassReaderSource getClassSource() {
return classSource;
}
}
private List<MethodOptimization> getOptimizations() {
@ -742,8 +782,72 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
}
@Override
public TeaVMProgressFeedback reportProgress(int progres) {
return progressListener.progressReached(progres);
public TeaVMProgressFeedback reportProgress(int progress) {
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 {
DEPENDENCY_ANALYSIS,
LINKING,
OPTIMIZATION,
RENDERING
COMPILING
}

View File

@ -21,7 +21,6 @@ import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyListener;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReader;
import org.teavm.model.Program;
import org.teavm.vm.spi.TeaVMHostExtension;
@ -39,9 +38,9 @@ public interface TeaVMTarget {
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;

View File

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

View File

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

View File

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

View File

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