From 35ca7fd15266b8ae83344a522acee787395c93a9 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 30 Jan 2019 19:00:07 +0300 Subject: [PATCH] Add lazy compilation pipeline that can work a little bit faster in incremental compiler --- .../java/org/teavm/backend/c/CTarget.java | 4 +- .../backend/javascript/JavaScriptTarget.java | 7 +- .../org/teavm/backend/wasm/WasmTarget.java | 4 +- .../teavm/dependency/DependencyAnalyzer.java | 13 +- .../java/org/teavm/dependency/Linker.java | 16 +- .../java/org/teavm/model/MethodHolder.java | 19 ++ .../model/optimization/Devirtualization.java | 9 +- .../MethodOptimizationContext.java | 3 - .../UnusedVariableElimination.java | 3 - .../model/util/MissingItemsProcessor.java | 18 +- .../java/org/teavm/model/util/ModelUtils.java | 30 +- .../teavm/model/util/RegisterAllocator.java | 5 +- core/src/main/java/org/teavm/vm/TeaVM.java | 290 ++++++++++++------ .../main/java/org/teavm/vm/TeaVMPhase.java | 4 +- .../main/java/org/teavm/vm/TeaVMTarget.java | 5 +- .../test/ScalarReplacementTest.java | 6 - .../main/java/org/teavm/cli/TeaVMRunner.java | 10 +- .../java/org/teavm/devserver/CodeServlet.java | 10 +- .../java/org/teavm/idea/jps/TeaVMBuild.java | 8 +- 19 files changed, 292 insertions(+), 172 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 06621c586..ea94f0ff0 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -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); diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index c92776325..0bd19358c 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -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; } - }; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index a086db882..fb8f4df19 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -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); diff --git a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java index f322f87cb..c4293351e 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java @@ -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 { diff --git a/core/src/main/java/org/teavm/dependency/Linker.java b/core/src/main/java/org/teavm/dependency/Linker.java index 78e604ab0..e3a37ec35 100644 --- a/core/src/main/java/org/teavm/dependency/Linker.java +++ b/core/src/main/java/org/teavm/dependency/Linker.java @@ -34,8 +34,13 @@ import org.teavm.model.instructions.PutFieldInstruction; public class Linker { private static final MethodDescriptor clinitDescriptor = new MethodDescriptor("", 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); diff --git a/core/src/main/java/org/teavm/model/MethodHolder.java b/core/src/main/java/org/teavm/model/MethodHolder.java index 151d448a4..a35c95c46 100644 --- a/core/src/main/java/org/teavm/model/MethodHolder.java +++ b/core/src/main/java/org/teavm/model/MethodHolder.java @@ -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 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 programSupplier) { + if (this.program != null) { + this.program.setMethod(null); + } + this.program = null; + this.programSupplier = programSupplier; + } + @Override public AnnotationValue getAnnotationDefault() { return annotationDefault; diff --git a/core/src/main/java/org/teavm/model/optimization/Devirtualization.java b/core/src/main/java/org/teavm/model/optimization/Devirtualization.java index c6a915fb5..2774896e3 100644 --- a/core/src/main/java/org/teavm/model/optimization/Devirtualization.java +++ b/core/src/main/java/org/teavm/model/optimization/Devirtualization.java @@ -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 virtualMethods = new HashSet<>(); private Set 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; } diff --git a/core/src/main/java/org/teavm/model/optimization/MethodOptimizationContext.java b/core/src/main/java/org/teavm/model/optimization/MethodOptimizationContext.java index 0151f6e2c..d29e74ebc 100644 --- a/core/src/main/java/org/teavm/model/optimization/MethodOptimizationContext.java +++ b/core/src/main/java/org/teavm/model/optimization/MethodOptimizationContext.java @@ -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(); } diff --git a/core/src/main/java/org/teavm/model/optimization/UnusedVariableElimination.java b/core/src/main/java/org/teavm/model/optimization/UnusedVariableElimination.java index 42d3319d9..aa8a7c4f0 100644 --- a/core/src/main/java/org/teavm/model/optimization/UnusedVariableElimination.java +++ b/core/src/main/java/org/teavm/model/optimization/UnusedVariableElimination.java @@ -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]; diff --git a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java index abc64f8ae..40c0b350f 100644 --- a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java +++ b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java @@ -30,7 +30,7 @@ public class MissingItemsProcessor { private ClassHierarchy hierarchy; private Diagnostics diagnostics; private List instructionsToAdd = new ArrayList<>(); - private MethodHolder methodHolder; + private MethodReference methodRef; private Program program; private Collection reachableClasses; private Collection 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; diff --git a/core/src/main/java/org/teavm/model/util/ModelUtils.java b/core/src/main/java/org/teavm/model/util/ModelUtils.java index d71e08071..c0ed52820 100644 --- a/core/src/main/java/org/teavm/model/util/ModelUtils.java +++ b/core/src/main/java/org/teavm/model/util/ModelUtils.java @@ -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()); diff --git a/core/src/main/java/org/teavm/model/util/RegisterAllocator.java b/core/src/main/java/org/teavm/model/util/RegisterAllocator.java index 1e606ab3e..364c8e525 100644 --- a/core/src/main/java/org/teavm/model/util/RegisterAllocator.java +++ b/core/src/main/java/org/teavm/model/util/RegisterAllocator.java @@ -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); diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 8a0957321..c126c71ad 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -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> 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 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 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 cache = new HashMap<>(); + private Set 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 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 getClassNames() { + return classNames; + } + } } diff --git a/core/src/main/java/org/teavm/vm/TeaVMPhase.java b/core/src/main/java/org/teavm/vm/TeaVMPhase.java index 1b6688099..4ae4f6edc 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMPhase.java +++ b/core/src/main/java/org/teavm/vm/TeaVMPhase.java @@ -17,7 +17,5 @@ package org.teavm.vm; public enum TeaVMPhase { DEPENDENCY_ANALYSIS, - LINKING, - OPTIMIZATION, - RENDERING + COMPILING } diff --git a/core/src/main/java/org/teavm/vm/TeaVMTarget.java b/core/src/main/java/org/teavm/vm/TeaVMTarget.java index 2d2be8522..d0d17f22a 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTarget.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTarget.java @@ -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; diff --git a/core/src/test/java/org/teavm/model/optimization/test/ScalarReplacementTest.java b/core/src/test/java/org/teavm/model/optimization/test/ScalarReplacementTest.java index 4817fc3c7..2087e4d96 100644 --- a/core/src/test/java/org/teavm/model/optimization/test/ScalarReplacementTest.java +++ b/core/src/test/java/org/teavm/model/optimization/test/ScalarReplacementTest.java @@ -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); diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java index 06ef798d8..ac56034e6 100644 --- a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java +++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java @@ -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; diff --git a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java index aaa0f9d97..e7f0547ae 100644 --- a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java +++ b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java @@ -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; } diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java index 5b83d7ea1..ac8d0ddda 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java @@ -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(); }