wasm gc: generate field names and write them to name section

This commit is contained in:
Alexey Andreev 2024-08-20 14:16:24 +02:00
parent 29f29cea1d
commit eb0eb1f146
11 changed files with 217 additions and 31 deletions

View File

@ -33,6 +33,7 @@ import org.teavm.backend.wasm.WasmFunctionTypes;
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("<clinit>", 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 class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
if (!isInterface) {
fillFields(classInfo, type);
}
}
}
var pointerName = names.forClassInstance(type);
classInfo.hasOwnVirtualTable = virtualTable != null && virtualTable.hasValidEntries();
@ -411,7 +414,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
&& !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 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
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<WasmField> 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 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
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 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
}
private void fillClassFields(List<WasmStorageType> fields, String className) {
private void fillClassFields(List<WasmField> fields, String className) {
var classReader = classSource.get(className);
if (classReader == null || classReader.hasModifier(ElementModifier.INTERFACE)) {
fillSimpleClassFields(fields, "java.lang.Object");
@ -546,7 +558,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
}
private void fillSimpleClassFields(List<WasmStorageType> fields, String className) {
private void fillSimpleClassFields(List<WasmField> fields, String className) {
var classReader = classSource.get(className);
if (classReader.getParent() != null) {
fillClassFields(fields, classReader.getParent());
@ -562,29 +574,37 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
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 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
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;
}

View File

@ -62,7 +62,7 @@ public class WasmGCGenerationUtil {
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(),

View File

@ -176,7 +176,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
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 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
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();

View File

@ -85,9 +85,9 @@ public class SystemArrayCopyIntrinsic implements WasmGCIntrinsic {
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);

View File

@ -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;
}
}

View File

@ -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<WasmStorageType> fields = new ArrayList<>();
private List<WasmField> fieldsStorage = new ArrayList<>();
private WasmStructure supertype;
private boolean indexesValid = true;
public WasmStructure(String name) {
super(name);
}
public List<WasmStorageType> getFields() {
public List<WasmField> getFields() {
return fields;
}
@ -48,8 +50,80 @@ public class WasmStructure extends WasmCompositeType {
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<WasmField> fields = new AbstractList<WasmField>() {
@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;
}
};
}

View File

@ -48,7 +48,7 @@ final class WasmTypeGraphBuilder {
addEdge(type.getSupertype().getReference());
}
for (var field : type.getFields()) {
addEdge(field.asUnpackedType());
addEdge(field.getUnpackedType());
}
}

View File

@ -170,7 +170,7 @@ public class UnusedTypeElimination {
@Override
public void visit(WasmStructure type) {
for (var field : type.getFields()) {
visit(field);
visit(field.getType());
}
}

View File

@ -29,6 +29,7 @@ import org.teavm.backend.wasm.model.WasmCustomSection;
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 @@ public class WasmBinaryRenderer {
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());
}

View File

@ -43,7 +43,7 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor
section.writeByte(0x5F);
section.writeLEB(type.getFields().size());
for (var fieldType : type.getFields()) {
writeStorageType(fieldType);
writeStorageType(fieldType.getType());
section.writeLEB(0x01); // mutable
}
}

View File

@ -290,7 +290,7 @@ public class WasmTypeInference implements WasmExpressionVisitor {
@Override
public void visit(WasmStructGet expression) {
result = expression.getType().getFields().get(expression.getFieldIndex()).asUnpackedType();
result = expression.getType().getFields().get(expression.getFieldIndex()).getUnpackedType();
}
@Override