Continue developing decompiler

This commit is contained in:
Alexey Andreev 2016-07-26 22:06:40 +03:00 committed by Alexey Andreev
parent 96ba2bbf7d
commit d672fe068a
4 changed files with 344 additions and 97 deletions

View File

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

View File

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

View File

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

View File

@ -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;
public EnterStep(int index) {
this.index = index;
private void deferEnter(int node, List<WasmExpression> expressions) {
push(() -> enter(node, expressions));
}
@Override
public void perform() {
Loop loop = cfg.loopAt(index);
if (loop != null && loop.getHead() == index) {
enterLoop();
} else if (cfg.outgoingEdgesCount(index) == 1) {
enterOrdinaryBlock();
private void enter(int node, List<WasmExpression> expressions) {
Loop loop = cfg.loopAt(node);
if (loop != null && loop.getHead() == node) {
expressions = enterLoop(loop, expressions);
}
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 {
enterBranching();
compileFork(node, expressions);
}
}
private void enterLoop() {
}
private void enterOrdinaryBlock() {
}
private void enterBranching() {
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 class LoopContinueLabel implements Label {
private int index;
private WasmBlock wrapper;
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);
public LoopContinueLabel(int index) {
this.index = index;
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;
}
@Override
public WasmBlock getTarget() {
if (wrapper == null) {
wrapper = new WasmBlock(false);
wrapper.getBody().add(blockExpressions[index]);
blockExpressions[index] = wrapper;
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 wrapper;
}
}
}
return exits.getAll();
}
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 BreakLabel implements Label {
private WasmBlock target;
public BreakLabel(WasmBlock target) {
this.target = target;
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());
}
@Override
public WasmBlock getTarget() {
return null;
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());
}
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 interface Step {
void perform();
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 Label {
WasmBlock getTarget();
private void withLabelsImpl(IntObjectMap<Label> labels) {
for (int node : labels.keys().toArray()) {
this.labels[node] = labels.get(node);
}
}
}