From eb0eb1f1469de9a3aeaa1770e45d0ce50ac0f6ec Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 20 Aug 2024 14:16:24 +0200 Subject: [PATCH] wasm gc: generate field names and write them to name section --- .../gc/classes/WasmGCClassGenerator.java | 60 +++++++++----- .../gc/methods/WasmGCGenerationUtil.java | 2 +- .../gc/methods/WasmGCGenerationVisitor.java | 4 +- .../gc/SystemArrayCopyIntrinsic.java | 4 +- .../teavm/backend/wasm/model/WasmField.java | 66 ++++++++++++++++ .../backend/wasm/model/WasmStructure.java | 78 ++++++++++++++++++- .../wasm/model/WasmTypeGraphBuilder.java | 2 +- .../optimization/UnusedTypeElimination.java | 2 +- .../wasm/render/WasmBinaryRenderer.java | 26 +++++++ .../WasmCompositeTypeBinaryRenderer.java | 2 +- .../wasm/render/WasmTypeInference.java | 2 +- 11 files changed, 217 insertions(+), 31 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/model/WasmField.java 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 be9d7faff..41a0637b1 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 @@ -33,6 +33,7 @@ import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor; import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool; import org.teavm.backend.wasm.model.WasmArray; +import org.teavm.backend.wasm.model.WasmField; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunctionType; import org.teavm.backend.wasm.model.WasmGlobal; @@ -73,6 +74,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private static final MethodDescriptor CLINIT_METHOD_DESC = new MethodDescriptor("", ValueType.VOID); private static final MethodDescriptor GET_CLASS_METHOD = new MethodDescriptor("getClass", ValueType.parse(Class.class)); + private static final FieldReference FAKE_CLASS_FIELD = new FieldReference(Object.class.getName(), "class"); + private static final FieldReference FAKE_MONITOR_FIELD = new FieldReference(Object.class.getName(), "monitor"); private final WasmModule module; private ClassReaderSource classSource; @@ -259,7 +262,7 @@ public WasmGCClassInfo getClassInfo(ValueType type) { } if (!isInterface) { fillFields(classInfo, type); - } + } } var pointerName = names.forClassInstance(type); classInfo.hasOwnVirtualTable = virtualTable != null && virtualTable.hasValidEntries(); @@ -411,7 +414,7 @@ private void fillVirtualTableMethods(List target, WasmStructure && !method.equals(GET_CLASS_METHOD)) { var fieldIndex = virtualTableFieldOffset + entry.getIndex(); var expectedType = (WasmType.CompositeReference) structure.getFields().get(fieldIndex) - .asUnpackedType(); + .getUnpackedType(); var expectedFunctionType = (WasmFunctionType) expectedType.composite; var function = functionProvider.forInstanceMethod(entry.getImplementor()); if (!virtualTable.getClassName().equals(entry.getImplementor().getClassName()) @@ -444,24 +447,34 @@ private WasmStructure initRegularClassStructure(String className) { var structure = new WasmStructure(names.forClassClass(className)); structure.setSupertype(standardClasses.classClass().getStructure()); module.types.add(structure); - structure.getFields().add(standardClasses.classClass().getType().asStorage()); - structure.getFields().add(WasmType.Reference.ANY.asStorage()); + addSystemFields(structure.getFields()); fillSimpleClassFields(structure.getFields(), "java.lang.Class"); addVirtualTableFields(structure, virtualTable); return structure; } + private void addSystemFields(List fields) { + var classField = new WasmField(standardClasses.classClass().getType().asStorage()); + classField.setName(names.forMemberField(FAKE_CLASS_FIELD)); + fields.add(classField); + var monitorField = new WasmField(WasmType.Reference.ANY.asStorage()); + monitorField.setName(names.forMemberField(FAKE_MONITOR_FIELD)); + fields.add(monitorField); + } + private void addVirtualTableFields(WasmStructure structure, VirtualTable virtualTable) { if (virtualTable.getParent() != null) { addVirtualTableFields(structure, virtualTable.getParent()); } for (var methodDesc : virtualTable.getMethods()) { if (methodDesc == null) { - structure.getFields().add(WasmType.Reference.FUNC.asStorage()); + structure.getFields().add(new WasmField(WasmType.Reference.FUNC.asStorage())); } else { var originalVirtualTable = virtualTable.findMethodContainer(methodDesc); var functionType = typeMapper.getFunctionType(originalVirtualTable.getClassName(), methodDesc, false); - structure.getFields().add(functionType.getReference().asStorage()); + var field = new WasmField(functionType.getReference().asStorage()); + field.setName(names.forVirtualMethod(methodDesc)); + structure.getFields().add(field); } } } @@ -528,8 +541,7 @@ private WasmGlobal generateStaticFieldLocation(FieldReference fieldRef) { private void fillFields(WasmGCClassInfo classInfo, ValueType type) { var fields = classInfo.structure.getFields(); - fields.add(standardClasses.classClass().getType().asStorage()); - fields.add(WasmType.Reference.ANY.asStorage()); + addSystemFields(fields); if (type instanceof ValueType.Object) { fillClassFields(fields, ((ValueType.Object) type).getClassName()); } else if (type instanceof ValueType.Array) { @@ -537,7 +549,7 @@ private void fillFields(WasmGCClassInfo classInfo, ValueType type) { } } - private void fillClassFields(List fields, String className) { + private void fillClassFields(List fields, String className) { var classReader = classSource.get(className); if (classReader == null || classReader.hasModifier(ElementModifier.INTERFACE)) { fillSimpleClassFields(fields, "java.lang.Object"); @@ -546,7 +558,7 @@ private void fillClassFields(List fields, String className) { } } - private void fillSimpleClassFields(List fields, String className) { + private void fillSimpleClassFields(List fields, String className) { var classReader = classSource.get(className); if (classReader.getParent() != null) { fillClassFields(fields, classReader.getParent()); @@ -562,29 +574,37 @@ private void fillSimpleClassFields(List fields, String classNam continue; } fieldIndexes.putIfAbsent(field.getReference(), fields.size()); - fields.add(typeMapper.mapStorageType(field.getType())); + var wasmField = new WasmField(typeMapper.mapStorageType(field.getType()), + names.forMemberField(field.getReference())); + fields.add(wasmField); } if (className.equals("java.lang.Class")) { classFlagsOffset = fields.size(); - fields.add(WasmType.INT32.asStorage()); + fields.add(createClassField(WasmType.INT32.asStorage(), "lowerIndex")); classTagOffset = fields.size(); - fields.add(WasmType.INT32.asStorage()); + fields.add(createClassField(WasmType.INT32.asStorage(), "upperIndex")); classParentOffset = fields.size(); - fields.add(standardClasses.classClass().getType().asStorage()); + fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "parent")); classArrayItemOffset = fields.size(); - fields.add(standardClasses.classClass().getType().asStorage()); + fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "arrayItem")); classArrayOffset = fields.size(); - fields.add(standardClasses.classClass().getType().asStorage()); + fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "array")); classSupertypeFunctionOffset = fields.size(); - fields.add(supertypeGenerator.getFunctionType().getReference().asStorage()); + fields.add(createClassField(supertypeGenerator.getFunctionType().getReference().asStorage(), + "isSupertype")); classNewArrayOffset = fields.size(); - fields.add(newArrayGenerator.getNewArrayFunctionType().getReference().asStorage()); + fields.add(createClassField(newArrayGenerator.getNewArrayFunctionType().getReference().asStorage(), + "createArrayInstance")); classNameOffset = fields.size(); - fields.add(standardClasses.stringClass().getType().asStorage()); + fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "name")); virtualTableFieldOffset = fields.size(); } } + private WasmField createClassField(WasmStorageType type, String name) { + return new WasmField(type, names.forMemberField(new FieldReference("java.lang.Class", name))); + } + private void fillArrayFields(WasmGCClassInfo classInfo, ValueType elementType) { WasmStorageType wasmElementType; if (elementType instanceof ValueType.Primitive) { @@ -617,7 +637,7 @@ private void fillArrayFields(WasmGCClassInfo classInfo, ValueType elementType) { } var wasmArray = new WasmArray(null, wasmElementType); module.types.add(wasmArray); - classInfo.structure.getFields().add(wasmArray.getReference().asStorage()); + classInfo.structure.getFields().add(new WasmField(wasmArray.getReference().asStorage(), "data")); classInfo.array = wasmArray; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java index 819bbdbde..0d69e326f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java @@ -62,7 +62,7 @@ public void allocateArray(ValueType itemType, WasmExpression length, TextLocatio var wasmArrayType = (WasmType.CompositeReference) classInfo.getStructure().getFields() .get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET) - .asUnpackedType(); + .getUnpackedType(); var wasmArray = (WasmArray) wasmArrayType.composite; var initArrayField = new WasmStructSet( classInfo.getStructure(), 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 e4263f857..91bbfffa8 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 @@ -176,7 +176,7 @@ protected void storeField(Expr qualified, FieldReference field, Expr value, Text var struct = (WasmStructure) type.composite; var fieldIndex = context.classInfoProvider().getFieldIndex(field); - accept(value, struct.getFields().get(fieldIndex).asUnpackedType()); + accept(value, struct.getFields().get(fieldIndex).getUnpackedType()); var wasmValue = result; var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue); @@ -284,7 +284,7 @@ protected WasmExpression generateVirtualCall(WasmLocal instance, MethodReference classRef = new WasmCast(classRef, vtableStruct.getReference()); var functionRef = new WasmStructGet(vtableStruct, classRef, index); - var functionTypeRef = (WasmType.CompositeReference) vtableStruct.getFields().get(index).asUnpackedType(); + var functionTypeRef = (WasmType.CompositeReference) vtableStruct.getFields().get(index).getUnpackedType(); var invoke = new WasmCallReference(functionRef, (WasmFunctionType) functionTypeRef.composite); WasmExpression instanceRef = new WasmGetLocal(instance); var instanceType = (WasmType.CompositeReference) instance.getType(); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemArrayCopyIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemArrayCopyIntrinsic.java index 3da3f1ff5..1e9700c90 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemArrayCopyIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemArrayCopyIntrinsic.java @@ -85,9 +85,9 @@ private WasmExpression tryGenerateSpecialCase(InvocationExpr invocation, WasmGCI var wasmSize = context.generate(invocation.getArguments().get(4)); var wasmTargetArrayTypeRef = (WasmType.CompositeReference) wasmTargetArrayStruct.getFields() - .get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).asUnpackedType(); + .get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).getUnpackedType(); var wasmSourceArrayTypeRef = (WasmType.CompositeReference) wasmSourceArrayStruct.getFields() - .get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).asUnpackedType(); + .get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).getUnpackedType(); return new WasmArrayCopy((WasmArray) wasmTargetArrayTypeRef.composite, wasmTargetArray, wasmTargetIndex, (WasmArray) wasmSourceArrayTypeRef.composite, wasmSourceArray, wasmSourceIndex, wasmSize); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmField.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmField.java new file mode 100644 index 000000000..464f3fcef --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmField.java @@ -0,0 +1,66 @@ +/* + * Copyright 2024 konsoletyper. + * + * 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 java.util.Objects; + +public class WasmField { + WasmStructure structure; + int index; + private String name; + private WasmStorageType type; + + public WasmField(WasmStorageType type, String name) { + this(type); + this.name = name; + } + + public WasmField(WasmStorageType type) { + this.type = Objects.requireNonNull(type); + } + + public WasmStructure getStructure() { + return structure; + } + + public WasmStorageType getType() { + return type; + } + + public WasmType getUnpackedType() { + return type.asUnpackedType(); + } + + public void setType(WasmStorageType type) { + this.type = Objects.requireNonNull(type); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getIndex() { + if (structure == null) { + return -1; + } + structure.ensureIndexes(); + return index; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java index 596973ff2..862542cd6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java @@ -15,18 +15,20 @@ */ package org.teavm.backend.wasm.model; +import java.util.AbstractList; import java.util.ArrayList; import java.util.List; public class WasmStructure extends WasmCompositeType { - private List fields = new ArrayList<>(); + private List fieldsStorage = new ArrayList<>(); private WasmStructure supertype; + private boolean indexesValid = true; public WasmStructure(String name) { super(name); } - public List getFields() { + public List getFields() { return fields; } @@ -48,8 +50,80 @@ public boolean isSupertypeOf(WasmStructure subtype) { return false; } + void ensureIndexes() { + if (!indexesValid) { + indexesValid = true; + for (var i = 0; i < fieldsStorage.size(); ++i) { + fieldsStorage.get(i).index = i; + } + } + } + @Override public void acceptVisitor(WasmCompositeTypeVisitor visitor) { visitor.visit(this); } + + private List fields = new AbstractList() { + @Override + public WasmField get(int index) { + return fieldsStorage.get(index); + } + + @Override + public int size() { + return fieldsStorage.size(); + } + + @Override + public void add(int index, WasmField element) { + if (element.structure != null) { + throw new IllegalArgumentException("This field already belongs to structure"); + } + element.structure = WasmStructure.this; + indexesValid = false; + fieldsStorage.add(index, element); + } + + @Override + public WasmField remove(int index) { + var result = fieldsStorage.remove(index); + indexesValid = false; + result.structure = null; + return result; + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + var sublist = fieldsStorage.subList(fromIndex, toIndex); + for (var field : sublist) { + field.structure = null; + } + indexesValid = false; + sublist.clear(); + } + + @Override + public void clear() { + for (var field : fieldsStorage) { + field.structure = null; + } + indexesValid = true; + fieldsStorage.clear(); + } + + @Override + public WasmField set(int index, WasmField element) { + if (element.structure != null) { + throw new IllegalArgumentException("This field already belongs to structure"); + } + var former = fieldsStorage.set(index, element); + former.structure = null; + if (indexesValid) { + element.index = former.index; + } + element.structure = WasmStructure.this; + return former; + } + }; } 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 aafddb758..25462b5e7 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 @@ -48,7 +48,7 @@ public void visit(WasmStructure type) { addEdge(type.getSupertype().getReference()); } for (var field : type.getFields()) { - addEdge(field.asUnpackedType()); + addEdge(field.getUnpackedType()); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java index e7fd4125a..a05fb3b8f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java +++ b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java @@ -170,7 +170,7 @@ public void visit(WasmArrayCopy expression) { @Override public void visit(WasmStructure type) { for (var field : type.getFields()) { - visit(field); + visit(field.getType()); } } 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 51b0827bc..ced1b80a9 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 @@ -29,6 +29,7 @@ import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmMemorySegment; import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmStructure; import org.teavm.backend.wasm.model.WasmType; public class WasmBinaryRenderer { @@ -509,6 +510,31 @@ private void renderNames(WasmModule module) { section.writeBytes(payload); } + var typesWithNamedFields = module.types.stream() + .filter(t -> t instanceof WasmStructure) + .filter(t -> ((WasmStructure) t).getFields().stream().anyMatch(f -> f.getName() != null)) + .collect(Collectors.toList()); + if (!typesWithNamedFields.isEmpty()) { + var subsection = new WasmBinaryWriter(); + subsection.writeLEB(typesWithNamedFields.size()); + for (var type : typesWithNamedFields) { + subsection.writeLEB(module.types.indexOf(type)); + var fields = ((WasmStructure) type).getFields().stream() + .filter(t -> t.getName() != null) + .collect(Collectors.toList()); + subsection.writeLEB(fields.size()); + for (var field : fields) { + subsection.writeLEB(field.getIndex()); + subsection.writeAsciiString(field.getName()); + } + } + + payload = subsection.getData(); + section.writeLEB(10); + section.writeLEB(payload.length); + section.writeBytes(payload); + } + writeSection(SECTION_UNKNOWN, "name", section.getData()); } 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 630c29337..746e7b500 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 @@ -43,7 +43,7 @@ public void visit(WasmStructure type) { section.writeByte(0x5F); section.writeLEB(type.getFields().size()); for (var fieldType : type.getFields()) { - writeStorageType(fieldType); + writeStorageType(fieldType.getType()); section.writeLEB(0x01); // mutable } } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java index cf46938e5..ab15baf88 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java @@ -290,7 +290,7 @@ public void visit(WasmStructNewDefault expression) { @Override public void visit(WasmStructGet expression) { - result = expression.getType().getFields().get(expression.getFieldIndex()).asUnpackedType(); + result = expression.getType().getFields().get(expression.getFieldIndex()).getUnpackedType(); } @Override