From 29f29cea1d7b569e39dde0517cfb1fec7dc58e6f Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 19 Aug 2024 15:02:11 +0200 Subject: [PATCH] wasm gc: trying to fix virtual calls --- .../gc/classes/WasmGCClassGenerator.java | 52 +++++++++++-------- .../gc/methods/WasmGCGenerationVisitor.java | 39 +++++++------- .../backend/wasm/model/WasmCollection.java | 3 ++ .../teavm/backend/wasm/model/WasmModule.java | 2 +- .../wasm/model/WasmTypeGraphBuilder.java | 12 +++-- .../wasm/model/expression/WasmStructGet.java | 8 +++ .../model/analysis/BaseTypeInference.java | 4 +- 7 files changed, 70 insertions(+), 50 deletions(-) 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 761095795..be9d7faff 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 @@ -100,7 +100,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private int classTagOffset; private int classFlagsOffset; - private int classNameOffset = -1; + private int classNameOffset; private int classParentOffset; private int classArrayOffset; private int classArrayItemOffset; @@ -162,7 +162,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit if (classInfo.virtualTableStructure != null && classInfo.getValueType() instanceof ValueType.Object && classInfo.hasOwnVirtualTable) { var className = ((ValueType.Object) classInfo.getValueType()).getClassName(); - classInfo.virtualTableStructure.setSupertype(findVirtualTableSupertype(className)); + var candidate = findVirtualTableSupertype(className); + if (candidate != null) { + classInfo.virtualTableStructure.setSupertype(candidate); + } } } } @@ -235,20 +238,28 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var name = type instanceof ValueType.Object ? ((ValueType.Object) type).getClassName() : null; - classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null); + var isInterface = false; + var classReader = name != null ? classSource.get(name) : null; + if (classReader != null && classReader.hasModifier(ElementModifier.INTERFACE)) { + isInterface = true; + classInfo.structure = standardClasses.objectClass().structure; + } else { + classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null); + module.types.add(classInfo.structure); + } if (name != null) { - var classReader = classSource.get(name); - if (classReader == null || !classReader.hasModifier(ElementModifier.INTERFACE)) { + if (!isInterface) { virtualTable = virtualTables.lookup(name); } - if (classReader != null && classReader.getParent() != null) { + if (classReader != null && classReader.getParent() != null && !isInterface) { classInfo.structure.setSupertype(getClassInfo(classReader.getParent()).structure); } } else { classInfo.structure.setSupertype(standardClasses.objectClass().structure); } - module.types.add(classInfo.structure); - fillFields(classInfo, type); + if (!isInterface) { + fillFields(classInfo, type); + } } var pointerName = names.forClassInstance(type); classInfo.hasOwnVirtualTable = virtualTable != null && virtualTable.hasValidEntries(); @@ -405,10 +416,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var function = functionProvider.forInstanceMethod(entry.getImplementor()); if (!virtualTable.getClassName().equals(entry.getImplementor().getClassName()) || expectedFunctionType != function.getType()) { - var functionType = typeMapper.getFunctionType(virtualTable.getClassName(), method, true); - functionType.getSupertypes().add(expectedFunctionType); - expectedFunctionType.setFinal(false); - var wrapperFunction = new WasmFunction(functionType); + var wrapperFunction = new WasmFunction(expectedFunctionType); module.functions.add(wrapperFunction); var call = new WasmCall(function); var instanceParam = new WasmLocal(getClassInfo(virtualTable.getClassName()).getType()); @@ -451,7 +459,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit if (methodDesc == null) { structure.getFields().add(WasmType.Reference.FUNC.asStorage()); } else { - var functionType = typeMapper.getFunctionType(virtualTable.getClassName(), methodDesc, false); + var originalVirtualTable = virtualTable.findMethodContainer(methodDesc); + var functionType = typeMapper.getFunctionType(originalVirtualTable.getClassName(), methodDesc, false); structure.getFields().add(functionType.getReference().asStorage()); } } @@ -570,8 +579,9 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit fields.add(supertypeGenerator.getFunctionType().getReference().asStorage()); classNewArrayOffset = fields.size(); fields.add(newArrayGenerator.getNewArrayFunctionType().getReference().asStorage()); + classNameOffset = fields.size(); + fields.add(standardClasses.stringClass().getType().asStorage()); virtualTableFieldOffset = fields.size(); - classNameOffset = fieldIndexes.getOrDefault(new FieldReference(className, "name"), -1); } } @@ -654,14 +664,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit classFlagsOffset, flagsExpr )); - if (classNameOffset >= 0) { - function.getBody().add(new WasmStructSet( - standardClasses.classClass().getStructure(), - new WasmGetLocal(targetVar), - classNameOffset, - new WasmGetLocal(nameVar) - )); - } + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classNameOffset, + new WasmGetLocal(nameVar) + )); function.getBody().add(new WasmStructSet( standardClasses.classClass().getStructure(), new WasmGetLocal(targetVar), 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 162205647..e4263f857 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 @@ -260,42 +260,41 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { List arguments) { var vtable = context.virtualTables().lookup(method.getClassName()); if (vtable != null) { + var cls = context.classes().get(method.getClassName()); + assert cls != null : "Virtual table can't be generated for absent class"; + if (cls.hasModifier(ElementModifier.INTERFACE)) { + vtable = pickVirtualTableForInterfaceCall(vtable, method.getDescriptor()); + } vtable = vtable.findMethodContainer(method.getDescriptor()); } if (vtable == null) { return new WasmUnreachable(); } - var cls = context.classes().get(method.getClassName()); - assert cls != null : "Virtual table can't be generated for absent class"; - - if (cls.hasModifier(ElementModifier.INTERFACE)) { - vtable = pickVirtualTableForInterfaceCall(vtable, method.getDescriptor()); - } int vtableIndex = vtable.getMethods().indexOf(method.getDescriptor()); if (vtable.getParent() != null) { vtableIndex += vtable.getParent().size(); } - 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); + WasmExpression classRef = new WasmStructGet(context.standardClasses().objectClass().getStructure(), + new WasmGetLocal(instance), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); var index = context.classInfoProvider().getVirtualMethodsOffset() + vtableIndex; - var vtableStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()) - .getVirtualTableStructure(); - if (!vtableStruct.isSupertypeOf(actualVtableStruct)) { - classRef = new WasmCast(classRef, vtableStruct.getReference()); - } + var expectedInstanceClassInfo = context.classInfoProvider().getClassInfo(vtable.getClassName()); + var vtableStruct = expectedInstanceClassInfo.getVirtualTableStructure(); + classRef = new WasmCast(classRef, vtableStruct.getReference()); var functionRef = new WasmStructGet(vtableStruct, classRef, index); var functionTypeRef = (WasmType.CompositeReference) vtableStruct.getFields().get(index).asUnpackedType(); var invoke = new WasmCallReference(functionRef, (WasmFunctionType) functionTypeRef.composite); - invoke.getArguments().addAll(arguments); + WasmExpression instanceRef = new WasmGetLocal(instance); + var instanceType = (WasmType.CompositeReference) instance.getType(); + var instanceStruct = (WasmStructure) instanceType.composite; + if (!expectedInstanceClassInfo.getStructure().isSupertypeOf(instanceStruct)) { + instanceRef = new WasmCast(instanceRef, expectedInstanceClassInfo.getType()); + } + + invoke.getArguments().add(instanceRef); + invoke.getArguments().addAll(arguments.subList(1, arguments.size())); return invoke; } 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 f72ee121d..b85ab1858 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 @@ -43,6 +43,9 @@ public class WasmCollection implements Iterable { } public void add(T entity) { + if (entity.collection != null) { + throw new IllegalArgumentException("Entity already belongs some collection"); + } if (!indexesInvalid) { entity.index = items.size(); } 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 165f5e89a..7e7f9eb7a 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 @@ -98,7 +98,7 @@ public class WasmModule { } private void prepareTypes() { - var typeGraph = WasmTypeGraphBuilder.buildTypeGraph(types, types.size()); + var typeGraph = WasmTypeGraphBuilder.buildTypeGraph(this, types, types.size()); var sccs = GraphUtils.findStronglyConnectedComponents(typeGraph); var sccStartNode = new int[types.size()]; for (var i = 0; i < sccStartNode.length; ++i) { 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 d6ad54ba7..aafddb758 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 @@ -22,21 +22,23 @@ final class WasmTypeGraphBuilder { private WasmTypeGraphBuilder() { } - static Graph buildTypeGraph(Iterable types, int size) { + static Graph buildTypeGraph(WasmModule module, Iterable types, int size) { var graphBuilder = new GraphBuilder(size); - var visitor = new GraphBuilderVisitor(graphBuilder); + var visitor = new GraphBuilderVisitor(module, graphBuilder); for (var type : types) { - visitor.currentIndex = type.index; + visitor.currentIndex = module.types.indexOf(type); type.acceptVisitor(visitor); } return graphBuilder.build(); } private static class GraphBuilderVisitor implements WasmCompositeTypeVisitor { + final WasmModule module; final GraphBuilder graphBuilder; int currentIndex; - GraphBuilderVisitor(GraphBuilder graphBuilder) { + GraphBuilderVisitor(WasmModule module, GraphBuilder graphBuilder) { + this.module = module; this.graphBuilder = graphBuilder; } @@ -71,7 +73,7 @@ final class WasmTypeGraphBuilder { private void addEdge(WasmType type) { if (type instanceof WasmType.CompositeReference) { var composite = ((WasmType.CompositeReference) type).composite; - graphBuilder.addEdge(currentIndex, composite.index); + graphBuilder.addEdge(currentIndex, module.types.indexOf(composite)); } } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmStructGet.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmStructGet.java index 4440ca7d6..b581bceb5 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmStructGet.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmStructGet.java @@ -25,6 +25,7 @@ public class WasmStructGet extends WasmExpression { private WasmSignedType signedType; public WasmStructGet(WasmStructure type, WasmExpression instance, int fieldIndex) { + checkFieldIndex(fieldIndex); this.type = Objects.requireNonNull(type); this.instance = Objects.requireNonNull(instance); this.fieldIndex = fieldIndex; @@ -51,6 +52,7 @@ public class WasmStructGet extends WasmExpression { } public void setFieldIndex(int fieldIndex) { + checkFieldIndex(fieldIndex); this.fieldIndex = fieldIndex; } @@ -66,4 +68,10 @@ public class WasmStructGet extends WasmExpression { public void acceptVisitor(WasmExpressionVisitor visitor) { visitor.visit(this); } + + private static void checkFieldIndex(int fieldIndex) { + if (fieldIndex < 0) { + throw new IllegalArgumentException("Field index must be >= 0"); + } + } } diff --git a/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java b/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java index 4883a1171..92c70b2e8 100644 --- a/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java +++ b/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java @@ -316,12 +316,12 @@ public abstract class BaseTypeInference { @Override public void visit(ClassConstantInstruction insn) { - type(insn.getReceiver(), ValueType.object("java/lang/Class")); + type(insn.getReceiver(), ValueType.object("java.lang.Class")); } @Override public void visit(StringConstantInstruction insn) { - type(insn.getReceiver(), ValueType.object("java/lang/String")); + type(insn.getReceiver(), ValueType.object("java.lang.String")); } @Override