mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-08 07:54:11 -08:00
Continue developing decompiler
This commit is contained in:
parent
96ba2bbf7d
commit
d672fe068a
|
@ -165,6 +165,36 @@ public final class GraphUtils {
|
|||
return graph.build();
|
||||
}
|
||||
|
||||
public static int[] dfs(Graph graph) {
|
||||
int[] result = new int[graph.size()];
|
||||
int[] state = new int[graph.size()];
|
||||
int[] stack = new int[graph.size() * 2];
|
||||
int top = 0;
|
||||
stack[top++] = 0;
|
||||
int index = graph.size();
|
||||
|
||||
while (top > 0) {
|
||||
int node = stack[--top];
|
||||
switch (state[node]) {
|
||||
case 0:
|
||||
state[node] = 1;
|
||||
stack[top++] = node;
|
||||
for (int successor : graph.outgoingEdges(node)) {
|
||||
if (state[successor] == 0) {
|
||||
stack[top++] = node;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
result[node] = --index;
|
||||
state[node] = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void splitIrreducibleGraph(Graph graph, int[] weights, GraphSplittingBackend backend) {
|
||||
new IrreducibleGraphConverter().convertToReducible(graph, weights, backend);
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 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.wasm.decompile;
|
||||
|
||||
import com.carrotsearch.hppc.IntObjectMap;
|
||||
import com.carrotsearch.hppc.IntObjectOpenHashMap;
|
||||
|
||||
public final class ContextUtils {
|
||||
private ContextUtils() {
|
||||
}
|
||||
|
||||
public static void withLabels(Context context, IntObjectMap<Label> labels) {
|
||||
IntObjectMap<Label> undo = new IntObjectOpenHashMap<>();
|
||||
for (int node : labels.keys().toArray()) {
|
||||
undo.put(node, context.getLabel(node));
|
||||
}
|
||||
context.push(() -> withLabelsImpl(context, undo));
|
||||
withLabelsImpl(context, labels);
|
||||
}
|
||||
|
||||
private static void withLabelsImpl(Context context, IntObjectMap<Label> labels) {
|
||||
for (int node : labels.keys().toArray()) {
|
||||
context.setLabel(node, labels.get(node));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright 2016 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.wasm.decompile;
|
||||
|
||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||
import org.teavm.model.instructions.BranchingCondition;
|
||||
import org.teavm.model.instructions.BranchingInstruction;
|
||||
import org.teavm.model.util.VariableType;
|
||||
import org.teavm.wasm.model.WasmLocal;
|
||||
import org.teavm.wasm.model.WasmType;
|
||||
import org.teavm.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.wasm.model.expression.WasmFloat32Constant;
|
||||
import org.teavm.wasm.model.expression.WasmFloat64Constant;
|
||||
import org.teavm.wasm.model.expression.WasmFloatBinary;
|
||||
import org.teavm.wasm.model.expression.WasmFloatBinaryOperation;
|
||||
import org.teavm.wasm.model.expression.WasmFloatType;
|
||||
import org.teavm.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.wasm.model.expression.WasmInt64Constant;
|
||||
import org.teavm.wasm.model.expression.WasmIntBinary;
|
||||
import org.teavm.wasm.model.expression.WasmIntBinaryOperation;
|
||||
import org.teavm.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.wasm.model.expression.WasmLocalReference;
|
||||
|
||||
final class DecompileSupport {
|
||||
private DecompileSupport() {
|
||||
}
|
||||
|
||||
public static WasmExpression getCondition(BranchingInstruction instruction, WasmLocal local, VariableType type) {
|
||||
WasmExpression operand = new WasmLocalReference(local);
|
||||
|
||||
WasmExpression condition;
|
||||
switch (type) {
|
||||
case FLOAT:
|
||||
condition = new WasmFloatBinary(WasmFloatType.FLOAT32, getFloatCondition(instruction.getCondition()),
|
||||
operand, new WasmFloat32Constant(0));
|
||||
break;
|
||||
case DOUBLE:
|
||||
condition = new WasmFloatBinary(WasmFloatType.FLOAT64, getFloatCondition(instruction.getCondition()),
|
||||
operand, new WasmFloat64Constant(0));
|
||||
break;
|
||||
case INT:
|
||||
condition = new WasmIntBinary(WasmIntType.INT32, getIntCondition(instruction.getCondition()),
|
||||
operand, new WasmInt32Constant(0));
|
||||
break;
|
||||
case LONG:
|
||||
condition = new WasmIntBinary(WasmIntType.INT64, getIntCondition(instruction.getCondition()),
|
||||
operand, new WasmInt64Constant(0));
|
||||
break;
|
||||
default:
|
||||
condition = new WasmIntBinary(WasmIntType.INT32, getReferenceCondition(instruction.getCondition()),
|
||||
operand, new WasmInt32Constant(0));
|
||||
break;
|
||||
}
|
||||
|
||||
return operand;
|
||||
}
|
||||
|
||||
public static WasmExpression getCondition(BinaryBranchingInstruction instruction, WasmLocal first,
|
||||
WasmLocal second) {
|
||||
WasmExpression a = new WasmLocalReference(first);
|
||||
WasmExpression b = new WasmLocalReference(second);
|
||||
|
||||
WasmExpression condition;
|
||||
switch (instruction.getCondition()) {
|
||||
case REFERENCE_EQUAL:
|
||||
case EQUAL:
|
||||
condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, a, b);
|
||||
break;
|
||||
case REFERENCE_NOT_EQUAL:
|
||||
case NOT_EQUAL:
|
||||
condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, a, b);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(instruction.getCondition().toString());
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
private static WasmFloatBinaryOperation getFloatCondition(BranchingCondition condition) {
|
||||
switch (condition) {
|
||||
case EQUAL:
|
||||
return WasmFloatBinaryOperation.EQ;
|
||||
case NOT_EQUAL:
|
||||
return WasmFloatBinaryOperation.NE;
|
||||
case GREATER:
|
||||
return WasmFloatBinaryOperation.GT;
|
||||
case GREATER_OR_EQUAL:
|
||||
return WasmFloatBinaryOperation.GE;
|
||||
case LESS:
|
||||
return WasmFloatBinaryOperation.LT;
|
||||
case LESS_OR_EQUAL:
|
||||
return WasmFloatBinaryOperation.LE;
|
||||
case NULL:
|
||||
case NOT_NULL:
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException(condition.toString());
|
||||
}
|
||||
|
||||
private static WasmIntBinaryOperation getIntCondition(BranchingCondition condition) {
|
||||
switch (condition) {
|
||||
case EQUAL:
|
||||
return WasmIntBinaryOperation.EQ;
|
||||
case NOT_EQUAL:
|
||||
return WasmIntBinaryOperation.NE;
|
||||
case GREATER:
|
||||
return WasmIntBinaryOperation.GT_SIGNED;
|
||||
case GREATER_OR_EQUAL:
|
||||
return WasmIntBinaryOperation.GE_SIGNED;
|
||||
case LESS:
|
||||
return WasmIntBinaryOperation.LT_SIGNED;
|
||||
case LESS_OR_EQUAL:
|
||||
return WasmIntBinaryOperation.LE_SIGNED;
|
||||
case NULL:
|
||||
case NOT_NULL:
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException(condition.toString());
|
||||
}
|
||||
|
||||
private static WasmIntBinaryOperation getReferenceCondition(BranchingCondition condition) {
|
||||
switch (condition) {
|
||||
case NULL:
|
||||
return WasmIntBinaryOperation.EQ;
|
||||
case NOT_NULL:
|
||||
return WasmIntBinaryOperation.NE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException(condition.toString());
|
||||
}
|
||||
|
||||
public static WasmType mapType(VariableType type) {
|
||||
switch (type) {
|
||||
case INT:
|
||||
return WasmType.INT32;
|
||||
case LONG:
|
||||
return WasmType.INT64;
|
||||
case FLOAT:
|
||||
return WasmType.FLOAT32;
|
||||
case DOUBLE:
|
||||
return WasmType.FLOAT64;
|
||||
default:
|
||||
return WasmType.INT32;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,40 +15,81 @@
|
|||
*/
|
||||
package org.teavm.wasm.decompile;
|
||||
|
||||
import com.carrotsearch.hppc.IntObjectMap;
|
||||
import com.carrotsearch.hppc.IntObjectOpenHashMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.teavm.common.DominatorTree;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphUtils;
|
||||
import org.teavm.common.IntegerArray;
|
||||
import org.teavm.common.Loop;
|
||||
import org.teavm.common.LoopGraph;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||
import org.teavm.model.instructions.BranchingInstruction;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.model.util.TypeInferer;
|
||||
import org.teavm.model.util.VariableType;
|
||||
import org.teavm.wasm.model.WasmFunction;
|
||||
import org.teavm.wasm.model.WasmLocal;
|
||||
import org.teavm.wasm.model.WasmType;
|
||||
import org.teavm.wasm.model.expression.WasmBlock;
|
||||
import org.teavm.wasm.model.expression.WasmConditional;
|
||||
import org.teavm.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.wasm.model.expression.WasmIntBinary;
|
||||
import org.teavm.wasm.model.expression.WasmIntBinaryOperation;
|
||||
import org.teavm.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.wasm.model.expression.WasmLocalReference;
|
||||
|
||||
public class WasmDecompiler {
|
||||
private WasmFunction function;
|
||||
private Program program;
|
||||
private LoopGraph cfg;
|
||||
private DominatorTree dom;
|
||||
private Graph domGraph;
|
||||
private Label[] labels;
|
||||
private WasmExpression[] blockExpressions;
|
||||
private TypeInferer typeInferer;
|
||||
private Step[] stack;
|
||||
private int stackTop;
|
||||
|
||||
public void decompile(Program program) {
|
||||
public void decompile(Program program, MethodReference methodReference, WasmFunction function) {
|
||||
this.function = function;
|
||||
this.program = program;
|
||||
prepare();
|
||||
push(new EnterStep(0));
|
||||
prepare(methodReference);
|
||||
deferEnter(0, function.getBody());
|
||||
run();
|
||||
}
|
||||
|
||||
private void prepare() {
|
||||
private void prepare(MethodReference methodReference) {
|
||||
cfg = new LoopGraph(ProgramUtils.buildControlFlowGraph(program));
|
||||
dom = GraphUtils.buildDominatorTree(cfg);
|
||||
domGraph = GraphUtils.buildDominatorGraph(dom, cfg.size());
|
||||
labels = new Label[cfg.size()];
|
||||
blockExpressions = new WasmExpression[cfg.size()];
|
||||
stack = new Step[cfg.size() * 4];
|
||||
typeInferer = new TypeInferer();
|
||||
typeInferer.inferTypes(program, methodReference);
|
||||
|
||||
int maxLocal = 0;
|
||||
for (int i = 0; i < program.variableCount(); ++i) {
|
||||
maxLocal = Math.max(maxLocal, program.variableAt(i).getRegister());
|
||||
}
|
||||
|
||||
WasmType[] types = new WasmType[maxLocal];
|
||||
for (int i = 0; i < program.variableCount(); ++i) {
|
||||
int register = program.variableAt(i).getRegister();
|
||||
if (types[register] == null) {
|
||||
types[register] = DecompileSupport.mapType(typeInferer.typeOf(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < maxLocal; ++i) {
|
||||
WasmType type = types[i];
|
||||
function.getLocalVariables().add(new WasmLocal(type != null ? type : WasmType.INT32, null));
|
||||
}
|
||||
}
|
||||
|
||||
private void run() {
|
||||
|
@ -61,75 +102,129 @@ public class WasmDecompiler {
|
|||
stack[stackTop++] = step;
|
||||
}
|
||||
|
||||
private class EnterStep implements Step {
|
||||
private int index;
|
||||
private void deferEnter(int node, List<WasmExpression> expressions) {
|
||||
push(() -> enter(node, expressions));
|
||||
}
|
||||
|
||||
public EnterStep(int index) {
|
||||
this.index = index;
|
||||
private void enter(int node, List<WasmExpression> expressions) {
|
||||
Loop loop = cfg.loopAt(node);
|
||||
if (loop != null && loop.getHead() == node) {
|
||||
expressions = enterLoop(loop, expressions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void perform() {
|
||||
Loop loop = cfg.loopAt(index);
|
||||
if (loop != null && loop.getHead() == index) {
|
||||
enterLoop();
|
||||
} else if (cfg.outgoingEdgesCount(index) == 1) {
|
||||
enterOrdinaryBlock();
|
||||
} else {
|
||||
enterBranching();
|
||||
DecompilationVisitor decompilationVisitor = new DecompilationVisitor(expressions, context);
|
||||
compileInstructions(node, decompilationVisitor);
|
||||
|
||||
int successors = domGraph.outgoingEdgesCount(node);
|
||||
if (successors == 1) {
|
||||
deferEnter(domGraph.outgoingEdges(node)[0], expressions);
|
||||
} else if (successors == 0) {
|
||||
BasicBlock block = program.basicBlockAt(node);
|
||||
block.getLastInstruction().acceptVisitor(decompilationVisitor);
|
||||
} else {
|
||||
compileFork(node, expressions);
|
||||
}
|
||||
}
|
||||
|
||||
private void compileInstructions(int node, DecompilationVisitor decompilationVisitor) {
|
||||
BasicBlock block = program.basicBlockAt(node);
|
||||
int lastInstructionIndex = block.getInstructions().size() - 1;
|
||||
for (int i = 0; i < lastInstructionIndex; ++i) {
|
||||
block.getInstructions().get(i).acceptVisitor(decompilationVisitor);
|
||||
}
|
||||
}
|
||||
|
||||
private List<WasmExpression> enterLoop(Loop loop, List<WasmExpression> expressions) {
|
||||
WasmBlock wasmLoop = new WasmBlock(true);
|
||||
List<WasmExpression> innerExpressions = wasmLoop.getBody();
|
||||
expressions.add(wasmLoop);
|
||||
LoopExitStep exitStep = new LoopExitStep(expressions, wasmLoop);
|
||||
push(exitStep);
|
||||
|
||||
IntObjectMap<Label> newLabels = new IntObjectOpenHashMap<>();
|
||||
int[] exits = findLoopExits(loop);
|
||||
Label breakLabel = exitStep.createBreakLabel();
|
||||
for (int exit : exits) {
|
||||
newLabels.put(exit, breakLabel);
|
||||
}
|
||||
newLabels.put(loop.getHead(), exitStep.createContinueLabel());
|
||||
withLabels(newLabels);
|
||||
|
||||
return innerExpressions;
|
||||
}
|
||||
|
||||
private int[] findLoopExits(Loop loop) {
|
||||
IntegerArray exits = new IntegerArray(2);
|
||||
for (int i = 0; i < cfg.size(); ++i) {
|
||||
Loop nodeLoop = cfg.loopAt(i);
|
||||
if (nodeLoop != null && nodeLoop.isChildOf(loop)) {
|
||||
for (int successor : cfg.outgoingEdges(i)) {
|
||||
Loop successorLoop = cfg.loopAt(successor);
|
||||
if (successorLoop == null || !successorLoop.isChildOf(loop)) {
|
||||
exits.add(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return exits.getAll();
|
||||
}
|
||||
|
||||
private void enterLoop() {
|
||||
|
||||
}
|
||||
|
||||
private void enterOrdinaryBlock() {
|
||||
|
||||
}
|
||||
|
||||
private void enterBranching() {
|
||||
|
||||
private void compileFork(int node, List<WasmExpression> expressions) {
|
||||
Instruction instruction = program.basicBlockAt(node).getLastInstruction();
|
||||
if (instruction instanceof BranchingInstruction) {
|
||||
compileFork(node, expressions, (BranchingInstruction) instruction);
|
||||
} else if (instruction instanceof BinaryBranchingInstruction) {
|
||||
compileFork(node, expressions, (BinaryBranchingInstruction) instruction);
|
||||
}
|
||||
}
|
||||
|
||||
private class LoopContinueLabel implements Label {
|
||||
private int index;
|
||||
private WasmBlock wrapper;
|
||||
private void compileFork(int node, List<WasmExpression> expressions, BranchingInstruction instruction) {
|
||||
WasmLocal local = function.getLocalVariables().get(instruction.getOperand().getRegister());
|
||||
WasmExpression operand = new WasmLocalReference(local);
|
||||
VariableType operandType = typeInferer.typeOf(instruction.getOperand().getIndex());
|
||||
WasmExpression condition = DecompileSupport.getCondition(instruction, local, operandType);
|
||||
compileIf(node, expressions, condition, instruction.getConsequent().getIndex(),
|
||||
instruction.getAlternative().getIndex());
|
||||
}
|
||||
|
||||
public LoopContinueLabel(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
private void compileFork(int node, List<WasmExpression> expressions, BinaryBranchingInstruction instruction) {
|
||||
WasmLocal a = function.getLocalVariables().get(instruction.getFirstOperand().getIndex());
|
||||
WasmLocal b = function.getLocalVariables().get(instruction.getSecondOperand().getIndex());
|
||||
WasmExpression condition = DecompileSupport.getCondition(instruction, a, b);
|
||||
compileIf(node, expressions, condition, instruction.getConsequent().getIndex(),
|
||||
instruction.getAlternative().getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmBlock getTarget() {
|
||||
if (wrapper == null) {
|
||||
wrapper = new WasmBlock(false);
|
||||
wrapper.getBody().add(blockExpressions[index]);
|
||||
blockExpressions[index] = wrapper;
|
||||
}
|
||||
return wrapper;
|
||||
private void compileIf(int node, List<WasmExpression> expressions, WasmExpression condition,
|
||||
int thenNode, int elseNode) {
|
||||
boolean ownsThen = dom.directlyDominates(node, thenNode);
|
||||
boolean ownsElse = dom.directlyDominates(node, elseNode);
|
||||
int[] exits = Arrays.stream(domGraph.outgoingEdges(node))
|
||||
.filter(n -> n != thenNode && n != elseNode)
|
||||
.toArray();
|
||||
|
||||
if (ownsThen && ownsElse) {
|
||||
WasmConditional conditional = new WasmConditional(condition);
|
||||
expressions.add(conditional);
|
||||
compileBranch(conditional.getElseBlock(), elseNode, exits);
|
||||
compileBranch(conditional.getThenBlock(), thenNode, exits);
|
||||
}
|
||||
}
|
||||
|
||||
private class BreakLabel implements Label {
|
||||
private WasmBlock target;
|
||||
|
||||
public BreakLabel(WasmBlock target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmBlock getTarget() {
|
||||
return null;
|
||||
private void withLabels(IntObjectMap<Label> labels) {
|
||||
IntObjectMap<Label> undo = new IntObjectOpenHashMap<>();
|
||||
for (int node : labels.keys().toArray()) {
|
||||
undo.put(node, this.labels[node]);
|
||||
}
|
||||
push(() -> withLabelsImpl(undo));
|
||||
withLabelsImpl(labels);
|
||||
}
|
||||
|
||||
private interface Step {
|
||||
void perform();
|
||||
}
|
||||
|
||||
private interface Label {
|
||||
WasmBlock getTarget();
|
||||
private void withLabelsImpl(IntObjectMap<Label> labels) {
|
||||
for (int node : labels.keys().toArray()) {
|
||||
this.labels[node] = labels.get(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user