wasm gc: implement backend-specific virtual table builder

This commit is contained in:
Alexey Andreev 2024-08-20 21:03:51 +02:00
parent eb0eb1f146
commit 1aebe51256
12 changed files with 436 additions and 90 deletions

View File

@ -118,7 +118,6 @@ public class WasmGCTarget implements TeaVMTarget {
var declarationsGenerator = new WasmGCDeclarationsGenerator( var declarationsGenerator = new WasmGCDeclarationsGenerator(
module, module,
classes, classes,
controller::isVirtual,
controller.getClassInitializerInfo(), controller.getClassInitializerInfo(),
controller.getDependencyInfo(), controller.getDependencyInfo(),
controller.getDiagnostics(), controller.getDiagnostics(),

View File

@ -0,0 +1,61 @@
/*
* 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.gc.vtable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
public class WasmGCVirtualTable {
private WasmGCVirtualTable parent;
private String className;
List<WasmGCVirtualTableEntry> entries;
MethodReference[] implementors;
private Map<MethodDescriptor, WasmGCVirtualTableEntry> entryMap;
WasmGCVirtualTable(WasmGCVirtualTable parent, String className) {
this.parent = parent;
this.className = className;
}
public WasmGCVirtualTable getParent() {
return parent;
}
public String getClassName() {
return className;
}
public List<? extends WasmGCVirtualTableEntry> getEntries() {
return entries;
}
public MethodReference implementor(WasmGCVirtualTableEntry entry) {
return implementors[entry.getIndex()];
}
public WasmGCVirtualTableEntry entry(MethodDescriptor method) {
if (entryMap == null) {
entryMap = new HashMap<>();
for (var entry : entries) {
entryMap.put(entry.getMethod(), entry);
}
}
return entryMap.get(method);
}
}

View File

@ -0,0 +1,243 @@
/*
* 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.gc.vtable;
import com.carrotsearch.hppc.ObjectIntHashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.teavm.common.LCATree;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
class WasmGCVirtualTableBuilder {
ListableClassReaderSource classes;
Collection<MethodReference> methodsAtCallSites;
private Map<String, Set<MethodDescriptor>> groupedMethodsAtCallSites = new HashMap<>();
private List<Table> tables = new ArrayList<>();
private Map<String, Table> tableMap = new HashMap<>();
private LCATree lcaTree;
private Map<String, Table> interfaceImplementors = new HashMap<>();
Map<String, WasmGCVirtualTable> result = new HashMap<>();
void build() {
initTables();
buildLCA();
fillInterfaceImplementors();
groupMethodsFromCallSites();
fillTables();
buildResult();
}
private void initTables() {
for (var className : classes.getClassNames()) {
initTable(className);
}
}
private void initTable(String className) {
var cls = classes.get(className);
if (!cls.hasModifier(ElementModifier.INTERFACE) && !tableMap.containsKey(className)) {
if (cls.getParent() != null) {
initTable(cls.getParent());
}
var table = new Table(cls, tables.size());
tables.add(table);
tableMap.put(className, table);
}
}
private void buildLCA() {
lcaTree = new LCATree(tables.size() + 1);
for (var i = 0; i < tables.size(); i++) {
var table = tables.get(i);
var parentTable = table.cls.getParent() != null ? tableMap.get(table.cls.getParent()) : null;
lcaTree.addNode(parentTable != null ? parentTable.index + 1 : 0);
}
}
private void fillInterfaceImplementors() {
for (var className : classes.getClassNames()) {
var cls = classes.get(className);
if (!cls.hasModifier(ElementModifier.INTERFACE)) {
var visited = new HashSet<String>();
do {
var table = tableMap.get(cls.getName());
for (var itfName : cls.getInterfaces()) {
addImplementorToInterface(itfName, table, visited);
}
cls = cls.getParent() != null ? classes.get(cls.getParent()) : null;
} while (cls != null);
}
}
}
private void addImplementorToInterface(String interfaceName, Table newImplementor, Set<String> visited) {
if (!visited.add(interfaceName)) {
return;
}
var knownImplementor = interfaceImplementors.get(interfaceName);
if (knownImplementor == null) {
interfaceImplementors.put(interfaceName, newImplementor);
} else {
var lcaIndex = lcaTree.lcaOf(newImplementor.index + 1, knownImplementor.index + 1);
if (lcaIndex > 0) {
interfaceImplementors.put(interfaceName, tables.get(lcaIndex - 1));
}
}
var cls = classes.get(interfaceName);
if (cls != null) {
for (var superInterface : cls.getInterfaces()) {
addImplementorToInterface(superInterface, newImplementor, visited);
}
}
}
private void groupMethodsFromCallSites() {
for (var methodRef : methodsAtCallSites) {
var className = mapInterface(methodRef.getClassName());
var group = groupedMethodsAtCallSites.computeIfAbsent(className, k -> new LinkedHashSet<>());
group.add(methodRef.getDescriptor());
}
}
private String mapInterface(String name) {
var cls = classes.get(name);
if (cls == null || !cls.hasModifier(ElementModifier.INTERFACE)) {
return name;
}
var implementor = interfaceImplementors.get(cls.getName());
if (implementor == null) {
return name;
}
return implementor.cls.getName();
}
private void fillTables() {
for (var className : classes.getClassNames()) {
var table = tableMap.get(className);
if (table != null) {
fillTable(table);
}
}
}
private void fillTable(Table table) {
if (table.filled) {
return;
}
table.filled = true;
var parent = table.cls.getParent() != null ? tableMap.get(table.cls.getParent()) : null;
table.parent = parent;
var indexes = new ObjectIntHashMap<MethodDescriptor>();
if (parent != null) {
fillTable(parent);
table.entries.addAll(parent.entries);
table.implementors.addAll(parent.implementors);
for (var entry : table.entries) {
indexes.put(entry.method, entry.index);
}
}
var group = groupedMethodsAtCallSites.get(table.cls.getName());
if (group != null) {
for (var method : group) {
if (indexes.getOrDefault(method, -1) < 0) {
var entry = new Entry(method, table, table.entries.size());
table.entries.add(entry);
indexes.put(method, entry.index);
table.implementors.add(null);
}
}
}
for (var method : table.cls.getMethods()) {
if (!method.hasModifier(ElementModifier.STATIC) && !method.hasModifier(ElementModifier.ABSTRACT)) {
var index = indexes.getOrDefault(method.getDescriptor(), -1);
if (index >= 0) {
table.implementors.set(index, method.getReference());
}
}
}
}
private void buildResult() {
for (var className : classes.getClassNames()) {
var cls = classes.get(className);
var table = !cls.hasModifier(ElementModifier.INTERFACE)
? tableMap.get(className)
: interfaceImplementors.get(className);
if (table != null) {
result.put(className, table.getBuildResult());
}
}
}
private static class Table {
final ClassReader cls;
int index;
boolean filled;
Table parent;
List<Entry> entries = new ArrayList<>();
List<MethodReference> implementors = new ArrayList<>();
private WasmGCVirtualTable buildResult;
Table(ClassReader cls, int index) {
this.cls = cls;
this.index = index;
}
WasmGCVirtualTable getBuildResult() {
if (buildResult == null) {
buildResult = new WasmGCVirtualTable(parent != null ? parent.getBuildResult() : null, cls.getName());
buildResult.entries = entries.stream()
.map(Entry::getBuildResult)
.collect(Collectors.toList());
buildResult.implementors = implementors.toArray(new MethodReference[0]);
}
return buildResult;
}
}
private static class Entry {
MethodDescriptor method;
Table origin;
int index;
private WasmGCVirtualTableEntry buildResult;
Entry(MethodDescriptor method, Table origin, int index) {
this.method = method;
this.origin = origin;
this.index = index;
}
WasmGCVirtualTableEntry getBuildResult() {
if (buildResult == null) {
buildResult = new WasmGCVirtualTableEntry(origin.getBuildResult(), method, index);
}
return buildResult;
}
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.gc.vtable;
import org.teavm.model.MethodDescriptor;
public class WasmGCVirtualTableEntry {
private WasmGCVirtualTable origin;
private MethodDescriptor method;
private int index;
WasmGCVirtualTableEntry(WasmGCVirtualTable origin, MethodDescriptor method, int index) {
this.origin = origin;
this.method = method;
this.index = index;
}
public WasmGCVirtualTable getOrigin() {
return origin;
}
public MethodDescriptor getMethod() {
return method;
}
public int getIndex() {
return index;
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.gc.vtable;
import java.util.Collection;
import java.util.Map;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReference;
public class WasmGCVirtualTableProvider {
private Map<String, WasmGCVirtualTable> virtualTables;
public WasmGCVirtualTableProvider(ListableClassReaderSource classes,
Collection<MethodReference> methodsAtCallSites) {
var builder = new WasmGCVirtualTableBuilder();
builder.classes = classes;
builder.methodsAtCallSites = methodsAtCallSites;
builder.build();
virtualTables = builder.result;
}
public WasmGCVirtualTable lookup(String name) {
return virtualTables.get(name);
}
}

View File

@ -16,9 +16,9 @@
package org.teavm.backend.wasm.generate.gc; package org.teavm.backend.wasm.generate.gc;
import java.util.List; import java.util.List;
import java.util.function.Predicate;
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.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.WasmNameProvider; import org.teavm.backend.wasm.generate.WasmNameProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassGenerator; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassGenerator;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
@ -33,12 +33,10 @@ import org.teavm.dependency.DependencyInfo;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.MethodReference;
import org.teavm.model.analysis.ClassInitializerInfo; import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.analysis.ClassMetadataRequirements; import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableBuilder; import org.teavm.model.classes.VirtualTableBuilder;
import org.teavm.model.classes.VirtualTableProvider;
public class WasmGCDeclarationsGenerator { public class WasmGCDeclarationsGenerator {
public final ClassHierarchy hierarchy; public final ClassHierarchy hierarchy;
@ -50,7 +48,6 @@ public class WasmGCDeclarationsGenerator {
public WasmGCDeclarationsGenerator( public WasmGCDeclarationsGenerator(
WasmModule module, WasmModule module,
ListableClassHolderSource classes, ListableClassHolderSource classes,
Predicate<MethodReference> virtualMethods,
ClassInitializerInfo classInitializerInfo, ClassInitializerInfo classInitializerInfo,
DependencyInfo dependencyInfo, DependencyInfo dependencyInfo,
Diagnostics diagnostics, Diagnostics diagnostics,
@ -59,7 +56,7 @@ public class WasmGCDeclarationsGenerator {
) { ) {
this.module = module; this.module = module;
hierarchy = new ClassHierarchy(classes); hierarchy = new ClassHierarchy(classes);
var virtualTables = createVirtualTableProvider(classes, virtualMethods); var virtualTables = createVirtualTableProvider(classes);
functionTypes = new WasmFunctionTypes(module); functionTypes = new WasmFunctionTypes(module);
var names = new WasmNameProvider(); var names = new WasmNameProvider();
methodGenerator = new WasmGCMethodGenerator( methodGenerator = new WasmGCMethodGenerator(
@ -133,12 +130,8 @@ public class WasmGCDeclarationsGenerator {
} }
} }
private static VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes, private static WasmGCVirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
Predicate<MethodReference> virtualMethods) { return new WasmGCVirtualTableProvider(classes, VirtualTableBuilder.getMethodsUsedOnCallSites(classes, false));
var builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes, false));
builder.setMethodCalledVirtually(virtualMethods);
return builder.build();
} }
public WasmFunction dummyInitializer() { public WasmFunction dummyInitializer() {

View File

@ -20,16 +20,16 @@ import com.carrotsearch.hppc.ObjectIntMap;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.lowlevel.generate.NameProvider;
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.vtable.WasmGCVirtualTable;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor; import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool; import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmArray;
@ -66,8 +66,6 @@ import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassInitializerInfo; import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.analysis.ClassMetadataRequirements; import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTable;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.util.ReflectionUtil; import org.teavm.model.util.ReflectionUtil;
public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInitializerContributor { public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInitializerContributor {
@ -82,7 +80,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private WasmFunctionTypes functionTypes; private WasmFunctionTypes functionTypes;
private TagRegistry tagRegistry; private TagRegistry tagRegistry;
private ClassMetadataRequirements metadataRequirements; private ClassMetadataRequirements metadataRequirements;
private VirtualTableProvider virtualTables; private WasmGCVirtualTableProvider virtualTables;
private BaseWasmFunctionRepository functionProvider; private BaseWasmFunctionRepository functionProvider;
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<>();
@ -113,7 +111,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
WasmFunctionTypes functionTypes, TagRegistry tagRegistry, WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables, ClassMetadataRequirements metadataRequirements, WasmGCVirtualTableProvider virtualTables,
BaseWasmFunctionRepository functionProvider, NameProvider names, BaseWasmFunctionRepository functionProvider, NameProvider names,
ClassInitializerInfo classInitializerInfo) { ClassInitializerInfo classInitializerInfo) {
this.module = module; this.module = module;
@ -236,7 +234,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
classInfo = new WasmGCClassInfo(type); classInfo = new WasmGCClassInfo(type);
classInfoQueue.add(classInfo); classInfoQueue.add(classInfo);
classInfoMap.put(type, classInfo); classInfoMap.put(type, classInfo);
VirtualTable virtualTable = null; WasmGCVirtualTable virtualTable = null;
if (!(type instanceof ValueType.Primitive)) { if (!(type instanceof ValueType.Primitive)) {
var name = type instanceof ValueType.Object var name = type instanceof ValueType.Object
? ((ValueType.Object) type).getClassName() ? ((ValueType.Object) type).getClassName()
@ -265,7 +263,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
} }
} }
var pointerName = names.forClassInstance(type); var pointerName = names.forClassInstance(type);
classInfo.hasOwnVirtualTable = virtualTable != null && virtualTable.hasValidEntries(); classInfo.hasOwnVirtualTable = virtualTable != null && !virtualTable.getEntries().isEmpty();
var classStructure = classInfo.hasOwnVirtualTable var classStructure = classInfo.hasOwnVirtualTable
? initRegularClassStructure(((ValueType.Object) type).getClassName()) ? initRegularClassStructure(((ValueType.Object) type).getClassName())
: standardClasses.classClass().getStructure(); : standardClasses.classClass().getStructure();
@ -366,8 +364,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}; };
} }
private void initRegularClass(WasmGCClassInfo classInfo, VirtualTable virtualTable, WasmStructure classStructure, private void initRegularClass(WasmGCClassInfo classInfo, WasmGCVirtualTable virtualTable,
String name) { WasmStructure classStructure, String name) {
var cls = classSource.get(name); var cls = classSource.get(name);
if (classInitializerInfo.isDynamicInitializer(name)) { if (classInitializerInfo.isDynamicInitializer(name)) {
if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) { if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) {
@ -396,39 +394,34 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer))); target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer)));
} }
} }
if (virtualTable != null && virtualTable.hasValidEntries()) { if (virtualTable != null) {
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTable, fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable);
new HashSet<>());
} }
}; };
} }
private void fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global, private void fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
VirtualTable virtualTable, VirtualTable original, Set<MethodDescriptor> filled) { WasmGCVirtualTable virtualTable) {
if (virtualTable.getParent() != null) { for (var entry : virtualTable.getEntries()) {
fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), original, filled); var implementor = virtualTable.implementor(entry);
} if (implementor != null && !entry.getMethod().equals(GET_CLASS_METHOD)) {
for (var method : virtualTable.getMethods()) {
var entry = original.getEntry(method);
if (entry != null && entry.getImplementor() != null && filled.add(method)
&& !method.equals(GET_CLASS_METHOD)) {
var fieldIndex = virtualTableFieldOffset + entry.getIndex(); var fieldIndex = virtualTableFieldOffset + entry.getIndex();
var expectedType = (WasmType.CompositeReference) structure.getFields().get(fieldIndex) var expectedType = (WasmType.CompositeReference) structure.getFields().get(fieldIndex)
.getUnpackedType(); .getUnpackedType();
var expectedFunctionType = (WasmFunctionType) expectedType.composite; var expectedFunctionType = (WasmFunctionType) expectedType.composite;
var function = functionProvider.forInstanceMethod(entry.getImplementor()); var function = functionProvider.forInstanceMethod(implementor);
if (!virtualTable.getClassName().equals(entry.getImplementor().getClassName()) if (entry.getOrigin().getClassName().equals(implementor.getClassName())
|| expectedFunctionType != function.getType()) { || expectedFunctionType != function.getType()) {
var wrapperFunction = new WasmFunction(expectedFunctionType); var wrapperFunction = new WasmFunction(expectedFunctionType);
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.add(instanceParam); wrapperFunction.add(instanceParam);
var castTarget = getClassInfo(entry.getImplementor().getClassName()).getType(); var castTarget = getClassInfo(implementor.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[entry.getMethod().parameterCount()];
for (var i = 0; i < method.parameterCount(); ++i) { for (var i = 0; i < entry.getMethod().parameterCount(); ++i) {
params[i] = new WasmLocal(typeMapper.mapType(method.parameterType(i))); params[i] = new WasmLocal(typeMapper.mapType(entry.getMethod().parameterType(i)));
call.getArguments().add(new WasmGetLocal(params[i])); call.getArguments().add(new WasmGetLocal(params[i]));
wrapperFunction.add(params[i]); wrapperFunction.add(params[i]);
} }
@ -462,20 +455,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
fields.add(monitorField); fields.add(monitorField);
} }
private void addVirtualTableFields(WasmStructure structure, VirtualTable virtualTable) { private void addVirtualTableFields(WasmStructure structure, WasmGCVirtualTable virtualTable) {
if (virtualTable.getParent() != null) { for (var entry : virtualTable.getEntries()) {
addVirtualTableFields(structure, virtualTable.getParent()); var functionType = typeMapper.getFunctionType(entry.getOrigin().getClassName(), entry.getMethod(), false);
} var field = new WasmField(functionType.getReference().asStorage());
for (var methodDesc : virtualTable.getMethods()) { field.setName(names.forVirtualMethod(entry.getMethod()));
if (methodDesc == null) { structure.getFields().add(field);
structure.getFields().add(new WasmField(WasmType.Reference.FUNC.asStorage()));
} else {
var originalVirtualTable = virtualTable.findMethodContainer(methodDesc);
var functionType = typeMapper.getFunctionType(originalVirtualTable.getClassName(), methodDesc, false);
var field = new WasmField(functionType.getReference().asStorage());
field.setName(names.forVirtualMethod(methodDesc));
structure.getFields().add(field);
}
} }
} }

View File

@ -23,6 +23,7 @@ import java.util.Map;
import java.util.Set; 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.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext; import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses; import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
@ -40,14 +41,13 @@ import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.classes.VirtualTableProvider;
public class WasmGCGenerationContext implements BaseWasmGenerationContext { public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private WasmModule module; private WasmModule module;
private WasmGCClassInfoProvider classInfoProvider; private WasmGCClassInfoProvider classInfoProvider;
private WasmGCStandardClasses standardClasses; private WasmGCStandardClasses standardClasses;
private WasmGCStringProvider strings; private WasmGCStringProvider strings;
private VirtualTableProvider virtualTables; private WasmGCVirtualTableProvider virtualTables;
private WasmGCTypeMapper typeMapper; private WasmGCTypeMapper typeMapper;
private WasmFunctionTypes functionTypes; private WasmFunctionTypes functionTypes;
private ListableClassReaderSource classes; private ListableClassReaderSource classes;
@ -63,7 +63,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private WasmTag exceptionTag; private WasmTag exceptionTag;
private Map<String, Set<String>> interfaceImplementors; private Map<String, Set<String>> interfaceImplementors;
public WasmGCGenerationContext(WasmModule module, VirtualTableProvider virtualTables, public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables,
WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes, WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes,
ClassHierarchy hierarchy, BaseWasmFunctionRepository functions, ClassHierarchy hierarchy, BaseWasmFunctionRepository functions,
WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider, WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider,
@ -96,7 +96,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
return strings; return strings;
} }
public VirtualTableProvider virtualTables() { public WasmGCVirtualTableProvider virtualTables() {
return virtualTables; return virtualTables;
} }

View File

@ -62,13 +62,10 @@ 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;
@ -259,26 +256,18 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
protected WasmExpression generateVirtualCall(WasmLocal instance, MethodReference method, protected WasmExpression generateVirtualCall(WasmLocal instance, MethodReference method,
List<WasmExpression> arguments) { List<WasmExpression> arguments) {
var vtable = context.virtualTables().lookup(method.getClassName()); var vtable = context.virtualTables().lookup(method.getClassName());
if (vtable != null) {
var cls = context.classes().get(method.getClassName());
assert cls != null : "Virtual table can't be generated for absent class";
if (cls.hasModifier(ElementModifier.INTERFACE)) {
vtable = pickVirtualTableForInterfaceCall(vtable, method.getDescriptor());
}
vtable = vtable.findMethodContainer(method.getDescriptor());
}
if (vtable == null) { if (vtable == null) {
return new WasmUnreachable(); return new WasmUnreachable();
} }
int vtableIndex = vtable.getMethods().indexOf(method.getDescriptor()); var entry = vtable.entry(method.getDescriptor());
if (vtable.getParent() != null) { if (entry == null) {
vtableIndex += vtable.getParent().size(); return new WasmUnreachable();
} }
WasmExpression classRef = new WasmStructGet(context.standardClasses().objectClass().getStructure(), WasmExpression classRef = new WasmStructGet(context.standardClasses().objectClass().getStructure(),
new WasmGetLocal(instance), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); new WasmGetLocal(instance), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
var index = context.classInfoProvider().getVirtualMethodsOffset() + vtableIndex; var index = context.classInfoProvider().getVirtualMethodsOffset() + entry.getIndex();
var expectedInstanceClassInfo = context.classInfoProvider().getClassInfo(vtable.getClassName()); var expectedInstanceClassInfo = context.classInfoProvider().getClassInfo(vtable.getClassName());
var vtableStruct = expectedInstanceClassInfo.getVirtualTableStructure(); var vtableStruct = expectedInstanceClassInfo.getVirtualTableStructure();
classRef = new WasmCast(classRef, vtableStruct.getReference()); classRef = new WasmCast(classRef, vtableStruct.getReference());
@ -298,20 +287,6 @@ 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

@ -26,6 +26,7 @@ import org.teavm.backend.lowlevel.generate.NameProvider;
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.WasmGCVariableCategoryProvider; import org.teavm.backend.wasm.gc.WasmGCVariableCategoryProvider;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses; import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCSupertypeFunctionProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCSupertypeFunctionProvider;
@ -51,14 +52,13 @@ import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassInitializerInfo; import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.util.RegisterAllocator; 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 ListableClassHolderSource classes; private ListableClassHolderSource classes;
private VirtualTableProvider virtualTables; private WasmGCVirtualTableProvider virtualTables;
private ClassInitializerInfo classInitInfo; private ClassInitializerInfo classInitInfo;
private WasmFunctionTypes functionTypes; private WasmFunctionTypes functionTypes;
private WasmGCSupertypeFunctionProvider supertypeFunctions; private WasmGCSupertypeFunctionProvider supertypeFunctions;
@ -82,7 +82,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
WasmModule module, WasmModule module,
ClassHierarchy hierarchy, ClassHierarchy hierarchy,
ListableClassHolderSource classes, ListableClassHolderSource classes,
VirtualTableProvider virtualTables, WasmGCVirtualTableProvider virtualTables,
ClassInitializerInfo classInitInfo, ClassInitializerInfo classInitInfo,
WasmFunctionTypes functionTypes, WasmFunctionTypes functionTypes,
NameProvider names, NameProvider names,

View File

@ -25,7 +25,7 @@ public class LCATree {
pathsToRoot = new int[capacity][]; pathsToRoot = new int[capacity][];
sz = 1; sz = 1;
depths[0] = 0; depths[0] = 0;
pathsToRoot[0] = new int[0]; pathsToRoot[0] = new int[] { 0 };
} }
public int size() { public int size() {

View File

@ -15,11 +15,21 @@
*/ */
package org.teavm.junit; package org.teavm.junit;
import org.teavm.classlib.impl.console.JSStdoutPrintStream;
final class TestWasmGCEntryPoint { final class TestWasmGCEntryPoint {
private TestWasmGCEntryPoint() { private TestWasmGCEntryPoint() {
} }
public static void main(String[] args) throws Throwable { public static void main(String[] args) throws Throwable {
try {
TestEntryPoint.run(args.length > 0 ? args[0] : null);
new JSStdoutPrintStream().println("SUCCESS");
} catch (Throwable e) {
var out = new JSStdoutPrintStream();
e.printStackTrace(out);
out.println("FAILURE");
}
TestEntryPoint.run(args.length > 0 ? args[0] : null); TestEntryPoint.run(args.length > 0 ? args[0] : null);
} }
} }