Add WASM generator

This commit is contained in:
Alexey Andreev 2016-07-28 15:17:00 +03:00
parent 888710102f
commit 1484e970dd
18 changed files with 980 additions and 44 deletions

View File

@ -23,8 +23,6 @@ public enum BinaryOperation {
MODULO,
EQUALS,
NOT_EQUALS,
STRICT_EQUALS,
STRICT_NOT_EQUALS,
LESS,
LESS_OR_EQUALS,
GREATER,

View File

@ -15,10 +15,6 @@
*/
package org.teavm.ast;
/**
*
* @author Alexey Andreev
*/
public class BreakStatement extends Statement {
private IdentifiedStatement target;
private NodeLocation location;

View File

@ -15,10 +15,6 @@
*/
package org.teavm.ast;
/**
*
* @author Alexey Andreev
*/
public abstract class IdentifiedStatement extends Statement {
private String id;

View File

@ -20,10 +20,6 @@ import java.util.List;
import java.util.Map;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class InvocationExpr extends Expr {
private MethodReference method;
private InvocationType type;

View File

@ -20,10 +20,6 @@ import java.util.List;
import java.util.Set;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public abstract class MethodNode {
private MethodReference reference;
private Set<NodeModifier> modifiers = EnumSet.noneOf(NodeModifier.class);

View File

@ -20,10 +20,6 @@ import java.util.List;
import java.util.Set;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class RegularMethodNode extends MethodNode {
private Statement body;
private List<Integer> variables = new ArrayList<>();

View File

@ -18,10 +18,6 @@ package org.teavm.ast;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Alexey Andreev
*/
public class WhileStatement extends IdentifiedStatement {
private Expr condition;
private List<Statement> body = new ArrayList<>();

View File

@ -270,11 +270,11 @@ class StatementGenerator implements InstructionVisitor {
insn.getConsequent(), insn.getAlternative());
break;
case NOT_NULL:
branch(Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, null, Expr.var(insn.getOperand().getIndex()),
branch(Expr.binary(BinaryOperation.NOT_EQUALS, null, Expr.var(insn.getOperand().getIndex()),
Expr.constant(null)), insn.getConsequent(), insn.getAlternative());
break;
case NULL:
branch(Expr.binary(BinaryOperation.STRICT_EQUALS, null, Expr.var(insn.getOperand().getIndex()),
branch(Expr.binary(BinaryOperation.EQUALS, null, Expr.var(insn.getOperand().getIndex()),
Expr.constant(null)), insn.getConsequent(), insn.getAlternative());
break;
}
@ -292,7 +292,7 @@ class StatementGenerator implements InstructionVisitor {
consequent, alternative);
break;
case REFERENCE_EQUAL:
branch(withLocation(Expr.binary(BinaryOperation.STRICT_EQUALS, null, Expr.var(a), Expr.var(b))),
branch(withLocation(Expr.binary(BinaryOperation.EQUALS, null, Expr.var(a), Expr.var(b))),
consequent, alternative);
break;
case NOT_EQUAL:
@ -300,7 +300,7 @@ class StatementGenerator implements InstructionVisitor {
Expr.var(b))), consequent, alternative);
break;
case REFERENCE_NOT_EQUAL:
branch(withLocation(Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, null, Expr.var(a), Expr.var(b))),
branch(withLocation(Expr.binary(BinaryOperation.NOT_EQUALS, null, Expr.var(a), Expr.var(b))),
consequent, alternative);
break;
}

View File

@ -48,10 +48,6 @@ final class ExprOptimizer {
return Expr.binary(BinaryOperation.LESS_OR_EQUALS, binary.getType(), a, b, expr.getLocation());
case GREATER_OR_EQUALS:
return Expr.binary(BinaryOperation.LESS, binary.getType(), a, b, expr.getLocation());
case STRICT_EQUALS:
return Expr.binary(BinaryOperation.STRICT_NOT_EQUALS, binary.getType(), a, b, expr.getLocation());
case STRICT_NOT_EQUALS:
return Expr.binary(BinaryOperation.STRICT_EQUALS, binary.getType(), a, b, expr.getLocation());
default:
break;
}

View File

@ -1391,8 +1391,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
return Precedence.LOGICAL_AND;
case OR:
return Precedence.LOGICAL_OR;
case STRICT_EQUALS:
case STRICT_NOT_EQUALS:
case EQUALS:
case NOT_EQUALS:
return Precedence.EQUALITY;
@ -1516,10 +1514,10 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
visitBinary(expr, "%", expr.getType() == OperationType.INT);
break;
case EQUALS:
visitBinary(expr, "==", false);
visitBinary(expr, "===", false);
break;
case NOT_EQUALS:
visitBinary(expr, "!=", false);
visitBinary(expr, "!==", false);
break;
case GREATER:
visitBinary(expr, ">", false);
@ -1533,12 +1531,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
case LESS_OR_EQUALS:
visitBinary(expr, "<=", false);
break;
case STRICT_EQUALS:
visitBinary(expr, "===", false);
break;
case STRICT_NOT_EQUALS:
visitBinary(expr, "!==", false);
break;
case COMPARE:
visitBinaryFunction(expr, naming.getNameForFunction("$rt_compare"));
break;

View File

@ -0,0 +1,31 @@
/*
* 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;
public final class Example {
private Example() {
}
public static void main(String[] args) {
int a = 0;
int b = 1;
for (int i = 0; i < 10; ++i) {
int c = a + b;
a = b;
b = c;
}
}
}

View File

@ -0,0 +1,620 @@
/*
* 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.generate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.ast.AssignmentStatement;
import org.teavm.ast.BinaryExpr;
import org.teavm.ast.BlockStatement;
import org.teavm.ast.BreakStatement;
import org.teavm.ast.CastExpr;
import org.teavm.ast.ConditionalExpr;
import org.teavm.ast.ConditionalStatement;
import org.teavm.ast.ConstantExpr;
import org.teavm.ast.ContinueStatement;
import org.teavm.ast.Expr;
import org.teavm.ast.ExprVisitor;
import org.teavm.ast.GotoPartStatement;
import org.teavm.ast.IdentifiedStatement;
import org.teavm.ast.InitClassStatement;
import org.teavm.ast.InstanceOfExpr;
import org.teavm.ast.InvocationExpr;
import org.teavm.ast.InvocationType;
import org.teavm.ast.MonitorEnterStatement;
import org.teavm.ast.MonitorExitStatement;
import org.teavm.ast.NewArrayExpr;
import org.teavm.ast.NewExpr;
import org.teavm.ast.NewMultiArrayExpr;
import org.teavm.ast.OperationType;
import org.teavm.ast.PrimitiveCastExpr;
import org.teavm.ast.QualificationExpr;
import org.teavm.ast.ReturnStatement;
import org.teavm.ast.SequentialStatement;
import org.teavm.ast.Statement;
import org.teavm.ast.StatementVisitor;
import org.teavm.ast.SubscriptExpr;
import org.teavm.ast.SwitchClause;
import org.teavm.ast.SwitchStatement;
import org.teavm.ast.ThrowStatement;
import org.teavm.ast.TryCatchStatement;
import org.teavm.ast.UnaryExpr;
import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr;
import org.teavm.ast.WhileStatement;
import org.teavm.wasm.model.WasmFunction;
import org.teavm.wasm.model.WasmLocal;
import org.teavm.wasm.model.expression.WasmAssignment;
import org.teavm.wasm.model.expression.WasmBlock;
import org.teavm.wasm.model.expression.WasmBranch;
import org.teavm.wasm.model.expression.WasmBreak;
import org.teavm.wasm.model.expression.WasmCall;
import org.teavm.wasm.model.expression.WasmConditional;
import org.teavm.wasm.model.expression.WasmConversion;
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;
import org.teavm.wasm.model.expression.WasmReturn;
import org.teavm.wasm.model.expression.WasmSwitch;
class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private WasmFunction function;
private IdentifiedStatement currentContinueTarget;
private IdentifiedStatement currentBreakTarget;
private Map<IdentifiedStatement, WasmBlock> breakTargets = new HashMap<>();
private Map<IdentifiedStatement, WasmBlock> continueTargets = new HashMap<>();
private Set<WasmBlock> usedBlocks = new HashSet<>();
WasmExpression result;
public WasmGenerationVisitor(WasmFunction function) {
this.function = function;
}
@Override
public void visit(BinaryExpr expr) {
switch (expr.getOperation()) {
case ADD:
generateBinary(WasmIntBinaryOperation.ADD, WasmFloatBinaryOperation.ADD, expr);
break;
case SUBTRACT:
generateBinary(WasmIntBinaryOperation.SUB, WasmFloatBinaryOperation.ADD, expr);
break;
case MULTIPLY:
generateBinary(WasmIntBinaryOperation.MUL, WasmFloatBinaryOperation.ADD, expr);
break;
case DIVIDE:
generateBinary(WasmIntBinaryOperation.DIV_SIGNED, WasmFloatBinaryOperation.DIV, expr);
break;
case MODULO: {
switch (expr.getType()) {
case INT:
case LONG:
generateBinary(WasmIntBinaryOperation.REM_SIGNED, expr);
break;
default:
WasmCall call = new WasmCall("rt$remainder." + typeAsString(expr.getType()), false);
expr.getFirstOperand().acceptVisitor(this);
call.getArguments().add(result);
expr.getSecondOperand().acceptVisitor(this);
call.getArguments().add(result);
result = call;
break;
}
break;
}
case BITWISE_AND:
generateBinary(WasmIntBinaryOperation.AND, expr);
break;
case BITWISE_OR:
generateBinary(WasmIntBinaryOperation.OR, expr);
break;
case BITWISE_XOR:
generateBinary(WasmIntBinaryOperation.XOR, expr);
break;
case EQUALS:
generateBinary(WasmIntBinaryOperation.EQ, WasmFloatBinaryOperation.EQ, expr);
break;
case NOT_EQUALS:
generateBinary(WasmIntBinaryOperation.NE, WasmFloatBinaryOperation.NE, expr);
break;
case GREATER:
generateBinary(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.GT, expr);
break;
case GREATER_OR_EQUALS:
generateBinary(WasmIntBinaryOperation.GE_SIGNED, WasmFloatBinaryOperation.GE, expr);
break;
case LESS:
generateBinary(WasmIntBinaryOperation.LT_SIGNED, WasmFloatBinaryOperation.LT, expr);
break;
case LESS_OR_EQUALS:
generateBinary(WasmIntBinaryOperation.LE_SIGNED, WasmFloatBinaryOperation.LE, expr);
break;
case LEFT_SHIFT:
generateBinary(WasmIntBinaryOperation.SHL, expr);
break;
case RIGHT_SHIFT:
generateBinary(WasmIntBinaryOperation.SHR_SIGNED, expr);
break;
case UNSIGNED_RIGHT_SHIFT:
generateBinary(WasmIntBinaryOperation.SHR_UNSIGNED, expr);
break;
case COMPARE: {
WasmCall call = new WasmCall("rt$compare." + typeAsString(expr.getType()), false);
expr.getFirstOperand().acceptVisitor(this);
call.getArguments().add(result);
expr.getSecondOperand().acceptVisitor(this);
call.getArguments().add(result);
result = call;
break;
}
case AND:
generateAnd(expr);
break;
case OR:
generateOr(expr);
break;
}
}
private void generateBinary(WasmIntBinaryOperation intOp, WasmFloatBinaryOperation floatOp, BinaryExpr expr) {
expr.getFirstOperand().acceptVisitor(this);
WasmExpression first = result;
expr.getSecondOperand().acceptVisitor(this);
WasmExpression second = result;
if (expr.getType() == null) {
result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second);
} else {
switch (expr.getType()) {
case INT:
result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second);
break;
case LONG:
result = new WasmIntBinary(WasmIntType.INT64, intOp, first, second);
break;
case FLOAT:
result = new WasmFloatBinary(WasmFloatType.FLOAT32, floatOp, first, second);
break;
case DOUBLE:
result = new WasmFloatBinary(WasmFloatType.FLOAT64, floatOp, first, second);
break;
}
}
}
private void generateBinary(WasmIntBinaryOperation intOp, BinaryExpr expr) {
expr.getFirstOperand().acceptVisitor(this);
WasmExpression first = result;
expr.getSecondOperand().acceptVisitor(this);
WasmExpression second = result;
switch (expr.getType()) {
case INT:
result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second);
break;
case LONG:
result = new WasmIntBinary(WasmIntType.INT64, intOp, first, second);
break;
case FLOAT:
case DOUBLE:
throw new AssertionError("Can't translate operation " + intOp + " for type " + expr.getType());
}
}
private String typeAsString(OperationType type) {
switch (type) {
case INT:
return "i32";
case LONG:
return "i64";
case FLOAT:
return "float";
case DOUBLE:
return "double";
}
throw new AssertionError(type.toString());
}
private void generateAnd(BinaryExpr expr) {
WasmBlock block = new WasmBlock(false);
expr.getFirstOperand().acceptVisitor(this);
WasmBranch branch = new WasmBranch(negate(result), block);
branch.setResult(new WasmInt32Constant(0));
block.getBody().add(branch);
expr.getSecondOperand().acceptVisitor(this);
block.getBody().add(result);
result = block;
}
private void generateOr(BinaryExpr expr) {
WasmBlock block = new WasmBlock(false);
expr.getFirstOperand().acceptVisitor(this);
WasmBranch branch = new WasmBranch(result, block);
branch.setResult(new WasmInt32Constant(1));
block.getBody().add(branch);
expr.getSecondOperand().acceptVisitor(this);
block.getBody().add(result);
result = block;
}
@Override
public void visit(UnaryExpr expr) {
switch (expr.getOperation()) {
case INT_TO_BYTE:
expr.getOperand().acceptVisitor(this);
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL,
result, new WasmInt32Constant(24));
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED,
result, new WasmInt32Constant(24));
break;
case INT_TO_SHORT:
expr.getOperand().acceptVisitor(this);
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL,
result, new WasmInt32Constant(16));
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED,
result, new WasmInt32Constant(16));
break;
case INT_TO_CHAR:
expr.getOperand().acceptVisitor(this);
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL,
result, new WasmInt32Constant(16));
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_UNSIGNED,
result, new WasmInt32Constant(16));
break;
case LENGTH:
result = new WasmInt32Constant(0);
break;
case NOT:
expr.getOperand().acceptVisitor(this);
result = negate(result);
break;
case NEGATE:
expr.getOperand().acceptVisitor(this);
switch (expr.getType()) {
case INT:
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB,
new WasmInt32Constant(0), result);
break;
case LONG:
result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.SUB,
new WasmInt64Constant(0), result);
break;
case FLOAT:
result = new WasmFloatBinary(WasmFloatType.FLOAT32, WasmFloatBinaryOperation.SUB,
new WasmFloat32Constant(0), result);
break;
case DOUBLE:
result = new WasmFloatBinary(WasmFloatType.FLOAT64, WasmFloatBinaryOperation.SUB,
new WasmFloat64Constant(0), result);
break;
}
break;
case NULL_CHECK:
expr.getOperand().acceptVisitor(this);
break;
}
}
@Override
public void visit(AssignmentStatement statement) {
Expr left = statement.getLeftValue();
if (left instanceof VariableExpr) {
VariableExpr varExpr = (VariableExpr) left;
WasmLocal local = function.getLocalVariables().get(varExpr.getIndex());
statement.getRightValue().acceptVisitor(this);
result = new WasmAssignment(local, result);
} else {
throw new UnsupportedOperationException("This expression is not supported yet");
}
}
@Override
public void visit(ConditionalExpr expr) {
expr.getCondition().acceptVisitor(this);
WasmConditional conditional = new WasmConditional(result);
expr.getConsequent().acceptVisitor(this);
conditional.getThenBlock().getBody().add(result);
expr.getAlternative().acceptVisitor(this);
conditional.getThenBlock().getBody().add(result);
result = conditional;
}
@Override
public void visit(SequentialStatement statement) {
WasmBlock block = new WasmBlock(false);
for (Statement part : statement.getSequence()) {
part.acceptVisitor(this);
block.getBody().add(result);
}
result = block;
}
@Override
public void visit(ConstantExpr expr) {
if (expr.getValue() == null) {
result = new WasmInt32Constant(0);
} else if (expr.getValue() instanceof Integer) {
result = new WasmInt32Constant((Integer) expr.getValue());
} else if (expr.getValue() instanceof Long) {
result = new WasmInt64Constant((Long) expr.getValue());
} else if (expr.getValue() instanceof Float) {
result = new WasmFloat32Constant((Float) expr.getValue());
} else if (expr.getValue() instanceof Double) {
result = new WasmFloat64Constant((Double) expr.getValue());
} else {
throw new IllegalArgumentException("Constant unsupported: " + expr.getValue());
}
}
@Override
public void visit(ConditionalStatement statement) {
statement.getCondition().acceptVisitor(this);
WasmConditional conditional = new WasmConditional(result);
for (Statement part : statement.getConsequent()) {
part.acceptVisitor(this);
conditional.getThenBlock().getBody().add(result);
}
for (Statement part : statement.getAlternative()) {
part.acceptVisitor(this);
conditional.getElseBlock().getBody().add(result);
}
result = conditional;
}
@Override
public void visit(VariableExpr expr) {
result = new WasmLocalReference(function.getLocalVariables().get(expr.getIndex()));
}
@Override
public void visit(SubscriptExpr expr) {
throw new UnsupportedOperationException("Not supported yet");
}
@Override
public void visit(SwitchStatement statement) {
List<WasmBlock> wrappers = new ArrayList<>();
WasmBlock wrapper = new WasmBlock(false);
statement.getValue().acceptVisitor(this);
WasmSwitch wasmSwitch = new WasmSwitch(result, wrapper);
wrapper.getBody().add(wasmSwitch);
WasmBlock defaultBlock = new WasmBlock(false);
defaultBlock.getBody().add(wrapper);
for (Statement part : statement.getDefaultClause()) {
part.acceptVisitor(this);
defaultBlock.getBody().add(result);
}
wrapper = defaultBlock;
for (SwitchClause clause : statement.getClauses()) {
WasmBlock caseBlock = new WasmBlock(false);
caseBlock.getBody().add(wrapper);
wasmSwitch.getTargets().add(wrapper);
for (Statement part : clause.getBody()) {
part.acceptVisitor(this);
caseBlock.getBody().add(result);
}
wrappers.add(caseBlock);
wrapper = caseBlock;
}
for (WasmBlock nestedWrapper : wrappers) {
nestedWrapper.getBody().add(new WasmBreak(wrapper));
}
result = wrapper;
}
@Override
public void visit(UnwrapArrayExpr expr) {
expr.getArray().acceptVisitor(this);
}
@Override
public void visit(WhileStatement statement) {
WasmBlock wrapper = new WasmBlock(false);
WasmBlock loop = new WasmBlock(true);
continueTargets.put(statement, loop);
breakTargets.put(statement, wrapper);
IdentifiedStatement oldBreakTarget = currentBreakTarget;
IdentifiedStatement oldContinueTarget = currentContinueTarget;
currentBreakTarget = statement;
currentContinueTarget = statement;
for (Statement part : statement.getBody()) {
part.acceptVisitor(this);
loop.getBody().add(result);
}
currentBreakTarget = oldBreakTarget;
currentContinueTarget = oldContinueTarget;
continueTargets.remove(statement);
breakTargets.remove(statement);
if (usedBlocks.contains(wrapper)) {
wrapper.getBody().add(loop);
result = wrapper;
} else {
result = loop;
}
}
@Override
public void visit(InvocationExpr expr) {
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
String methodName = WasmMangling.mangleMethod(expr.getMethod());
WasmCall call = new WasmCall(methodName);
for (Expr argument : expr.getArguments()) {
argument.acceptVisitor(this);
call.getArguments().add(result);
}
result = call;
}
}
@Override
public void visit(BlockStatement statement) {
WasmBlock block = new WasmBlock(false);
if (statement.getId() != null) {
breakTargets.put(statement, block);
}
for (Statement part : statement.getBody()) {
part.acceptVisitor(this);
block.getBody().add(result);
}
if (statement.getId() != null) {
breakTargets.remove(statement);
}
result = block;
}
@Override
public void visit(QualificationExpr expr) {
}
@Override
public void visit(BreakStatement statement) {
IdentifiedStatement target = statement.getTarget();
if (target == null) {
target = currentBreakTarget;
}
WasmBlock wasmTarget = breakTargets.get(target);
usedBlocks.add(wasmTarget);
result = new WasmBreak(wasmTarget);
}
@Override
public void visit(NewExpr expr) {
}
@Override
public void visit(ContinueStatement statement) {
IdentifiedStatement target = statement.getTarget();
if (target == null) {
target = currentContinueTarget;
}
WasmBlock wasmTarget = continueTargets.get(target);
usedBlocks.add(wasmTarget);
result = new WasmBreak(wasmTarget);
}
@Override
public void visit(NewArrayExpr expr) {
}
@Override
public void visit(NewMultiArrayExpr expr) {
}
@Override
public void visit(ReturnStatement statement) {
if (statement.getResult() != null) {
statement.getResult().acceptVisitor(this);
} else {
result = null;
}
result = new WasmReturn(result);
}
@Override
public void visit(InstanceOfExpr expr) {
}
@Override
public void visit(ThrowStatement statement) {
}
@Override
public void visit(CastExpr expr) {
expr.getValue().acceptVisitor(this);
}
@Override
public void visit(InitClassStatement statement) {
}
@Override
public void visit(PrimitiveCastExpr expr) {
expr.getValue().acceptVisitor(this);
result = new WasmConversion(WasmGeneratorUtil.mapType(expr.getSource()),
WasmGeneratorUtil.mapType(expr.getTarget()), true, result);
}
@Override
public void visit(TryCatchStatement statement) {
}
@Override
public void visit(GotoPartStatement statement) {
}
@Override
public void visit(MonitorEnterStatement statement) {
}
@Override
public void visit(MonitorExitStatement statement) {
}
private WasmExpression negate(WasmExpression expr) {
if (expr instanceof WasmIntBinary) {
WasmIntBinary binary = (WasmIntBinary) expr;
if (binary.getType() == WasmIntType.INT32 && binary.getOperation() == WasmIntBinaryOperation.XOR) {
if (isOne(binary.getFirst())) {
return binary.getSecond();
}
if (isOne(binary.getSecond())) {
return binary.getFirst();
}
}
}
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.XOR, expr, new WasmInt32Constant(1));
}
private boolean isOne(WasmExpression expression) {
return expression instanceof WasmInt32Constant && ((WasmInt32Constant) expression).getValue() == 1;
}
}

View File

@ -15,5 +15,55 @@
*/
package org.teavm.wasm.generate;
import org.teavm.ast.RegularMethodNode;
import org.teavm.ast.decompilation.Decompiler;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.util.TypeInferer;
import org.teavm.model.util.VariableType;
import org.teavm.wasm.model.WasmFunction;
import org.teavm.wasm.model.WasmLocal;
public class WasmGenerator {
private Decompiler decompiler;
private ClassHolderSource classSource;
public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource) {
this.decompiler = decompiler;
this.classSource = classSource;
}
public WasmFunction generate(MethodReference methodReference) {
ClassHolder cls = classSource.get(methodReference.getClassName());
MethodHolder method = cls.getMethod(methodReference.getDescriptor());
Program program = method.getProgram();
RegularMethodNode methodAst = decompiler.decompileRegular(method);
TypeInferer inferer = new TypeInferer();
inferer.inferTypes(program, methodReference);
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference));
for (int i = 0; i < methodAst.getVariables().size(); ++i) {
int varIndex = methodAst.getVariables().get(i);
VariableType type = inferer.typeOf(varIndex);
function.add(new WasmLocal(WasmGeneratorUtil.mapType(type)));
}
for (int i = 0; i <= methodReference.parameterCount(); ++i) {
function.getParameters().add(function.getLocalVariables().get(i).getType());
}
if (methodReference.getReturnType() != ValueType.VOID) {
function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType()));
}
WasmGenerationVisitor visitor = new WasmGenerationVisitor(function);
methodAst.getBody().acceptVisitor(visitor);
function.getBody().add(visitor.result);
return function;
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.generate;
import org.teavm.ast.OperationType;
import org.teavm.model.ValueType;
import org.teavm.model.util.VariableType;
import org.teavm.wasm.model.WasmType;
public final class WasmGeneratorUtil {
private WasmGeneratorUtil() {
}
public static WasmType mapType(OperationType type) {
switch (type) {
case INT:
return WasmType.INT32;
case LONG:
return WasmType.INT64;
case FLOAT:
return WasmType.FLOAT32;
case DOUBLE:
return WasmType.FLOAT64;
}
throw new IllegalArgumentException(type.toString());
}
public static WasmType mapType(ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
case BYTE:
case SHORT:
case CHARACTER:
case INTEGER:
return WasmType.INT32;
case LONG:
return WasmType.INT64;
case FLOAT:
return WasmType.FLOAT32;
case DOUBLE:
return WasmType.FLOAT64;
}
}
return WasmType.INT32;
}
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

@ -0,0 +1,97 @@
/*
* 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.generate;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public final class WasmMangling {
private WasmMangling() {
}
public static String mangleMethod(MethodReference method) {
StringBuilder sb = new StringBuilder("method$" + mangleString(method.getClassName()) + "$");
String name = mangleString(method.getName());
sb.append(mangleType(method.getReturnType()));
sb.append(name.length() + "_" + name);
sb.append(Arrays.stream(method.getParameterTypes())
.map(WasmMangling::mangleType)
.collect(Collectors.joining()));
return sb.toString();
}
private static String mangleString(String string) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < string.length(); ++i) {
char c = string.charAt(i);
switch (c) {
case '$':
case '.':
case '-':
sb.append(c);
break;
case '_':
sb.append("__");
break;
default:
if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9') {
sb.append(c);
} else {
sb.append('_')
.append(Character.forDigit(c >>> 12, 16))
.append(Character.forDigit((c >>> 8) & 0xF, 16))
.append(Character.forDigit((c >>> 4) & 0xF, 16))
.append(Character.forDigit(c & 0xF, 16));
}
break;
}
}
return sb.toString();
}
public static String mangleType(ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
return "Z";
case BYTE:
return "B";
case SHORT:
return "S";
case CHARACTER:
return "C";
case INTEGER:
return "I";
case LONG:
return "L";
case FLOAT:
return "F";
case DOUBLE:
return "D";
}
} else if (type instanceof ValueType.Void) {
return "V";
} else if (type instanceof ValueType.Array) {
return "A" + mangleType(((ValueType.Array) type).getItemType());
} else if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName();
return "L" + className.length() + "_" + className;
}
throw new IllegalArgumentException("Don't know how to mangle " + type);
}
}

View File

@ -20,8 +20,11 @@ import java.util.Objects;
public class WasmBranch extends WasmExpression {
private WasmExpression condition;
private WasmBlock target;
private WasmExpression result;
public WasmBranch(WasmExpression condition, WasmBlock target) {
Objects.requireNonNull(condition);
Objects.requireNonNull(target);
this.condition = condition;
this.target = target;
}
@ -43,4 +46,12 @@ public class WasmBranch extends WasmExpression {
Objects.requireNonNull(target);
this.target = target;
}
public WasmExpression getResult() {
return result;
}
public void setResult(WasmExpression result) {
this.result = result;
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.model.expression;
import java.util.Objects;
public class WasmBreak extends WasmExpression {
private WasmBlock target;
private WasmExpression result;
public WasmBreak(WasmBlock target) {
Objects.requireNonNull(target);
this.target = target;
}
public WasmBlock getTarget() {
return target;
}
public void setTarget(WasmBlock target) {
Objects.requireNonNull(target);
this.target = target;
}
public WasmExpression getResult() {
return result;
}
public void setResult(WasmExpression result) {
this.result = result;
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.runtime;
public final class WasmRuntime {
private WasmRuntime() {
}
public static int compare(int a, int b) {
return a > b ? 1 : a < b ? -1 : 0;
}
public static int compare(long a, long b) {
return a > b ? 1 : a < b ? -1 : 0;
}
public static int compare(float a, float b) {
return a > b ? 1 : a < b ? -1 : 0;
}
public static int compare(double a, double b) {
return a > b ? 1 : a < b ? -1 : 0;
}
public static float remainder(float a, float b) {
return a - (float) (int) (a / b) * b;
}
public static double remainder(double a, double b) {
return a - (double) (long) (a / b) * b;
}
}