mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Improve inliner: use simlified heuristics in advanced optimization mode, remove methods that were fully inlined
This commit is contained in:
parent
89189c7454
commit
4de1c51e1a
|
@ -309,7 +309,7 @@ public class TObject {
|
|||
@Async
|
||||
private native void waitImpl(long timeout, int nanos) throws TInterruptedException;
|
||||
|
||||
public final void waitImpl(long timeout, int nanos, final AsyncCallback<Void> callback) {
|
||||
public final void waitImpl(long timeout, int nanos, AsyncCallback<Void> callback) {
|
||||
final NotifyListenerImpl listener = new NotifyListenerImpl(this, callback, monitor.count);
|
||||
monitor.notifyListeners.add(listener);
|
||||
TThread.currentThread().interruptHandler = listener;
|
||||
|
|
|
@ -239,6 +239,10 @@ public class Decompiler {
|
|||
|| methodsToSkip.contains(method.getReference())) {
|
||||
continue;
|
||||
}
|
||||
if (!method.hasModifier(ElementModifier.NATIVE) && method.getProgram() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MethodNode methodNode = decompile(method);
|
||||
clsNode.getMethods().add(methodNode);
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ import org.teavm.cache.MethodNodeCache;
|
|||
import org.teavm.debugging.information.DebugInformationEmitter;
|
||||
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
||||
import org.teavm.debugging.information.SourceLocation;
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.dependency.DependencyType;
|
||||
|
@ -100,6 +102,8 @@ import org.teavm.vm.spi.TeaVMHostExtension;
|
|||
public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||
private static final NumberFormat STATS_NUM_FORMAT = new DecimalFormat("#,##0");
|
||||
private static final NumberFormat STATS_PERCENT_FORMAT = new DecimalFormat("0.000 %");
|
||||
private static final MethodReference CURRENT_THREAD = new MethodReference(Thread.class,
|
||||
"currentThread", Thread.class);
|
||||
|
||||
private TeaVMTargetController controller;
|
||||
private boolean minifying = true;
|
||||
|
@ -116,7 +120,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
private ClassInitializerInsertionTransformer clinitInsertionTransformer;
|
||||
private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>();
|
||||
private boolean classScoped;
|
||||
|
||||
@Override
|
||||
public List<ClassHolderTransformer> getTransformers() {
|
||||
return Collections.emptyList();
|
||||
|
@ -261,6 +264,17 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
if (stackTraceIncluded) {
|
||||
includeStackTraceMethods(dependencyAnalyzer);
|
||||
}
|
||||
|
||||
dependencyAnalyzer.addDependencyListener(new AbstractDependencyListener() {
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
if (method.getReference().equals(CURRENT_THREAD)) {
|
||||
method.use();
|
||||
}
|
||||
agent.linkMethod(new MethodReference(Thread.class, "setCurrentThread", Thread.class, void.class))
|
||||
.use();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void includeStackTraceMethods(DependencyAnalyzer dependencyAnalyzer) {
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.teavm.backend.javascript.spi.GeneratorContext;
|
|||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.debugging.information.DebugInformationEmitter;
|
||||
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.dependency.MethodDependencyInfo;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassReader;
|
||||
|
@ -847,6 +848,11 @@ public class Renderer implements RenderingManager {
|
|||
this.statementRenderer = statementRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyInfo getDependency() {
|
||||
return context.getDependencyInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(NativeMethodNode methodNode) {
|
||||
try {
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.backend.javascript.spi;
|
|||
import java.util.Properties;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
|
@ -43,6 +44,8 @@ public interface GeneratorContext extends ServiceRepository {
|
|||
|
||||
Diagnostics getDiagnostics();
|
||||
|
||||
DependencyInfo getDependency();
|
||||
|
||||
void typeToClassString(SourceWriter writer, ValueType type);
|
||||
|
||||
void useLongLibrary();
|
||||
|
|
|
@ -337,7 +337,7 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
MethodDependency methodDep = dependencyAnalyzer.linkMethod(method);
|
||||
methodDep.addLocation(callLocation);
|
||||
methodDep.use();
|
||||
methodDep.use(false);
|
||||
if (methodDep.isMissing()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class FastInstructionAnalyzer extends AbstractInstructionAnalyzer {
|
|||
}
|
||||
MethodDependency methodDep = dependencyAnalyzer.linkMethod(method);
|
||||
methodDep.addLocation(callLocation);
|
||||
methodDep.use();
|
||||
methodDep.use(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,6 +35,7 @@ public class MethodDependency implements MethodDependencyInfo {
|
|||
MethodHolder method;
|
||||
private MethodReference reference;
|
||||
boolean used;
|
||||
boolean external;
|
||||
DependencyPlugin dependencyPlugin;
|
||||
boolean dependencyPluginAttached;
|
||||
private List<LocationListener> locationListeners;
|
||||
|
@ -151,11 +152,23 @@ public class MethodDependency implements MethodDependencyInfo {
|
|||
}
|
||||
|
||||
public void use() {
|
||||
use(true);
|
||||
}
|
||||
|
||||
void use(boolean external) {
|
||||
if (!used) {
|
||||
used = true;
|
||||
if (!isMissing()) {
|
||||
dependencyAnalyzer.scheduleMethodAnalysis(this);
|
||||
}
|
||||
}
|
||||
if (external) {
|
||||
this.external = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCalled() {
|
||||
return external;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,5 +34,7 @@ public interface MethodDependencyInfo {
|
|||
|
||||
boolean isUsed();
|
||||
|
||||
boolean isCalled();
|
||||
|
||||
boolean isMissing();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright 2019 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.optimization;
|
||||
|
||||
import java.util.List;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ProgramReader;
|
||||
import org.teavm.model.VariableReader;
|
||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||
import org.teavm.model.instructions.BinaryBranchingCondition;
|
||||
import org.teavm.model.instructions.BranchingCondition;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.SwitchTableEntryReader;
|
||||
|
||||
public class DefaultInliningStrategy implements InliningStrategy {
|
||||
private final int complexityThreshold;
|
||||
private final int depthThreshold;
|
||||
private final boolean onceUsedOnly;
|
||||
|
||||
public DefaultInliningStrategy(int complexityThreshold, int depthThreshold, boolean onceUsedOnly) {
|
||||
this.complexityThreshold = complexityThreshold;
|
||||
this.depthThreshold = depthThreshold;
|
||||
this.onceUsedOnly = onceUsedOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InliningStep start(MethodReference method, ProgramReader program) {
|
||||
int complexity = getComplexity(program);
|
||||
if (complexity > complexityThreshold) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ComplexityHolder complexityHolder = new ComplexityHolder();
|
||||
complexityHolder.complexity = complexity;
|
||||
return new InliningStepImpl(complexityHolder);
|
||||
}
|
||||
|
||||
static int getComplexity(ProgramReader program) {
|
||||
int complexity = 0;
|
||||
ComplexityCounter counter = new ComplexityCounter();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlockReader block = program.basicBlockAt(i);
|
||||
counter.complexity = 0;
|
||||
block.readAllInstructions(counter);
|
||||
complexity += block.instructionCount() + counter.complexity;
|
||||
}
|
||||
return complexity;
|
||||
}
|
||||
|
||||
class InliningStepImpl implements InliningStep {
|
||||
ComplexityHolder complexityHolder;
|
||||
|
||||
InliningStepImpl(ComplexityHolder complexityHolder) {
|
||||
this.complexityHolder = complexityHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InliningStep tryInline(MethodReference method, ProgramReader program, InliningContext context) {
|
||||
if (context.getDepth() > depthThreshold || (onceUsedOnly && !context.isUsedOnce(method))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int complexity = getComplexity(program);
|
||||
if (complexityHolder.complexity + complexity > complexityThreshold) {
|
||||
return null;
|
||||
}
|
||||
|
||||
complexityHolder.complexity += complexity;
|
||||
return new InliningStepImpl(complexityHolder);
|
||||
}
|
||||
}
|
||||
|
||||
static class ComplexityHolder {
|
||||
int complexity;
|
||||
}
|
||||
|
||||
static class ComplexityCounter extends AbstractInstructionReader {
|
||||
int complexity;
|
||||
|
||||
@Override
|
||||
public void nop() {
|
||||
complexity--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments, InvocationType type) {
|
||||
complexity++;
|
||||
if (instance != null) {
|
||||
complexity++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void choose(VariableReader condition, List<? extends SwitchTableEntryReader> table,
|
||||
BasicBlockReader defaultTarget) {
|
||||
complexity += 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent,
|
||||
BasicBlockReader alternative) {
|
||||
complexity += 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second,
|
||||
BasicBlockReader consequent, BasicBlockReader alternative) {
|
||||
complexity += 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jump(BasicBlockReader target) {
|
||||
complexity--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(VariableReader valueToReturn) {
|
||||
complexity--;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,54 +16,117 @@
|
|||
package org.teavm.model.optimization;
|
||||
|
||||
import com.carrotsearch.hppc.IntArrayList;
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.Incoming;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Phi;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ProgramReader;
|
||||
import org.teavm.model.TryCatchBlock;
|
||||
import org.teavm.model.VariableReader;
|
||||
import org.teavm.model.analysis.ClassInference;
|
||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||
import org.teavm.model.instructions.BranchingInstruction;
|
||||
import org.teavm.model.instructions.EmptyInstruction;
|
||||
import org.teavm.model.instructions.ExitInstruction;
|
||||
import org.teavm.model.instructions.InitClassInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.JumpInstruction;
|
||||
import org.teavm.model.instructions.SwitchInstruction;
|
||||
import org.teavm.model.util.BasicBlockMapper;
|
||||
import org.teavm.model.util.InstructionVariableMapper;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.model.util.TransitionExtractor;
|
||||
|
||||
public class Inlining {
|
||||
private static final int DEFAULT_THRESHOLD = 17;
|
||||
private static final int MAX_DEPTH = 7;
|
||||
private IntArrayList depthsByBlock;
|
||||
private Set<Instruction> instructionsToSkip;
|
||||
private ClassHierarchy hierarchy;
|
||||
private ClassReaderSource classes;
|
||||
private ListableClassReaderSource classes;
|
||||
private DependencyInfo dependencyInfo;
|
||||
private InliningStrategy strategy;
|
||||
private MethodUsageCounter usageCounter;
|
||||
private Set<MethodReference> methodsUsedOnce = new HashSet<>();
|
||||
|
||||
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo) {
|
||||
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy,
|
||||
ListableClassReaderSource classes, Predicate<MethodReference> externalMethods) {
|
||||
this.hierarchy = hierarchy;
|
||||
this.classes = hierarchy.getClassSource();
|
||||
this.classes = classes;
|
||||
this.dependencyInfo = dependencyInfo;
|
||||
this.strategy = strategy;
|
||||
usageCounter = new MethodUsageCounter(externalMethods);
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassReader cls = classes.get(className);
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
ProgramReader program = method.getProgram();
|
||||
if (program != null) {
|
||||
usageCounter.currentMethod = method.getReference();
|
||||
for (BasicBlockReader block : program.getBasicBlocks()) {
|
||||
block.readAllInstructions(usageCounter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ObjectCursor<MethodReference> cursor : usageCounter.methodUsageCount.keys()) {
|
||||
if (usageCounter.methodUsageCount.get(cursor.value) == 1) {
|
||||
methodsUsedOnce.add(cursor.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MethodReference> getOrder() {
|
||||
List<MethodReference> order = new ArrayList<>();
|
||||
Set<MethodReference> visited = new HashSet<>();
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassReader cls = classes.get(className);
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
if (method.getProgram() != null) {
|
||||
computeOrder(method.getReference(), order, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.reverse(order);
|
||||
return order;
|
||||
}
|
||||
|
||||
private void computeOrder(MethodReference method, List<MethodReference> order, Set<MethodReference> visited) {
|
||||
if (!visited.add(method)) {
|
||||
return;
|
||||
}
|
||||
Set<MethodReference> invokedMethods = usageCounter.methodDependencies.get(method);
|
||||
if (invokedMethods != null) {
|
||||
for (MethodReference invokedMethod : invokedMethods) {
|
||||
computeOrder(invokedMethod, order, visited);
|
||||
}
|
||||
}
|
||||
order.add(method);
|
||||
}
|
||||
|
||||
public boolean hasUsages(MethodReference method) {
|
||||
return usageCounter.methodUsageCount.getOrDefault(method, -1) != 0;
|
||||
}
|
||||
|
||||
public void apply(Program program, MethodReference method) {
|
||||
|
@ -73,7 +136,7 @@ public class Inlining {
|
|||
}
|
||||
instructionsToSkip = new HashSet<>();
|
||||
|
||||
while (applyOnce(program)) {
|
||||
while (applyOnce(program, method)) {
|
||||
devirtualize(program, method, dependencyInfo);
|
||||
}
|
||||
depthsByBlock = null;
|
||||
|
@ -82,8 +145,12 @@ public class Inlining {
|
|||
new UnreachableBasicBlockEliminator().optimize(program);
|
||||
}
|
||||
|
||||
private boolean applyOnce(Program program) {
|
||||
List<PlanEntry> plan = buildPlan(program, 0);
|
||||
private boolean applyOnce(Program program, MethodReference method) {
|
||||
InliningStep step = strategy.start(method, program);
|
||||
if (step == null) {
|
||||
return false;
|
||||
}
|
||||
List<PlanEntry> plan = buildPlan(program, -1, step);
|
||||
if (plan.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -98,6 +165,11 @@ public class Inlining {
|
|||
}
|
||||
|
||||
private void execPlanEntry(Program program, PlanEntry planEntry, int offset) {
|
||||
int usageCount = usageCounter.methodUsageCount.getOrDefault(planEntry.method, -1);
|
||||
if (usageCount > 0) {
|
||||
usageCounter.methodUsageCount.put(planEntry.method, usageCount - 1);
|
||||
}
|
||||
|
||||
BasicBlock block = program.basicBlockAt(planEntry.targetBlock + offset);
|
||||
InvokeInstruction invoke = (InvokeInstruction) planEntry.targetInstruction;
|
||||
BasicBlock splitBlock = program.createBasicBlock();
|
||||
|
@ -219,24 +291,18 @@ public class Inlining {
|
|||
execPlan(program, planEntry.innerPlan, firstInlineBlock.getIndex());
|
||||
}
|
||||
|
||||
private List<PlanEntry> buildPlan(Program program, int depth) {
|
||||
if (depth >= MAX_DEPTH) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
private List<PlanEntry> buildPlan(Program program, int depth, InliningStep step) {
|
||||
List<PlanEntry> plan = new ArrayList<>();
|
||||
int ownComplexity = getComplexity(program);
|
||||
int originalDepth = depth;
|
||||
|
||||
ContextImpl context = new ContextImpl();
|
||||
for (BasicBlock block : program.getBasicBlocks()) {
|
||||
if (!block.getTryCatchBlocks().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (originalDepth == 0) {
|
||||
if (originalDepth < 0) {
|
||||
depth = depthsByBlock.get(block.getIndex());
|
||||
if (depth >= MAX_DEPTH) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (Instruction insn : block) {
|
||||
|
@ -254,27 +320,28 @@ public class Inlining {
|
|||
|
||||
MethodReader invokedMethod = getMethod(invoke.getMethod());
|
||||
if (invokedMethod == null || invokedMethod.getProgram() == null
|
||||
|| invokedMethod.getProgram().basicBlockCount() == 0) {
|
||||
|| invokedMethod.getProgram().basicBlockCount() == 0
|
||||
|| invokedMethod.hasModifier(ElementModifier.SYNCHRONIZED)) {
|
||||
instructionsToSkip.add(insn);
|
||||
continue;
|
||||
}
|
||||
|
||||
Program invokedProgram = ProgramUtils.copy(invokedMethod.getProgram());
|
||||
int complexityThreshold = DEFAULT_THRESHOLD;
|
||||
if (ownComplexity < DEFAULT_THRESHOLD) {
|
||||
complexityThreshold += DEFAULT_THRESHOLD;
|
||||
}
|
||||
if (getComplexity(invokedProgram) > complexityThreshold) {
|
||||
context.depth = depth;
|
||||
InliningStep innerStep = step.tryInline(invokedMethod.getReference(), invokedMethod.getProgram(),
|
||||
context);
|
||||
if (innerStep == null) {
|
||||
instructionsToSkip.add(insn);
|
||||
continue;
|
||||
}
|
||||
Program invokedProgram = ProgramUtils.copy(invokedMethod.getProgram());
|
||||
|
||||
PlanEntry entry = new PlanEntry();
|
||||
entry.targetBlock = block.getIndex();
|
||||
entry.targetInstruction = insn;
|
||||
entry.program = invokedProgram;
|
||||
entry.innerPlan.addAll(buildPlan(invokedProgram, depth + 1));
|
||||
entry.innerPlan.addAll(buildPlan(invokedProgram, depth + 1, innerStep));
|
||||
entry.depth = depth;
|
||||
entry.method = invokedMethod.getReference();
|
||||
plan.add(entry);
|
||||
}
|
||||
}
|
||||
|
@ -288,34 +355,6 @@ public class Inlining {
|
|||
return cls != null ? cls.getMethod(methodRef.getDescriptor()) : null;
|
||||
}
|
||||
|
||||
private int getComplexity(Program program) {
|
||||
int complexity = 0;
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
int nopCount = 0;
|
||||
int invokeCount = 0;
|
||||
for (Instruction insn : block) {
|
||||
if (insn instanceof EmptyInstruction) {
|
||||
nopCount++;
|
||||
} else if (insn instanceof InvokeInstruction) {
|
||||
InvokeInstruction invoke = (InvokeInstruction) insn;
|
||||
invokeCount += invoke.getArguments().size();
|
||||
if (invoke.getInstance() != null) {
|
||||
invokeCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
complexity += block.instructionCount() - nopCount + invokeCount;
|
||||
Instruction lastInsn = block.getLastInstruction();
|
||||
if (lastInsn instanceof SwitchInstruction) {
|
||||
complexity += 3;
|
||||
} else if (lastInsn instanceof BinaryBranchingInstruction || lastInsn instanceof BranchingInstruction) {
|
||||
complexity += 2;
|
||||
}
|
||||
}
|
||||
return complexity;
|
||||
}
|
||||
|
||||
private void devirtualize(Program program, MethodReference method, DependencyInfo dependencyInfo) {
|
||||
ClassInference inference = new ClassInference(dependencyInfo, hierarchy);
|
||||
inference.infer(program, method);
|
||||
|
@ -350,8 +389,43 @@ public class Inlining {
|
|||
private class PlanEntry {
|
||||
int targetBlock;
|
||||
Instruction targetInstruction;
|
||||
MethodReference method;
|
||||
Program program;
|
||||
int depth;
|
||||
final List<PlanEntry> innerPlan = new ArrayList<>();
|
||||
}
|
||||
|
||||
static class MethodUsageCounter extends AbstractInstructionReader {
|
||||
ObjectIntMap<MethodReference> methodUsageCount = new ObjectIntHashMap<>();
|
||||
Map<MethodReference, Set<MethodReference>> methodDependencies = new LinkedHashMap<>();
|
||||
Predicate<MethodReference> externalMethods;
|
||||
MethodReference currentMethod;
|
||||
|
||||
MethodUsageCounter(Predicate<MethodReference> externalMethods) {
|
||||
this.externalMethods = externalMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments, InvocationType type) {
|
||||
if (type == InvocationType.SPECIAL && !externalMethods.test(method)) {
|
||||
methodUsageCount.put(method, methodUsageCount.get(method) + 1);
|
||||
methodDependencies.computeIfAbsent(currentMethod, k -> new LinkedHashSet<>()).add(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ContextImpl implements InliningContext {
|
||||
int depth;
|
||||
|
||||
@Override
|
||||
public boolean isUsedOnce(MethodReference method) {
|
||||
return methodsUsedOnce.contains(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDepth() {
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2019 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.optimization;
|
||||
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public interface InliningContext {
|
||||
boolean isUsedOnce(MethodReference method);
|
||||
|
||||
int getDepth();
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2019 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.optimization;
|
||||
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ProgramReader;
|
||||
|
||||
public interface InliningStep {
|
||||
InliningStep tryInline(MethodReference method, ProgramReader program, InliningContext context);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2019 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.optimization;
|
||||
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ProgramReader;
|
||||
|
||||
public interface InliningStrategy {
|
||||
InliningStep start(MethodReference method, ProgramReader program);
|
||||
}
|
|
@ -70,9 +70,11 @@ import org.teavm.model.ValueType;
|
|||
import org.teavm.model.optimization.ArrayUnwrapMotion;
|
||||
import org.teavm.model.optimization.ClassInitElimination;
|
||||
import org.teavm.model.optimization.ConstantConditionElimination;
|
||||
import org.teavm.model.optimization.DefaultInliningStrategy;
|
||||
import org.teavm.model.optimization.Devirtualization;
|
||||
import org.teavm.model.optimization.GlobalValueNumbering;
|
||||
import org.teavm.model.optimization.Inlining;
|
||||
import org.teavm.model.optimization.InliningStrategy;
|
||||
import org.teavm.model.optimization.LoopInvariantMotion;
|
||||
import org.teavm.model.optimization.MethodOptimization;
|
||||
import org.teavm.model.optimization.MethodOptimizationContext;
|
||||
|
@ -430,8 +432,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
compileProgressValue = 0;
|
||||
compileProgressLimit = dependencyAnalyzer.getReachableClasses().size();
|
||||
if (optimizationLevel == TeaVMOptimizationLevel.ADVANCED) {
|
||||
compileProgressLimit *= 3;
|
||||
} else if (optimizationLevel == TeaVMOptimizationLevel.FULL) {
|
||||
compileProgressLimit *= 4;
|
||||
} else {
|
||||
compileProgressLimit *= 2;
|
||||
|
@ -538,34 +538,44 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
}
|
||||
|
||||
private void inline(ListableClassHolderSource classes) {
|
||||
if (optimizationLevel != TeaVMOptimizationLevel.FULL) {
|
||||
if (optimizationLevel != TeaVMOptimizationLevel.ADVANCED) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<MethodReference, Program> inlinedPrograms = new HashMap<>();
|
||||
Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer);
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
InliningStrategy inliningStrategy;
|
||||
if (optimizationLevel == TeaVMOptimizationLevel.FULL) {
|
||||
inliningStrategy = new DefaultInliningStrategy(17, 7, false);
|
||||
} else {
|
||||
inliningStrategy = new DefaultInliningStrategy(100, 5, true);
|
||||
}
|
||||
|
||||
Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer, inliningStrategy,
|
||||
classes, this::isExternal);
|
||||
List<MethodReference> methodReferences = inlining.getOrder();
|
||||
int classCount = classes.getClassNames().size();
|
||||
int initialValue = compileProgressValue;
|
||||
for (int i = 0; i < methodReferences.size(); i++) {
|
||||
MethodReference methodReference = methodReferences.get(i);
|
||||
ClassHolder cls = classes.get(methodReference.getClassName());
|
||||
MethodHolder method = cls.getMethod(methodReference.getDescriptor());
|
||||
|
||||
if (method.getProgram() != null) {
|
||||
Program program = ProgramUtils.copy(method.getProgram());
|
||||
if (!inlining.hasUsages(methodReference)) {
|
||||
method.setProgram(null);
|
||||
} else {
|
||||
Program program = method.getProgram();
|
||||
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method);
|
||||
inlining.apply(program, method.getReference());
|
||||
new UnusedVariableElimination().optimize(context, program);
|
||||
inlinedPrograms.put(method.getReference(), program);
|
||||
}
|
||||
}
|
||||
reportCompileProgress(++compileProgressValue);
|
||||
if (wasCancelled()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (method.getProgram() != null) {
|
||||
method.setProgram(inlinedPrograms.get(method.getReference()));
|
||||
int newProgress = initialValue + classCount * i / methodReferences.size();
|
||||
if (newProgress > compileProgressValue) {
|
||||
compileProgressValue = newProgress;
|
||||
reportCompileProgress(++compileProgressValue);
|
||||
if (wasCancelled()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -716,6 +726,22 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
boolean isExternal(MethodReference method) {
|
||||
MethodDependencyInfo dep = dependencyAnalyzer.getMethod(method);
|
||||
if (dep != null && dep.isCalled()) {
|
||||
return true;
|
||||
}
|
||||
return isVirtual(method);
|
||||
}
|
||||
|
||||
boolean isVirtual(MethodReference method) {
|
||||
if (method.getName().equals("<init>") || method.getName().equals("<clinit>")) {
|
||||
return false;
|
||||
}
|
||||
return virtualMethods == null || virtualMethods.contains(method)
|
||||
|| additionalVirtualMethods.stream().anyMatch(p -> p.test(method));
|
||||
}
|
||||
|
||||
private TeaVMTargetController targetController = new TeaVMTargetController() {
|
||||
@Override
|
||||
public boolean wasCancelled() {
|
||||
|
@ -774,11 +800,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
|
||||
@Override
|
||||
public boolean isVirtual(MethodReference method) {
|
||||
if (method.getName().equals("<init>") || method.getName().equals("<clinit>")) {
|
||||
return false;
|
||||
}
|
||||
return virtualMethods == null || virtualMethods.contains(method)
|
||||
|| additionalVirtualMethods.stream().anyMatch(p -> p.test(method));
|
||||
return TeaVM.this.isVirtual(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,16 +26,15 @@ import org.teavm.dependency.DependencyPlugin;
|
|||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.platform.async.AsyncCallback;
|
||||
|
||||
public class AsyncMethodGenerator implements Generator, DependencyPlugin, VirtualMethodContributor {
|
||||
private static final MethodReference completeMethod = new MethodReference(AsyncCallback.class, "complete",
|
||||
Object.class, void.class);
|
||||
private static final MethodReference errorMethod = new MethodReference(AsyncCallback.class, "error",
|
||||
Throwable.class, void.class);
|
||||
private static final MethodDescriptor completeMethod = new MethodDescriptor("complete", Object.class, void.class);
|
||||
private static final MethodDescriptor errorMethod = new MethodDescriptor("error", Throwable.class, void.class);
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||
|
@ -52,13 +51,13 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin, Virtua
|
|||
writer.outdent().append("}").softNewLine();
|
||||
|
||||
writer.append("var callback").ws().append("=").ws().append("function()").ws().append("{};").softNewLine();
|
||||
writer.append("callback.").appendMethod(completeMethod.getDescriptor()).ws().append("=").ws()
|
||||
writer.append("callback.").appendMethod(completeMethod).ws().append("=").ws()
|
||||
.append("function(val)").ws().append("{").indent().softNewLine();
|
||||
writer.append("thread.attribute").ws().append('=').ws().append("val;").softNewLine();
|
||||
writer.append("$rt_setThread(javaThread);").softNewLine();
|
||||
writer.append("thread.resume();").softNewLine();
|
||||
writer.outdent().append("};").softNewLine();
|
||||
writer.append("callback.").appendMethod(errorMethod.getDescriptor()).ws().append("=").ws()
|
||||
writer.append("callback.").appendMethod(errorMethod).ws().append("=").ws()
|
||||
.append("function(e)").ws().append("{").indent().softNewLine();
|
||||
writer.append("thread.attribute").ws().append('=').ws().append("$rt_exception(e);").softNewLine();
|
||||
writer.append("$rt_setThread(javaThread);").softNewLine();
|
||||
|
@ -78,7 +77,7 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin, Virtua
|
|||
}
|
||||
writer.append("callback);").softNewLine();
|
||||
writer.outdent().append("}").ws().append("catch($e)").ws().append("{").indent().softNewLine();
|
||||
writer.append("callback.").appendMethod(errorMethod.getDescriptor()).append("($rt_exception($e));")
|
||||
writer.append("callback.").appendMethod(errorMethod).append("($rt_exception($e));")
|
||||
.softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
writer.outdent().append("});").softNewLine();
|
||||
|
@ -127,6 +126,13 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin, Virtua
|
|||
|
||||
@Override
|
||||
public boolean isVirtual(VirtualMethodContributorContext context, MethodReference methodRef) {
|
||||
return methodRef.equals(completeMethod) || methodRef.equals(errorMethod);
|
||||
ClassReader cls = context.getClassSource().get(methodRef.getClassName());
|
||||
if (cls == null) {
|
||||
return false;
|
||||
}
|
||||
if (!cls.getInterfaces().contains(AsyncCallback.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
return methodRef.getDescriptor().equals(completeMethod) || methodRef.getDescriptor().equals(errorMethod);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.teavm.backend.javascript.spi.InjectorContext;
|
|||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.dependency.MethodDependencyInfo;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
|
@ -108,11 +109,16 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
|||
}
|
||||
}
|
||||
|
||||
private void generatePrepareNewInstance(GeneratorContext context, SourceWriter writer)
|
||||
throws IOException {
|
||||
private void generatePrepareNewInstance(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||
MethodDependencyInfo newInstanceMethod = context.getDependency().getMethod(
|
||||
new MethodReference(Platform.class, "newInstanceImpl", PlatformClass.class, Object.class));
|
||||
writer.append("var c").ws().append("=").ws().append("'$$constructor$$';").softNewLine();
|
||||
for (String clsName : context.getClassSource().getClassNames()) {
|
||||
if (newInstanceMethod != null) {
|
||||
for (String clsName : newInstanceMethod.getResult().getTypes()) {
|
||||
ClassReader cls = context.getClassSource().get(clsName);
|
||||
if (cls == null) {
|
||||
continue;
|
||||
}
|
||||
MethodReader method = cls.getMethod(new MethodDescriptor("<init>", void.class));
|
||||
if (method != null) {
|
||||
writer.appendClass(clsName).append("[c]").ws().append("=").ws()
|
||||
|
@ -120,6 +126,7 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateNewInstance(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||
String cls = context.getParameterName(1);
|
||||
|
|
Loading…
Reference in New Issue
Block a user