wasm gc: fix issues with virtual calls

This commit is contained in:
Alexey Andreev 2024-08-14 17:20:19 +02:00
parent 2805631025
commit 40fbce0ddd
16 changed files with 245 additions and 60 deletions

View File

@ -74,6 +74,7 @@ public class WasmGCDependencies {
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class)) analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class))
.use(); .use();
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class)).use(); analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class)).use();
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cnse", CloneNotSupportedException.class)).use();
} }
private void contributeInitializerUtils() { private void contributeInitializerUtils() {

View File

@ -56,6 +56,8 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Subtype;
import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary;
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat32; import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
@ -302,6 +304,11 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
return new WasmInt32Constant(0); return new WasmInt32Constant(0);
} }
@Override
protected WasmExpression genIsNull(WasmExpression value) {
return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value);
}
@Override @Override
public void visit(SubscriptExpr expr) { public void visit(SubscriptExpr expr) {
WasmExpression ptr = getArrayElementPointer(expr); WasmExpression ptr = getArrayElementPointer(expr);

View File

@ -92,8 +92,6 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType; 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.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmSwitch;
@ -902,9 +900,16 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var block = new WasmBlock(false); var block = new WasmBlock(false);
block.setType(mapType(reference.getReturnType())); block.setType(mapType(reference.getReturnType()));
var instanceVar = tempVars.acquire(instanceWasmType); WasmLocal instanceVar;
block.getBody().add(new WasmSetLocal(instanceVar, instance)); var isTemporary = false;
instance = new WasmGetLocal(instanceVar); if (instance instanceof WasmGetLocal) {
instanceVar = ((WasmGetLocal) instance).getLocal();
} else {
instanceVar = tempVars.acquire(instanceWasmType);
block.getBody().add(new WasmSetLocal(instanceVar, instance));
instance = new WasmGetLocal(instanceVar);
isTemporary = true;
}
var arguments = new ArrayList<WasmExpression>(); var arguments = new ArrayList<WasmExpression>();
arguments.add(instance); arguments.add(instance);
@ -918,7 +923,9 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var call = generateVirtualCall(instanceVar, reference, arguments); var call = generateVirtualCall(instanceVar, reference, arguments);
block.getBody().add(call); block.getBody().add(call);
tempVars.release(instanceVar); if (isTemporary) {
tempVars.release(instanceVar);
}
return block; return block;
} }
} }
@ -1133,7 +1140,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
result.acceptVisitor(typeInference); result.acceptVisitor(typeInference);
var cachedObject = exprCache.create(result, typeInference.getResult(), expr.getLocation(), block.getBody()); var cachedObject = exprCache.create(result, typeInference.getResult(), expr.getLocation(), block.getBody());
var ifNull = new WasmBranch(genIsZero(cachedObject.expr()), block); var ifNull = new WasmBranch(genIsNull(cachedObject.expr()), block);
ifNull.setResult(new WasmInt32Constant(0)); ifNull.setResult(new WasmInt32Constant(0));
block.getBody().add(new WasmDrop(ifNull)); block.getBody().add(new WasmDrop(ifNull));
@ -1171,7 +1178,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var wasmSourceType = typeInference.getResult(); var wasmSourceType = typeInference.getResult();
var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody()); var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody());
var nullCheck = new WasmBranch(genIsZero(valueToCast.expr()), block); var nullCheck = new WasmBranch(genIsNull(valueToCast.expr()), block);
nullCheck.setResult(valueToCast.expr()); nullCheck.setResult(valueToCast.expr());
block.getBody().add(new WasmDrop(nullCheck)); block.getBody().add(new WasmDrop(nullCheck));
@ -1544,9 +1551,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
} }
} }
private WasmExpression genIsZero(WasmExpression value) { protected abstract WasmExpression genIsNull(WasmExpression value);
return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value);
}
protected abstract WasmType mapType(ValueType type); protected abstract WasmType mapType(ValueType type);

View File

@ -52,6 +52,7 @@ import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType; import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.model.expression.WasmStructSet;
@ -122,7 +123,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
standardClasses = new WasmGCStandardClasses(this); standardClasses = new WasmGCStandardClasses(this);
strings = new WasmGCStringPool(standardClasses, module, functionProvider); strings = new WasmGCStringPool(standardClasses, module, functionProvider);
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes); supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes);
typeMapper = new WasmGCTypeMapper(this); typeMapper = new WasmGCTypeMapper(this, functionTypes, module);
} }
public WasmGCSupertypeFunctionProvider getSupertypeProvider() { public WasmGCSupertypeFunctionProvider getSupertypeProvider() {
@ -143,6 +144,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
@Override @Override
public void contributeToInitializerDefinitions(WasmFunction function) { public void contributeToInitializerDefinitions(WasmFunction function) {
fillVirtualTableSupertypes();
for (var classInfo : classInfoMap.values()) { for (var classInfo : classInfoMap.values()) {
var classInstanceType = classInfo.virtualTableStructure != null var classInstanceType = classInfo.virtualTableStructure != null
? classInfo.virtualTableStructure ? classInfo.virtualTableStructure
@ -152,6 +154,35 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
} }
} }
private void fillVirtualTableSupertypes() {
for (var classInfo : classInfoMap.values()) {
if (classInfo.virtualTableStructure != null && classInfo.getValueType() instanceof ValueType.Object
&& classInfo.hasOwnVirtualTable) {
var className = ((ValueType.Object) classInfo.getValueType()).getClassName();
classInfo.virtualTableStructure.setSupertype(findVirtualTableSupertype(className));
}
}
}
private WasmStructure findVirtualTableSupertype(String className) {
while (className != null) {
var cls = classSource.get(className);
if (cls == null) {
break;
}
className = cls.getParent();
if (className == null) {
break;
}
var parentInfo = classInfoMap.get(ValueType.object(className));
if (parentInfo != null && parentInfo.virtualTableStructure != null) {
return parentInfo.virtualTableStructure;
}
}
var classClass = classInfoMap.get(ValueType.object("java.lang.Class"));
return classClass != null ? classClass.structure : null;
}
@Override @Override
public void contributeToInitializer(WasmFunction function) { public void contributeToInitializer(WasmFunction function) {
var classClass = standardClasses.classClass(); var classClass = standardClasses.classClass();
@ -209,7 +240,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
fillFields(classInfo, type); fillFields(classInfo, type);
} }
var pointerName = names.forClassInstance(type); var pointerName = names.forClassInstance(type);
var classStructure = virtualTable != null && virtualTable.hasValidEntries() classInfo.hasOwnVirtualTable = virtualTable != null && virtualTable.hasValidEntries();
var classStructure = classInfo.hasOwnVirtualTable
? initRegularClassStructure(((ValueType.Object) type).getClassName()) ? initRegularClassStructure(((ValueType.Object) type).getClassName())
: standardClasses.classClass().getStructure(); : standardClasses.classClass().getStructure();
classInfo.virtualTableStructure = classStructure; classInfo.virtualTableStructure = classStructure;
@ -330,21 +362,30 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
} }
} }
if (virtualTable != null && virtualTable.hasValidEntries()) { if (virtualTable != null && virtualTable.hasValidEntries()) {
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTable,
virtualTableFieldOffset, name, new HashSet<>()); new HashSet<>());
} }
}; };
} }
private int fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global, private void fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
VirtualTable virtualTable, int index, String origin, Set<MethodDescriptor> filled) { VirtualTable virtualTable, VirtualTable original, Set<MethodDescriptor> filled) {
if (virtualTable.getParent() != null) {
fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), original, filled);
}
for (var method : virtualTable.getMethods()) { for (var method : virtualTable.getMethods()) {
var entry = virtualTable.getEntry(method); var entry = original.getEntry(method);
if (entry != null && entry.getImplementor() != null && filled.add(method) if (entry != null && entry.getImplementor() != null && filled.add(method)
&& !method.equals(GET_CLASS_METHOD)) { && !method.equals(GET_CLASS_METHOD)) {
var fieldIndex = virtualTableFieldOffset + entry.getIndex();
var expectedType = (WasmType.CompositeReference) structure.getFields().get(fieldIndex)
.asUnpackedType();
var expectedFunctionType = (WasmFunctionType) expectedType.composite;
var function = functionProvider.forInstanceMethod(entry.getImplementor()); var function = functionProvider.forInstanceMethod(entry.getImplementor());
if (!origin.equals(entry.getImplementor().getClassName())) { if (!virtualTable.getClassName().equals(entry.getImplementor().getClassName())
var functionType = getFunctionType(virtualTable.getClassName(), method); || expectedFunctionType != function.getType()) {
var functionType = typeMapper.getFunctionType(virtualTable.getClassName(), method, true);
functionType.getSupertypes().add(expectedFunctionType);
var wrapperFunction = new WasmFunction(functionType); var wrapperFunction = new WasmFunction(functionType);
module.functions.add(wrapperFunction); module.functions.add(wrapperFunction);
var call = new WasmCall(function); var call = new WasmCall(function);
@ -358,17 +399,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
call.getArguments().add(new WasmGetLocal(params[i])); call.getArguments().add(new WasmGetLocal(params[i]));
wrapperFunction.add(params[i]); wrapperFunction.add(params[i]);
} }
wrapperFunction.getBody().add(new WasmReturn(call));
function = wrapperFunction;
} }
function.setReferenced(true); function.setReferenced(true);
var ref = new WasmFunctionReference(function); var ref = new WasmFunctionReference(function);
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index + entry.getIndex(), ref)); target.add(new WasmStructSet(structure, new WasmGetGlobal(global), fieldIndex, ref));
} }
} }
if (virtualTable.getParent() != null) {
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin,
filled);
}
return index;
} }
private WasmStructure initRegularClassStructure(String className) { private WasmStructure initRegularClassStructure(String className) {
@ -391,23 +429,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
if (methodDesc == null) { if (methodDesc == null) {
structure.getFields().add(WasmType.Reference.FUNC.asStorage()); structure.getFields().add(WasmType.Reference.FUNC.asStorage());
} else { } else {
var functionType = getFunctionType(virtualTable.getClassName(), methodDesc); var functionType = typeMapper.getFunctionType(virtualTable.getClassName(), methodDesc, false);
structure.getFields().add(functionType.getReference().asStorage()); structure.getFields().add(functionType.getReference().asStorage());
} }
} }
} }
private WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc) {
var returnType = typeMapper.mapType(methodDesc.getResultType());
var javaParamTypes = methodDesc.getParameterTypes();
var paramTypes = new WasmType[javaParamTypes.length + 1];
paramTypes[0] = getClassInfo(className).getType();
for (var i = 0; i < javaParamTypes.length; ++i) {
paramTypes[i + 1] = typeMapper.mapType(javaParamTypes[i]);
}
return functionTypes.of(returnType, paramTypes);
}
private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) { private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) {
classInfo.initializer = target -> { classInfo.initializer = target -> {
var itemTypeInfo = getClassInfo(type.getItemType()); var itemTypeInfo = getClassInfo(type.getItemType());
@ -497,6 +524,9 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
if (className.equals("java.lang.Object") && field.getName().equals("monitor")) { if (className.equals("java.lang.Object") && field.getName().equals("monitor")) {
continue; continue;
} }
if (className.equals("java.lang.Class") && field.getName().equals("platformClass")) {
continue;
}
if (field.hasModifier(ElementModifier.STATIC)) { if (field.hasModifier(ElementModifier.STATIC)) {
continue; continue;
} }

View File

@ -28,6 +28,7 @@ public class WasmGCClassInfo {
private ValueType valueType; private ValueType valueType;
WasmStructure structure; WasmStructure structure;
WasmArray array; WasmArray array;
boolean hasOwnVirtualTable;
WasmStructure virtualTableStructure; WasmStructure virtualTableStructure;
WasmGlobal pointer; WasmGlobal pointer;
WasmGlobal initializerPointer; WasmGlobal initializerPointer;

View File

@ -15,16 +15,26 @@
*/ */
package org.teavm.backend.wasm.generate.gc.classes; package org.teavm.backend.wasm.generate.gc.classes;
import java.util.List;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmPackedType; import org.teavm.backend.wasm.model.WasmPackedType;
import org.teavm.backend.wasm.model.WasmStorageType; import org.teavm.backend.wasm.model.WasmStorageType;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
public class WasmGCTypeMapper { public class WasmGCTypeMapper {
private WasmGCClassInfoProvider classInfoProvider; private WasmGCClassInfoProvider classInfoProvider;
private WasmFunctionTypes functionTypes;
private WasmModule module;
WasmGCTypeMapper(WasmGCClassInfoProvider classInfoProvider) { WasmGCTypeMapper(WasmGCClassInfoProvider classInfoProvider, WasmFunctionTypes functionTypes,
WasmModule module) {
this.classInfoProvider = classInfoProvider; this.classInfoProvider = classInfoProvider;
this.functionTypes = functionTypes;
this.module = module;
} }
public WasmStorageType mapStorageType(ValueType type) { public WasmStorageType mapStorageType(ValueType type) {
@ -76,4 +86,21 @@ public class WasmGCTypeMapper {
return classInfoProvider.getClassInfo(type).getType(); return classInfoProvider.getClassInfo(type).getType();
} }
} }
public WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc, boolean fresh) {
var returnType = mapType(methodDesc.getResultType());
var javaParamTypes = methodDesc.getParameterTypes();
var paramTypes = new WasmType[javaParamTypes.length + 1];
paramTypes[0] = classInfoProvider.getClassInfo(className).getType();
for (var i = 0; i < javaParamTypes.length; ++i) {
paramTypes[i + 1] = mapType(javaParamTypes[i]);
}
if (fresh) {
var type = new WasmFunctionType(null, returnType, List.of(paramTypes));
module.types.add(type);
return type;
} else {
return functionTypes.of(returnType, paramTypes);
}
}
} }

View File

@ -211,6 +211,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
: WasmType.Reference.STRUCT); : WasmType.Reference.STRUCT);
} }
@Override
protected WasmExpression genIsNull(WasmExpression value) {
return new WasmReferencesEqual(value, new WasmNullConstant(WasmType.Reference.STRUCT));
}
@Override @Override
protected CallSiteIdentifier generateCallSiteId(TextLocation location) { protected CallSiteIdentifier generateCallSiteId(TextLocation location) {
return new SimpleCallSite(); return new SimpleCallSite();
@ -268,12 +273,19 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
} }
var instanceStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()).getStructure(); var instanceStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()).getStructure();
var actualInstanceType = (WasmType.CompositeReference) instance.getType();
var actualInstanceStruct = (WasmStructure) actualInstanceType.composite;
var actualVtableType = (WasmType.CompositeReference) actualInstanceStruct.getFields().get(0).asUnpackedType();
var actualVtableStruct = (WasmStructure) actualVtableType.composite;
WasmExpression classRef = new WasmStructGet(instanceStruct, new WasmGetLocal(instance), WasmExpression classRef = new WasmStructGet(instanceStruct, new WasmGetLocal(instance),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
var index = context.classInfoProvider().getVirtualMethodsOffset() + vtableIndex; var index = context.classInfoProvider().getVirtualMethodsOffset() + vtableIndex;
var vtableStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()) var vtableStruct = context.classInfoProvider().getClassInfo(vtable.getClassName())
.getVirtualTableStructure(); .getVirtualTableStructure();
classRef = new WasmCast(classRef, vtableStruct.getReference()); if (!vtableStruct.isSupertypeOf(actualVtableStruct)) {
classRef = new WasmCast(classRef, vtableStruct.getReference());
}
var functionRef = new WasmStructGet(vtableStruct, classRef, index); var functionRef = new WasmStructGet(vtableStruct, classRef, index);
var functionTypeRef = (WasmType.CompositeReference) vtableStruct.getFields().get(index).asUnpackedType(); var functionTypeRef = (WasmType.CompositeReference) vtableStruct.getFields().get(index).asUnpackedType();
@ -432,7 +444,6 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
result = invocation(expr, null, false); result = invocation(expr, null, false);
} }
@Override @Override
protected WasmExpression invocation(InvocationExpr expr, List<WasmExpression> resultConsumer, boolean willDrop) { protected WasmExpression invocation(InvocationExpr expr, List<WasmExpression> resultConsumer, boolean willDrop) {
if (expr.getType() == InvocationType.SPECIAL || expr.getType() == InvocationType.STATIC) { if (expr.getType() == InvocationType.SPECIAL || expr.getType() == InvocationType.STATIC) {

View File

@ -72,6 +72,9 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
var value = new WasmCall(nextCharArrayFunction); var value = new WasmCall(nextCharArrayFunction);
function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global), function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global),
WasmGCClassInfoProvider.CUSTOM_FIELD_OFFSETS, value)); WasmGCClassInfoProvider.CUSTOM_FIELD_OFFSETS, value));
function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET,
new WasmGetGlobal(standardClasses.stringClass().getPointer())));
} }
} }

View File

@ -15,7 +15,9 @@
*/ */
package org.teavm.backend.wasm.model; package org.teavm.backend.wasm.model;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
public class WasmFunctionType extends WasmCompositeType { public class WasmFunctionType extends WasmCompositeType {
@ -23,6 +25,7 @@ public class WasmFunctionType extends WasmCompositeType {
private WasmType returnType; private WasmType returnType;
private Supplier<List<? extends WasmType>> parameterTypesSupplier; private Supplier<List<? extends WasmType>> parameterTypesSupplier;
private Supplier<WasmType> returnTypeSupplier; private Supplier<WasmType> returnTypeSupplier;
private Set<WasmFunctionType> supertypes = new LinkedHashSet<>();
public WasmFunctionType(String name, WasmType returnType, List<? extends WasmType> parameterTypes) { public WasmFunctionType(String name, WasmType returnType, List<? extends WasmType> parameterTypes) {
super(name); super(name);
@ -53,6 +56,10 @@ public class WasmFunctionType extends WasmCompositeType {
return returnType; return returnType;
} }
public Set<WasmFunctionType> getSupertypes() {
return supertypes;
}
@Override @Override
public void acceptVisitor(WasmCompositeTypeVisitor visitor) { public void acceptVisitor(WasmCompositeTypeVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

@ -121,6 +121,7 @@ public class WasmModule {
sorting.original = types; sorting.original = types;
sorting.graph = typeGraph; sorting.graph = typeGraph;
sorting.visited = new boolean[types.size()]; sorting.visited = new boolean[types.size()];
sorting.sccVisited = new boolean[types.size()];
sorting.sccMap = sccStartNode; sorting.sccMap = sccStartNode;
sorting.sccsByIndex = sccsByIndex; sorting.sccsByIndex = sccsByIndex;
for (var i = 0; i < types.size(); ++i) { for (var i = 0; i < types.size(); ++i) {
@ -137,6 +138,7 @@ public class WasmModule {
WasmCollection<WasmCompositeType> original; WasmCollection<WasmCompositeType> original;
Graph graph; Graph graph;
boolean[] visited; boolean[] visited;
boolean[] sccVisited;
int[] sccMap; int[] sccMap;
int[][] sccsByIndex; int[][] sccsByIndex;
List<WasmCompositeType> sorted = new ArrayList<>(); List<WasmCompositeType> sorted = new ArrayList<>();
@ -146,18 +148,46 @@ public class WasmModule {
if (visited[typeIndex]) { if (visited[typeIndex]) {
return; return;
} }
visited[typeIndex] = true;
for (var outgoing : graph.outgoingEdges(typeIndex)) {
visit(outgoing);
}
var scc = sccsByIndex[typeIndex]; var scc = sccsByIndex[typeIndex];
if (scc == null) { if (scc == null) {
visited[typeIndex] = true;
for (var outgoing : graph.outgoingEdges(typeIndex)) {
visit(outgoing);
}
sorted.add(original.get(typeIndex)); sorted.add(original.get(typeIndex));
} else { } else {
visited[typeIndex] = true;
for (var index : scc) { for (var index : scc) {
sorted.add(original.get(index)); for (var outgoing : graph.outgoingEdges(index)) {
visit(outgoing);
}
}
for (var index : scc) {
visitScc(index, typeIndex);
} }
} }
} }
void visitScc(int index, int sccBase) {
if (sccVisited[index]) {
return;
}
sccVisited[index] = true;
var type = original.get(index);
if (type instanceof WasmStructure) {
var supertype = ((WasmStructure) type).getSupertype();
if (supertype != null && sccMap[supertype.index] == sccBase) {
visitScc(supertype.index, sccBase);
}
} else if (type instanceof WasmFunctionType) {
var supertypes = ((WasmFunctionType) type).getSupertypes();
for (var supertype : supertypes) {
if (sccMap[supertype.index] == sccBase) {
visitScc(supertype.index, sccBase);
}
}
}
sorted.add(type);
}
} }
} }

View File

@ -57,6 +57,9 @@ final class WasmTypeGraphBuilder {
@Override @Override
public void visit(WasmFunctionType type) { public void visit(WasmFunctionType type) {
for (var supertype : type.getSupertypes()) {
addEdge(supertype.getReference());
}
for (var parameter : type.getParameterTypes()) { for (var parameter : type.getParameterTypes()) {
addEdge(parameter); addEdge(parameter);
} }

View File

@ -381,6 +381,9 @@ public class WasmBinaryRenderer {
var visitor = new WasmBinaryRenderingVisitor(code, module, dwarfGenerator, var visitor = new WasmBinaryRenderingVisitor(code, module, dwarfGenerator,
function.getJavaMethod() != null ? debugLines : null, offset); function.getJavaMethod() != null ? debugLines : null, offset);
for (var part : function.getBody()) {
visitor.preprocess(part);
}
for (var part : function.getBody()) { for (var part : function.getBody()) {
part.acceptVisitor(visitor); part.acceptVisitor(visitor);
} }

View File

@ -18,9 +18,11 @@ package org.teavm.backend.wasm.render;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import org.teavm.backend.wasm.debug.DebugLines; import org.teavm.backend.wasm.debug.DebugLines;
import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.generate.DwarfGenerator;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
@ -39,6 +41,7 @@ import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmCopy; import org.teavm.backend.wasm.model.expression.WasmCopy;
import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor;
import org.teavm.backend.wasm.model.expression.WasmDrop; import org.teavm.backend.wasm.model.expression.WasmDrop;
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;
@ -95,6 +98,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
private TextLocation lastEmittedLocation; private TextLocation lastEmittedLocation;
private int positionToEmit; private int positionToEmit;
private List<TextLocation> locationStack = new ArrayList<>(); private List<TextLocation> locationStack = new ArrayList<>();
private Set<WasmBlock> blocksToPreserve = new HashSet<>();
WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmModule module, WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmModule module,
DwarfGenerator dwarfGenerator, DebugLines debugLines, int addressOffset) { DwarfGenerator dwarfGenerator, DebugLines debugLines, int addressOffset) {
@ -105,23 +109,60 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
this.debugLines = debugLines; this.debugLines = debugLines;
} }
void preprocess(WasmExpression expression) {
expression.acceptVisitor(new WasmDefaultExpressionVisitor() {
@Override
public void visit(WasmBranch expression) {
super.visit(expression);
register(expression.getTarget());
}
@Override
public void visit(WasmBreak expression) {
super.visit(expression);
register(expression.getTarget());
}
@Override
public void visit(WasmSwitch expression) {
super.visit(expression);
for (WasmBlock target : expression.getTargets()) {
register(target);
}
register(expression.getDefaultTarget());
}
private void register(WasmBlock block) {
blocksToPreserve.add(block);
}
});
}
@Override @Override
public void visit(WasmBlock expression) { public void visit(WasmBlock expression) {
pushLocation(expression); if (blocksToPreserve.contains(expression) || expression.isLoop()) {
pushLocation(expression); pushLocation(expression);
int blockDepth = 1; pushLocation(expression);
depth += blockDepth; int blockDepth = 1;
blockDepths.put(expression, depth); depth += blockDepth;
writer.writeByte(expression.isLoop() ? 0x03 : 0x02); blockDepths.put(expression, depth);
writeBlockType(expression.getType()); writer.writeByte(expression.isLoop() ? 0x03 : 0x02);
for (WasmExpression part : expression.getBody()) { writeBlockType(expression.getType());
part.acceptVisitor(this); for (WasmExpression part : expression.getBody()) {
part.acceptVisitor(this);
}
popLocation();
writer.writeByte(0x0B);
popLocation();
blockDepths.remove(expression);
depth -= blockDepth;
} else {
pushLocation(expression);
for (var part : expression.getBody()) {
part.acceptVisitor(this);
}
popLocation();
} }
popLocation();
writer.writeByte(0x0B);
popLocation();
blockDepths.remove(expression);
depth -= blockDepth;
} }
private void writeBlockType(WasmType type) { private void writeBlockType(WasmType type) {

View File

@ -57,6 +57,11 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor
@Override @Override
public void visit(WasmFunctionType type) { public void visit(WasmFunctionType type) {
section.writeByte(0x50);
section.writeLEB(type.getSupertypes().size());
for (var supertype : type.getSupertypes()) {
section.writeLEB(module.types.indexOf(supertype));
}
section.writeByte(0x60); section.writeByte(0x60);
section.writeLEB(type.getParameterTypes().size()); section.writeLEB(type.getParameterTypes().size());
for (var inputType : type.getParameterTypes()) { for (var inputType : type.getParameterTypes()) {

View File

@ -33,6 +33,10 @@ public class WasmGCSupport {
return new ClassCastException(); return new ClassCastException();
} }
public static CloneNotSupportedException cnse() {
return new CloneNotSupportedException();
}
@Import(name = "putcharStdout") @Import(name = "putcharStdout")
public static native void putCharStdout(char c); public static native void putCharStdout(char c);

View File

@ -15,10 +15,12 @@
*/ */
package org.teavm.backend.wasm.transformation.gc; package org.teavm.backend.wasm.transformation.gc;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassHolderTransformerContext; import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.emit.ProgramEmitter;
public class BaseClassesTransformation implements ClassHolderTransformer { public class BaseClassesTransformation implements ClassHolderTransformer {
@Override @Override
@ -30,6 +32,11 @@ public class BaseClassesTransformation implements ClassHolderTransformer {
method.setProgram(null); method.setProgram(null);
method.getModifiers().add(ElementModifier.NATIVE); method.getModifiers().add(ElementModifier.NATIVE);
break; break;
case "clone": {
var em = ProgramEmitter.create(method, context.getHierarchy());
em.invoke(WasmGCSupport.class, "cnse", CloneNotSupportedException.class).raise();
break;
}
} }
} }
} else if (cls.getName().equals("java.lang.Class")) { } else if (cls.getName().equals("java.lang.Class")) {