From 1fb929e9ae69306849ff69d850e0596e9fafae7c Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 22 Aug 2016 00:22:23 +0300 Subject: [PATCH] Creating C renderer for WASM tree, in order to enable debugging until WASM gets its own debugging support --- .../org/teavm/backend/wasm/render/CBlock.java | 34 ++ .../backend/wasm/render/CExpression.java | 43 ++ .../org/teavm/backend/wasm/render/CLine.java | 19 + .../backend/wasm/render/CSingleLine.java | 28 + .../backend/wasm/render/WasmCRenderer.java | 19 + .../wasm/render/WasmCRenderingVisitor.java | 564 ++++++++++++++++++ 6 files changed, 707 insertions(+) create mode 100644 core/src/main/java/org/teavm/backend/wasm/render/CBlock.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/render/CExpression.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/render/CLine.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/render/CSingleLine.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java diff --git a/core/src/main/java/org/teavm/backend/wasm/render/CBlock.java b/core/src/main/java/org/teavm/backend/wasm/render/CBlock.java new file mode 100644 index 000000000..ba0c89326 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/render/CBlock.java @@ -0,0 +1,34 @@ +/* + * 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.backend.wasm.render; + +import java.util.ArrayList; +import java.util.List; + +class CBlock extends CLine { + private List lines = new ArrayList<>(); + + public CBlock() { + } + + public CBlock(List lines) { + this.lines.addAll(lines); + } + + public List getLines() { + return lines; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/render/CExpression.java b/core/src/main/java/org/teavm/backend/wasm/render/CExpression.java new file mode 100644 index 000000000..5e1c2aaf6 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/render/CExpression.java @@ -0,0 +1,43 @@ +/* + * 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.backend.wasm.render; + +import java.util.ArrayList; +import java.util.List; + +class CExpression { + private String text; + private List lines = new ArrayList<>(); + + public CExpression(String text) { + this.text = text; + } + + public CExpression() { + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public List getLines() { + return lines; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/render/CLine.java b/core/src/main/java/org/teavm/backend/wasm/render/CLine.java new file mode 100644 index 000000000..66b282208 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/render/CLine.java @@ -0,0 +1,19 @@ +/* + * 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.backend.wasm.render; + +abstract class CLine { +} diff --git a/core/src/main/java/org/teavm/backend/wasm/render/CSingleLine.java b/core/src/main/java/org/teavm/backend/wasm/render/CSingleLine.java new file mode 100644 index 000000000..829b970ae --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/render/CSingleLine.java @@ -0,0 +1,28 @@ +/* + * 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.backend.wasm.render; + +class CSingleLine extends CLine { + private String text; + + public CSingleLine(String text) { + this.text = text; + } + + public String getText() { + return text; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java new file mode 100644 index 000000000..eafc8997e --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java @@ -0,0 +1,19 @@ +/* + * 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.backend.wasm.render; + +public class WasmCRenderer { +} diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java new file mode 100644 index 000000000..ba277f5b5 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java @@ -0,0 +1,564 @@ +/* + * 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.backend.wasm.render; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmBlock; +import org.teavm.backend.wasm.model.expression.WasmBranch; +import org.teavm.backend.wasm.model.expression.WasmBreak; +import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmConditional; +import org.teavm.backend.wasm.model.expression.WasmConversion; +import org.teavm.backend.wasm.model.expression.WasmDrop; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor; +import org.teavm.backend.wasm.model.expression.WasmFloat32Constant; +import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; +import org.teavm.backend.wasm.model.expression.WasmFloatBinary; +import org.teavm.backend.wasm.model.expression.WasmFloatUnary; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmIndirectCall; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.backend.wasm.model.expression.WasmInt64Constant; +import org.teavm.backend.wasm.model.expression.WasmIntBinary; +import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.backend.wasm.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmLoadFloat32; +import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; +import org.teavm.backend.wasm.model.expression.WasmLoadInt32; +import org.teavm.backend.wasm.model.expression.WasmLoadInt64; +import org.teavm.backend.wasm.model.expression.WasmReturn; +import org.teavm.backend.wasm.model.expression.WasmSetLocal; +import org.teavm.backend.wasm.model.expression.WasmStoreFloat32; +import org.teavm.backend.wasm.model.expression.WasmStoreFloat64; +import org.teavm.backend.wasm.model.expression.WasmStoreInt32; +import org.teavm.backend.wasm.model.expression.WasmStoreInt64; +import org.teavm.backend.wasm.model.expression.WasmSwitch; +import org.teavm.backend.wasm.model.expression.WasmUnreachable; +import org.teavm.model.TextLocation; + +class WasmCRenderingVisitor implements WasmExpressionVisitor { + private CExpression value; + private Map blockInfoMap = new HashMap<>(); + private WasmType requiredType; + private int temporaryIndex; + private int blockIndex; + private WasmType functionType; + + public WasmCRenderingVisitor(WasmType functionType) { + this.functionType = functionType; + } + + public CExpression getValue() { + return value; + } + + @Override + public void visit(WasmBlock expression) { + BlockInfo info = new BlockInfo(); + info.type = requiredType; + info.index = blockInfoMap.size(); + blockInfoMap.put(expression, info); + + List body = expression.getBody(); + CExpression result = new CExpression(); + + if (!body.isEmpty()) { + for (int i = 0; i < body.size() - 1; ++i) { + requiredType = null; + body.get(i).acceptVisitor(this); + result.getLines().addAll(value.getLines()); + } + + requiredType = info.type; + WasmExpression last = body.get(body.size() - 1); + last.acceptVisitor(this); + result.getLines().addAll(value.getLines()); + if (info.type != null) { + if (info.temporaryVariable != null) { + result.getLines().add(new CSingleLine(info.temporaryVariable + " = " + result.getText())); + result.setText(info.temporaryVariable); + } else { + result.setText(value.getText()); + } + } + + if (info.temporaryVariable != null) { + result.getLines().add(0, declareVariable(info.temporaryVariable, info.type)); + } + + if (info.label != null || expression.isLoop()) { + List lines = new ArrayList<>(); + String header = expression.isLoop() ? "for(;;) " : ""; + lines.add(new CSingleLine(info.label + ": " + header + "{")); + lines.add(new CBlock(result.getLines())); + lines.add(new CSingleLine("}")); + result.getLines().clear(); + result.getLines().addAll(lines); + } + } + + blockInfoMap.remove(expression); + value = result; + } + + @Override + public void visit(WasmBranch expression) { + CExpression result = new CExpression(); + + requiredType = WasmType.INT32; + expression.getCondition().acceptVisitor(this); + result.getLines().addAll(value.getLines()); + reportLocation(expression.getLocation(), result.getLines()); + result.getLines().add(new CSingleLine("if (" + value.getText() + ") {")); + CBlock breakBlock = new CBlock(generateBreak(expression.getResult(), expression.getTarget())); + result.getLines().add(breakBlock); + result.getLines().add(new CSingleLine("}")); + + value = result; + } + + @Override + public void visit(WasmBreak expression) { + CExpression result = new CExpression(); + result.getLines().addAll(generateBreak(expression.getResult(), expression.getTarget())); + value = result; + } + + private List generateBreak(WasmExpression result, WasmBlock target) { + List lines = new ArrayList<>(); + BlockInfo targetInfo = blockInfoMap.get(target); + + if (result != null) { + if (targetInfo.temporaryVariable == null) { + targetInfo.temporaryVariable = "tmp_" + temporaryIndex++; + } + if (targetInfo.label == null) { + targetInfo.label = "block_" + blockIndex++; + } + requiredType = targetInfo.type; + result.acceptVisitor(this); + lines.addAll(value.getLines()); + reportLocation(result.getLocation(), lines); + lines.add(new CSingleLine(targetInfo.temporaryVariable + " = " + value.getText() + ";")); + } + reportLocation(result.getLocation(), lines); + if (target.isLoop()) { + lines.add(new CSingleLine("break " + targetInfo.label + ";")); + } else { + lines.add(new CSingleLine("continue " + targetInfo.label + ";")); + } + + return lines; + } + + @Override + public void visit(WasmSwitch expression) { + CExpression result = new CExpression(); + + requiredType = WasmType.INT32; + expression.getSelector().acceptVisitor(this); + result.getLines().addAll(value.getLines()); + reportLocation(expression.getLocation(), result.getLines()); + result.getLines().add(new CSingleLine("switch (" + value.getLines() + ") {")); + + CBlock switchBlock = new CBlock(); + result.getLines().add(switchBlock); + for (int i = 0; i < expression.getTargets().size(); ++i) { + BlockInfo targetInfo = blockInfoMap.get(expression.getTargets().get(i)); + if (targetInfo.label == null) { + targetInfo.label = "block_" + blockIndex++; + } + switchBlock.getLines().add(new CSingleLine("case " + i + ": break " + targetInfo.label + ";")); + } + + BlockInfo defaultTargetInfo = blockInfoMap.get(expression.getDefaultTarget()); + if (defaultTargetInfo.label == null) { + defaultTargetInfo.label = "block_" + blockIndex++; + } + switchBlock.getLines().add(new CSingleLine("default: break " + defaultTargetInfo.label + ";")); + + result.getLines().add(new CSingleLine("}")); + + value = result; + } + + @Override + public void visit(WasmConditional expression) { + WasmType type = requiredType; + + requiredType = WasmType.INT32; + expression.getCondition().acceptVisitor(this); + CExpression condition = value; + + requiredType = type; + expression.getThenBlock().acceptVisitor(this); + CExpression thenExpression = value; + + requiredType = type; + expression.getElseBlock().acceptVisitor(this); + CExpression elseExpression = value; + + CExpression result = new CExpression(); + result.getLines().addAll(condition.getLines()); + + if (type != null && thenExpression.getLines().isEmpty() && elseExpression.getLines().isEmpty() + && elseExpression.getText() != null) { + result.setText("(" + condition.getText() + " ? " + thenExpression.getText() + + " : " + elseExpression.getText() + ")"); + } else { + String temporary = null; + if (type != null) { + temporary = "tmp_" + temporaryIndex++; + result.setText(temporary); + result.getLines().add(declareVariable(temporary, type)); + } + result.getLines().add(new CSingleLine("if (" + condition.getText() + ") {")); + + CBlock thenBlock = new CBlock(); + thenBlock.getLines().addAll(thenExpression.getLines()); + if (temporary != null) { + thenBlock.getLines().add(new CSingleLine(temporary + " = " + thenExpression.getText())); + } + result.getLines().add(thenBlock); + + if (!elseExpression.getText().isEmpty() || elseExpression.getText() != null) { + result.getLines().add(new CSingleLine("} else {")); + CBlock elseBlock = new CBlock(); + elseBlock.getLines().addAll(elseBlock.getLines()); + if (temporary != null) { + elseBlock.getLines().add(new CSingleLine(temporary + " = " + elseExpression.getText())); + } + } + + result.getLines().add(new CSingleLine("}")); + } + + value = result; + } + + @Override + public void visit(WasmReturn expression) { + CExpression result = new CExpression(); + if (expression.getValue() != null) { + requiredType = functionType; + expression.getValue().acceptVisitor(this); + result.getLines().addAll(value.getLines()); + reportLocation(expression.getLocation(), result.getLines()); + result.getLines().add(new CSingleLine("return " + value.getText() + ";")); + } else { + reportLocation(expression.getLocation(), result.getLines()); + result.getLines().add(new CSingleLine("return;")); + } + + value = result; + } + + @Override + public void visit(WasmUnreachable expression) { + CExpression result = new CExpression(); + reportLocation(expression.getLocation(), result.getLines()); + result.getLines().add(new CSingleLine("assert(false);")); + value = result; + } + + @Override + public void visit(WasmInt32Constant expression) { + value = new CExpression("INT32_C(" + String.valueOf(expression.getValue()) + ")"); + } + + @Override + public void visit(WasmInt64Constant expression) { + value = new CExpression("INT64_C(" + String.valueOf(expression.getValue()) + ")"); + } + + @Override + public void visit(WasmFloat32Constant expression) { + value = new CExpression(Float.toHexString(expression.getValue()) + "F"); + } + + @Override + public void visit(WasmFloat64Constant expression) { + value = new CExpression(Double.toHexString(expression.getValue())); + } + + @Override + public void visit(WasmGetLocal expression) { + value = new CExpression("var_" + expression.getLocal().getIndex()); + } + + @Override + public void visit(WasmSetLocal expression) { + CExpression result = new CExpression(); + requiredType = expression.getLocal().getType(); + expression.getValue().acceptVisitor(this); + result.getLines().addAll(value.getLines()); + + reportLocation(expression.getLocation(), result.getLines()); + + value = result; + } + + @Override + public void visit(WasmIntBinary expression) { + WasmType type = requiredType; + WasmType opType = asWasmType(expression.getType()); + CExpression result = new CExpression(); + + requiredType = opType; + expression.getFirst().acceptVisitor(this); + CExpression first = value; + + requiredType = opType; + expression.getSecond().acceptVisitor(this); + CExpression second = value; + + if (type == null) { + result.getLines().addAll(first.getLines()); + result.getLines().addAll(second.getLines()); + } else { + String firstOp = first.getText(); + String secondOp = second.getText(); + result.getLines().addAll(first.getLines()); + if (!second.getLines().isEmpty()) { + firstOp = "tmp_" + temporaryIndex; + result.getLines().add(new CSingleLine(mapType(opType) + " " + firstOp + " = " + + first.getText() + ";")); + result.getLines().addAll(second.getLines()); + } + + String unsingedType = "u" + mapType(opType); + String firstOpUnsinged = "(" + unsingedType + ") " + firstOp; + String secondOpUnsigned = "(" + unsingedType + ") " + secondOp; + switch (expression.getOperation()) { + case ADD: + result.setText("(" + firstOp + " + " + secondOp + ")"); + break; + case SUB: + result.setText("(" + firstOp + " - " + secondOp + ")"); + break; + case MUL: + result.setText("(" + firstOp + " * " + secondOp + ")"); + break; + case DIV_SIGNED: + result.setText("(" + firstOp + " / " + secondOp + ")"); + break; + case DIV_UNSIGNED: + result.setText("(" + firstOpUnsinged + " / " + secondOpUnsigned + ")"); + break; + case REM_SIGNED: + result.setText("(" + firstOp + " % " + secondOp + ")"); + break; + case REM_UNSIGNED: + result.setText("(" + firstOpUnsinged + " % " + secondOpUnsigned + ")"); + break; + case AND: + result.setText("(" + firstOp + " & " + secondOp + ")"); + break; + case OR: + result.setText("(" + firstOp + " | " + secondOp + ")"); + break; + case XOR: + result.setText("(" + firstOp + " ^ " + secondOp + ")"); + break; + case SHL: + result.setText("(" + firstOp + " << " + secondOp + ")"); + break; + case SHR_SIGNED: + result.setText("(" + firstOp + " >> " + secondOp + ")"); + break; + case SHR_UNSIGNED: + result.setText("(" + firstOpUnsinged + " >> " + secondOp + ")"); + break; + case EQ: + result.setText("(" + firstOp + " == " + secondOp + ")"); + break; + case NE: + result.setText("(" + firstOp + " != " + secondOp + ")"); + break; + case GT_SIGNED: + result.setText("(" + firstOp + " > " + secondOp + ")"); + break; + case GT_UNSIGNED: + result.setText("(" + firstOpUnsinged + " > " + secondOpUnsigned + ")"); + break; + case GE_SIGNED: + result.setText("(" + firstOp + " >= " + secondOp + ")"); + break; + case GE_UNSIGNED: + result.setText("(" + firstOpUnsinged + " >= " + secondOpUnsigned + ")"); + break; + case LT_SIGNED: + result.setText("(" + firstOp + " < " + secondOp + ")"); + break; + case LT_UNSIGNED: + result.setText("(" + firstOpUnsinged + " < " + secondOpUnsigned + ")"); + break; + case LE_SIGNED: + result.setText("(" + firstOp + " <= " + secondOp + ")"); + break; + case LE_UNSIGNED: + result.setText("(" + firstOpUnsinged + " <= " + secondOpUnsigned + ")"); + break; + case ROTL: + result.setText("rotl(" + firstOp + ", " + secondOp + ")"); + break; + case ROTR: + result.setText("rotr(" + firstOp + ", " + secondOp + ")"); + break; + } + } + + value = result; + } + + @Override + public void visit(WasmFloatBinary expression) { + + } + + @Override + public void visit(WasmIntUnary expression) { + + } + + @Override + public void visit(WasmFloatUnary expression) { + + } + + @Override + public void visit(WasmConversion expression) { + + } + + @Override + public void visit(WasmCall expression) { + CExpression result = new CExpression(); + + StringBuilder sb = new StringBuilder(); + sb.append(expression.getFunctionName()).append('('); + sb.append(')'); + result.setText(sb.toString()); + + if (requiredType == null) { + reportLocation(expression.getLocation(), result.getLines()); + result.getLines().add(new CSingleLine(result.getText())); + result.setText(null); + } + value = result; + } + + @Override + public void visit(WasmIndirectCall expression) { + + } + + @Override + public void visit(WasmDrop expression) { + CExpression result = new CExpression(); + requiredType = null; + reportLocation(expression.getLocation(), result.getLines()); + expression.getOperand().acceptVisitor(this); + result.getLines().addAll(value.getLines()); + value = result; + } + + @Override + public void visit(WasmLoadInt32 expression) { + + } + + @Override + public void visit(WasmLoadInt64 expression) { + + } + + @Override + public void visit(WasmLoadFloat32 expression) { + + } + + @Override + public void visit(WasmLoadFloat64 expression) { + + } + + @Override + public void visit(WasmStoreInt32 expression) { + + } + + @Override + public void visit(WasmStoreInt64 expression) { + + } + + @Override + public void visit(WasmStoreFloat32 expression) { + + } + + @Override + public void visit(WasmStoreFloat64 expression) { + + } + + private CLine declareVariable(String name, WasmType type) { + return new CSingleLine(mapType(type) + " " + name + ";"); + } + + private void reportLocation(TextLocation location, List lines) { + if (location != null) { + lines.add(new CSingleLine("#line " + location.getFileName() + " " + location.getLine())); + } + } + + private static String mapType(WasmType type) { + switch (type) { + case INT32: + return "int32_t"; + case INT64: + return "int64_t"; + case FLOAT32: + return "float"; + case FLOAT64: + return "double"; + } + throw new AssertionError(type.toString()); + } + + private static WasmType asWasmType(WasmIntType type) { + switch (type) { + case INT32: + return WasmType.INT32; + case INT64: + return WasmType.INT64; + } + throw new AssertionError(type.toString()); + } + + static class BlockInfo { + int index; + String label; + String temporaryVariable; + WasmType type; + } +}