From 87aaa0b45234e4bfd9d9b32c7bb9595458894bd2 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 29 Jul 2024 20:41:58 +0200 Subject: [PATCH] wasm gc: fix issues in generation of types, globals and some expressions --- .../wasm/generate/WasmGenerationVisitor.java | 5 ++ .../methods/BaseWasmGenerationVisitor.java | 5 +- .../gc/classes/WasmGCClassGenerator.java | 2 +- .../gc/methods/WasmGCGenerationVisitor.java | 39 ++++++++++++ .../teavm/backend/wasm/model/WasmModule.java | 63 +++++++++++++++---- .../teavm/backend/wasm/model/WasmType.java | 2 +- .../wasm/render/WasmBinaryRenderer.java | 20 ++++++ .../render/WasmBinaryRenderingVisitor.java | 4 +- .../backend/wasm/render/WasmBinaryWriter.java | 52 +++++++++------ .../WasmCompositeTypeBinaryRenderer.java | 2 +- 10 files changed, 156 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index cbcd60e8d..6cabefef3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -300,6 +300,11 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { return new WasmInt32Constant(classGenerator.getClassPointer(type)); } + @Override + protected WasmExpression nullLiteral() { + return new WasmInt32Constant(0); + } + @Override public void visit(SubscriptExpr expr) { WasmExpression ptr = getArrayElementPointer(expr); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java index 9ac77329f..6c90e22bf 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java @@ -553,7 +553,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp @Override public void visit(ConstantExpr expr) { if (expr.getValue() == null) { - result = new WasmInt32Constant(0); + result = nullLiteral(); } else if (expr.getValue() instanceof Integer) { result = new WasmInt32Constant((Integer) expr.getValue()); } else if (expr.getValue() instanceof Long) { @@ -569,8 +569,11 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp } else { throw new IllegalArgumentException("Constant unsupported: " + expr.getValue()); } + result.setLocation(expr.getLocation()); } + protected abstract WasmExpression nullLiteral(); + protected abstract WasmExpression stringLiteral(String s); protected abstract WasmExpression classLiteral(ValueType type); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index 56c8e374a..c2eb9bda1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -335,8 +335,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var virtualTable = virtualTables.lookup(className); var structure = new WasmStructure(names.forClassClass(className)); module.types.add(structure); - addVirtualTableFields(structure, virtualTable); fillClassFields(structure.getFields(), "java.lang.Class"); + addVirtualTableFields(structure, virtualTable); return structure; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index ef22ac79c..40f715f98 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -17,6 +17,7 @@ package org.teavm.backend.wasm.generate.gc.methods; import java.util.List; import org.teavm.ast.ArrayType; +import org.teavm.ast.BinaryExpr; import org.teavm.ast.Expr; import org.teavm.ast.InvocationExpr; import org.teavm.ast.QualificationExpr; @@ -40,7 +41,11 @@ import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.backend.wasm.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; import org.teavm.backend.wasm.model.expression.WasmNullConstant; +import org.teavm.backend.wasm.model.expression.WasmReferencesEqual; import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmStructGet; @@ -167,11 +172,45 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { return new WasmGetGlobal(classConstant.getPointer()); } + @Override + protected WasmExpression nullLiteral() { + return new WasmNullConstant(WasmType.Reference.STRUCT); + } + @Override protected CallSiteIdentifier generateCallSiteId(TextLocation location) { return new SimpleCallSite(); } + @Override + public void visit(BinaryExpr expr) { + if (expr.getType() == null) { + switch (expr.getOperation()) { + case EQUALS: { + accept(expr.getFirstOperand()); + var first = result; + accept(expr.getSecondOperand()); + var second = result; + result = new WasmReferencesEqual(first, second); + result.setLocation(expr.getLocation()); + return; + } + case NOT_EQUALS: + accept(expr.getFirstOperand()); + var first = result; + accept(expr.getSecondOperand()); + var second = result; + result = new WasmReferencesEqual(first, second); + result = new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, result); + result.setLocation(expr.getLocation()); + return; + default: + break; + } + } + super.visit(expr); + } + @Override protected WasmExpression generateVirtualCall(WasmLocal instance, MethodReference method, List arguments) { diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmModule.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmModule.java index 9297609e9..77fb31939 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmModule.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmModule.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.teavm.common.Graph; import org.teavm.common.GraphUtils; public class WasmModule { @@ -93,32 +94,70 @@ public class WasmModule { } public void prepareForRendering() { - prepareRecursiveTypes(); + prepareTypes(); } - private void prepareRecursiveTypes() { + private void prepareTypes() { var typeGraph = WasmTypeGraphBuilder.buildTypeGraph(types, types.size()); - var newList = new ArrayList(); - var typesInScc = new boolean[types.size()]; - for (var scc : GraphUtils.findStronglyConnectedComponents(typeGraph)) { + var sccs = GraphUtils.findStronglyConnectedComponents(typeGraph); + var sccStartNode = new int[types.size()]; + for (var i = 0; i < sccStartNode.length; ++i) { + sccStartNode[i] = i; + } + var sccsByIndex = new int[types.size()][]; + for (var scc : sccs) { + sccsByIndex[scc[0]] = scc; var firstType = types.get(scc[0]); firstType.recursiveTypeCount = scc.length; for (var i = 0; i < scc.length; i++) { var index = scc[i]; var type = types.get(index); - newList.add(type); type.indexInRecursiveType = i; - typesInScc[index] = true; + sccStartNode[scc[i]] = sccStartNode[scc[0]]; } } - for (var type : types) { - if (!typesInScc[type.index]) { - newList.add(type); - } + + var sorting = new TypeSorting(); + sorting.original = types; + sorting.graph = typeGraph; + sorting.visited = new boolean[types.size()]; + sorting.sccMap = sccStartNode; + sorting.sccsByIndex = sccsByIndex; + for (var i = 0; i < types.size(); ++i) { + sorting.visit(i); } + types.clear(); - for (var type : newList) { + for (var type : sorting.sorted) { types.add(type); } } + + private static class TypeSorting { + WasmCollection original; + Graph graph; + boolean[] visited; + int[] sccMap; + int[][] sccsByIndex; + List sorted = new ArrayList<>(); + + void visit(int typeIndex) { + typeIndex = sccMap[typeIndex]; + if (visited[typeIndex]) { + return; + } + visited[typeIndex] = true; + for (var outgoing : graph.outgoingEdges(typeIndex)) { + visit(outgoing); + } + var scc = sccsByIndex[typeIndex]; + if (scc == null) { + sorted.add(original.get(typeIndex)); + } else { + for (var index : scc) { + sorted.add(original.get(index)); + } + } + } + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java index b47fb0d1c..9be0d526c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java @@ -72,7 +72,7 @@ public abstract class WasmType { } } - public static final class SpecialReference extends WasmType { + public static final class SpecialReference extends Reference { public final SpecialReferenceKind kind; private SpecialReference(SpecialReferenceKind kind) { diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java index 21cfeb349..a591a63eb 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java @@ -38,6 +38,7 @@ public class WasmBinaryRenderer { private static final int SECTION_FUNCTION = 3; private static final int SECTION_TABLE = 4; private static final int SECTION_MEMORY = 5; + private static final int SECTION_GLOBAL = 6; private static final int SECTION_EXPORT = 7; private static final int SECTION_START = 8; private static final int SECTION_ELEMENT = 9; @@ -88,6 +89,7 @@ public class WasmBinaryRenderer { renderTable(module); renderMemory(module); renderTags(module); + renderGlobals(module); renderExport(module); renderStart(module); renderElement(module); @@ -205,6 +207,24 @@ public class WasmBinaryRenderer { writeSection(SECTION_MEMORY, "memory", section.getData()); } + private void renderGlobals(WasmModule module) { + if (module.globals.isEmpty()) { + return; + } + + var section = new WasmBinaryWriter(); + var visitor = new WasmBinaryRenderingVisitor(section, module, null, null, 0); + section.writeLEB(module.globals.size()); + for (var global : module.globals) { + section.writeType(global.getType(), module); + section.writeByte(1); // mutable + global.getInitialValue().acceptVisitor(visitor); + section.writeByte(0x0b); + } + + writeSection(SECTION_GLOBAL, "global", section.getData()); + } + private void renderExport(WasmModule module) { // https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#export-section diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java index 8b3269ab5..d9f60a273 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java @@ -253,7 +253,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmNullConstant expression) { pushLocation(expression); writer.writeByte(0xD0); - writeBlockType(expression.getType()); + writer.writeHeapType(expression.getType(), module); popLocation(); } @@ -1146,7 +1146,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmFunctionReference expression) { pushLocation(expression); writer.writeByte(0xd2); - writer.writeInt32(module.functions.indexOf(expression.getFunction())); + writer.writeLEB(module.functions.indexOf(expression.getFunction())); popLocation(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java index d495a0a24..a121c2c96 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java @@ -41,33 +41,45 @@ public class WasmBinaryWriter { if (type instanceof WasmType.Number) { writeType(((WasmType.Number) type).number); } else if (type instanceof WasmType.SpecialReference) { - switch (((WasmType.SpecialReference) type).kind) { - case ANY: - writeByte(0x6e); - break; - case EXTERN: - writeByte(0x6f); - break; - case FUNC: - writeByte(0x70); - break; - case STRUCT: - writeByte(0x6b); - break; - case ARRAY: - writeByte(0x6a); - break; - } + writeSpecialHeapType(((WasmType.SpecialReference) type).kind); } else if (type instanceof WasmType.CompositeReference) { writeByte(0x63); var composite = ((WasmType.CompositeReference) type).composite; - var index = isRecursiveMember - ? composite.getIndexInRecursiveType() - : module.types.indexOf(composite); + var index = module.types.indexOf(composite); writeSignedLEB(index); } } + public void writeHeapType(WasmType.Reference type, WasmModule module) { + if (type instanceof WasmType.CompositeReference) { + var composite = ((WasmType.CompositeReference) type).composite; + var index = module.types.indexOf(composite); + writeSignedLEB(index); + } else { + writeSpecialHeapType(((WasmType.SpecialReference) type).kind); + } + } + + private void writeSpecialHeapType(WasmType.SpecialReferenceKind type) { + switch (type) { + case ANY: + writeByte(0x6e); + break; + case EXTERN: + writeByte(0x6f); + break; + case FUNC: + writeByte(0x70); + break; + case STRUCT: + writeByte(0x6b); + break; + case ARRAY: + writeByte(0x6a); + break; + } + } + public void writeType(WasmNumType type) { switch (type) { case INT32: diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java index 2b082e252..2b44fa5a3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java @@ -75,7 +75,7 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor break; } } else { - section.writeType(storageType.asUnpackedType(), module); + section.writeType(storageType.asUnpackedType(), module, true); } } }