diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java index 687aa50d7..53b19b904 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -131,6 +131,7 @@ public class WasmGCTarget implements TeaVMTarget { var binaryWriter = new WasmBinaryWriter(); var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated, null, null, null, null, WasmBinaryStatsCollector.EMPTY); + module.prepareForRendering(); binaryRenderer.render(module); var data = binaryWriter.getData(); if (!outputName.endsWith(".wasm")) { diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmCollection.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmCollection.java index 3024dafaf..f72ee121d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmCollection.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmCollection.java @@ -30,6 +30,10 @@ public class WasmCollection implements Iterable { WasmCollection() { } + public T get(int index) { + return items.get(index); + } + public int size() { return items.size(); } @@ -57,6 +61,13 @@ public class WasmCollection implements Iterable { } } + public void clear() { + for (var item : items) { + item.collection = null; + } + items.clear(); + } + void invalidateIndexes() { indexesInvalid = true; } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java index f71b561b3..e9923e0f1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmCompositeType.java @@ -18,6 +18,8 @@ package org.teavm.backend.wasm.model; public abstract class WasmCompositeType extends WasmEntity { private String name; private WasmType.CompositeReference reference; + int indexInRecursiveType = -1; + int recursiveTypeCount = -1; WasmCompositeType(String name) { this.name = name; @@ -34,5 +36,13 @@ public abstract class WasmCompositeType extends WasmEntity { return reference; } + public int getIndexInRecursiveType() { + return indexInRecursiveType; + } + + public int getRecursiveTypeCount() { + return recursiveTypeCount; + } + public abstract void acceptVisitor(WasmCompositeTypeVisitor visitor); } 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 199b13add..9297609e9 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.GraphUtils; public class WasmModule { private int minMemorySize; @@ -90,4 +91,34 @@ public class WasmModule { public void setStartFunction(WasmFunction startFunction) { this.startFunction = startFunction; } + + public void prepareForRendering() { + prepareRecursiveTypes(); + } + + private void prepareRecursiveTypes() { + var typeGraph = WasmTypeGraphBuilder.buildTypeGraph(types, types.size()); + var newList = new ArrayList(); + var typesInScc = new boolean[types.size()]; + for (var scc : GraphUtils.findStronglyConnectedComponents(typeGraph)) { + 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; + } + } + for (var type : types) { + if (!typesInScc[type.index]) { + newList.add(type); + } + } + types.clear(); + for (var type : newList) { + types.add(type); + } + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmTypeGraphBuilder.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmTypeGraphBuilder.java new file mode 100644 index 000000000..38d114cf6 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmTypeGraphBuilder.java @@ -0,0 +1,72 @@ +/* + * Copyright 2024 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.model; + +import org.teavm.common.Graph; +import org.teavm.common.GraphBuilder; + +final class WasmTypeGraphBuilder { + private WasmTypeGraphBuilder() { + } + + static Graph buildTypeGraph(Iterable types, int size) { + var graphBuilder = new GraphBuilder(size); + var visitor = new GraphBuilderVisitor(graphBuilder); + for (var type : types) { + visitor.currentIndex = type.index; + type.acceptVisitor(visitor); + } + return graphBuilder.build(); + } + + private static class GraphBuilderVisitor implements WasmCompositeTypeVisitor { + final GraphBuilder graphBuilder; + int currentIndex; + + GraphBuilderVisitor(GraphBuilder graphBuilder) { + this.graphBuilder = graphBuilder; + } + + @Override + public void visit(WasmStructure type) { + for (var field : type.getFields()) { + addEdge(field.asUnpackedType()); + } + } + + @Override + public void visit(WasmArray type) { + addEdge(type.getElementType().asUnpackedType()); + } + + @Override + public void visit(WasmFunctionType type) { + for (var parameter : type.getParameterTypes()) { + addEdge(parameter); + } + if (type.getReturnType() != null) { + addEdge(type.getReturnType()); + } + } + + private void addEdge(WasmType type) { + if (type instanceof WasmType.CompositeReference) { + var composite = ((WasmType.CompositeReference) type).composite; + graphBuilder.addEdge(currentIndex, composite.index); + } + } + } +} 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 a94cfdd49..21cfeb349 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 @@ -103,9 +103,30 @@ public class WasmBinaryRenderer { var section = new WasmBinaryWriter(); var typeRenderer = new WasmCompositeTypeBinaryRenderer(module, section); - section.writeLEB(module.types.size()); - for (var type : module.types) { - type.acceptVisitor(typeRenderer); + var recTypeCount = 0; + for (var i = 0; i < module.types.size();) { + var type = module.types.get(i); + if (type.getRecursiveTypeCount() > 0) { + i += type.getRecursiveTypeCount(); + } else { + ++i; + } + recTypeCount++; + } + section.writeLEB(recTypeCount); + for (var i = 0; i < module.types.size();) { + var type = module.types.get(i); + if (type.getRecursiveTypeCount() > 0) { + section.writeByte(0x4E); + section.writeLEB(type.getRecursiveTypeCount()); + for (var j = 0; j < type.getRecursiveTypeCount(); ++j) { + var subtype = module.types.get(i++); + subtype.acceptVisitor(typeRenderer); + } + } else { + type.acceptVisitor(typeRenderer); + ++i; + } } writeSection(SECTION_TYPE, "type", section.getData()); 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 89d28b63e..d495a0a24 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 @@ -30,6 +30,10 @@ public class WasmBinaryWriter { } public void writeType(WasmType type, WasmModule module) { + writeType(type, module, false); + } + + public void writeType(WasmType type, WasmModule module, boolean isRecursiveMember) { if (type == null) { writeByte(0x40); return; @@ -55,7 +59,12 @@ public class WasmBinaryWriter { break; } } else if (type instanceof WasmType.CompositeReference) { - writeSignedLEB(module.types.indexOf(((WasmType.CompositeReference) type).composite)); + writeByte(0x63); + var composite = ((WasmType.CompositeReference) type).composite; + var index = isRecursiveMember + ? composite.getIndexInRecursiveType() + : module.types.indexOf(composite); + writeSignedLEB(index); } } 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 fb929cd32..2b082e252 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 @@ -25,6 +25,7 @@ import org.teavm.backend.wasm.model.WasmStructure; public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor { private WasmModule module; private WasmBinaryWriter section; + private boolean reference; public WasmCompositeTypeBinaryRenderer(WasmModule module, WasmBinaryWriter section) { this.module = module; @@ -43,6 +44,7 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor @Override public void visit(WasmArray type) { + section.writeByte(0x5E); writeStorageType(type.getElementType()); section.writeLEB(0x01); // mutable }