wasm gc: fix issues related to virtual calls

This commit is contained in:
Alexey Andreev 2024-08-13 19:57:48 +02:00
parent 73edc0cf6e
commit a84c5fc77f
12 changed files with 150 additions and 78 deletions

View File

@ -617,7 +617,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
VirtualTableBuilder builder = new VirtualTableBuilder(classes); VirtualTableBuilder builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes)); builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes, true));
builder.setMethodCalledVirtually(controller::isVirtual); builder.setMethodCalledVirtually(controller::isVirtual);
return builder.build(); return builder.build();
} }

View File

@ -1070,7 +1070,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
var builder = new VirtualTableBuilder(classes); var builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes)); builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes, true));
builder.setMethodCalledVirtually(controller::isVirtual); builder.setMethodCalledVirtually(controller::isVirtual);
return builder.build(); return builder.build();
} }

View File

@ -34,12 +34,12 @@ public final class WasmGCUtil {
if (firstPath.get(0) != secondPath.get(0)) { if (firstPath.get(0) != secondPath.get(0)) {
return "java.lang.Object"; return "java.lang.Object";
} }
var max = Math.max(firstPath.size(), secondPath.size()); var min = Math.min(firstPath.size(), secondPath.size());
var index = 1; var index = 1;
while (index < max && firstPath.get(index) == secondPath.get(index)) { while (index < min && firstPath.get(index) == secondPath.get(index)) {
++index; ++index;
} }
return firstPath.get(index).getName(); return index < firstPath.size() ? firstPath.get(index).getName() : secondPath.get(index).getName();
} }
private static List<ClassReader> findPathToRoot(ClassHierarchy hierarchy, ClassReader cls) { private static List<ClassReader> findPathToRoot(ClassHierarchy hierarchy, ClassReader cls) {

View File

@ -29,7 +29,6 @@ import org.teavm.ast.QualificationExpr;
import org.teavm.ast.Statement; import org.teavm.ast.Statement;
import org.teavm.ast.SubscriptExpr; import org.teavm.ast.SubscriptExpr;
import org.teavm.ast.TryCatchStatement; import org.teavm.ast.TryCatchStatement;
import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.backend.wasm.WasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.WasmHeap;
@ -299,7 +298,7 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression nullLiteral() { protected WasmExpression nullLiteral(Expr expr) {
return new WasmInt32Constant(0); return new WasmInt32Constant(0);
} }
@ -371,11 +370,6 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, array, index); return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, array, index);
} }
@Override
public void visit(UnwrapArrayExpr expr) {
accept(expr.getArray());
}
@Override @Override
protected WasmExpression invocation(InvocationExpr expr, List<WasmExpression> resultConsumer, boolean willDrop) { protected WasmExpression invocation(InvocationExpr expr, List<WasmExpression> resultConsumer, boolean willDrop) {
if (expr.getMethod().getClassName().equals(ShadowStack.class.getName())) { if (expr.getMethod().getClassName().equals(ShadowStack.class.getName())) {

View File

@ -60,6 +60,7 @@ import org.teavm.ast.SwitchStatement;
import org.teavm.ast.ThrowStatement; import org.teavm.ast.ThrowStatement;
import org.teavm.ast.TryCatchStatement; import org.teavm.ast.TryCatchStatement;
import org.teavm.ast.UnaryExpr; import org.teavm.ast.UnaryExpr;
import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr; import org.teavm.ast.VariableExpr;
import org.teavm.ast.WhileStatement; import org.teavm.ast.WhileStatement;
import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.WasmRuntime;
@ -453,7 +454,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
accept(value); accept(value);
result.acceptVisitor(typeInference); result.acceptVisitor(typeInference);
block.setType(typeInference.getResult()); block.setType(typeInference.getResult());
var cachedValue = exprCache.create(result, WasmType.INT32, location, block.getBody()); var cachedValue = exprCache.create(result, typeInference.getResult(), location, block.getBody());
var check = new WasmBranch(cachedValue.expr(), block); var check = new WasmBranch(cachedValue.expr(), block);
check.setResult(cachedValue.expr()); check.setResult(cachedValue.expr());
@ -555,7 +556,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
@Override @Override
public void visit(ConstantExpr expr) { public void visit(ConstantExpr expr) {
if (expr.getValue() == null) { if (expr.getValue() == null) {
result = nullLiteral(); result = nullLiteral(expr);
} else if (expr.getValue() instanceof Integer) { } else if (expr.getValue() instanceof Integer) {
result = new WasmInt32Constant((Integer) expr.getValue()); result = new WasmInt32Constant((Integer) expr.getValue());
} else if (expr.getValue() instanceof Long) { } else if (expr.getValue() instanceof Long) {
@ -574,7 +575,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
result.setLocation(expr.getLocation()); result.setLocation(expr.getLocation());
} }
protected abstract WasmExpression nullLiteral(); protected abstract WasmExpression nullLiteral(Expr expr);
protected abstract WasmExpression stringLiteral(String s); protected abstract WasmExpression stringLiteral(String s);
@ -894,12 +895,14 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
return block; return block;
} else { } else {
var reference = expr.getMethod(); var reference = expr.getMethod();
acceptWithType(expr.getArguments().get(0), ValueType.object(expr.getMethod().getClassName())); var instanceType = ValueType.object(expr.getMethod().getClassName());
acceptWithType(expr.getArguments().get(0), instanceType);
var instanceWasmType = mapType(instanceType);
var instance = result; var instance = result;
var block = new WasmBlock(false); var block = new WasmBlock(false);
block.setType(mapType(reference.getReturnType())); block.setType(mapType(reference.getReturnType()));
var instanceVar = tempVars.acquire(WasmType.INT32); var instanceVar = tempVars.acquire(instanceWasmType);
block.getBody().add(new WasmSetLocal(instanceVar, instance)); block.getBody().add(new WasmSetLocal(instanceVar, instance));
instance = new WasmGetLocal(instanceVar); instance = new WasmGetLocal(instanceVar);
@ -1066,7 +1069,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
for (int i = 0; i < expr.getData().size(); ++i) { for (int i = 0; i < expr.getData().size(); ++i) {
expr.getData().get(i).acceptVisitor(this); expr.getData().get(i).acceptVisitor(this);
block.getBody().add(storeArrayItem(new WasmGetLocal(array), new WasmInt32Constant(i), result, arrayType)); var arrayData = unwrapArray(new WasmGetLocal(array));
block.getBody().add(storeArrayItem(arrayData, new WasmInt32Constant(i), result, arrayType));
} }
block.getBody().add(new WasmGetLocal(array)); block.getBody().add(new WasmGetLocal(array));
@ -1126,7 +1130,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
block.setType(WasmType.INT32); block.setType(WasmType.INT32);
block.setLocation(expr.getLocation()); block.setLocation(expr.getLocation());
var cachedObject = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); result.acceptVisitor(typeInference);
var cachedObject = exprCache.create(result, typeInference.getResult(), expr.getLocation(), block.getBody());
var ifNull = new WasmBranch(genIsZero(cachedObject.expr()), block); var ifNull = new WasmBranch(genIsZero(cachedObject.expr()), block);
ifNull.setResult(new WasmInt32Constant(0)); ifNull.setResult(new WasmInt32Constant(0));
@ -1545,6 +1550,17 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
protected abstract WasmType mapType(ValueType type); protected abstract WasmType mapType(ValueType type);
protected WasmExpression unwrapArray(WasmExpression array) {
return array;
}
@Override
public void visit(UnwrapArrayExpr expr) {
accept(expr.getArray());
result = unwrapArray(result);
result.setLocation(expr.getLocation());
}
protected abstract class CallSiteIdentifier { protected abstract class CallSiteIdentifier {
public abstract void generateRegister(List<WasmExpression> consumer, TextLocation location); public abstract void generateRegister(List<WasmExpression> consumer, TextLocation location);

View File

@ -140,7 +140,7 @@ public class WasmGCDeclarationsGenerator {
private static VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes, private static VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes,
Predicate<MethodReference> virtualMethods) { Predicate<MethodReference> virtualMethods) {
var builder = new VirtualTableBuilder(classes); var builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes)); builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes, false));
builder.setMethodCalledVirtually(virtualMethods); builder.setMethodCalledVirtually(virtualMethods);
return builder.build(); return builder.build();
} }

View File

@ -81,7 +81,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>(); private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>();
private Queue<WasmGCClassInfo> classInfoQueue = new ArrayDeque<>(); private Queue<WasmGCClassInfo> classInfoQueue = new ArrayDeque<>();
private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>(); private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>();
private ObjectIntMap<MethodReference> methodIndexes = new ObjectIntHashMap<>();
private Map<FieldReference, WasmGlobal> staticFieldLocations = new HashMap<>(); private Map<FieldReference, WasmGlobal> staticFieldLocations = new HashMap<>();
private List<Consumer<WasmFunction>> staticFieldInitializers = new ArrayList<>(); private List<Consumer<WasmFunction>> staticFieldInitializers = new ArrayList<>();
private ClassInitializerInfo classInitializerInfo; private ClassInitializerInfo classInitializerInfo;
@ -195,8 +194,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null); classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null);
if (name != null) { if (name != null) {
var classReader = classSource.get(name); var classReader = classSource.get(name);
if (classReader == null || !classReader.hasModifier(ElementModifier.ABSTRACT) if (classReader == null || !classReader.hasModifier(ElementModifier.INTERFACE)) {
&& !classReader.hasModifier(ElementModifier.INTERFACE)) {
virtualTable = virtualTables.lookup(name); virtualTable = virtualTables.lookup(name);
} }
if (classReader != null && classReader.getParent() != null) { if (classReader != null && classReader.getParent() != null) {
@ -237,6 +235,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return classArrayItemOffset; return classArrayItemOffset;
} }
@Override
public int getVirtualMethodsOffset() {
return virtualTableFieldOffset;
}
private void initPrimitiveClass(WasmGCClassInfo classInfo, ValueType.Primitive type) { private void initPrimitiveClass(WasmGCClassInfo classInfo, ValueType.Primitive type) {
classInfo.initializer = target -> { classInfo.initializer = target -> {
int kind; int kind;
@ -327,6 +330,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private int fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global, private int fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
VirtualTable virtualTable, int index, String origin, Set<MethodDescriptor> filled) { VirtualTable virtualTable, int index, String origin, Set<MethodDescriptor> filled) {
if (virtualTable.getParent() != null) {
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin,
filled);
}
for (var method : virtualTable.getMethods()) { for (var method : virtualTable.getMethods()) {
var entry = virtualTable.getEntry(method); var entry = virtualTable.getEntry(method);
if (entry != null && entry.getImplementor() != null && filled.add(method)) { if (entry != null && entry.getImplementor() != null && filled.add(method)) {
@ -337,24 +344,21 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
module.functions.add(wrapperFunction); module.functions.add(wrapperFunction);
var call = new WasmCall(function); var call = new WasmCall(function);
var instanceParam = new WasmLocal(getClassInfo(virtualTable.getClassName()).getType()); var instanceParam = new WasmLocal(getClassInfo(virtualTable.getClassName()).getType());
wrapperFunction.getLocalVariables().add(instanceParam); wrapperFunction.add(instanceParam);
var castTarget = getClassInfo(entry.getImplementor().getClassName()).getType(); var castTarget = getClassInfo(entry.getImplementor().getClassName()).getType();
call.getArguments().add(new WasmCast(new WasmGetLocal(instanceParam), castTarget)); call.getArguments().add(new WasmCast(new WasmGetLocal(instanceParam), castTarget));
var params = new WasmLocal[method.parameterCount()]; var params = new WasmLocal[method.parameterCount()];
for (var i = 0; i < method.parameterCount(); ++i) { for (var i = 0; i < method.parameterCount(); ++i) {
params[i] = new WasmLocal(typeMapper.mapType(method.parameterType(i))); params[i] = new WasmLocal(typeMapper.mapType(method.parameterType(i)));
call.getArguments().add(new WasmGetLocal(params[i])); call.getArguments().add(new WasmGetLocal(params[i]));
wrapperFunction.add(params[i]);
} }
wrapperFunction.getLocalVariables().addAll(List.of(params));
} }
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, ref)); target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref));
} }
} ++index;
if (virtualTable.getParent() != null) {
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin,
filled);
} }
return index; return index;
} }
@ -376,12 +380,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
addVirtualTableFields(structure, virtualTable.getParent()); addVirtualTableFields(structure, virtualTable.getParent());
} }
for (var methodDesc : virtualTable.getMethods()) { for (var methodDesc : virtualTable.getMethods()) {
if (methodDesc == null) {
structure.getFields().add(WasmType.Reference.FUNC.asStorage());
} else {
var functionType = getFunctionType(virtualTable.getClassName(), methodDesc); 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()); structure.getFields().add(functionType.getReference().asStorage());
} }
} }
}
private WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc) { private WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc) {
var returnType = typeMapper.mapType(methodDesc.getResultType()); var returnType = typeMapper.mapType(methodDesc.getResultType());
@ -454,15 +460,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return global; return global;
} }
@Override
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(WasmGCClassInfo classInfo, ValueType type) { private void fillFields(WasmGCClassInfo classInfo, ValueType type) {
var fields = classInfo.structure.getFields(); var fields = classInfo.structure.getFields();
fields.add(standardClasses.classClass().getType().asStorage()); fields.add(standardClasses.classClass().getType().asStorage());

View File

@ -17,7 +17,6 @@ package org.teavm.backend.wasm.generate.gc.classes;
import org.teavm.backend.wasm.model.WasmGlobal; import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
public interface WasmGCClassInfoProvider { public interface WasmGCClassInfoProvider {
@ -32,7 +31,7 @@ public interface WasmGCClassInfoProvider {
WasmGlobal getStaticFieldLocation(FieldReference fieldRef); WasmGlobal getStaticFieldLocation(FieldReference fieldRef);
int getVirtualMethodIndex(MethodReference methodRef); int getVirtualMethodsOffset();
default WasmGCClassInfo getClassInfo(String name) { default WasmGCClassInfo getClassInfo(String name) {
return getClassInfo(ValueType.object(name)); return getClassInfo(ValueType.object(name));

View File

@ -15,6 +15,12 @@
*/ */
package org.teavm.backend.wasm.generate.gc.methods; package org.teavm.backend.wasm.generate.gc.methods;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.WasmGCMethodReturnTypes; import org.teavm.backend.wasm.gc.WasmGCMethodReturnTypes;
@ -32,6 +38,8 @@ import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.runtime.WasmGCSupport; import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.classes.VirtualTableProvider;
@ -43,7 +51,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private VirtualTableProvider virtualTables; private VirtualTableProvider virtualTables;
private WasmGCTypeMapper typeMapper; private WasmGCTypeMapper typeMapper;
private WasmFunctionTypes functionTypes; private WasmFunctionTypes functionTypes;
private ClassReaderSource classes; private ListableClassReaderSource classes;
private ClassHierarchy hierarchy; private ClassHierarchy hierarchy;
private BaseWasmFunctionRepository functions; private BaseWasmFunctionRepository functions;
private WasmGCSupertypeFunctionProvider supertypeFunctions; private WasmGCSupertypeFunctionProvider supertypeFunctions;
@ -55,9 +63,10 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private WasmFunction cceMethod; private WasmFunction cceMethod;
private WasmGlobal exceptionGlobal; private WasmGlobal exceptionGlobal;
private WasmTag exceptionTag; private WasmTag exceptionTag;
private Map<String, Set<String>> interfaceImplementors;
public WasmGCGenerationContext(WasmModule module, VirtualTableProvider virtualTables, public WasmGCGenerationContext(WasmModule module, VirtualTableProvider virtualTables,
WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ClassReaderSource classes, WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes,
ClassHierarchy hierarchy, BaseWasmFunctionRepository functions, ClassHierarchy hierarchy, BaseWasmFunctionRepository functions,
WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider, WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider,
WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, WasmGCStandardClasses standardClasses, WasmGCStringProvider strings,
@ -180,4 +189,36 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
public WasmGCMethodReturnTypes returnTypes() { public WasmGCMethodReturnTypes returnTypes() {
return returnTypes; return returnTypes;
} }
public Collection<String> getInterfaceImplementors(String className) {
if (interfaceImplementors == null) {
fillInterfaceImplementors();
}
var result = interfaceImplementors.get(className);
return result != null ? result : List.of();
}
private void fillInterfaceImplementors() {
interfaceImplementors = new HashMap<>();
for (var className : classes.getClassNames()) {
var cls = classes.get(className);
if (!cls.hasModifier(ElementModifier.INTERFACE)) {
for (var itf : cls.getInterfaces()) {
addInterfaceImplementor(className, itf);
}
}
}
}
private void addInterfaceImplementor(String implementorName, String interfaceName) {
var implementorsByKey = interfaceImplementors.computeIfAbsent(interfaceName, k -> new LinkedHashSet<>());
if (implementorsByKey.add(implementorName)) {
var itf = classes.get(implementorName);
if (itf != null) {
for (var parentItf : itf.getInterfaces()) {
addInterfaceImplementor(implementorName, parentItf);
}
}
}
}
} }

View File

@ -23,7 +23,6 @@ import org.teavm.ast.InvocationExpr;
import org.teavm.ast.InvocationType; import org.teavm.ast.InvocationType;
import org.teavm.ast.QualificationExpr; import org.teavm.ast.QualificationExpr;
import org.teavm.ast.SubscriptExpr; import org.teavm.ast.SubscriptExpr;
import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.PreciseTypeInference; import org.teavm.backend.wasm.gc.PreciseTypeInference;
@ -63,10 +62,13 @@ import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmThrow; import org.teavm.backend.wasm.model.expression.WasmThrow;
import org.teavm.backend.wasm.model.expression.WasmUnreachable; import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation; import org.teavm.model.TextLocation;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.classes.VirtualTable;
public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
private WasmGCGenerationContext context; private WasmGCGenerationContext context;
@ -136,13 +138,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
public void visit(UnwrapArrayExpr expr) { protected WasmExpression unwrapArray(WasmExpression array) {
accept(expr.getArray());
result = unwrapArray(result);
result.setLocation(expr.getLocation());
}
private WasmExpression unwrapArray(WasmExpression array) {
array.acceptVisitor(typeInference); array.acceptVisitor(typeInference);
var arrayType = (WasmType.CompositeReference) typeInference.getResult(); var arrayType = (WasmType.CompositeReference) typeInference.getResult();
var arrayStruct = (WasmStructure) arrayType.composite; var arrayStruct = (WasmStructure) arrayType.composite;
@ -202,9 +198,16 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression nullLiteral() { protected WasmExpression nullLiteral(Expr expr) {
return new WasmNullConstant(expectedType instanceof WasmType.Reference var type = expectedType;
? (WasmType.Reference) expectedType if (expr.getVariableIndex() >= 0) {
var javaType = types.typeOf(expr.getVariableIndex());
if (javaType != null) {
type = mapType(javaType.valueType);
}
}
return new WasmNullConstant(type instanceof WasmType.Reference
? (WasmType.Reference) type
: WasmType.Reference.STRUCT); : WasmType.Reference.STRUCT);
} }
@ -252,15 +255,22 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
if (vtable == null) { if (vtable == null) {
return new WasmUnreachable(); return new WasmUnreachable();
} }
method = new MethodReference(vtable.getClassName(), method.getDescriptor()); var cls = context.classes().get(method.getClassName());
assert cls != null : "Virtual table can't be generated for absent class";
arguments.get(0).acceptVisitor(typeInference); if (cls.hasModifier(ElementModifier.INTERFACE)) {
var instanceType = (WasmType.CompositeReference) typeInference.getResult(); vtable = pickVirtualTableForInterfaceCall(vtable, method.getDescriptor());
var instanceStruct = (WasmStructure) instanceType.composite; }
int vtableIndex = vtable.getMethods().indexOf(method.getDescriptor());
if (vtable.getParent() != null) {
vtableIndex += vtable.getParent().size();
}
var instanceStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()).getStructure();
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().getVirtualMethodIndex(method); 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()); classRef = new WasmCast(classRef, vtableStruct.getReference());
@ -272,6 +282,20 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
return invoke; return invoke;
} }
private VirtualTable pickVirtualTableForInterfaceCall(VirtualTable virtualTable, MethodDescriptor descriptor) {
var implementors = context.getInterfaceImplementors(virtualTable.getClassName());
for (var implementor : implementors) {
var implementorVtable = context.virtualTables().lookup(implementor);
if (implementorVtable != null && implementorVtable.hasMethod(descriptor)) {
while (implementorVtable.getParent() != null && implementorVtable.getParent().hasMethod(descriptor)) {
implementorVtable = implementorVtable.getParent();
}
return implementorVtable;
}
}
throw new IllegalStateException();
}
@Override @Override
protected void allocateObject(String className, TextLocation location, WasmLocal local, protected void allocateObject(String className, TextLocation location, WasmLocal local,
List<WasmExpression> target) { List<WasmExpression> target) {

View File

@ -45,8 +45,8 @@ import org.teavm.diagnostics.Diagnostics;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.model.CallLocation; import org.teavm.model.CallLocation;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassHolderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -58,7 +58,7 @@ import org.teavm.model.util.RegisterAllocator;
public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
private WasmModule module; private WasmModule module;
private ClassHierarchy hierarchy; private ClassHierarchy hierarchy;
private ClassHolderSource classes; private ListableClassHolderSource classes;
private VirtualTableProvider virtualTables; private VirtualTableProvider virtualTables;
private ClassInitializerInfo classInitInfo; private ClassInitializerInfo classInitInfo;
private WasmFunctionTypes functionTypes; private WasmFunctionTypes functionTypes;
@ -83,7 +83,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
public WasmGCMethodGenerator( public WasmGCMethodGenerator(
WasmModule module, WasmModule module,
ClassHierarchy hierarchy, ClassHierarchy hierarchy,
ClassHolderSource classes, ListableClassHolderSource classes,
VirtualTableProvider virtualTables, VirtualTableProvider virtualTables,
ClassInitializerInfo classInitInfo, ClassInitializerInfo classInitInfo,
WasmFunctionTypes functionTypes, WasmFunctionTypes functionTypes,

View File

@ -127,7 +127,7 @@ public class VirtualTableBuilder {
} }
} }
List<MethodDescriptor> methodsAtCallSites = methodsUsedAtCallSites.get(className); var methodsAtCallSites = methodsUsedAtCallSites.get(className);
if (methodsAtCallSites != null) { if (methodsAtCallSites != null) {
for (MethodDescriptor methodDesc : methodsAtCallSites) { for (MethodDescriptor methodDesc : methodsAtCallSites) {
if (cls.hasModifier(ElementModifier.FINAL) && !table.entries.containsKey(methodDesc)) { if (cls.hasModifier(ElementModifier.FINAL) && !table.entries.containsKey(methodDesc)) {
@ -162,7 +162,7 @@ public class VirtualTableBuilder {
} }
private void copyEntries(TableBuilder source, TableBuilder target) { private void copyEntries(TableBuilder source, TableBuilder target) {
for (Map.Entry<MethodDescriptor, EntryBuilder> entry : source.entries.entrySet()) { for (var entry : source.entries.entrySet()) {
EntryBuilder targetEntry = target.entries.computeIfAbsent(entry.getKey(), k -> new EntryBuilder()); EntryBuilder targetEntry = target.entries.computeIfAbsent(entry.getKey(), k -> new EntryBuilder());
targetEntry.addParent(entry.getValue()); targetEntry.addParent(entry.getValue());
if (entry.getValue().implementor != null && targetEntry.implementor == null) { if (entry.getValue().implementor != null && targetEntry.implementor == null) {
@ -197,7 +197,7 @@ public class VirtualTableBuilder {
private void liftEntries() { private void liftEntries() {
buildClassTree(); buildClassTree();
for (Map.Entry<MethodDescriptor, List<String>> group : groupMethods().entrySet()) { for (var group : groupMethods().entrySet()) {
String commonSuperclass = commonSuperclass(group.getValue()); String commonSuperclass = commonSuperclass(group.getValue());
Set<String> visited = new HashSet<>(); Set<String> visited = new HashSet<>();
for (String cls : group.getValue()) { for (String cls : group.getValue()) {
@ -397,17 +397,16 @@ public class VirtualTableBuilder {
} }
} }
List<MethodDescriptor> newMethods = context.methods.subList(methodsStart, context.methods.size()); var newMethods = context.methods.subList(methodsStart, context.methods.size());
Set<MethodDescriptor> methodSet = new HashSet<>(); Set<MethodDescriptor> methodSet = new HashSet<>();
for (MethodDescriptor method : newMethods) { for (MethodDescriptor method : newMethods) {
if (method != null) { if (method != null) {
methodSet.add(method); methodSet.add(method);
} }
} }
List<? extends MethodDescriptor> readonlyNewMethods = Collections.unmodifiableList( var readonlyNewMethods = Collections.unmodifiableList(Arrays.asList(
Arrays.asList(newMethods.toArray(new MethodDescriptor[0]))); newMethods.toArray(new MethodDescriptor[0])));
VirtualTable resultTable = new VirtualTable(className, parent, readonlyNewMethods, var resultTable = new VirtualTable(className, parent, readonlyNewMethods, methodSet, resultEntries);
methodSet, resultEntries);
result.virtualTables.put(className, resultTable); result.virtualTables.put(className, resultTable);
List<String> children = classChildren.get(className); List<String> children = classChildren.get(className);
@ -489,10 +488,9 @@ public class VirtualTableBuilder {
methodSet.add(method); methodSet.add(method);
} }
List<? extends MethodDescriptor> readonlyNewMethods = Collections.unmodifiableList( var readonlyMethods = Collections.unmodifiableList(
Arrays.asList(methods.toArray(new MethodDescriptor[0]))); Arrays.asList(methods.toArray(new MethodDescriptor[0])));
VirtualTable resultTable = new VirtualTable(className, null, readonlyNewMethods, var resultTable = new VirtualTable(className, null, readonlyMethods, methodSet, Map.of());
methodSet, Collections.emptyMap());
result.virtualTables.put(className, resultTable); result.virtualTables.put(className, resultTable);
} }
} }
@ -523,7 +521,8 @@ public class VirtualTableBuilder {
List<MethodDescriptor> methods = new ArrayList<>(); List<MethodDescriptor> methods = new ArrayList<>();
} }
public static Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) { public static Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes,
boolean withCloneArray) {
var virtualMethods = new HashSet<MethodReference>(); var virtualMethods = new HashSet<MethodReference>();
for (var className : classes.getClassNames()) { for (var className : classes.getClassNames()) {
@ -542,12 +541,14 @@ public class VirtualTableBuilder {
virtualMethods.add(invoke.getMethod()); virtualMethods.add(invoke.getMethod());
} }
} else if (insn instanceof CloneArrayInstruction) { } else if (insn instanceof CloneArrayInstruction) {
if (withCloneArray) {
virtualMethods.add(new MethodReference(Object.class, "clone", Object.class)); virtualMethods.add(new MethodReference(Object.class, "clone", Object.class));
} }
} }
} }
} }
} }
}
return virtualMethods; return virtualMethods;
} }