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;
}
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) {
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 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();

View File

@ -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<Class<?>, 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<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());
List<ClassNode> 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()) {
private List<ClassNode> modelToAst(ListableClassHolderSource classes) {
Decompiler decompiler = new Decompiler(classes, 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<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 (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);
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<MethodOptimization> getOptimizations() {
return Arrays.<MethodOptimization>asList(new ArrayUnwrapMotion(), new LoopInvariantMotion(),
new GlobalValueNumbering(), new UnusedVariableElimination());
}
private void logMethodBytecode(PrintWriter writer, MethodHolder method) {

View File

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