From 40fbce0ddd80be74de6196430521c4cefbf680d5 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 14 Aug 2024 17:20:19 +0200 Subject: [PATCH] wasm gc: fix issues with virtual calls --- .../backend/wasm/gc/WasmGCDependencies.java | 1 + .../wasm/generate/WasmGenerationVisitor.java | 7 ++ .../methods/BaseWasmGenerationVisitor.java | 27 +++--- .../gc/classes/WasmGCClassGenerator.java | 84 +++++++++++++------ .../generate/gc/classes/WasmGCClassInfo.java | 1 + .../generate/gc/classes/WasmGCTypeMapper.java | 29 ++++++- .../gc/methods/WasmGCGenerationVisitor.java | 15 +++- .../generate/gc/strings/WasmGCStringPool.java | 3 + .../backend/wasm/model/WasmFunctionType.java | 7 ++ .../teavm/backend/wasm/model/WasmModule.java | 40 +++++++-- .../wasm/model/WasmTypeGraphBuilder.java | 3 + .../wasm/render/WasmBinaryRenderer.java | 3 + .../render/WasmBinaryRenderingVisitor.java | 69 +++++++++++---- .../WasmCompositeTypeBinaryRenderer.java | 5 ++ .../backend/wasm/runtime/WasmGCSupport.java | 4 + .../gc/BaseClassesTransformation.java | 7 ++ 16 files changed, 245 insertions(+), 60 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java index c70c76794..b4397a4ff 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java @@ -74,6 +74,7 @@ public class WasmGCDependencies { analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class)) .use(); analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class)).use(); + analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cnse", CloneNotSupportedException.class)).use(); } private void contributeInitializerUtils() { 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 c4de914d7..04ee57d9a 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 @@ -56,6 +56,8 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Subtype; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; 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.WasmLoadFloat32; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; @@ -302,6 +304,11 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { return new WasmInt32Constant(0); } + @Override + protected WasmExpression genIsNull(WasmExpression value) { + return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value); + } + @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 20dab9800..809c22ef8 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 @@ -92,8 +92,6 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Constant; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; 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.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSwitch; @@ -902,9 +900,16 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp var block = new WasmBlock(false); block.setType(mapType(reference.getReturnType())); - var instanceVar = tempVars.acquire(instanceWasmType); - block.getBody().add(new WasmSetLocal(instanceVar, instance)); - instance = new WasmGetLocal(instanceVar); + WasmLocal instanceVar; + var isTemporary = false; + if (instance instanceof WasmGetLocal) { + instanceVar = ((WasmGetLocal) instance).getLocal(); + } else { + instanceVar = tempVars.acquire(instanceWasmType); + block.getBody().add(new WasmSetLocal(instanceVar, instance)); + instance = new WasmGetLocal(instanceVar); + isTemporary = true; + } var arguments = new ArrayList(); arguments.add(instance); @@ -918,7 +923,9 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp var call = generateVirtualCall(instanceVar, reference, arguments); block.getBody().add(call); - tempVars.release(instanceVar); + if (isTemporary) { + tempVars.release(instanceVar); + } return block; } } @@ -1133,7 +1140,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp result.acceptVisitor(typeInference); var cachedObject = exprCache.create(result, typeInference.getResult(), expr.getLocation(), block.getBody()); - var ifNull = new WasmBranch(genIsZero(cachedObject.expr()), block); + var ifNull = new WasmBranch(genIsNull(cachedObject.expr()), block); ifNull.setResult(new WasmInt32Constant(0)); block.getBody().add(new WasmDrop(ifNull)); @@ -1171,7 +1178,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp var wasmSourceType = typeInference.getResult(); var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody()); - var nullCheck = new WasmBranch(genIsZero(valueToCast.expr()), block); + var nullCheck = new WasmBranch(genIsNull(valueToCast.expr()), block); nullCheck.setResult(valueToCast.expr()); block.getBody().add(new WasmDrop(nullCheck)); @@ -1544,9 +1551,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp } } - private WasmExpression genIsZero(WasmExpression value) { - return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value); - } + protected abstract WasmExpression genIsNull(WasmExpression value); protected abstract WasmType mapType(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 3d98528d1..d58951c9d 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 @@ -52,6 +52,7 @@ import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmNullConstant; +import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; import org.teavm.backend.wasm.model.expression.WasmStructSet; @@ -122,7 +123,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit standardClasses = new WasmGCStandardClasses(this); strings = new WasmGCStringPool(standardClasses, module, functionProvider); supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes); - typeMapper = new WasmGCTypeMapper(this); + typeMapper = new WasmGCTypeMapper(this, functionTypes, module); } public WasmGCSupertypeFunctionProvider getSupertypeProvider() { @@ -143,6 +144,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit @Override public void contributeToInitializerDefinitions(WasmFunction function) { + fillVirtualTableSupertypes(); for (var classInfo : classInfoMap.values()) { var classInstanceType = classInfo.virtualTableStructure != null ? classInfo.virtualTableStructure @@ -152,6 +154,35 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit } } + private void fillVirtualTableSupertypes() { + for (var classInfo : classInfoMap.values()) { + if (classInfo.virtualTableStructure != null && classInfo.getValueType() instanceof ValueType.Object + && classInfo.hasOwnVirtualTable) { + var className = ((ValueType.Object) classInfo.getValueType()).getClassName(); + classInfo.virtualTableStructure.setSupertype(findVirtualTableSupertype(className)); + } + } + } + + private WasmStructure findVirtualTableSupertype(String className) { + while (className != null) { + var cls = classSource.get(className); + if (cls == null) { + break; + } + className = cls.getParent(); + if (className == null) { + break; + } + var parentInfo = classInfoMap.get(ValueType.object(className)); + if (parentInfo != null && parentInfo.virtualTableStructure != null) { + return parentInfo.virtualTableStructure; + } + } + var classClass = classInfoMap.get(ValueType.object("java.lang.Class")); + return classClass != null ? classClass.structure : null; + } + @Override public void contributeToInitializer(WasmFunction function) { var classClass = standardClasses.classClass(); @@ -209,7 +240,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit fillFields(classInfo, type); } var pointerName = names.forClassInstance(type); - var classStructure = virtualTable != null && virtualTable.hasValidEntries() + classInfo.hasOwnVirtualTable = virtualTable != null && virtualTable.hasValidEntries(); + var classStructure = classInfo.hasOwnVirtualTable ? initRegularClassStructure(((ValueType.Object) type).getClassName()) : standardClasses.classClass().getStructure(); classInfo.virtualTableStructure = classStructure; @@ -330,21 +362,30 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit } } if (virtualTable != null && virtualTable.hasValidEntries()) { - fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, - virtualTableFieldOffset, name, new HashSet<>()); + fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTable, + new HashSet<>()); } }; } - private int fillVirtualTableMethods(List target, WasmStructure structure, WasmGlobal global, - VirtualTable virtualTable, int index, String origin, Set filled) { + private void fillVirtualTableMethods(List target, WasmStructure structure, WasmGlobal global, + VirtualTable virtualTable, VirtualTable original, Set filled) { + if (virtualTable.getParent() != null) { + fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), original, filled); + } for (var method : virtualTable.getMethods()) { - var entry = virtualTable.getEntry(method); + var entry = original.getEntry(method); if (entry != null && entry.getImplementor() != null && filled.add(method) && !method.equals(GET_CLASS_METHOD)) { + var fieldIndex = virtualTableFieldOffset + entry.getIndex(); + var expectedType = (WasmType.CompositeReference) structure.getFields().get(fieldIndex) + .asUnpackedType(); + var expectedFunctionType = (WasmFunctionType) expectedType.composite; var function = functionProvider.forInstanceMethod(entry.getImplementor()); - if (!origin.equals(entry.getImplementor().getClassName())) { - var functionType = getFunctionType(virtualTable.getClassName(), method); + if (!virtualTable.getClassName().equals(entry.getImplementor().getClassName()) + || expectedFunctionType != function.getType()) { + var functionType = typeMapper.getFunctionType(virtualTable.getClassName(), method, true); + functionType.getSupertypes().add(expectedFunctionType); var wrapperFunction = new WasmFunction(functionType); module.functions.add(wrapperFunction); var call = new WasmCall(function); @@ -358,17 +399,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit call.getArguments().add(new WasmGetLocal(params[i])); wrapperFunction.add(params[i]); } + wrapperFunction.getBody().add(new WasmReturn(call)); + function = wrapperFunction; } function.setReferenced(true); var ref = new WasmFunctionReference(function); - target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index + entry.getIndex(), ref)); + target.add(new WasmStructSet(structure, new WasmGetGlobal(global), fieldIndex, ref)); } } - if (virtualTable.getParent() != null) { - index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin, - filled); - } - return index; } private WasmStructure initRegularClassStructure(String className) { @@ -391,23 +429,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit if (methodDesc == null) { structure.getFields().add(WasmType.Reference.FUNC.asStorage()); } else { - var functionType = getFunctionType(virtualTable.getClassName(), methodDesc); + var functionType = typeMapper.getFunctionType(virtualTable.getClassName(), methodDesc, false); structure.getFields().add(functionType.getReference().asStorage()); } } } - private WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc) { - var returnType = typeMapper.mapType(methodDesc.getResultType()); - var javaParamTypes = methodDesc.getParameterTypes(); - var paramTypes = new WasmType[javaParamTypes.length + 1]; - paramTypes[0] = getClassInfo(className).getType(); - for (var i = 0; i < javaParamTypes.length; ++i) { - paramTypes[i + 1] = typeMapper.mapType(javaParamTypes[i]); - } - return functionTypes.of(returnType, paramTypes); - } - private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) { classInfo.initializer = target -> { var itemTypeInfo = getClassInfo(type.getItemType()); @@ -497,6 +524,9 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit if (className.equals("java.lang.Object") && field.getName().equals("monitor")) { continue; } + if (className.equals("java.lang.Class") && field.getName().equals("platformClass")) { + continue; + } if (field.hasModifier(ElementModifier.STATIC)) { continue; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java index 4ae7a5936..eb4ca2ca5 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfo.java @@ -28,6 +28,7 @@ public class WasmGCClassInfo { private ValueType valueType; WasmStructure structure; WasmArray array; + boolean hasOwnVirtualTable; WasmStructure virtualTableStructure; WasmGlobal pointer; WasmGlobal initializerPointer; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java index 26c6cfabc..2e00283d6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java @@ -15,16 +15,26 @@ */ package org.teavm.backend.wasm.generate.gc.classes; +import java.util.List; +import org.teavm.backend.wasm.WasmFunctionTypes; +import org.teavm.backend.wasm.model.WasmFunctionType; +import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmPackedType; import org.teavm.backend.wasm.model.WasmStorageType; import org.teavm.backend.wasm.model.WasmType; +import org.teavm.model.MethodDescriptor; import org.teavm.model.ValueType; public class WasmGCTypeMapper { private WasmGCClassInfoProvider classInfoProvider; + private WasmFunctionTypes functionTypes; + private WasmModule module; - WasmGCTypeMapper(WasmGCClassInfoProvider classInfoProvider) { + WasmGCTypeMapper(WasmGCClassInfoProvider classInfoProvider, WasmFunctionTypes functionTypes, + WasmModule module) { this.classInfoProvider = classInfoProvider; + this.functionTypes = functionTypes; + this.module = module; } public WasmStorageType mapStorageType(ValueType type) { @@ -76,4 +86,21 @@ public class WasmGCTypeMapper { return classInfoProvider.getClassInfo(type).getType(); } } + + public WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc, boolean fresh) { + var returnType = mapType(methodDesc.getResultType()); + var javaParamTypes = methodDesc.getParameterTypes(); + var paramTypes = new WasmType[javaParamTypes.length + 1]; + paramTypes[0] = classInfoProvider.getClassInfo(className).getType(); + for (var i = 0; i < javaParamTypes.length; ++i) { + paramTypes[i + 1] = mapType(javaParamTypes[i]); + } + if (fresh) { + var type = new WasmFunctionType(null, returnType, List.of(paramTypes)); + module.types.add(type); + return type; + } else { + return functionTypes.of(returnType, paramTypes); + } + } } 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 1ffbdee35..41dbb354b 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 @@ -211,6 +211,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { : WasmType.Reference.STRUCT); } + @Override + protected WasmExpression genIsNull(WasmExpression value) { + return new WasmReferencesEqual(value, new WasmNullConstant(WasmType.Reference.STRUCT)); + } + @Override protected CallSiteIdentifier generateCallSiteId(TextLocation location) { return new SimpleCallSite(); @@ -268,12 +273,19 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { } var instanceStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()).getStructure(); + var actualInstanceType = (WasmType.CompositeReference) instance.getType(); + var actualInstanceStruct = (WasmStructure) actualInstanceType.composite; + var actualVtableType = (WasmType.CompositeReference) actualInstanceStruct.getFields().get(0).asUnpackedType(); + var actualVtableStruct = (WasmStructure) actualVtableType.composite; + WasmExpression classRef = new WasmStructGet(instanceStruct, new WasmGetLocal(instance), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); var index = context.classInfoProvider().getVirtualMethodsOffset() + vtableIndex; var vtableStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()) .getVirtualTableStructure(); - classRef = new WasmCast(classRef, vtableStruct.getReference()); + if (!vtableStruct.isSupertypeOf(actualVtableStruct)) { + classRef = new WasmCast(classRef, vtableStruct.getReference()); + } var functionRef = new WasmStructGet(vtableStruct, classRef, index); var functionTypeRef = (WasmType.CompositeReference) vtableStruct.getFields().get(index).asUnpackedType(); @@ -432,7 +444,6 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { result = invocation(expr, null, false); } - @Override protected WasmExpression invocation(InvocationExpr expr, List resultConsumer, boolean willDrop) { if (expr.getType() == InvocationType.SPECIAL || expr.getType() == InvocationType.STATIC) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java index dea33a1a8..eb99b97cb 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java @@ -72,6 +72,9 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer var value = new WasmCall(nextCharArrayFunction); function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global), WasmGCClassInfoProvider.CUSTOM_FIELD_OFFSETS, value)); + function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global), + WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, + new WasmGetGlobal(standardClasses.stringClass().getPointer()))); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmFunctionType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunctionType.java index 3e4dcd437..911fe89c1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmFunctionType.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunctionType.java @@ -15,7 +15,9 @@ */ package org.teavm.backend.wasm.model; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import java.util.function.Supplier; public class WasmFunctionType extends WasmCompositeType { @@ -23,6 +25,7 @@ public class WasmFunctionType extends WasmCompositeType { private WasmType returnType; private Supplier> parameterTypesSupplier; private Supplier returnTypeSupplier; + private Set supertypes = new LinkedHashSet<>(); public WasmFunctionType(String name, WasmType returnType, List parameterTypes) { super(name); @@ -53,6 +56,10 @@ public class WasmFunctionType extends WasmCompositeType { return returnType; } + public Set getSupertypes() { + return supertypes; + } + @Override public void acceptVisitor(WasmCompositeTypeVisitor visitor) { visitor.visit(this); 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 77fb31939..165f5e89a 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 @@ -121,6 +121,7 @@ public class WasmModule { sorting.original = types; sorting.graph = typeGraph; sorting.visited = new boolean[types.size()]; + sorting.sccVisited = new boolean[types.size()]; sorting.sccMap = sccStartNode; sorting.sccsByIndex = sccsByIndex; for (var i = 0; i < types.size(); ++i) { @@ -137,6 +138,7 @@ public class WasmModule { WasmCollection original; Graph graph; boolean[] visited; + boolean[] sccVisited; int[] sccMap; int[][] sccsByIndex; List sorted = new ArrayList<>(); @@ -146,18 +148,46 @@ public class WasmModule { if (visited[typeIndex]) { return; } - visited[typeIndex] = true; - for (var outgoing : graph.outgoingEdges(typeIndex)) { - visit(outgoing); - } var scc = sccsByIndex[typeIndex]; if (scc == null) { + visited[typeIndex] = true; + for (var outgoing : graph.outgoingEdges(typeIndex)) { + visit(outgoing); + } sorted.add(original.get(typeIndex)); } else { + visited[typeIndex] = true; for (var index : scc) { - sorted.add(original.get(index)); + for (var outgoing : graph.outgoingEdges(index)) { + visit(outgoing); + } + } + for (var index : scc) { + visitScc(index, typeIndex); } } } + + void visitScc(int index, int sccBase) { + if (sccVisited[index]) { + return; + } + sccVisited[index] = true; + var type = original.get(index); + if (type instanceof WasmStructure) { + var supertype = ((WasmStructure) type).getSupertype(); + if (supertype != null && sccMap[supertype.index] == sccBase) { + visitScc(supertype.index, sccBase); + } + } else if (type instanceof WasmFunctionType) { + var supertypes = ((WasmFunctionType) type).getSupertypes(); + for (var supertype : supertypes) { + if (sccMap[supertype.index] == sccBase) { + visitScc(supertype.index, sccBase); + } + } + } + sorted.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 index da13c2833..d6ad54ba7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmTypeGraphBuilder.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmTypeGraphBuilder.java @@ -57,6 +57,9 @@ final class WasmTypeGraphBuilder { @Override public void visit(WasmFunctionType type) { + for (var supertype : type.getSupertypes()) { + addEdge(supertype.getReference()); + } for (var parameter : type.getParameterTypes()) { addEdge(parameter); } 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 4a39b169a..51b0827bc 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 @@ -381,6 +381,9 @@ public class WasmBinaryRenderer { var visitor = new WasmBinaryRenderingVisitor(code, module, dwarfGenerator, function.getJavaMethod() != null ? debugLines : null, offset); + for (var part : function.getBody()) { + visitor.preprocess(part); + } for (var part : function.getBody()) { part.acceptVisitor(visitor); } 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 271c2543e..866c3383b 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 @@ -18,9 +18,11 @@ package org.teavm.backend.wasm.render; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import org.teavm.backend.wasm.debug.DebugLines; import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.model.WasmModule; @@ -39,6 +41,7 @@ import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmCopy; +import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor; import org.teavm.backend.wasm.model.expression.WasmDrop; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor; @@ -95,6 +98,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { private TextLocation lastEmittedLocation; private int positionToEmit; private List locationStack = new ArrayList<>(); + private Set blocksToPreserve = new HashSet<>(); WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmModule module, DwarfGenerator dwarfGenerator, DebugLines debugLines, int addressOffset) { @@ -105,23 +109,60 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { this.debugLines = debugLines; } + void preprocess(WasmExpression expression) { + expression.acceptVisitor(new WasmDefaultExpressionVisitor() { + @Override + public void visit(WasmBranch expression) { + super.visit(expression); + register(expression.getTarget()); + } + + @Override + public void visit(WasmBreak expression) { + super.visit(expression); + register(expression.getTarget()); + } + + @Override + public void visit(WasmSwitch expression) { + super.visit(expression); + for (WasmBlock target : expression.getTargets()) { + register(target); + } + register(expression.getDefaultTarget()); + } + + private void register(WasmBlock block) { + blocksToPreserve.add(block); + } + }); + } + @Override public void visit(WasmBlock expression) { - pushLocation(expression); - pushLocation(expression); - int blockDepth = 1; - depth += blockDepth; - blockDepths.put(expression, depth); - writer.writeByte(expression.isLoop() ? 0x03 : 0x02); - writeBlockType(expression.getType()); - for (WasmExpression part : expression.getBody()) { - part.acceptVisitor(this); + if (blocksToPreserve.contains(expression) || expression.isLoop()) { + pushLocation(expression); + pushLocation(expression); + int blockDepth = 1; + depth += blockDepth; + blockDepths.put(expression, depth); + writer.writeByte(expression.isLoop() ? 0x03 : 0x02); + writeBlockType(expression.getType()); + for (WasmExpression part : expression.getBody()) { + part.acceptVisitor(this); + } + popLocation(); + writer.writeByte(0x0B); + popLocation(); + blockDepths.remove(expression); + depth -= blockDepth; + } else { + pushLocation(expression); + for (var part : expression.getBody()) { + part.acceptVisitor(this); + } + popLocation(); } - popLocation(); - writer.writeByte(0x0B); - popLocation(); - blockDepths.remove(expression); - depth -= blockDepth; } private void writeBlockType(WasmType type) { 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 4886be007..a3132396b 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 @@ -57,6 +57,11 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor @Override public void visit(WasmFunctionType type) { + section.writeByte(0x50); + section.writeLEB(type.getSupertypes().size()); + for (var supertype : type.getSupertypes()) { + section.writeLEB(module.types.indexOf(supertype)); + } section.writeByte(0x60); section.writeLEB(type.getParameterTypes().size()); for (var inputType : type.getParameterTypes()) { diff --git a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java index 3753cdab3..29bafd8b4 100644 --- a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java +++ b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java @@ -33,6 +33,10 @@ public class WasmGCSupport { return new ClassCastException(); } + public static CloneNotSupportedException cnse() { + return new CloneNotSupportedException(); + } + @Import(name = "putcharStdout") public static native void putCharStdout(char c); diff --git a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java index 937a72cea..017672f4c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java +++ b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java @@ -15,10 +15,12 @@ */ package org.teavm.backend.wasm.transformation.gc; +import org.teavm.backend.wasm.runtime.WasmGCSupport; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformerContext; import org.teavm.model.ElementModifier; +import org.teavm.model.emit.ProgramEmitter; public class BaseClassesTransformation implements ClassHolderTransformer { @Override @@ -30,6 +32,11 @@ public class BaseClassesTransformation implements ClassHolderTransformer { method.setProgram(null); method.getModifiers().add(ElementModifier.NATIVE); break; + case "clone": { + var em = ProgramEmitter.create(method, context.getHierarchy()); + em.invoke(WasmGCSupport.class, "cnse", CloneNotSupportedException.class).raise(); + break; + } } } } else if (cls.getName().equals("java.lang.Class")) {