Cache optimized programs with allocated registers

This commit is contained in:
konsoletyper 2014-09-05 11:26:19 +04:00
parent 520008913e
commit b4a172b8f7
6 changed files with 144 additions and 43 deletions

View File

@ -91,6 +91,15 @@ public class Decompiler {
return result; return result;
} }
public List<String> getClassOrdering(Collection<String> classNames) {
List<String> sequence = new ArrayList<>();
Set<String> visited = new HashSet<>();
for (String className : classNames) {
orderClasses(className, visited, sequence);
}
return sequence;
}
public void addGenerator(MethodReference method, Generator generator) { public void addGenerator(MethodReference method, Generator generator) {
generators.put(method, generator); generators.put(method, generator);
} }

View File

@ -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<MethodReference, Program> 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);
}
}

View File

@ -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);
}

View File

@ -53,6 +53,7 @@ public class TeaVMTestTool {
private TeaVMToolLog log = new EmptyTeaVMToolLog(); private TeaVMToolLog log = new EmptyTeaVMToolLog();
private boolean incremental; private boolean incremental;
private RegularMethodNodeCache astCache; private RegularMethodNodeCache astCache;
private ProgramCache programCache;
public File getOutputDir() { public File getOutputDir() {
return outputDir; 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-expand.png", "res/toggle-small-expand.png");
resourceToFile(prefix + "/res/toggle-small.png", "res/toggle-small.png"); resourceToFile(prefix + "/res/toggle-small.png", "res/toggle-small.png");
resourceToFile(prefix + "/junit.html", "junit.html"); resourceToFile(prefix + "/junit.html", "junit.html");
final ClassHolderSource classSource = new PreOptimizingClassHolderSource( ClassHolderSource classSource = new ClasspathClassHolderSource(classLoader);
new ClasspathClassHolderSource(classLoader)); if (incremental) {
classSource = new PreOptimizingClassHolderSource(classSource);
}
for (String testClass : testClasses) { for (String testClass : testClasses) {
ClassHolder classHolder = classSource.get(testClass); ClassHolder classHolder = classSource.get(testClass);
if (classHolder == null) { if (classHolder == null) {
@ -158,6 +161,7 @@ public class TeaVMTestTool {
astCache = new EmptyRegularMethodNodeCache(); astCache = new EmptyRegularMethodNodeCache();
if (incremental) { if (incremental) {
astCache = new InMemoryRegularMethodNodeCache(); astCache = new InMemoryRegularMethodNodeCache();
programCache = new InMemoryProgramCache();
} }
File allTestsFile = new File(outputDir, "tests/all.js"); File allTestsFile = new File(outputDir, "tests/all.js");
try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) { try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) {
@ -221,11 +225,12 @@ public class TeaVMTestTool {
executor = threadedExecutor; executor = threadedExecutor;
} }
for (final MethodReference method : testMethods) { for (final MethodReference method : testMethods) {
final ClassHolderSource builderClassSource = classSource;
executor.execute(new Runnable() { executor.execute(new Runnable() {
@Override public void run() { @Override public void run() {
log.debug("Building test for " + method); log.debug("Building test for " + method);
try { try {
decompileClassesForTest(classLoader, new CopyClassHolderSource(classSource), method, decompileClassesForTest(classLoader, new CopyClassHolderSource(builderClassSource), method,
fileNames.get(method)); fileNames.get(method));
} catch (IOException e) { } catch (IOException e) {
log.error("Error generating JavaScript", e); log.error("Error generating JavaScript", e);
@ -300,6 +305,7 @@ public class TeaVMTestTool {
.build(); .build();
vm.setIncremental(incremental); vm.setIncremental(incremental);
vm.setAstCache(astCache); vm.setAstCache(astCache);
vm.setProgramCache(programCache);
vm.setProperties(properties); vm.setProperties(properties);
vm.setMinifying(minifying); vm.setMinifying(minifying);
vm.installPlugins(); vm.installPlugins();

View File

@ -30,8 +30,7 @@ import org.teavm.model.*;
import org.teavm.model.util.ListingBuilder; import org.teavm.model.util.ListingBuilder;
import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.ProgramUtils;
import org.teavm.model.util.RegisterAllocator; import org.teavm.model.util.RegisterAllocator;
import org.teavm.optimization.ClassSetOptimizer; import org.teavm.optimization.*;
import org.teavm.optimization.Devirtualization;
import org.teavm.vm.spi.RendererListener; import org.teavm.vm.spi.RendererListener;
import org.teavm.vm.spi.TeaVMHost; import org.teavm.vm.spi.TeaVMHost;
import org.teavm.vm.spi.TeaVMPlugin; import org.teavm.vm.spi.TeaVMPlugin;
@ -80,6 +79,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
private Map<Class<?>, Object> services = new HashMap<>(); private Map<Class<?>, Object> services = new HashMap<>();
private Properties properties = new Properties(); private Properties properties = new Properties();
private DebugInformationEmitter debugEmitter; private DebugInformationEmitter debugEmitter;
private ProgramCache programCache;
private RegularMethodNodeCache astCache = new EmptyRegularMethodNodeCache(); private RegularMethodNodeCache astCache = new EmptyRegularMethodNodeCache();
private boolean incremental; private boolean incremental;
@ -172,6 +172,14 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
this.astCache = methodAstCache; this.astCache = methodAstCache;
} }
public ProgramCache getProgramCache() {
return programCache;
}
public void setProgramCache(ProgramCache programCache) {
this.programCache = programCache;
}
public boolean isIncremental() { public boolean isIncremental() {
return incremental; return incremental;
} }
@ -333,27 +341,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
if (!incremental) { if (!incremental) {
devirtualize(classSet, dependencyChecker); 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 List<ClassNode> clsNodes = modelToAst(classSet);
Decompiler decompiler = new Decompiler(classSet, classLoader);
decompiler.setRegularMethodCache(incremental ? astCache : null);
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
decompiler.addGenerator(entry.getKey(), entry.getValue());
}
for (MethodReference injectedMethod : methodInjectors.keySet()) {
decompiler.addMethodToPass(injectedMethod);
}
List<ClassNode> clsNodes = decompiler.decompile(classSet.getClassNames());
// Render // Render
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, dependencyChecker.getClassSource()); DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, dependencyChecker.getClassSource());
@ -440,29 +429,61 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
} }
private void allocateRegisters(ListableClassHolderSource classes) { private List<ClassNode> modelToAst(ListableClassHolderSource classes) {
for (String className : classes.getClassNames()) { Decompiler decompiler = new Decompiler(classes, classLoader);
ClassHolder cls = classes.get(className); decompiler.setRegularMethodCache(incremental ? astCache : null);
for (final MethodHolder method : cls.getMethods()) {
if (method.getProgram() != null && method.getProgram().basicBlockCount() > 0) { for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
RegisterAllocator allocator = new RegisterAllocator(); decompiler.addGenerator(entry.getKey(), entry.getValue());
Program program = ProgramUtils.copy(method.getProgram());
allocator.allocateRegisters(method, program);
method.setProgram(program);
}
}
} }
for (MethodReference injectedMethod : methodInjectors.keySet()) {
decompiler.addMethodToPass(injectedMethod);
}
List<String> classOrder = decompiler.getClassOrdering(classes.getClassNames());
List<ClassNode> 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) { private void processMethod(MethodHolder method) {
for (String className : classes.getClassNames()) { if (method.getProgram() == null) {
ClassHolder classHolder = classes.get(className); return;
printModifiers(writer, classHolder); }
writer.println("class " + className); Program optimizedProgram = incremental && programCache != null ?
for (MethodHolder method : classHolder.getMethods()) { programCache.get(method.getReference()) : null;
logMethodBytecode(writer, method); 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<MethodOptimization> getOptimizations() {
return Arrays.<MethodOptimization>asList(new ArrayUnwrapMotion(), new LoopInvariantMotion(),
new GlobalValueNumbering(), new UnusedVariableElimination());
} }
private void logMethodBytecode(PrintWriter writer, MethodHolder method) { private void logMethodBytecode(PrintWriter writer, MethodHolder method) {

View File

@ -163,6 +163,7 @@ public class BuildJavascriptTestMojo extends AbstractMojo {
tool.setOutputDir(outputDir); tool.setOutputDir(outputDir);
tool.setNumThreads(numThreads); tool.setNumThreads(numThreads);
tool.setMinifying(minifying); tool.setMinifying(minifying);
tool.setIncremental(incremental);
if (properties != null) { if (properties != null) {
tool.getProperties().putAll(properties); tool.getProperties().putAll(properties);
} }