wasm gc: draft first parts of Wasm GC backend

* class metadata generator
* string pool generator
This commit is contained in:
Alexey Andreev 2024-07-01 20:41:29 +02:00
parent efcb22b639
commit 7efb3c97a0
33 changed files with 1674 additions and 87 deletions

View File

@ -73,6 +73,7 @@ import org.teavm.model.instructions.IsInstanceInstruction;
import org.teavm.model.instructions.StringConstantInstruction; import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.lowlevel.CallSiteDescriptor; import org.teavm.model.lowlevel.CallSiteDescriptor;
import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.Characteristics;
import org.teavm.model.util.ReflectionUtil;
import org.teavm.runtime.CallSite; import org.teavm.runtime.CallSite;
import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeArray;
import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeClass;
@ -1295,26 +1296,7 @@ public class ClassGenerator {
public String nameOfType(ValueType type) { public String nameOfType(ValueType type) {
if (type instanceof ValueType.Primitive) { if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) { return ReflectionUtil.typeName(((ValueType.Primitive) type).getKind());
case BOOLEAN:
return "boolean";
case BYTE:
return "byte";
case SHORT:
return "short";
case CHARACTER:
return "char";
case INTEGER:
return "int";
case LONG:
return "long";
case FLOAT:
return "float";
case DOUBLE:
return "double";
default:
throw new AssertionError();
}
} else if (type instanceof ValueType.Array) { } else if (type instanceof ValueType.Array) {
if (isArrayOfPrimitives(type)) { if (isArrayOfPrimitives(type)) {
return type.toString().replace('/', '.'); return type.toString().replace('/', '.');

View File

@ -55,6 +55,7 @@ import org.teavm.model.classes.VirtualTable;
import org.teavm.model.classes.VirtualTableEntry; import org.teavm.model.classes.VirtualTableEntry;
import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.Characteristics;
import org.teavm.model.util.ReflectionUtil;
import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeClass;
import org.teavm.runtime.RuntimeObject; import org.teavm.runtime.RuntimeObject;
@ -250,34 +251,7 @@ public class WasmClassGenerator {
if (type == ValueType.VOID) { if (type == ValueType.VOID) {
name = "void"; name = "void";
} else { } else {
switch (((ValueType.Primitive) type).getKind()) { name = ReflectionUtil.typeName(((ValueType.Primitive) type).getKind());
case BOOLEAN:
name = "boolean";
break;
case BYTE:
name = "byte";
break;
case SHORT:
name = "short";
break;
case CHARACTER:
name = "char";
break;
case INTEGER:
name = "int";
break;
case LONG:
name = "long";
break;
case FLOAT:
name = "float";
break;
case DOUBLE:
name = "double";
break;
default:
name = "";
}
} }
value.setAddress(CLASS_NAME, stringPool.getStringPointer(name)); value.setAddress(CLASS_NAME, stringPool.getStringPointer(name));

View File

@ -0,0 +1,49 @@
/*
* 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.generate.gc.classes;
public final class WasmGCClassFlags {
public static final int ABSTRACT = 1;
public static final int INTERFACE = 1 << 1;
public static final int FINAL = 1 << 2;
public static final int ENUM = 1 << 3;
public static final int ANNOTATION = 1 << 4;
public static final int SYNTHETIC = 1 << 5;
public static final int BRIDGE = 1 << 6;
public static final int DEPRECATED = 1 << 7;
public static final int NATIVE = 1 << 8;
public static final int STATIC = 1 << 9;
public static final int STRICT = 1 << 10;
public static final int SYNCHRONIZED = 1 << 11;
public static final int TRANSIENT = 1 << 12;
public static final int VARARGS = 1 << 13;
public static final int VOLATILE = 1 << 14;
public static final int PRIMITIVE = 1 << 15;
public static final int PRIMITIVE_KIND_SHIFT = 16;
public static final int PRIMITIVE_BOOLEAN = 0;
public static final int PRIMITIVE_BYTE = 1;
public static final int PRIMITIVE_SHORT = 2;
public static final int PRIMITIVE_CHAR = 3;
public static final int PRIMITIVE_INT = 4;
public static final int PRIMITIVE_LONG = 5;
public static final int PRIMITIVE_FLOAT = 6;
public static final int PRIMITIVE_DOUBLE = 7;
public static final int PRIMITIVE_VOID = 8;
private WasmGCClassFlags() {
}
}

View File

@ -0,0 +1,538 @@
/*
* 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.generate.gc.classes;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.method.WasmGCFunctionProvider;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmStorageType;
import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
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.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTable;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.util.ReflectionUtil;
public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInitializerContributor {
public static final int CLASS_FIELD_OFFSET = 0;
private final WasmModule module;
private ClassReaderSource classSource;
private WasmFunctionTypes functionTypes;
private TagRegistry tagRegistry;
private ClassMetadataRequirements metadataRequirements;
private VirtualTableProvider virtualTables;
private WasmGCFunctionProvider functionProvider;
private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>();
private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>();
private ObjectIntMap<MethodReference> methodIndexes = new ObjectIntHashMap<>();
public final WasmGCStringPool strings;
public final WasmGCStandardClasses standardClasses;
private final WasmGCTypeMapper typeMapper;
private final NameProvider names;
private WasmFunction initializer;
private WasmFunction createPrimitiveClassFunction;
private WasmFunction createArrayClassFunction;
private final WasmGCSupertypeFunctionGenerator supertypeGenerator;
private int classTagOffset;
private int classFlagsOffset;
private int classNameOffset;
private int classParentOffset;
private int classArrayOffset;
private int classArrayItemOffset;
private int classSupertypeFunctionOffset;
private int virtualTableFieldOffset;
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables,
WasmGCFunctionProvider functionProvider, NameProvider names) {
this.module = module;
this.classSource = classSource;
this.functionTypes = functionTypes;
this.tagRegistry = tagRegistry;
this.metadataRequirements = metadataRequirements;
this.virtualTables = virtualTables;
this.functionProvider = functionProvider;
this.names = names;
standardClasses = new WasmGCStandardClasses(this);
strings = new WasmGCStringPool(standardClasses, module, functionProvider);
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes);
typeMapper = new WasmGCTypeMapper(this);
}
@Override
public void contributeToInitializerDefinitions(WasmFunction function) {
for (var classInfo : classInfoMap.values()) {
var newStruct = new WasmStructNewDefault(standardClasses.classClass().getStructure());
function.getBody().add(new WasmSetGlobal(classInfo.pointer, newStruct));
}
}
@Override
public void contributeToInitializer(WasmFunction function) {
var classClass = standardClasses.classClass();
for (var classInfo : classInfoMap.values()) {
classInfo.initializer.accept(function.getBody());
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset,
new WasmFunctionReference(supertypeFunction)));
function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET,
new WasmGetGlobal(classClass.pointer)));
}
}
@Override
public WasmGCClassInfo getClassInfo(ValueType type) {
var classInfo = classInfoMap.get(type);
if (classInfo == null) {
classInfo = new WasmGCClassInfo(type);
classInfoMap.put(type, classInfo);
if (!(type instanceof ValueType.Primitive)) {
var name = type instanceof ValueType.Object
? ((ValueType.Object) type).getClassName()
: null;
classInfo.structure = new WasmStructure(name);
classInfo.structure.getFields().add(standardClasses.classClass().getType().asStorage());
fillFields(classInfo.structure.getFields(), type);
}
var pointerName = names.forClassInstance(type);
var classStructure = type instanceof ValueType.Object
? initRegularClassStructure(((ValueType.Object) type).getClassName())
: standardClasses.classClass().getStructure();
classInfo.virtualTableStructure = classStructure;
classInfo.pointer = new WasmGlobal(pointerName, classStructure.getReference(),
new WasmNullConstant(classStructure.getReference()));
module.globals.add(classInfo.pointer);
if (type instanceof ValueType.Primitive) {
initPrimitiveClass(classInfo, (ValueType.Primitive) type);
} else if (type instanceof ValueType.Void) {
initVoidClass(classInfo);
} else if (type instanceof ValueType.Array) {
initArrayClass(classInfo, (ValueType.Array) type);
} else if (type instanceof ValueType.Object) {
initRegularClass(classInfo, classStructure, ((ValueType.Object) type).getClassName());
}
}
return classInfo;
}
public int getClassTagOffset() {
return classTagOffset;
}
public int getClassArrayItemOffset() {
return classArrayItemOffset;
}
private void initPrimitiveClass(WasmGCClassInfo classInfo, ValueType.Primitive type) {
classInfo.initializer = target -> {
int kind;
switch (type.getKind()) {
case BOOLEAN:
kind = WasmGCClassFlags.PRIMITIVE_BOOLEAN;
break;
case BYTE:
kind = WasmGCClassFlags.PRIMITIVE_BYTE;
break;
case SHORT:
kind = WasmGCClassFlags.PRIMITIVE_SHORT;
break;
case CHARACTER:
kind = WasmGCClassFlags.PRIMITIVE_CHAR;
break;
case INTEGER:
kind = WasmGCClassFlags.PRIMITIVE_INT;
break;
case LONG:
kind = WasmGCClassFlags.PRIMITIVE_LONG;
break;
case FLOAT:
kind = WasmGCClassFlags.PRIMITIVE_FLOAT;
break;
case DOUBLE:
kind = WasmGCClassFlags.PRIMITIVE_DOUBLE;
break;
default:
throw new IllegalArgumentException();
}
target.add(fillPrimitiveClass(
classInfo.pointer,
ReflectionUtil.typeName(type.getKind()),
kind
));
};
}
private void initVoidClass(WasmGCClassInfo classInfo) {
classInfo.initializer = target -> {
target.add(fillPrimitiveClass(
classInfo.pointer,
"void",
WasmGCClassFlags.PRIMITIVE_VOID
));
};
}
private void initRegularClass(WasmGCClassInfo classInfo, WasmStructure classStructure, String name) {
classInfo.initializer = target -> {
var ranges = tagRegistry.getRanges(name);
int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
target.add(setClassField(classInfo, classTagOffset, new WasmInt32Constant(tag)));
var metadataReg = metadataRequirements.getInfo(name);
if (metadataReg.name()) {
var namePtr = strings.getStringConstant(name).global;
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
}
var cls = classSource.get(name);
if (cls != null) {
if (metadataReg.simpleName() && cls.getSimpleName() != null) {
var namePtr = strings.getStringConstant(cls.getSimpleName()).global;
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
}
if (cls.getParent() != null) {
var parent = getClassInfo(cls.getParent());
target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer)));
}
}
var virtualTable = virtualTables.lookup(name);
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTableFieldOffset,
name);
};
}
private int fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
VirtualTable virtualTable, int index, String origin) {
if (virtualTable.getParent() != null) {
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin);
}
for (var method : virtualTable.getMethods()) {
var entry = virtualTable.getEntry(method);
if (entry != null && entry.getImplementor() != null) {
var function = functionProvider.getMemberFunction(entry.getImplementor());
if (!origin.equals(entry.getImplementor().getClassName())) {
var functionType = getFunctionType(virtualTable.getClassName(), method);
var wrapperFunction = new WasmFunction(functionType);
module.functions.add(wrapperFunction);
var call = new WasmCall(function);
var instanceParam = new WasmLocal(getClassInfo(virtualTable.getClassName()).getType());
wrapperFunction.getLocalVariables().add(instanceParam);
var castTarget = getClassInfo(entry.getImplementor().getClassName()).getType();
call.getArguments().add(new WasmCast(new WasmGetLocal(instanceParam), castTarget));
var params = new WasmLocal[method.parameterCount()];
for (var i = 0; i < method.parameterCount(); ++i) {
params[i] = new WasmLocal(typeMapper.mapType(method.parameterType(i)).asUnpackedType());
call.getArguments().add(new WasmGetLocal(params[i]));
}
wrapperFunction.getLocalVariables().addAll(List.of(params));
}
var ref = new WasmFunctionReference(function);
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref));
}
}
return index;
}
private WasmStructure initRegularClassStructure(String className) {
var virtualTable = virtualTables.lookup(className);
var structure = new WasmStructure(names.forClassClass(className));
module.types.add(structure);
addVirtualTableFields(structure, virtualTable);
fillClassFields(structure.getFields(), "java.lang.Class");
return structure;
}
private void addVirtualTableFields(WasmStructure structure, VirtualTable virtualTable) {
if (virtualTable.getParent() != null) {
addVirtualTableFields(structure, virtualTable.getParent());
}
for (var methodDesc : virtualTable.getMethods()) {
var functionType = getFunctionType(virtualTable.getClassName(), methodDesc);
var methodRef = new MethodReference(virtualTable.getClassName(), methodDesc);
methodIndexes.put(methodRef, structure.getFields().size());
structure.getFields().add(functionType.getReference().asStorage());
}
}
private WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc) {
var returnType = typeMapper.mapType(methodDesc.getResultType()).asUnpackedType();
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]).asUnpackedType();
}
return functionTypes.of(returnType, paramTypes);
}
private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) {
classInfo.initializer = target -> {
var itemTypeInfo = getClassInfo(type.getItemType());
target.add(new WasmCall(
getCreateArrayClassFunction(),
new WasmGetGlobal(classInfo.pointer),
new WasmGetGlobal(itemTypeInfo.pointer)
));
};
}
private WasmExpression fillPrimitiveClass(WasmGlobal global, String name, int kind) {
return new WasmCall(
getCreatePrimitiveClassFunction(),
new WasmGetGlobal(global),
new WasmGetGlobal(strings.getStringConstant(name).global),
new WasmInt32Constant(kind)
);
}
public int getFieldIndex(FieldReference fieldRef) {
var result = fieldIndexes.getOrDefault(fieldRef, -1);
if (result < 0) {
throw new IllegalStateException("Can't get offset of field " + fieldRef);
}
return result;
}
public int getVirtualMethodIndex(MethodReference methodRef) {
var result = methodIndexes.getOrDefault(methodRef, -1);
if (result < 0) {
throw new IllegalStateException("Can't get offset of method " + methodRef);
}
return result;
}
private void fillFields(List<WasmStorageType> fields, ValueType type) {
fields.add(standardClasses.classClass().getType().asStorage());
if (type instanceof ValueType.Object) {
fillClassFields(fields, ((ValueType.Object) type).getClassName());
} else if (type instanceof ValueType.Array) {
fillArrayField(fields, ((ValueType.Array) type).getItemType());
}
}
private void fillClassFields(List<WasmStorageType> fields, String className) {
var classReader = classSource.get(className);
if (classReader.hasModifier(ElementModifier.INTERFACE)) {
fillSimpleClassFields(fields, "java.lang.Object");
} else {
fillSimpleClassFields(fields, className);
}
}
private void fillSimpleClassFields(List<WasmStorageType> fields, String className) {
var classReader = classSource.get(className);
if (classReader.getParent() != null) {
fillClassFields(fields, classReader.getParent());
} else {
fields.add(standardClasses.classClass().getType().asStorage());
}
for (var field : classReader.getFields()) {
if (field.hasModifier(ElementModifier.STATIC)) {
continue;
}
fieldIndexes.putIfAbsent(field.getReference(), fields.size());
fields.add(typeMapper.mapType(field.getType()));
}
if (className.equals("java.lang.Class")) {
classFlagsOffset = fields.size();
fields.add(WasmType.INT32.asStorage());
classTagOffset = fields.size();
fields.add(WasmType.INT32.asStorage());
classParentOffset = fields.size();
fields.add(standardClasses.classClass().getType().asStorage());
classArrayItemOffset = fields.size();
fields.add(standardClasses.classClass().getType().asStorage());
classArrayOffset = fields.size();
fields.add(standardClasses.classClass().getType().asStorage());
classSupertypeFunctionOffset = fields.size();
fields.add(supertypeGenerator.getFunctionType().getReference().asStorage());
virtualTableFieldOffset = fields.size();
classNameOffset = fieldIndexes.get(new FieldReference(className, "name"));
}
}
private void fillArrayField(List<WasmStorageType> fields, ValueType elementType) {
var wasmArray = new WasmArray(null, () -> typeMapper.mapType(elementType));
module.types.add(wasmArray);
fields.add(wasmArray.getReference().asStorage());
}
private WasmFunction getCreatePrimitiveClassFunction() {
if (createPrimitiveClassFunction == null) {
createPrimitiveClassFunction = createCreatePrimitiveClassFunction();
}
return createPrimitiveClassFunction;
}
private WasmFunction createCreatePrimitiveClassFunction() {
var functionType = functionTypes.of(
null,
standardClasses.classClass().getType(),
standardClasses.stringClass().getType(),
WasmType.INT32
);
var function = new WasmFunction(functionType);
function.setName("_teavm_fill_primitive_class_");
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name");
var kindVar = new WasmLocal(WasmType.INT32, "kind");
function.getLocalVariables().add(targetVar);
function.getLocalVariables().add(nameVar);
function.getLocalVariables().add(kindVar);
var flagsExpr = new WasmIntBinary(
WasmIntType.INT32,
WasmIntBinaryOperation.SHL,
new WasmGetLocal(kindVar),
new WasmInt32Constant(WasmGCClassFlags.PRIMITIVE_KIND_SHIFT)
);
flagsExpr = new WasmIntBinary(
WasmIntType.INT32,
WasmIntBinaryOperation.OR,
flagsExpr,
new WasmInt32Constant(WasmGCClassFlags.FINAL | WasmGCClassFlags.PRIMITIVE)
);
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
classFlagsOffset,
flagsExpr
));
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),
classTagOffset,
new WasmInt32Constant(Integer.MAX_VALUE)
));
return function;
}
private WasmFunction getCreateArrayClassFunction() {
if (createArrayClassFunction == null) {
createArrayClassFunction = createCreateArrayClassFunction();
}
return createCreateArrayClassFunction();
}
private WasmFunction createCreateArrayClassFunction() {
var functionType = functionTypes.of(
null,
standardClasses.classClass().getType(),
standardClasses.classClass().getType()
);
var function = new WasmFunction(functionType);
function.setName("_teavm_fill_array_class_");
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item");
function.getLocalVariables().add(targetVar);
function.getLocalVariables().add(itemVar);
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
classFlagsOffset,
new WasmInt32Constant(WasmGCClassFlags.FINAL)
));
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
classArrayItemOffset,
new WasmGetLocal(itemVar)
));
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(itemVar),
classArrayOffset,
new WasmGetLocal(targetVar)
));
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
classTagOffset,
new WasmInt32Constant(0)
));
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
classParentOffset,
new WasmGetGlobal(standardClasses.classClass().pointer)
));
return function;
}
private WasmFunction getInitializer() {
if (initializer == null) {
initializer = new WasmFunction(functionTypes.of(null));
initializer.setName("_teavm_init_classes_");
module.functions.add(initializer);
}
return initializer;
}
private WasmExpression setClassField(WasmGCClassInfo classInfo, int fieldIndex, WasmExpression value) {
return new WasmStructSet(
standardClasses.classClass().getStructure(),
new WasmGetGlobal(classInfo.pointer),
fieldIndex,
value
);
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.generate.gc.classes;
import java.util.List;
import java.util.function.Consumer;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.model.ValueType;
public class WasmGCClassInfo {
private ValueType valueType;
WasmStructure structure;
WasmStructure virtualTableStructure;
WasmGlobal pointer;
Consumer<List<WasmExpression>> initializer;
WasmGCClassInfo(ValueType valueType) {
this.valueType = valueType;
}
public ValueType getValueType() {
return valueType;
}
public WasmStructure getStructure() {
return structure;
}
public WasmStructure getVirtualTableStructure() {
return virtualTableStructure;
}
public WasmType.CompositeReference getType() {
return getStructure().getReference();
}
public WasmGlobal getPointer() {
return pointer;
}
}

View File

@ -0,0 +1,26 @@
/*
* 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.generate.gc.classes;
import org.teavm.model.ValueType;
public interface WasmGCClassInfoProvider {
WasmGCClassInfo getClassInfo(ValueType type);
default WasmGCClassInfo getClassInfo(String name) {
return getClassInfo(ValueType.object(name));
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.generate.gc.classes;
public class WasmGCStandardClasses {
private WasmGCClassInfoProvider classGenerator;
private WasmGCClassInfo classClassInfo;
private WasmGCClassInfo stringClassInfo;
private WasmGCClassInfo objectClassInfo;
public WasmGCStandardClasses(WasmGCClassInfoProvider classGenerator) {
this.classGenerator = classGenerator;
}
public WasmGCClassInfo classClass() {
if (classClassInfo == null) {
classClassInfo = classGenerator.getClassInfo("java.lang.Class");
}
return classClassInfo;
}
public WasmGCClassInfo stringClass() {
if (stringClassInfo == null) {
stringClassInfo = classGenerator.getClassInfo("java.lang.Class");
}
return stringClassInfo;
}
public WasmGCClassInfo objectClass() {
if (objectClassInfo == null) {
objectClassInfo = classGenerator.getClassInfo("java.lang.Object");
}
return objectClassInfo;
}
}

View File

@ -0,0 +1,186 @@
/*
* 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.generate.gc.classes;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
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.WasmStructGet;
import org.teavm.model.ValueType;
import org.teavm.model.classes.TagRegistry;
public class WasmGCSupertypeFunctionGenerator {
private ObjectIntMap<ValueType> tableIndexes = new ObjectIntHashMap<>();
private Map<ValueType, WasmFunction> functions = new HashMap<>();
private WasmModule module;
private WasmGCClassGenerator classGenerator;
private NameProvider nameProvider;
private TagRegistry tagRegistry;
private WasmFunctionTypes functionTypes;
private WasmFunctionType functionType;
WasmGCSupertypeFunctionGenerator(
WasmModule module,
WasmGCClassGenerator classGenerator,
NameProvider nameProvider,
TagRegistry tagRegistry,
WasmFunctionTypes functionTypes
) {
this.module = module;
this.classGenerator = classGenerator;
this.nameProvider = nameProvider;
this.tagRegistry = tagRegistry;
this.functionTypes = functionTypes;
}
public WasmFunction getIsSupertypeFunction(ValueType type) {
var result = functions.get(type);
if (result == null) {
result = generateIsSupertypeFunction(type);
functions.put(type, result);
}
return result;
}
private WasmFunction generateIsSupertypeFunction(ValueType type) {
var function = new WasmFunction(getFunctionType());
function.setName(nameProvider.forSupertypeFunction(type));
var subtypeVar = new WasmLocal(WasmType.INT32, "subtype");
function.add(subtypeVar);
module.functions.add(function);
if (type instanceof ValueType.Object) {
var className = ((ValueType.Object) type).getClassName();
generateIsClass(subtypeVar, className, function.getBody());
} else if (type instanceof ValueType.Array) {
ValueType itemType = ((ValueType.Array) type).getItemType();
generateIsArray(subtypeVar, itemType, function.getBody());
} else {
var expected = classGenerator.getClassInfo(type).pointer;
var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ,
new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected));
function.getBody().add(new WasmReturn(condition));
}
return function;
}
private void generateIsClass(WasmLocal subtypeVar, String className, List<WasmExpression> body) {
var ranges = tagRegistry.getRanges(className);
if (ranges.isEmpty()) {
body.add(new WasmReturn(new WasmInt32Constant(0)));
return;
}
int tagOffset = classGenerator.getClassTagOffset();
var tagExpression = getClassField(new WasmGetLocal(subtypeVar), tagOffset);
body.add(new WasmSetLocal(subtypeVar, tagExpression));
ranges.sort(Comparator.comparingInt(range -> range.lower));
int lower = ranges.get(0).lower;
int upper = ranges.get(ranges.size() - 1).upper;
var lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lower));
var testLower = new WasmConditional(lowerCondition);
testLower.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
body.add(testLower);
var upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(upper));
var testUpper = new WasmConditional(upperCondition);
testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
body.add(testUpper);
for (int i = 1; i < ranges.size(); ++i) {
int lowerHole = ranges.get(i - 1).upper;
int upperHole = ranges.get(i).lower;
lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lowerHole));
testLower = new WasmConditional(lowerCondition);
body.add(testLower);
upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(upperHole));
testUpper = new WasmConditional(upperCondition);
testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
testLower.getThenBlock().getBody().add(testUpper);
}
body.add(new WasmReturn(new WasmInt32Constant(1)));
}
private void generateIsArray(WasmLocal subtypeVar, ValueType itemType, List<WasmExpression> body) {
int itemOffset = classGenerator.getClassArrayItemOffset();
var itemExpression = getClassField(new WasmGetLocal(subtypeVar), itemOffset);
body.add(new WasmSetLocal(subtypeVar, itemExpression));
var itemTest = new WasmConditional(new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ,
new WasmGetLocal(subtypeVar)));
itemTest.setType(WasmType.INT32);
itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0));
var delegateToItem = new WasmCall(getIsSupertypeFunction(itemType));
delegateToItem.getArguments().add(new WasmGetLocal(subtypeVar));
itemTest.getElseBlock().getBody().add(delegateToItem);
body.add(new WasmReturn(itemTest));
}
public WasmFunctionType getFunctionType() {
if (functionType == null) {
functionType = functionTypes.of(WasmType.INT32, classGenerator.standardClasses.classClass().getType());
}
return functionType;
}
private WasmExpression getClassField(WasmExpression instance, int fieldIndex) {
return new WasmStructGet(
classGenerator.standardClasses.classClass().getStructure(),
instance,
fieldIndex
);
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.generate.gc.classes;
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.ValueType;
public class WasmGCTypeMapper {
private WasmGCClassInfoProvider classInfoProvider;
WasmGCTypeMapper(WasmGCClassInfoProvider classInfoProvider) {
this.classInfoProvider = classInfoProvider;
}
public WasmStorageType mapType(ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BYTE:
case BOOLEAN:
return WasmStorageType.packed(WasmPackedType.INT8);
case SHORT:
case CHARACTER:
return WasmStorageType.packed(WasmPackedType.INT8);
case INTEGER:
return WasmType.INT32.asStorage();
case LONG:
return WasmType.INT64.asStorage();
case FLOAT:
return WasmType.FLOAT32.asStorage();
case DOUBLE:
return WasmType.FLOAT64.asStorage();
default:
throw new IllegalArgumentException();
}
} else {
return classInfoProvider.getClassInfo(type).getType().asStorage();
}
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.generate.gc.initialization;
import org.teavm.backend.wasm.model.WasmFunction;
public interface WasmGCInitializerContributor {
void contributeToInitializerDefinitions(WasmFunction function);
void contributeToInitializer(WasmFunction function);
}

View File

@ -0,0 +1,25 @@
/*
* 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.generate.gc.method;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.model.MethodReference;
public interface WasmGCFunctionProvider {
WasmFunction getMemberFunction(MethodReference methodRef);
WasmFunction getStaticFunction(MethodReference methodRef);
}

View File

@ -0,0 +1,28 @@
/*
* 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.generate.gc.strings;
import org.teavm.backend.wasm.model.WasmGlobal;
public class WasmGCStringConstant {
public final int index;
public final WasmGlobal global;
public WasmGCStringConstant(int index, WasmGlobal global) {
this.index = index;
this.global = global;
}
}

View File

@ -0,0 +1,120 @@
/*
* 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.generate.gc.strings;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.method.WasmGCFunctionProvider;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.model.MethodReference;
public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializerContributor {
private WasmGCStandardClasses standardClasses;
private WasmModule module;
private WasmBinaryWriter binaryWriter = new WasmBinaryWriter();
private Map<String, WasmGCStringConstant> stringMap = new LinkedHashMap<>();
private WasmGCFunctionProvider functionProvider;
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
WasmGCFunctionProvider functionProvider) {
this.standardClasses = standardClasses;
this.module = module;
this.functionProvider = functionProvider;
}
@Override
public void contributeToInitializerDefinitions(WasmFunction function) {
for (var str : stringMap.values()) {
var newStruct = new WasmStructNewDefault(standardClasses.stringClass().getStructure());
function.getBody().add(new WasmSetGlobal(str.global, newStruct));
}
}
@Override
public void contributeToInitializer(WasmFunction function) {
var nextCharArrayFunction = functionProvider.getStaticFunction(new MethodReference(WasmGCStringPool.class,
"nextCharArray", char[].class));
}
@Override
public WasmGCStringConstant getStringConstant(String string) {
return stringMap.computeIfAbsent(string, s -> {
binaryWriter.writeInt32(string.length());
binaryWriter.writeBytes(string.getBytes(StandardCharsets.UTF_8));
var globalName = "_teavm_java_string_" + stringMap.size();
var globalType = standardClasses.stringClass().getType();
var global = new WasmGlobal(globalName, globalType, WasmExpression.defaultValueOfType(globalType));
module.globals.add(global);
return new WasmGCStringConstant(stringMap.size(), global);
});
}
static char[] nextCharArray() {
var length = nextLEB();
var result = new char[length];
var pos = 0;
while (pos < length) {
var b = nextByte();
if ((b & 0x80) == 0) {
result[pos++] = (char) b;
} else if ((b & 0xE0) == 0xC0) {
var b2 = nextByte();
result[pos++] = (char) (((b & 0x1F) << 6) | (b2 & 0x3F));
} else if ((b & 0xF0) == 0xE0) {
var b2 = nextByte();
var b3 = nextByte();
var c = (char) (((b & 0x0F) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3F));
result[pos++] = c;
} else if ((b & 0xF8) == 0xF0) {
var b2 = nextByte();
var b3 = nextByte();
var b4 = nextByte();
var code = ((b & 0x07) << 18) | ((b2 & 0x3f) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F);
result[pos++] = Character.highSurrogate(code);
result[pos++] = Character.lowSurrogate(code);
}
}
return result;
}
private static int nextLEB() {
var shift = 0;
var result = 0;
while (true) {
var b = nextByte();
var digit = b & 0x7F;
result |= digit << shift;
if ((b & 0x80) == 0) {
break;
}
shift += 7;
}
return result;
}
private static native byte nextByte();
private static native void error();
}

View File

@ -0,0 +1,20 @@
/*
* 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.generate.gc.strings;
public interface WasmGCStringProvider {
WasmGCStringConstant getStringConstant(String string);
}

View File

@ -17,7 +17,7 @@ package org.teavm.backend.wasm.model;
public abstract class WasmCompositeType extends WasmEntity { public abstract class WasmCompositeType extends WasmEntity {
private String name; private String name;
private WasmType.Reference reference; private WasmType.CompositeReference reference;
WasmCompositeType(String name) { WasmCompositeType(String name) {
this.name = name; this.name = name;
@ -27,9 +27,9 @@ public abstract class WasmCompositeType extends WasmEntity {
return name; return name;
} }
public WasmType.Reference getReference() { public WasmType.CompositeReference getReference() {
if (reference == null) { if (reference == null) {
reference = new WasmType.Reference(this); reference = new WasmType.CompositeReference(this);
} }
return reference; return reference;
} }

View File

@ -16,27 +16,15 @@
package org.teavm.backend.wasm.model; package org.teavm.backend.wasm.model;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
public class WasmStructure extends WasmCompositeType { public class WasmStructure extends WasmCompositeType {
private List<? extends WasmStorageType> fields; private List<WasmStorageType> fields;
private Supplier<List<? extends WasmStorageType>> fieldsSupplier;
public WasmStructure(String name, List<? extends WasmStorageType> fields) { public WasmStructure(String name) {
super(name); super(name);
this.fields = List.copyOf(fields);
} }
public WasmStructure(String name, Supplier<List<? extends WasmStorageType>> fieldsSupplier) { public List<WasmStorageType> getFields() {
super(name);
this.fieldsSupplier = fieldsSupplier;
}
public List<? extends WasmStorageType> getFields() {
if (fields == null) {
fields = List.copyOf(fieldsSupplier.get());
fieldsSupplier = null;
}
return fields; return fields;
} }

View File

@ -56,11 +56,44 @@ public abstract class WasmType {
} }
} }
public static final class Reference extends WasmType { public static abstract class Reference extends WasmType {
public static final SpecialReference FUNC = SpecialReferenceKind.FUNC.asType();
public static final SpecialReference ANY = SpecialReferenceKind.ANY.asType();
public static final SpecialReference EXTERN = SpecialReferenceKind.EXTERN.asType();
public static final SpecialReference STRUCT = SpecialReferenceKind.STRUCT.asType();
public static final SpecialReference ARRAY = SpecialReferenceKind.ARRAY.asType();
}
public static final class CompositeReference extends Reference {
public final WasmCompositeType composite; public final WasmCompositeType composite;
Reference(WasmCompositeType composite) { CompositeReference(WasmCompositeType composite) {
this.composite = composite; this.composite = composite;
} }
} }
public static final class SpecialReference extends WasmType {
public final SpecialReferenceKind kind;
private SpecialReference(SpecialReferenceKind kind) {
this.kind = kind;
}
}
public enum SpecialReferenceKind {
FUNC,
ANY,
EXTERN,
STRUCT,
ARRAY;
private SpecialReference type;
final SpecialReference asType() {
if (type == null) {
type = new SpecialReference(this);
}
return type;
}
}
} }

View File

@ -0,0 +1,57 @@
/*
* 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.expression;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.teavm.backend.wasm.model.WasmFunctionType;
public class WasmCallReference extends WasmExpression {
private WasmFunctionType type;
private WasmExpression functionReference;
private List<WasmExpression> arguments = new ArrayList<>();
public WasmCallReference(WasmExpression functionReference, WasmFunctionType type) {
this.functionReference = Objects.requireNonNull(functionReference);
this.type = Objects.requireNonNull(type);
}
public WasmExpression getFunctionReference() {
return functionReference;
}
public void setFunctionReference(WasmExpression functionReference) {
this.functionReference = Objects.requireNonNull(functionReference);
}
public List<WasmExpression> getArguments() {
return arguments;
}
public WasmFunctionType getType() {
return type;
}
public void setType(WasmFunctionType type) {
this.type = Objects.requireNonNull(type);
}
@Override
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -145,6 +145,14 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
} }
} }
@Override
public void visit(WasmCallReference expression) {
expression.getFunctionReference().acceptVisitor(this);
for (var argument : expression.getArguments()) {
argument.acceptVisitor(this);
}
}
@Override @Override
public void visit(WasmDrop expression) { public void visit(WasmDrop expression) {
expression.getOperand().acceptVisitor(this); expression.getOperand().acceptVisitor(this);
@ -250,6 +258,10 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
} }
} }
@Override
public void visit(WasmStructNewDefault expression) {
}
@Override @Override
public void visit(WasmStructGet expression) { public void visit(WasmStructGet expression) {
expression.getInstance().acceptVisitor(this); expression.getInstance().acceptVisitor(this);
@ -283,4 +295,8 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
public void visit(WasmArrayLength expression) { public void visit(WasmArrayLength expression) {
expression.getInstance().acceptVisitor(this); expression.getInstance().acceptVisitor(this);
} }
@Override
public void visit(WasmFunctionReference expression) {
}
} }

View File

@ -56,7 +56,7 @@ public abstract class WasmExpression {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
} else if (type instanceof WasmType.Reference) { } else if (type instanceof WasmType.Reference) {
return new WasmNullConstant(((WasmType.Reference) type).composite); return new WasmNullConstant((WasmType.Reference) type);
} else { } else {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }

View File

@ -62,6 +62,8 @@ public interface WasmExpressionVisitor {
void visit(WasmIndirectCall expression); void visit(WasmIndirectCall expression);
void visit(WasmCallReference expression);
void visit(WasmDrop expression); void visit(WasmDrop expression);
void visit(WasmLoadInt32 expression); void visit(WasmLoadInt32 expression);
@ -96,6 +98,8 @@ public interface WasmExpressionVisitor {
void visit(WasmStructNew expression); void visit(WasmStructNew expression);
void visit(WasmStructNewDefault expression);
void visit(WasmStructGet expression); void visit(WasmStructGet expression);
void visit(WasmStructSet expression); void visit(WasmStructSet expression);
@ -107,4 +111,6 @@ public interface WasmExpressionVisitor {
void visit(WasmArraySet expression); void visit(WasmArraySet expression);
void visit(WasmArrayLength expression); void visit(WasmArrayLength expression);
void visit(WasmFunctionReference expression);
} }

View File

@ -0,0 +1,40 @@
/*
* 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.expression;
import java.util.Objects;
import org.teavm.backend.wasm.model.WasmFunction;
public class WasmFunctionReference extends WasmExpression {
private WasmFunction function;
public WasmFunctionReference(WasmFunction function) {
this.function = Objects.requireNonNull(function);
}
public WasmFunction getFunction() {
return function;
}
public void setFunction(WasmFunction function) {
this.function = Objects.requireNonNull(function);
}
@Override
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -15,20 +15,20 @@
*/ */
package org.teavm.backend.wasm.model.expression; package org.teavm.backend.wasm.model.expression;
import org.teavm.backend.wasm.model.WasmCompositeType; import org.teavm.backend.wasm.model.WasmType;
public class WasmNullConstant extends WasmExpression { public class WasmNullConstant extends WasmExpression {
public WasmCompositeType type; public WasmType.Reference type;
public WasmNullConstant(WasmCompositeType type) { public WasmNullConstant(WasmType.Reference type) {
this.type = type; this.type = type;
} }
public WasmCompositeType getType() { public WasmType.Reference getType() {
return type; return type;
} }
public void setType(WasmCompositeType type) { public void setType(WasmType.Reference type) {
this.type = type; this.type = type;
} }

View File

@ -173,6 +173,13 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
replaceExpressions(expression.getArguments()); replaceExpressions(expression.getArguments());
} }
@Override
public void visit(WasmCallReference expression) {
expression.getFunctionReference().acceptVisitor(this);
expression.setFunctionReference(mapper.apply(expression.getFunctionReference()));
replaceExpressions(expression.getArguments());
}
@Override @Override
public void visit(WasmDrop expression) { public void visit(WasmDrop expression) {
expression.getOperand().acceptVisitor(this); expression.getOperand().acceptVisitor(this);
@ -295,6 +302,11 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmStructNew expression) { public void visit(WasmStructNew expression) {
replaceExpressions(expression.getInitializers());
}
@Override
public void visit(WasmStructNewDefault expression) {
} }
@Override @Override
@ -344,4 +356,8 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
expression.getInstance().acceptVisitor(this); expression.getInstance().acceptVisitor(this);
expression.setInstance(mapper.apply(expression.getInstance())); expression.setInstance(mapper.apply(expression.getInstance()));
} }
@Override
public void visit(WasmFunctionReference expression) {
}
} }

View File

@ -0,0 +1,43 @@
/*
* 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.expression;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.teavm.backend.wasm.model.WasmStructure;
public class WasmStructNewDefault extends WasmExpression {
private WasmStructure type;
private List<WasmExpression> initializers = new ArrayList<>();
public WasmStructNewDefault(WasmStructure type) {
this.type = Objects.requireNonNull(type);
}
public WasmStructure getType() {
return type;
}
public void setType(WasmStructure type) {
this.type = Objects.requireNonNull(type);
}
@Override
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -25,6 +25,7 @@ import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor; import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor; import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
public class UnusedFunctionElimination { public class UnusedFunctionElimination {
private WasmModule module; private WasmModule module;
@ -64,10 +65,13 @@ public class UnusedFunctionElimination {
@Override @Override
public void visit(WasmCall expression) { public void visit(WasmCall expression) {
super.visit(expression); super.visit(expression);
var function = expression.getFunction(); use(expression.getFunction());
if (function != null) {
use(function);
} }
@Override
public void visit(WasmFunctionReference expression) {
super.visit(expression);
use(expression.getFunction());
} }
}; };
} }

View File

@ -27,9 +27,19 @@ import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmStorageType; import org.teavm.backend.wasm.model.WasmStorageType;
import org.teavm.backend.wasm.model.WasmStructure; import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayGet;
import org.teavm.backend.wasm.model.expression.WasmArraySet;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor; import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor;
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor; import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall; import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
public class UnusedTypeElimination { public class UnusedTypeElimination {
private WasmModule module; private WasmModule module;
@ -51,15 +61,27 @@ public class UnusedTypeElimination {
for (var tag : module.tags) { for (var tag : module.tags) {
use(tag.getType()); use(tag.getType());
} }
for (var global : module.globals) {
use(global.getType());
}
} }
private void useFrom(WasmFunction function) { private void useFrom(WasmFunction function) {
use(function.getType()); use(function.getType());
for (var local : function.getLocalVariables()) {
use(local.getType());
}
for (var part : function.getBody()) { for (var part : function.getBody()) {
part.acceptVisitor(exprVisitor); part.acceptVisitor(exprVisitor);
} }
} }
private void use(WasmType type) {
if (type instanceof WasmType.CompositeReference) {
use(((WasmType.CompositeReference) type).composite);
}
}
private void use(WasmCompositeType type) { private void use(WasmCompositeType type) {
if (!usedTypes.add(type)) { if (!usedTypes.add(type)) {
return; return;
@ -73,6 +95,67 @@ public class UnusedTypeElimination {
super.visit(expression); super.visit(expression);
use(expression.getType()); use(expression.getType());
} }
@Override
public void visit(WasmCast expression) {
super.visit(expression);
use(expression.getTargetType());
}
@Override
public void visit(WasmArrayGet expression) {
super.visit(expression);
use(expression.getType());
}
@Override
public void visit(WasmArraySet expression) {
super.visit(expression);
use(expression.getType());
}
@Override
public void visit(WasmStructNew expression) {
super.visit(expression);
use(expression.getType());
}
@Override
public void visit(WasmStructNewDefault expression) {
super.visit(expression);
use(expression.getType());
}
@Override
public void visit(WasmStructGet expression) {
super.visit(expression);
use(expression.getType());
}
@Override
public void visit(WasmStructSet expression) {
super.visit(expression);
use(expression.getType());
}
@Override
public void visit(WasmNullConstant expression) {
super.visit(expression);
use(expression.getType());
}
@Override
public void visit(WasmFunctionReference expression) {
super.visit(expression);
useFrom(expression.getFunction());
}
@Override
public void visit(WasmCallReference expression) {
super.visit(expression);
use(expression.getType());
}
}; };
private WasmCompositeTypeVisitor typeVisitor = new WasmDefaultCompositeTypeVisitor() { private WasmCompositeTypeVisitor typeVisitor = new WasmDefaultCompositeTypeVisitor() {
@ -105,8 +188,8 @@ public class UnusedTypeElimination {
} }
private void visit(WasmType type) { private void visit(WasmType type) {
if (type instanceof WasmType.Reference) { if (type instanceof WasmType.CompositeReference) {
use(((WasmType.Reference) type).composite); use(((WasmType.CompositeReference) type).composite);
} }
} }
}; };

View File

@ -33,6 +33,7 @@ import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBranch;
import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmBreak;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
@ -45,6 +46,7 @@ import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
import org.teavm.backend.wasm.model.expression.WasmFloatBinary; import org.teavm.backend.wasm.model.expression.WasmFloatBinary;
import org.teavm.backend.wasm.model.expression.WasmFloatUnary; import org.teavm.backend.wasm.model.expression.WasmFloatUnary;
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall; import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
@ -68,6 +70,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
import org.teavm.backend.wasm.model.expression.WasmStructGet; import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructNew; import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmThrow; import org.teavm.backend.wasm.model.expression.WasmThrow;
@ -250,7 +253,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
public void visit(WasmNullConstant expression) { public void visit(WasmNullConstant expression) {
pushLocation(expression); pushLocation(expression);
writer.writeByte(0xD0); writer.writeByte(0xD0);
writer.writeSignedLEB(module.types.indexOf(expression.getType())); writeBlockType(expression.getType());
popLocation(); popLocation();
} }
@ -776,6 +779,18 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
popLocation(); popLocation();
} }
@Override
public void visit(WasmCallReference expression) {
pushLocation(expression);
for (var argument : expression.getArguments()) {
argument.acceptVisitor(this);
}
expression.getFunctionReference().acceptVisitor(this);
writer.writeByte(0x14);
writer.writeLEB(module.types.indexOf(expression.getType()));
popLocation();
}
@Override @Override
public void visit(WasmDrop expression) { public void visit(WasmDrop expression) {
pushLocation(expression); pushLocation(expression);
@ -1031,6 +1046,15 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
popLocation(); popLocation();
} }
@Override
public void visit(WasmStructNewDefault expression) {
pushLocation(expression);
writer.writeByte(0xfb);
writer.writeByte(1);
writer.writeInt32(module.types.indexOf(expression.getType()));
popLocation();
}
@Override @Override
public void visit(WasmStructGet expression) { public void visit(WasmStructGet expression) {
pushLocation(expression); pushLocation(expression);
@ -1118,6 +1142,14 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
popLocation(); popLocation();
} }
@Override
public void visit(WasmFunctionReference expression) {
pushLocation(expression);
writer.writeByte(0xd2);
writer.writeInt32(module.functions.indexOf(expression.getFunction()));
popLocation();
}
private int alignment(int value) { private int alignment(int value) {
return 31 - Integer.numberOfLeadingZeros(Math.max(1, value)); return 31 - Integer.numberOfLeadingZeros(Math.max(1, value));
} }

View File

@ -36,8 +36,26 @@ public class WasmBinaryWriter {
} }
if (type instanceof WasmType.Number) { if (type instanceof WasmType.Number) {
writeType(((WasmType.Number) type).number); writeType(((WasmType.Number) type).number);
} else if (type instanceof WasmType.Reference) { } else if (type instanceof WasmType.SpecialReference) {
writeSignedLEB(module.types.indexOf(((WasmType.Reference) type).composite)); switch (((WasmType.SpecialReference) type).kind) {
case ANY:
writeByte(0x6e);
break;
case EXTERN:
writeByte(0x6f);
break;
case FUNC:
writeByte(0x70);
break;
case STRUCT:
writeByte(0x6b);
break;
case ARRAY:
writeByte(0x6a);
break;
}
} else if (type instanceof WasmType.CompositeReference) {
writeSignedLEB(module.types.indexOf(((WasmType.CompositeReference) type).composite));
} }
} }

View File

@ -35,6 +35,7 @@ import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBranch;
import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmBreak;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
@ -48,6 +49,7 @@ import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
import org.teavm.backend.wasm.model.expression.WasmFloatBinary; import org.teavm.backend.wasm.model.expression.WasmFloatBinary;
import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatType;
import org.teavm.backend.wasm.model.expression.WasmFloatUnary; import org.teavm.backend.wasm.model.expression.WasmFloatUnary;
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall; import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
@ -72,6 +74,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
import org.teavm.backend.wasm.model.expression.WasmStructGet; import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructNew; import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmThrow; import org.teavm.backend.wasm.model.expression.WasmThrow;
@ -783,6 +786,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
value = result; value = result;
} }
@Override
public void visit(WasmCallReference expression) {
unsupported();
}
private void translateArguments(List<? extends WasmExpression> wasmArguments, List<? extends WasmType> signature, private void translateArguments(List<? extends WasmExpression> wasmArguments, List<? extends WasmType> signature,
CExpression result, StringBuilder sb) { CExpression result, StringBuilder sb) {
if (wasmArguments.isEmpty()) { if (wasmArguments.isEmpty()) {
@ -1166,6 +1174,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
unsupported(); unsupported();
} }
@Override
public void visit(WasmStructNewDefault expression) {
unsupported();
}
@Override @Override
public void visit(WasmStructGet expression) { public void visit(WasmStructGet expression) {
unsupported(); unsupported();
@ -1196,6 +1209,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
unsupported(); unsupported();
} }
@Override
public void visit(WasmFunctionReference expression) {
unsupported();
}
private void unsupported() { private void unsupported() {
value = new CExpression("/* unsupported */"); value = new CExpression("/* unsupported */");
} }

View File

@ -31,6 +31,7 @@ import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBranch;
import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmBreak;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
@ -47,6 +48,7 @@ import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatType;
import org.teavm.backend.wasm.model.expression.WasmFloatUnary; import org.teavm.backend.wasm.model.expression.WasmFloatUnary;
import org.teavm.backend.wasm.model.expression.WasmFloatUnaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatUnaryOperation;
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall; import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
@ -73,6 +75,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
import org.teavm.backend.wasm.model.expression.WasmStructGet; import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructNew; import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmThrow; import org.teavm.backend.wasm.model.expression.WasmThrow;
@ -288,7 +291,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmNullConstant expression) { public void visit(WasmNullConstant expression) {
open().append("ref.null " + module.types.indexOf(expression.getType())).close(); open().append("ref.null " + type(expression.getType())).close();
} }
@Override @Override
@ -446,6 +449,16 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
} }
@Override
public void visit(WasmCallReference expression) {
open().append("call_ref ").append(type(expression.getType().getReference()));
line(expression.getFunctionReference());
for (var argument : expression.getArguments()) {
line(argument);
}
close();
}
@Override @Override
public void visit(WasmDrop expression) { public void visit(WasmDrop expression) {
open().append("drop").lf(); open().append("drop").lf();
@ -696,6 +709,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
close(); close();
} }
@Override
public void visit(WasmStructNewDefault expression) {
open().append("struct.new_default ").append(expression.getType().getReference()).close();
}
@Override @Override
public void visit(WasmStructGet expression) { public void visit(WasmStructGet expression) {
open(); open();
@ -773,11 +791,32 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
close(); close();
} }
@Override
public void visit(WasmFunctionReference expression) {
open().append("ref.func ").append(" $" + module.functions.indexOf(expression.getFunction()));
close();
}
private String type(WasmType type) { private String type(WasmType type) {
if (type instanceof WasmType.Number) { if (type instanceof WasmType.Number) {
return type(((WasmType.Number) type).number); return type(((WasmType.Number) type).number);
} else if (type instanceof WasmType.Reference) { } else if (type instanceof WasmType.SpecialReference) {
return "(ref " + typeName(((WasmType.Reference) type).composite) + ")"; switch (((WasmType.SpecialReference) type).kind) {
case ANY:
return "anyref";
case EXTERN:
return "externref";
case STRUCT:
return "structref";
case FUNC:
return "funcref";
case ARRAY:
return "arrayref";
default:
throw new IllegalArgumentException();
}
} else if (type instanceof WasmType.CompositeReference) {
return "(ref " + typeName(((WasmType.CompositeReference) type).composite) + ")";
} else { } else {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }

View File

@ -24,6 +24,7 @@ import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBranch;
import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmBreak;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
@ -36,6 +37,7 @@ import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
import org.teavm.backend.wasm.model.expression.WasmFloatBinary; import org.teavm.backend.wasm.model.expression.WasmFloatBinary;
import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmFloatType;
import org.teavm.backend.wasm.model.expression.WasmFloatUnary; import org.teavm.backend.wasm.model.expression.WasmFloatUnary;
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall; import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
@ -60,6 +62,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
import org.teavm.backend.wasm.model.expression.WasmStructGet; import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructNew; import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmThrow; import org.teavm.backend.wasm.model.expression.WasmThrow;
@ -175,7 +178,7 @@ public class WasmTypeInference implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmNullConstant expression) { public void visit(WasmNullConstant expression) {
result = expression.type.getReference(); result = expression.type;
} }
@Override @Override
@ -189,6 +192,11 @@ public class WasmTypeInference implements WasmExpressionVisitor {
result = expression.getType().getReturnType(); result = expression.getType().getReturnType();
} }
@Override
public void visit(WasmCallReference expression) {
result = expression.getType().getReturnType();
}
@Override @Override
public void visit(WasmDrop expression) { public void visit(WasmDrop expression) {
result = null; result = null;
@ -274,6 +282,11 @@ public class WasmTypeInference implements WasmExpressionVisitor {
result = expression.getType().getReference(); result = expression.getType().getReference();
} }
@Override
public void visit(WasmStructNewDefault expression) {
result = expression.getType().getReference();
}
@Override @Override
public void visit(WasmStructGet expression) { public void visit(WasmStructGet expression) {
result = expression.getType().getFields().get(expression.getFieldIndex()).asUnpackedType(); result = expression.getType().getFields().get(expression.getFieldIndex()).asUnpackedType();
@ -304,6 +317,11 @@ public class WasmTypeInference implements WasmExpressionVisitor {
result = WasmType.INT32; result = WasmType.INT32;
} }
@Override
public void visit(WasmFunctionReference expression) {
result = expression.getFunction().getType().getReference();
}
private static WasmType map(WasmIntType type) { private static WasmType map(WasmIntType type) {
switch (type) { switch (type) {
case INT32: case INT32:

View File

@ -0,0 +1,46 @@
/*
* 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.model.util;
import org.teavm.model.PrimitiveType;
public final class ReflectionUtil {
private ReflectionUtil() {
}
public static String typeName(PrimitiveType type) {
switch (type) {
case BOOLEAN:
return "boolean";
case BYTE:
return "byte";
case SHORT:
return "short";
case CHARACTER:
return "char";
case INTEGER:
return "int";
case LONG:
return "long";
case FLOAT:
return "float";
case DOUBLE:
return "double";
default:
throw new IllegalArgumentException();
}
}
}