From b4a172b8f795d30445bfd8e9c4dcd5191d38eacb Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Fri, 5 Sep 2014 11:26:19 +0400 Subject: [PATCH] Cache optimized programs with allocated registers --- .../java/org/teavm/javascript/Decompiler.java | 9 ++ .../org/teavm/model/InMemoryProgramCache.java | 37 +++++++ .../java/org/teavm/model/ProgramCache.java | 27 +++++ .../java/org/teavm/tooling/TeaVMTestTool.java | 12 ++- .../src/main/java/org/teavm/vm/TeaVM.java | 101 +++++++++++------- .../teavm/maven/BuildJavascriptTestMojo.java | 1 + 6 files changed, 144 insertions(+), 43 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/model/InMemoryProgramCache.java create mode 100644 teavm-core/src/main/java/org/teavm/model/ProgramCache.java diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index c0e920a3d..06c351f80 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -91,6 +91,15 @@ public class Decompiler { return result; } + public List getClassOrdering(Collection classNames) { + List sequence = new ArrayList<>(); + Set visited = new HashSet<>(); + for (String className : classNames) { + orderClasses(className, visited, sequence); + } + return sequence; + } + public void addGenerator(MethodReference method, Generator generator) { generators.put(method, generator); } diff --git a/teavm-core/src/main/java/org/teavm/model/InMemoryProgramCache.java b/teavm-core/src/main/java/org/teavm/model/InMemoryProgramCache.java new file mode 100644 index 000000000..e9046950a --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/InMemoryProgramCache.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.model; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Alexey Andreev + */ +public class InMemoryProgramCache implements ProgramCache { + private Map cache = new HashMap<>(); + + @Override + public Program get(MethodReference method) { + return cache.get(method); + } + + @Override + public void store(MethodReference method, Program program) { + cache.put(method, program); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/ProgramCache.java b/teavm-core/src/main/java/org/teavm/model/ProgramCache.java new file mode 100644 index 000000000..10f641c80 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/ProgramCache.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.model; + + +/** + * + * @author Alexey Andreev + */ +public interface ProgramCache { + Program get(MethodReference method); + + void store(MethodReference method, Program program); +} diff --git a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java index 7d62967d2..c65678caf 100644 --- a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java +++ b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTestTool.java @@ -53,6 +53,7 @@ public class TeaVMTestTool { private TeaVMToolLog log = new EmptyTeaVMToolLog(); private boolean incremental; private RegularMethodNodeCache astCache; + private ProgramCache programCache; public File getOutputDir() { return outputDir; @@ -144,8 +145,10 @@ public class TeaVMTestTool { resourceToFile(prefix + "/res/toggle-small-expand.png", "res/toggle-small-expand.png"); resourceToFile(prefix + "/res/toggle-small.png", "res/toggle-small.png"); resourceToFile(prefix + "/junit.html", "junit.html"); - final ClassHolderSource classSource = new PreOptimizingClassHolderSource( - new ClasspathClassHolderSource(classLoader)); + ClassHolderSource classSource = new ClasspathClassHolderSource(classLoader); + if (incremental) { + classSource = new PreOptimizingClassHolderSource(classSource); + } for (String testClass : testClasses) { ClassHolder classHolder = classSource.get(testClass); if (classHolder == null) { @@ -158,6 +161,7 @@ public class TeaVMTestTool { astCache = new EmptyRegularMethodNodeCache(); if (incremental) { astCache = new InMemoryRegularMethodNodeCache(); + programCache = new InMemoryProgramCache(); } File allTestsFile = new File(outputDir, "tests/all.js"); try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) { @@ -221,11 +225,12 @@ public class TeaVMTestTool { executor = threadedExecutor; } for (final MethodReference method : testMethods) { + final ClassHolderSource builderClassSource = classSource; executor.execute(new Runnable() { @Override public void run() { log.debug("Building test for " + method); try { - decompileClassesForTest(classLoader, new CopyClassHolderSource(classSource), method, + decompileClassesForTest(classLoader, new CopyClassHolderSource(builderClassSource), method, fileNames.get(method)); } catch (IOException e) { log.error("Error generating JavaScript", e); @@ -300,6 +305,7 @@ public class TeaVMTestTool { .build(); vm.setIncremental(incremental); vm.setAstCache(astCache); + vm.setProgramCache(programCache); vm.setProperties(properties); vm.setMinifying(minifying); vm.installPlugins(); diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java index 107613c37..f4194205a 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -30,8 +30,7 @@ import org.teavm.model.*; import org.teavm.model.util.ListingBuilder; import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.RegisterAllocator; -import org.teavm.optimization.ClassSetOptimizer; -import org.teavm.optimization.Devirtualization; +import org.teavm.optimization.*; import org.teavm.vm.spi.RendererListener; import org.teavm.vm.spi.TeaVMHost; import org.teavm.vm.spi.TeaVMPlugin; @@ -80,6 +79,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { private Map, Object> services = new HashMap<>(); private Properties properties = new Properties(); private DebugInformationEmitter debugEmitter; + private ProgramCache programCache; private RegularMethodNodeCache astCache = new EmptyRegularMethodNodeCache(); private boolean incremental; @@ -172,6 +172,14 @@ public class TeaVM implements TeaVMHost, ServiceRepository { this.astCache = methodAstCache; } + public ProgramCache getProgramCache() { + return programCache; + } + + public void setProgramCache(ProgramCache programCache) { + this.programCache = programCache; + } + public boolean isIncremental() { return incremental; } @@ -333,27 +341,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository { if (!incremental) { devirtualize(classSet, dependencyChecker); } - ClassSetOptimizer optimizer = new ClassSetOptimizer(); - optimizer.optimizeAll(classSet); - allocateRegisters(classSet); - if (bytecodeLogging) { - try { - logBytecode(new PrintWriter(new OutputStreamWriter(logStream, "UTF-8")), classSet); - } catch (UnsupportedEncodingException e) { - // Just don't do anything - } - } - // Decompile - Decompiler decompiler = new Decompiler(classSet, classLoader); - decompiler.setRegularMethodCache(incremental ? astCache : null); - for (Map.Entry entry : methodGenerators.entrySet()) { - decompiler.addGenerator(entry.getKey(), entry.getValue()); - } - for (MethodReference injectedMethod : methodInjectors.keySet()) { - decompiler.addMethodToPass(injectedMethod); - } - List clsNodes = decompiler.decompile(classSet.getClassNames()); + List clsNodes = modelToAst(classSet); // Render DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, dependencyChecker.getClassSource()); @@ -440,29 +429,61 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } } - private void allocateRegisters(ListableClassHolderSource classes) { - for (String className : classes.getClassNames()) { - ClassHolder cls = classes.get(className); - for (final MethodHolder method : cls.getMethods()) { - if (method.getProgram() != null && method.getProgram().basicBlockCount() > 0) { - RegisterAllocator allocator = new RegisterAllocator(); - Program program = ProgramUtils.copy(method.getProgram()); - allocator.allocateRegisters(method, program); - method.setProgram(program); - } - } + private List modelToAst(ListableClassHolderSource classes) { + Decompiler decompiler = new Decompiler(classes, classLoader); + decompiler.setRegularMethodCache(incremental ? astCache : null); + + for (Map.Entry entry : methodGenerators.entrySet()) { + decompiler.addGenerator(entry.getKey(), entry.getValue()); } + for (MethodReference injectedMethod : methodInjectors.keySet()) { + decompiler.addMethodToPass(injectedMethod); + } + List classOrder = decompiler.getClassOrdering(classes.getClassNames()); + List classNodes = new ArrayList<>(); + try (PrintWriter bytecodeLogger = bytecodeLogging ? + new PrintWriter(new OutputStreamWriter(logStream, "UTF-8")) : null) { + for (String className : classOrder) { + ClassHolder cls = classes.get(className); + for (MethodHolder method : cls.getMethods()) { + processMethod(method); + if (bytecodeLogging) { + logMethodBytecode(bytecodeLogger, method); + } + } + classNodes.add(decompiler.decompile(cls)); + } + } catch (UnsupportedEncodingException e) { + throw new AssertionError("UTF-8 is expected to be supported"); + } + return classNodes; } - private void logBytecode(PrintWriter writer, ListableClassHolderSource classes) { - for (String className : classes.getClassNames()) { - ClassHolder classHolder = classes.get(className); - printModifiers(writer, classHolder); - writer.println("class " + className); - for (MethodHolder method : classHolder.getMethods()) { - logMethodBytecode(writer, method); + private void processMethod(MethodHolder method) { + if (method.getProgram() == null) { + return; + } + Program optimizedProgram = incremental && programCache != null ? + programCache.get(method.getReference()) : null; + if (optimizedProgram == null) { + optimizedProgram = ProgramUtils.copy(method.getProgram()); + if (optimizedProgram.basicBlockCount() > 0) { + for (MethodOptimization optimization : getOptimizations()) { + optimization.optimize(method, optimizedProgram); + } + RegisterAllocator allocator = new RegisterAllocator(); + allocator.allocateRegisters(method, optimizedProgram); + } + if (incremental && programCache != null) { + programCache.store(method.getReference(), optimizedProgram); } } + method.setProgram(optimizedProgram); + } + + private List getOptimizations() { + return Arrays.asList(new ArrayUnwrapMotion(), new LoopInvariantMotion(), + new GlobalValueNumbering(), new UnusedVariableElimination()); } private void logMethodBytecode(PrintWriter writer, MethodHolder method) { diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java index bc8117e40..d72fc1a3e 100644 --- a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java +++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java @@ -163,6 +163,7 @@ public class BuildJavascriptTestMojo extends AbstractMojo { tool.setOutputDir(outputDir); tool.setNumThreads(numThreads); tool.setMinifying(minifying); + tool.setIncremental(incremental); if (properties != null) { tool.getProperties().putAll(properties); }