Support of multithreaded execution. Performance optimizations.

This commit is contained in:
konsoletyper 2014-01-28 16:46:40 +04:00
parent 0c240f5636
commit 24921c6e80
15 changed files with 213 additions and 66 deletions

View File

@ -53,7 +53,7 @@
<phase>process-test-classes</phase> <phase>process-test-classes</phase>
<configuration> <configuration>
<minifying>false</minifying> <minifying>false</minifying>
<numThreads>1</numThreads> <numThreads>0</numThreads>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>

View File

@ -66,17 +66,19 @@ public class ThreadPoolFiniteExecutor implements FiniteExecutor {
@Override @Override
public void complete() { public void complete() {
synchronized (monitor) { synchronized (monitor) {
try { while (true) {
monitor.wait(); if (thrownException.get() != null) {
} catch (InterruptedException e) { throw thrownException.get();
Thread.currentThread().interrupt(); }
return; if (runningTasks.get() == 0) {
} return;
if (thrownException.get() != null) { }
throw thrownException.get(); try {
} monitor.wait();
if (runningTasks.get() == 0) { } catch (InterruptedException e) {
return; Thread.currentThread().interrupt();
return;
}
} }
} }
} }

View File

@ -40,10 +40,12 @@ public class Decompiler {
private RangeTree codeTree; private RangeTree codeTree;
private RangeTree.Node currentNode; private RangeTree.Node currentNode;
private RangeTree.Node parentNode; private RangeTree.Node parentNode;
private FiniteExecutor executor;
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader) { public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) {
this.classSource = classSource; this.classSource = classSource;
this.classLoader = classLoader; this.classLoader = classLoader;
this.executor = executor;
} }
public int getGraphSize() { public int getGraphSize() {
@ -70,10 +72,19 @@ public class Decompiler {
for (String className : classNames) { for (String className : classNames) {
orderClasses(className, visited, sequence); orderClasses(className, visited, sequence);
} }
List<ClassNode> result = new ArrayList<>(); final List<ClassNode> result = new ArrayList<>();
for (String className : sequence) { for (int i = 0; i < sequence.size(); ++i) {
result.add(decompile(classSource.getClassHolder(className))); final String className = sequence.get(i);
result.add(null);
final int index = i;
executor.execute(new Runnable() {
@Override public void run() {
Decompiler copy = new Decompiler(classSource, classLoader, executor);
result.set(index, copy.decompile(classSource.getClassHolder(className)));
}
});
} }
executor.complete();
return result; return result;
} }

View File

@ -103,7 +103,7 @@ public class JavascriptBuilder {
ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID))); ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)));
executor.complete(); executor.complete();
ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(); ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses();
Decompiler decompiler = new Decompiler(classSet, classLoader); Decompiler decompiler = new Decompiler(classSet, classLoader, executor);
ClassSetOptimizer optimizer = new ClassSetOptimizer(executor); ClassSetOptimizer optimizer = new ClassSetOptimizer(executor);
optimizer.optimizeAll(classSet); optimizer.optimizeAll(classSet);
executor.complete(); executor.complete();
@ -143,7 +143,9 @@ public class JavascriptBuilder {
executor.execute(new Runnable() { executor.execute(new Runnable() {
@Override public void run() { @Override public void run() {
RegisterAllocator allocator = new RegisterAllocator(); RegisterAllocator allocator = new RegisterAllocator();
allocator.allocateRegisters(method); Program program = ProgramUtils.copy(method.getProgram());
allocator.allocateRegisters(method, program);
method.setProgram(program);
} }
}); });
} }
@ -208,7 +210,7 @@ public class JavascriptBuilder {
writer.print("byte"); writer.print("byte");
break; break;
case CHARACTER: case CHARACTER:
writer.print("character"); writer.print("char");
break; break;
case DOUBLE: case DOUBLE:
writer.print("double"); writer.print("double");

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.model; package org.teavm.model;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@ -22,18 +23,18 @@ import java.util.List;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class AnnotationValue { public class AnnotationValue {
private static final byte BOOLEAN = 0; public static final byte BOOLEAN = 0;
private static final byte BYTE = 1; public static final byte BYTE = 1;
private static final byte SHORT = 2; public static final byte SHORT = 2;
private static final byte INT = 3; public static final byte INT = 3;
private static final byte LONG = 4; public static final byte LONG = 4;
private static final byte FLOAT = 5; public static final byte FLOAT = 5;
private static final byte DOUBLE = 6; public static final byte DOUBLE = 6;
private static final byte STRING = 7; public static final byte STRING = 7;
private static final byte CLASS = 8; public static final byte CLASS = 8;
private static final byte LIST = 9; public static final byte LIST = 9;
private static final byte ENUM = 10; public static final byte ENUM = 10;
private static final byte ANNOTATION = 11; public static final byte ANNOTATION = 11;
private byte type; private byte type;
private Object value; private Object value;
@ -165,7 +166,7 @@ public class AnnotationValue {
if (type != LIST) { if (type != LIST) {
throw new IllegalStateException("There is no List value"); throw new IllegalStateException("There is no List value");
} }
return (List<AnnotationValue>)value; return Collections.unmodifiableList((List<AnnotationValue>)value);
} }
public FieldReference getEnumValue() { public FieldReference getEnumValue() {
@ -181,4 +182,8 @@ public class AnnotationValue {
} }
return (AnnotationHolder)value; return (AnnotationHolder)value;
} }
public byte getType() {
return type;
}
} }

View File

@ -0,0 +1,114 @@
/*
* 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.ArrayList;
import java.util.List;
import java.util.Map;
import org.teavm.common.Mapper;
import org.teavm.model.resource.MapperClassHolderSource;
import org.teavm.model.util.ProgramUtils;
/**
*
* @author Alexey Andreev
*/
public class CopyClassHolderSource implements ClassHolderSource {
private ClassHolderSource innerSource;
private MapperClassHolderSource mapperSource = new MapperClassHolderSource(new Mapper<String, ClassHolder>() {
@Override public ClassHolder map(String preimage) {
return copyClass(preimage);
}
});
public CopyClassHolderSource(ClassHolderSource innerSource) {
this.innerSource = innerSource;
}
@Override
public ClassHolder getClassHolder(String name) {
return mapperSource.getClassHolder(name);
}
private ClassHolder copyClass(String className) {
ClassHolder original = innerSource.getClassHolder(className);
if (original == null) {
return null;
}
ClassHolder copy = new ClassHolder(className);
copy.setLevel(original.getLevel());
copy.getModifiers().addAll(original.getModifiers());
copy.setParent(original.getParent());
copy.getInterfaces().addAll(original.getInterfaces());
for (MethodHolder method : original.getMethods()) {
copy.addMethod(copyMethod(method));
}
for (FieldHolder field : original.getFields()) {
copy.addField(copyField(field));
}
copyAnnotations(original.getAnnotations(), copy.getAnnotations());
return copy;
}
private MethodHolder copyMethod(MethodHolder method) {
MethodHolder copy = new MethodHolder(method.getDescriptor());
copy.setLevel(method.getLevel());
copy.getModifiers().addAll(method.getModifiers());
copy.setProgram(ProgramUtils.copy(method.getProgram()));
copyAnnotations(method.getAnnotations(), copy.getAnnotations());
return copy;
}
private FieldHolder copyField(FieldHolder field) {
FieldHolder copy = new FieldHolder(field.getName());
copy.setLevel(field.getLevel());
copy.getModifiers().addAll(field.getModifiers());
copy.setType(field.getType());
copy.setInitialValue(field.getInitialValue());
copyAnnotations(field.getAnnotations(), copy.getAnnotations());
return copy;
}
private void copyAnnotations(AnnotationContainer src, AnnotationContainer dst) {
for (AnnotationHolder annot : src.all()) {
dst.add(copyAnnotation(annot));
}
}
private AnnotationHolder copyAnnotation(AnnotationHolder annot) {
AnnotationHolder copy = new AnnotationHolder(annot.getType());
for (Map.Entry<String, AnnotationValue> entry : annot.getValues().entrySet()) {
copy.getValues().put(entry.getKey(), copyAnnotationValue(entry.getValue()));
}
return copy;
}
private AnnotationValue copyAnnotationValue(AnnotationValue value) {
switch (value.getType()) {
case AnnotationValue.LIST: {
List<AnnotationValue> listCopy = new ArrayList<>();
for (AnnotationValue item : value.getList()) {
listCopy.add(copyAnnotationValue(item));
}
return new AnnotationValue(listCopy);
}
case AnnotationValue.ANNOTATION:
return new AnnotationValue(copyAnnotation(value.getAnnotation()));
default:
return value;
}
}
}

View File

@ -48,10 +48,10 @@ public class ProgramUtils {
CopyVisitor insnCopier = new CopyVisitor(); CopyVisitor insnCopier = new CopyVisitor();
insnCopier.programCopy = copy; insnCopier.programCopy = copy;
for (int i = 0; i < program.variableCount(); ++i) { for (int i = 0; i < program.variableCount(); ++i) {
program.createVariable(); copy.createVariable();
} }
for (int i = 0; i < program.basicBlockCount(); ++i) { for (int i = 0; i < program.basicBlockCount(); ++i) {
program.createBasicBlock(); copy.createBasicBlock();
} }
for (int i = 0; i < program.basicBlockCount(); ++i) { for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i); BasicBlock block = program.basicBlockAt(i);
@ -346,7 +346,7 @@ public class ProgramUtils {
insnCopy.setMethod(insn.getMethod()); insnCopy.setMethod(insn.getMethod());
insnCopy.setType(insn.getType()); insnCopy.setType(insn.getType());
insnCopy.setInstance(insn.getInstance() != null ? copyVar(insn.getInstance()) : null); insnCopy.setInstance(insn.getInstance() != null ? copyVar(insn.getInstance()) : null);
insnCopy.setReceiver(copyVar(insn.getReceiver())); insnCopy.setReceiver(insn.getReceiver() != null ? copyVar(insn.getReceiver()) : null);
for (Variable arg : insn.getArguments()) { for (Variable arg : insn.getArguments()) {
insnCopy.getArguments().add(copyVar(arg)); insnCopy.getArguments().add(copyVar(arg));
} }

View File

@ -28,8 +28,7 @@ import org.teavm.model.instructions.JumpInstruction;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class RegisterAllocator { public class RegisterAllocator {
public void allocateRegisters(MethodHolder method) { public void allocateRegisters(MethodReader method, Program program) {
Program program = method.getProgram();
List<PhiArgumentCopy> phiArgsCopies = insertPhiArgumentsCopies(program); List<PhiArgumentCopy> phiArgsCopies = insertPhiArgumentsCopies(program);
InterferenceGraphBuilder interferenceBuilder = new InterferenceGraphBuilder(); InterferenceGraphBuilder interferenceBuilder = new InterferenceGraphBuilder();
LivenessAnalyzer liveness = new LivenessAnalyzer(); LivenessAnalyzer liveness = new LivenessAnalyzer();
@ -46,7 +45,7 @@ public class RegisterAllocator {
GraphColorer colorer = new GraphColorer(); GraphColorer colorer = new GraphColorer();
colorer.colorize(interferenceGraph, classArray, colors); colorer.colorize(interferenceGraph, classArray, colors);
for (int i = 0; i < colors.length; ++i) { for (int i = 0; i < colors.length; ++i) {
method.getProgram().variableAt(i).setRegister(colors[i]); program.variableAt(i).setRegister(colors[i]);
} }
} }

View File

@ -21,21 +21,24 @@ import java.util.concurrent.Executor;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.Program;
import org.teavm.model.util.ProgramUtils;
/** /**
* *
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
*/ */
public class ClassSetOptimizer { public class ClassSetOptimizer {
private List<MethodOptimization> optimizations = Arrays.<MethodOptimization>asList(
new CommonSubexpressionElimination(), new UnusedVariableElimination());
private Executor executor; private Executor executor;
public ClassSetOptimizer(Executor executor) { public ClassSetOptimizer(Executor executor) {
super();
this.executor = executor; this.executor = executor;
} }
private List<MethodOptimization> getOptimizations() {
return Arrays.<MethodOptimization>asList(new CommonSubexpressionElimination(), new UnusedVariableElimination());
}
public void optimizeAll(ListableClassHolderSource classSource) { public void optimizeAll(ListableClassHolderSource classSource) {
for (String className : classSource.getClassNames()) { for (String className : classSource.getClassNames()) {
ClassHolder cls = classSource.getClassHolder(className); ClassHolder cls = classSource.getClassHolder(className);
@ -44,9 +47,11 @@ public class ClassSetOptimizer {
executor.execute(new Runnable() { executor.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
for (MethodOptimization optimization : optimizations) { Program program = ProgramUtils.copy(method.getProgram());
optimization.optimize(method); for (MethodOptimization optimization : getOptimizations()) {
optimization.optimize(method, program);
} }
method.setProgram(program);
} }
}); });
} }

View File

@ -42,10 +42,10 @@ public class CommonSubexpressionElimination implements MethodOptimization {
} }
@Override @Override
public void optimize(MethodHolder method) { public void optimize(MethodReader method, Program program) {
program = method.getProgram(); this.program = program;
knownValues.clear(); knownValues.clear();
Graph cfg = ProgramUtils.buildControlFlowGraph(method.getProgram()); Graph cfg = ProgramUtils.buildControlFlowGraph(program);
domTree = GraphUtils.buildDominatorTree(cfg); domTree = GraphUtils.buildDominatorTree(cfg);
Graph dom = GraphUtils.buildDominatorGraph(domTree, cfg.size()); Graph dom = GraphUtils.buildDominatorGraph(domTree, cfg.size());
map = new int[program.variableCount()]; map = new int[program.variableCount()];

View File

@ -16,7 +16,7 @@
package org.teavm.optimization; package org.teavm.optimization;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodReader;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.util.BasicBlockMapper; import org.teavm.model.util.BasicBlockMapper;
@ -27,8 +27,7 @@ import org.teavm.model.util.BasicBlockMapper;
*/ */
public class EmptyBlockElimination implements MethodOptimization { public class EmptyBlockElimination implements MethodOptimization {
@Override @Override
public void optimize(MethodHolder method) { public void optimize(MethodReader method, final Program program) {
final Program program = method.getProgram();
final int[] blockMapping = new int[program.basicBlockCount()]; final int[] blockMapping = new int[program.basicBlockCount()];
for (int i = 0; i < blockMapping.length; ++i) { for (int i = 0; i < blockMapping.length; ++i) {
blockMapping[i] = i; blockMapping[i] = i;

View File

@ -15,12 +15,13 @@
*/ */
package org.teavm.optimization; package org.teavm.optimization;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodReader;
import org.teavm.model.Program;
/** /**
* *
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
*/ */
public interface MethodOptimization { public interface MethodOptimization {
void optimize(MethodHolder method); void optimize(MethodReader method, Program program);
} }

View File

@ -25,12 +25,12 @@ import org.teavm.model.instructions.*;
*/ */
public class UnusedVariableElimination implements MethodOptimization { public class UnusedVariableElimination implements MethodOptimization {
@Override @Override
public void optimize(MethodHolder method) { public void optimize(MethodReader method, Program program) {
if (method.getProgram() == null) { if (method.getProgram() == null) {
return; return;
} }
Graph graph = VariableUsageGraphBuilder.build(method.getProgram()); Graph graph = VariableUsageGraphBuilder.build(program);
boolean[] escaping = VariableEscapeAnalyzer.findEscapingVariables(method.getProgram()); boolean[] escaping = VariableEscapeAnalyzer.findEscapingVariables(program);
boolean[] used = new boolean[escaping.length]; boolean[] used = new boolean[escaping.length];
int[] stack = new int[graph.size() * 2]; int[] stack = new int[graph.size() * 2];
@ -54,7 +54,6 @@ public class UnusedVariableElimination implements MethodOptimization {
} }
} }
Program program = method.getProgram();
InstructionOptimizer insnOptimizer = new InstructionOptimizer(used); InstructionOptimizer insnOptimizer = new InstructionOptimizer(used);
for (int i = 0; i < program.basicBlockCount(); ++i) { for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i); BasicBlock block = program.basicBlockAt(i);

View File

@ -103,16 +103,16 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
public void execute() throws MojoExecutionException, MojoFailureException { public void execute() throws MojoExecutionException, MojoFailureException {
Runnable finalizer = null; Runnable finalizer = null;
try { try {
ClassLoader classLoader = prepareClassLoader(); final ClassLoader classLoader = prepareClassLoader();
getLog().info("Searching for tests in the directory `" + testFiles.getAbsolutePath() + "'"); getLog().info("Searching for tests in the directory `" + testFiles.getAbsolutePath() + "'");
findTestClasses(classLoader, testFiles, ""); findTestClasses(classLoader, testFiles, "");
Log log = getLog(); final Log log = getLog();
new File(outputDir, "tests").mkdirs(); new File(outputDir, "tests").mkdirs();
resourceToFile("org/teavm/javascript/runtime.js", "runtime.js"); resourceToFile("org/teavm/javascript/runtime.js", "runtime.js");
resourceToFile("org/teavm/maven/junit-support.js", "junit-support.js"); resourceToFile("org/teavm/maven/junit-support.js", "junit-support.js");
resourceToFile("org/teavm/maven/junit.css", "junit.css"); resourceToFile("org/teavm/maven/junit.css", "junit.css");
resourceToFile("org/teavm/maven/junit.html", "junit.html"); resourceToFile("org/teavm/maven/junit.html", "junit.html");
ClassHolderSource classSource = new ClasspathClassHolderSource(classLoader); final ClassHolderSource classSource = new ClasspathClassHolderSource(classLoader);
for (String testClass : testClasses) { for (String testClass : testClasses) {
ClassHolder classHolder = classSource.getClassHolder(testClass); ClassHolder classHolder = classSource.getClassHolder(testClass);
if (classHolder == null) { if (classHolder == null) {
@ -169,12 +169,22 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
}; };
executor = threadedExecutor; executor = threadedExecutor;
} }
for (MethodReference method : testMethods) { for (final MethodReference method : testMethods) {
log.debug("Building test for " + method); executor.execute(new Runnable() {
decompileClassesForTest(classLoader, method, fileNames.get(method), executor); @Override public void run() {
log.debug("Building test for " + method);
try {
decompileClassesForTest(classLoader, new CopyClassHolderSource(classSource), method,
fileNames.get(method), new SimpleFiniteExecutor());
} catch (IOException e) {
log.error("Error generating JavaScript", e);
}
}
});
++methodsGenerated; ++methodsGenerated;
} }
log.info("Test files successfully generated for " + methodsGenerated + " method(s)"); executor.complete();
log.info("Test files successfully generated for " + methodsGenerated + " method(s).");
} catch (IOException e) { } catch (IOException e) {
throw new MojoFailureException("IO error occured generating JavaScript files", e); throw new MojoFailureException("IO error occured generating JavaScript files", e);
} finally { } finally {
@ -216,11 +226,11 @@ public class BuildJavascriptJUnitMojo extends AbstractMojo {
} }
} }
private void decompileClassesForTest(ClassLoader classLoader, MethodReference methodRef, String targetName, private void decompileClassesForTest(ClassLoader classLoader, ClassHolderSource classSource,
FiniteExecutor executor) throws IOException { MethodReference methodRef, String targetName, FiniteExecutor executor) throws IOException {
JavascriptBuilderFactory builderFactory = new JavascriptBuilderFactory(); JavascriptBuilderFactory builderFactory = new JavascriptBuilderFactory();
builderFactory.setClassLoader(classLoader); builderFactory.setClassLoader(classLoader);
builderFactory.setClassSource(new ClasspathClassHolderSource(classLoader)); builderFactory.setClassSource(classSource);
builderFactory.setExecutor(executor); builderFactory.setExecutor(executor);
JavascriptBuilder builder = builderFactory.create(); JavascriptBuilder builder = builderFactory.create();
builder.setMinifying(minifying); builder.setMinifying(minifying);

View File

@ -47,7 +47,7 @@
<minifying>false</minifying> <minifying>false</minifying>
<mainClass>org.teavm.samples.HelloWorld</mainClass> <mainClass>org.teavm.samples.HelloWorld</mainClass>
<mainPageIncluded>true</mainPageIncluded> <mainPageIncluded>true</mainPageIncluded>
<bytecodeLogging>true</bytecodeLogging> <bytecodeLogging>false</bytecodeLogging>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>