mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Implement CFG splitting
This commit is contained in:
parent
5593aa074e
commit
de85560876
|
@ -24,6 +24,7 @@ import org.teavm.javascript.ni.InjectedBy;
|
|||
import org.teavm.javascript.ni.PreserveOriginalName;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.util.AsyncProgramSplitter;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -45,10 +46,12 @@ public class Decompiler {
|
|||
private Map<MethodReference, Generator> generators = new HashMap<>();
|
||||
private Set<MethodReference> methodsToPass = new HashSet<>();
|
||||
private RegularMethodNodeCache regularMethodCache;
|
||||
private Set<MethodReference> asyncMethods;
|
||||
|
||||
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader) {
|
||||
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, Set<MethodReference> asyncMethods) {
|
||||
this.classSource = classSource;
|
||||
this.classLoader = classLoader;
|
||||
this.asyncMethods = asyncMethods;
|
||||
}
|
||||
|
||||
public RegularMethodNodeCache getRegularMethodCache() {
|
||||
|
@ -146,6 +149,7 @@ public class Decompiler {
|
|||
if (method.getAnnotations().get(PreserveOriginalName.class.getName()) != null) {
|
||||
methodNode.setOriginalNamePreserved(true);
|
||||
}
|
||||
clsNode.getAsyncMethods().add(decompileAsync(method));
|
||||
}
|
||||
clsNode.getInterfaces().addAll(cls.getInterfaces());
|
||||
clsNode.getModifiers().addAll(mapModifiers(cls.getModifiers()));
|
||||
|
@ -159,7 +163,7 @@ public class Decompiler {
|
|||
|
||||
public MethodNode decompileAsync(MethodHolder method) {
|
||||
return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method, true) :
|
||||
decompileAsync(method);
|
||||
decompileRegularAsync(method);
|
||||
}
|
||||
|
||||
public NativeMethodNode decompileNative(MethodHolder method, boolean async) {
|
||||
|
@ -189,7 +193,6 @@ public class Decompiler {
|
|||
}
|
||||
|
||||
public RegularMethodNode decompileRegular(MethodHolder method) {
|
||||
// TODO: add caching in case of incremental build
|
||||
if (regularMethodCache == null) {
|
||||
return decompileRegularCacheMiss(method);
|
||||
}
|
||||
|
@ -198,24 +201,18 @@ public class Decompiler {
|
|||
node = decompileRegularCacheMiss(method);
|
||||
regularMethodCache.store(method.getReference(), node);
|
||||
}
|
||||
// TODO: add optimization
|
||||
node.getModifiers().addAll(mapModifiers(method.getModifiers()));
|
||||
int paramCount = Math.min(method.getSignature().length, method.getProgram().variableCount());
|
||||
for (int i = 0; i < paramCount; ++i) {
|
||||
Variable var = method.getProgram().variableAt(i);
|
||||
node.getParameterDebugNames().add(new HashSet<>(var.getDebugNames()));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public AsyncMethodNode decompileRegularAsync(MethodHolder method) {
|
||||
AsyncMethodNode node = new AsyncMethodNode(method.getReference());
|
||||
AsyncProgramSplitter splitter = new AsyncProgramSplitter();
|
||||
AsyncProgramSplitter splitter = new AsyncProgramSplitter(asyncMethods);
|
||||
splitter.split(method.getProgram());
|
||||
for (int i = 0; i < splitter.size(); ++i) {
|
||||
AsyncMethodPart part = new AsyncMethodPart();
|
||||
part.setInputVariable(splitter.getInput(i));
|
||||
part.setStatement(getRegularMethodStatement(splitter.getProgram(i)));
|
||||
part.setStatement(getRegularMethodStatement(splitter.getProgram(i), i, splitter.getBlockSuccessors(i)));
|
||||
node.getBody().add(part);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
@ -223,7 +220,7 @@ public class Decompiler {
|
|||
public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) {
|
||||
RegularMethodNode methodNode = new RegularMethodNode(method.getReference());
|
||||
Program program = method.getProgram();
|
||||
methodNode.setBody(getRegularMethodStatement(program));
|
||||
methodNode.setBody(getRegularMethodStatement(program, 0, new int[program.basicBlockCount()]));
|
||||
for (int i = 0; i < program.variableCount(); ++i) {
|
||||
methodNode.getVariables().add(program.variableAt(i).getRegister());
|
||||
}
|
||||
|
@ -238,8 +235,9 @@ public class Decompiler {
|
|||
return methodNode;
|
||||
}
|
||||
|
||||
private Statement getRegularMethodStatement(Program program) {
|
||||
private Statement getRegularMethodStatement(Program program, int currentPart, int[] targetBlocks) {
|
||||
lastBlockId = 1;
|
||||
graph = ProgramUtils.buildControlFlowGraph(program);
|
||||
indexer = new GraphIndexer(graph);
|
||||
graph = indexer.getGraph();
|
||||
loopGraph = new LoopGraph(this.graph);
|
||||
|
@ -289,9 +287,12 @@ public class Decompiler {
|
|||
int tmp = indexer.nodeAt(next);
|
||||
generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null;
|
||||
generator.statements.clear();
|
||||
generator.asyncTarget = null;
|
||||
InstructionLocation lastLocation = null;
|
||||
NodeLocation nodeLocation = null;
|
||||
for (Instruction insn : generator.currentBlock.getInstructions()) {
|
||||
List<Instruction> instructions = generator.currentBlock.getInstructions();
|
||||
for (int j = 0; j < instructions.size(); ++j) {
|
||||
Instruction insn = generator.currentBlock.getInstructions().get(j);
|
||||
if (insn.getLocation() != null && lastLocation != insn.getLocation()) {
|
||||
lastLocation = insn.getLocation();
|
||||
nodeLocation = new NodeLocation(lastLocation.getFileName(), lastLocation.getLine());
|
||||
|
@ -299,6 +300,9 @@ public class Decompiler {
|
|||
if (insn.getLocation() != null) {
|
||||
generator.setCurrentLocation(nodeLocation);
|
||||
}
|
||||
if (targetBlocks[i] != currentPart && j == instructions.size() - 1) {
|
||||
generator.asyncTarget = targetBlocks[i];
|
||||
}
|
||||
insn.acceptVisitor(generator);
|
||||
}
|
||||
for (TryCatchBlock tryCatch : generator.currentBlock.getTryCatchBlocks()) {
|
||||
|
|
|
@ -370,6 +370,11 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
for (MethodNode method : nonInitMethods) {
|
||||
renderBody(method, false);
|
||||
}
|
||||
for (MethodNode method : cls.getAsyncMethods()) {
|
||||
writer.append("/*").softNewLine();
|
||||
renderBody(method, false);
|
||||
writer.append("*/").softNewLine();
|
||||
}
|
||||
renderVirtualDeclarations(cls.getName(), virtualMethods);
|
||||
} catch (NamingException e) {
|
||||
throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e);
|
||||
|
|
|
@ -37,6 +37,7 @@ class StatementGenerator implements InstructionVisitor {
|
|||
Program program;
|
||||
ClassHolderSource classSource;
|
||||
private NodeLocation currentLocation;
|
||||
Integer asyncTarget;
|
||||
|
||||
public void setCurrentLocation(NodeLocation currentLocation) {
|
||||
this.currentLocation = currentLocation;
|
||||
|
@ -546,7 +547,7 @@ class StatementGenerator implements InstructionVisitor {
|
|||
for (int i = 0; i < insn.getArguments().size(); ++i) {
|
||||
exprArgs[i] = Expr.var(insn.getArguments().get(i).getIndex());
|
||||
}
|
||||
Expr invocationExpr;
|
||||
InvocationExpr invocationExpr;
|
||||
if (insn.getInstance() != null) {
|
||||
if (insn.getType() == InvocationType.VIRTUAL) {
|
||||
invocationExpr = Expr.invoke(insn.getMethod(), Expr.var(insn.getInstance().getIndex()), exprArgs);
|
||||
|
@ -557,6 +558,8 @@ class StatementGenerator implements InstructionVisitor {
|
|||
} else {
|
||||
invocationExpr = Expr.invokeStatic(insn.getMethod(), exprArgs);
|
||||
}
|
||||
invocationExpr.setAsyncTarget(asyncTarget);
|
||||
if (asyncTarget == null) {
|
||||
if (insn.getReceiver() != null) {
|
||||
assign(invocationExpr, insn.getReceiver());
|
||||
} else {
|
||||
|
@ -564,6 +567,11 @@ class StatementGenerator implements InstructionVisitor {
|
|||
stmt.setLocation(currentLocation);
|
||||
statements.add(stmt);
|
||||
}
|
||||
} else {
|
||||
AssignmentStatement stmt = Statement.assign(null, invocationExpr);
|
||||
stmt.setLocation(currentLocation);
|
||||
statements.add(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,6 +30,7 @@ public class ClassNode {
|
|||
private Set<NodeModifier> modifiers = EnumSet.noneOf(NodeModifier.class);
|
||||
private List<FieldNode> fields = new ArrayList<>();
|
||||
private List<MethodNode> methods = new ArrayList<>();
|
||||
private List<MethodNode> asyncMethods = new ArrayList<>();
|
||||
private List<String> interfaces = new ArrayList<>();
|
||||
|
||||
public ClassNode(String name, String parentName) {
|
||||
|
@ -53,6 +54,10 @@ public class ClassNode {
|
|||
return methods;
|
||||
}
|
||||
|
||||
public List<MethodNode> getAsyncMethods() {
|
||||
return asyncMethods;
|
||||
}
|
||||
|
||||
public List<String> getInterfaces() {
|
||||
return interfaces;
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ public abstract class Expr implements Cloneable {
|
|||
return expr;
|
||||
}
|
||||
|
||||
public static Expr invoke(MethodReference method, Expr target, Expr[] arguments) {
|
||||
public static InvocationExpr invoke(MethodReference method, Expr target, Expr[] arguments) {
|
||||
InvocationExpr expr = new InvocationExpr();
|
||||
expr.setMethod(method);
|
||||
expr.setType(InvocationType.DYNAMIC);
|
||||
|
@ -128,7 +128,7 @@ public abstract class Expr implements Cloneable {
|
|||
return expr;
|
||||
}
|
||||
|
||||
public static Expr invokeSpecial(MethodReference method, Expr target, Expr[] arguments) {
|
||||
public static InvocationExpr invokeSpecial(MethodReference method, Expr target, Expr[] arguments) {
|
||||
InvocationExpr expr = new InvocationExpr();
|
||||
expr.setMethod(method);
|
||||
expr.setType(InvocationType.SPECIAL);
|
||||
|
@ -137,7 +137,7 @@ public abstract class Expr implements Cloneable {
|
|||
return expr;
|
||||
}
|
||||
|
||||
public static Expr invokeStatic(MethodReference method, Expr[] arguments) {
|
||||
public static InvocationExpr invokeStatic(MethodReference method, Expr[] arguments) {
|
||||
InvocationExpr expr = new InvocationExpr();
|
||||
expr.setMethod(method);
|
||||
expr.setType(InvocationType.STATIC);
|
||||
|
|
|
@ -15,11 +15,10 @@
|
|||
*/
|
||||
package org.teavm.model.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.teavm.model.Program;
|
||||
import java.util.*;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.JumpInstruction;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -28,13 +27,116 @@ import org.teavm.model.Program;
|
|||
public class AsyncProgramSplitter {
|
||||
private List<Part> parts = new ArrayList<>();
|
||||
private Map<Long, Integer> partMap = new HashMap<>();
|
||||
private Set<MethodReference> asyncMethods = new HashSet<>();
|
||||
|
||||
public AsyncProgramSplitter(Set<MethodReference> asyncMethods) {
|
||||
this.asyncMethods = asyncMethods;
|
||||
}
|
||||
|
||||
public void split(Program program) {
|
||||
parts.clear();
|
||||
// TODO: implement splitting algorithm
|
||||
Program initialProgram = createStubCopy(program);
|
||||
Part initialPart = new Part();
|
||||
initialPart.program = initialProgram;
|
||||
initialPart.blockSuccessors = new int[program.basicBlockCount()];
|
||||
parts.add(initialPart);
|
||||
partMap.put(0L, 0);
|
||||
Step initialStep = new Step();
|
||||
initialStep.source = 0;
|
||||
initialStep.targetPart = initialPart;
|
||||
Queue<Step> queue = new ArrayDeque<>();
|
||||
queue.add(initialStep);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
Step step = queue.remove();
|
||||
BasicBlock targetBlock = step.targetPart.program.basicBlockAt(step.source);
|
||||
if (targetBlock.instructionCount() > 0) {
|
||||
continue;
|
||||
}
|
||||
BasicBlock sourceBlock = program.basicBlockAt(step.source);
|
||||
int end = step.sourceIndex;
|
||||
boolean asyncOccured = false;
|
||||
for (int i = step.sourceIndex; i < sourceBlock.getInstructions().size(); ++i) {
|
||||
Instruction insn = sourceBlock.getInstructions().get(i);
|
||||
if (insn instanceof InvokeInstruction) {
|
||||
InvokeInstruction invoke = (InvokeInstruction)insn;
|
||||
if (true) { //asyncMethods.contains(invoke)) {
|
||||
asyncOccured = true;
|
||||
long key = ((long)step.source << 32) | i;
|
||||
if (partMap.containsKey(key)) {
|
||||
step.targetPart.blockSuccessors[step.sourceIndex] = partMap.get(key);
|
||||
break;
|
||||
}
|
||||
Program nextProgram = createStubCopy(program);
|
||||
BasicBlock nextBlock = nextProgram.basicBlockAt(step.source);
|
||||
if (step.source > 0) {
|
||||
JumpInstruction jumpToNextBlock = new JumpInstruction();
|
||||
jumpToNextBlock.setTarget(nextBlock);
|
||||
nextProgram.basicBlockAt(0).getInstructions().add(jumpToNextBlock);
|
||||
}
|
||||
Part part = new Part();
|
||||
part.input = invoke.getReceiver() != null ? invoke.getReceiver().getIndex() : null;
|
||||
part.program = nextProgram;
|
||||
int partId = parts.size();
|
||||
part.blockSuccessors = new int[program.basicBlockCount()];
|
||||
Arrays.fill(part.blockSuccessors, partId);
|
||||
partMap.put(key, partId);
|
||||
step.targetPart.blockSuccessors[step.source] = partId;
|
||||
parts.add(part);
|
||||
Step next = new Step();
|
||||
next.source = step.source;
|
||||
next.sourceIndex = i + 1;
|
||||
next.targetPart = part;
|
||||
queue.add(next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock, step.sourceIndex, end + 1,
|
||||
targetBlock.getProgram()));
|
||||
if (step.sourceIndex == 0) {
|
||||
targetBlock.getPhis().addAll(ProgramUtils.copyPhis(sourceBlock, targetBlock.getProgram()));
|
||||
}
|
||||
ProgramUtils.copyTryCatches(sourceBlock, targetBlock.getProgram());
|
||||
for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) {
|
||||
if (tryCatch.getHandler() != null) {
|
||||
Step next = new Step();
|
||||
next.source = tryCatch.getHandler().getIndex();
|
||||
next.sourceIndex = 0;
|
||||
next.targetPart = step.targetPart;
|
||||
queue.add(next);
|
||||
}
|
||||
}
|
||||
if (!asyncOccured) {
|
||||
InstructionTransitionExtractor successorExtractor = new InstructionTransitionExtractor();
|
||||
sourceBlock.getLastInstruction().acceptVisitor(successorExtractor);
|
||||
for (BasicBlock successor : successorExtractor.getTargets()) {
|
||||
BasicBlock targetSuccessor = targetBlock.getProgram().basicBlockAt(successor.getIndex());
|
||||
if (targetSuccessor.instructionCount() == 0) {
|
||||
Step next = new Step();
|
||||
next.source = successor.getIndex();
|
||||
next.sourceIndex = 0;
|
||||
next.targetPart = step.targetPart;
|
||||
queue.add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partMap.clear();
|
||||
}
|
||||
|
||||
private Program createStubCopy(Program program) {
|
||||
Program copy = new Program();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
copy.createBasicBlock();
|
||||
}
|
||||
for (int i = 0; i < program.variableCount(); ++i) {
|
||||
copy.createVariable();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return parts.size();
|
||||
}
|
||||
|
@ -47,8 +149,20 @@ public class AsyncProgramSplitter {
|
|||
return parts.get(index).input;
|
||||
}
|
||||
|
||||
public int[] getBlockSuccessors(int index) {
|
||||
int[] result = parts.get(index).blockSuccessors;
|
||||
return Arrays.copyOf(result, result.length);
|
||||
}
|
||||
|
||||
private static class Part {
|
||||
Program program;
|
||||
Integer input;
|
||||
int[] blockSuccessors;
|
||||
}
|
||||
|
||||
private static class Step {
|
||||
Part targetPart;
|
||||
int source;
|
||||
int sourceIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,21 +92,8 @@ public final class ProgramUtils {
|
|||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlockReader block = program.basicBlockAt(i);
|
||||
BasicBlock blockCopy = copy.basicBlockAt(i);
|
||||
for (int j = 0; j < block.instructionCount(); ++j) {
|
||||
block.readInstruction(j, insnCopier);
|
||||
blockCopy.getInstructions().add(insnCopier.copy);
|
||||
}
|
||||
for (PhiReader phi : block.readPhis()) {
|
||||
Phi phiCopy = new Phi();
|
||||
phiCopy.setReceiver(copy.variableAt(phi.getReceiver().getIndex()));
|
||||
for (IncomingReader incoming : phi.readIncomings()) {
|
||||
Incoming incomingCopy = new Incoming();
|
||||
incomingCopy.setSource(copy.basicBlockAt(incoming.getSource().getIndex()));
|
||||
incomingCopy.setValue(copy.variableAt(incoming.getValue().getIndex()));
|
||||
phiCopy.getIncomings().add(incomingCopy);
|
||||
}
|
||||
blockCopy.getPhis().add(phiCopy);
|
||||
}
|
||||
blockCopy.getInstructions().addAll(copyInstructions(block, 0, block.instructionCount(), copy));
|
||||
blockCopy.getPhis().addAll(copyPhis(block, copy));
|
||||
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
|
||||
TryCatchBlock tryCatchCopy = new TryCatchBlock();
|
||||
tryCatchCopy.setExceptionType(tryCatch.getExceptionType());
|
||||
|
@ -118,6 +105,46 @@ public final class ProgramUtils {
|
|||
return copy;
|
||||
}
|
||||
|
||||
public static List<Instruction> copyInstructions(BasicBlockReader block, int from, int to, Program target) {
|
||||
List<Instruction> result = new ArrayList<>();
|
||||
InstructionCopyReader copyReader = new InstructionCopyReader();
|
||||
copyReader.programCopy = target;
|
||||
for (int i = from; i < to; ++i) {
|
||||
block.readInstruction(i, copyReader);
|
||||
copyReader.copy.setLocation(copyReader.location);
|
||||
result.add(copyReader.copy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<Phi> copyPhis(BasicBlockReader block, Program target) {
|
||||
List<Phi> result = new ArrayList<>();
|
||||
for (PhiReader phi : block.readPhis()) {
|
||||
Phi phiCopy = new Phi();
|
||||
phiCopy.setReceiver(target.variableAt(phi.getReceiver().getIndex()));
|
||||
for (IncomingReader incoming : phi.readIncomings()) {
|
||||
Incoming incomingCopy = new Incoming();
|
||||
incomingCopy.setSource(target.basicBlockAt(incoming.getSource().getIndex()));
|
||||
incomingCopy.setValue(target.variableAt(incoming.getValue().getIndex()));
|
||||
phiCopy.getIncomings().add(incomingCopy);
|
||||
}
|
||||
result.add(phiCopy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<TryCatchBlock> copyTryCatches(BasicBlockReader block, Program target) {
|
||||
List<TryCatchBlock> result = new ArrayList<>();
|
||||
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
|
||||
TryCatchBlock tryCatchCopy = new TryCatchBlock();
|
||||
tryCatchCopy.setExceptionType(tryCatch.getExceptionType());
|
||||
tryCatchCopy.setExceptionVariable(target.variableAt(tryCatch.getExceptionVariable().getIndex()));
|
||||
tryCatchCopy.setHandler(target.basicBlockAt(tryCatch.getHandler().getIndex()));
|
||||
result.add(tryCatchCopy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class InstructionCopyReader implements InstructionReader {
|
||||
Instruction copy;
|
||||
Program programCopy;
|
||||
|
|
|
@ -527,7 +527,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
|
||||
private List<ClassNode> modelToAst(ListableClassHolderSource classes) {
|
||||
progressListener.phaseStarted(TeaVMPhase.DECOMPILATION, classes.getClassNames().size());
|
||||
Decompiler decompiler = new Decompiler(classes, classLoader);
|
||||
Decompiler decompiler = new Decompiler(classes, classLoader, new HashSet<MethodReference>());
|
||||
decompiler.setRegularMethodCache(incremental ? astCache : null);
|
||||
|
||||
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user