wasm gc: draft Wasm GC backend

This commit is contained in:
Alexey Andreev 2024-07-24 21:25:32 +02:00
parent 9f12917de9
commit a281c19363
44 changed files with 1514 additions and 523 deletions

View File

@ -32,6 +32,11 @@ public final class PlatformDetector {
return false;
}
@PlatformMarker(Platforms.WEBASSEMBLY_GC)
public static boolean isWebAssemblyGC() {
return false;
}
@PlatformMarker(Platforms.JAVASCRIPT)
public static boolean isJavaScript() {
return false;

View File

@ -16,6 +16,7 @@
package org.teavm.classlib.impl.console;
import org.teavm.backend.c.intrinsic.RuntimeInclude;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.backend.wasm.runtime.WasmSupport;
import org.teavm.classlib.PlatformDetector;
import org.teavm.interop.Address;
@ -35,6 +36,10 @@ public final class Console {
}
} else if (PlatformDetector.isWebAssembly()) {
WasmSupport.putCharsStderr(Address.ofData(data).add(off), len);
} else if (PlatformDetector.isWebAssemblyGC()) {
for (var i = 0; i < len; ++i) {
WasmGCSupport.putCharStderr((char) data[off + i]);
}
} else {
for (int i = 0; i < len; ++i) {
byte b = data[i + off];
@ -51,6 +56,10 @@ public final class Console {
}
} else if (PlatformDetector.isWebAssembly()) {
WasmSupport.putCharsStdout(Address.ofData(data).add(off), len);
} else if (PlatformDetector.isWebAssemblyGC()) {
for (var i = 0; i < len; ++i) {
WasmGCSupport.putCharStdout((char) data[off + i]);
}
} else {
for (int i = 0; i < len; ++i) {
byte b = data[i + off];

View File

@ -15,12 +15,20 @@
*/
package org.teavm.classlib.impl.console;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.classlib.PlatformDetector;
import org.teavm.jso.JSBody;
public class JSStderrPrintStream extends JsConsolePrintStream {
@Override
public void print(String s) {
writeJs(s);
if (PlatformDetector.isWebAssemblyGC()) {
for (int i = 0; i < s.length(); ++i) {
WasmGCSupport.putCharStderr(s.charAt(i));
}
} else {
writeJs(s);
}
}
@JSBody(params = "b", script = "$rt_putStderr(b);")

View File

@ -15,12 +15,20 @@
*/
package org.teavm.classlib.impl.console;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.classlib.PlatformDetector;
import org.teavm.jso.JSBody;
public class JSStdoutPrintStream extends JsConsolePrintStream {
@Override
public void print(String s) {
writeJs(s);
if (PlatformDetector.isWebAssemblyGC()) {
for (int i = 0; i < s.length(); ++i) {
WasmGCSupport.putCharStderr(s.charAt(i));
}
} else {
writeJs(s);
}
}
@JSBody(params = "b", script = "$rt_putStdout(b);")

View File

@ -56,7 +56,7 @@ public final class TSystem extends TObject {
public static TPrintStream out() {
if (outCache == null) {
if (PlatformDetector.isJavaScript()) {
if (PlatformDetector.isJavaScript() || PlatformDetector.isWebAssemblyGC()) {
outCache = (TPrintStream) (Object) new JSStdoutPrintStream();
} else {
outCache = new TPrintStream(StdoutOutputStream.INSTANCE, false);
@ -67,7 +67,7 @@ public final class TSystem extends TObject {
public static TPrintStream err() {
if (errCache == null) {
if (PlatformDetector.isJavaScript()) {
if (PlatformDetector.isJavaScript() || PlatformDetector.isWebAssemblyGC()) {
errCache = (TPrintStream) (Object) new JSStderrPrintStream();
} else {
errCache = new TPrintStream(StderrOutputStream.INSTANCE, false);

View File

@ -19,14 +19,20 @@ import org.teavm.model.util.VariableType;
public class VariableNode {
private int index;
private int originalIndex;
private VariableType type;
private String name;
public VariableNode(int index, VariableType type) {
this.index = index;
this.originalIndex = index;
this.type = type;
}
public int getOriginalIndex() {
return originalIndex;
}
public int getIndex() {
return index;
}

View File

@ -92,18 +92,15 @@ import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyListener;
import org.teavm.interop.Address;
import org.teavm.interop.Platforms;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader;
import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
@ -111,9 +108,6 @@ import org.teavm.model.ValueType;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableBuilder;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.lowlevel.CallSiteDescriptor;
import org.teavm.model.lowlevel.Characteristics;
import org.teavm.model.lowlevel.ClassInitializerEliminator;
@ -623,40 +617,11 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
VirtualTableBuilder builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes));
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes));
builder.setMethodCalledVirtually(controller::isVirtual);
return builder.build();
}
private Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) {
Set<MethodReference> virtualMethods = new HashSet<>();
for (String className : classes.getClassNames()) {
ClassHolder cls = classes.get(className);
for (MethodHolder method : cls.getMethods()) {
Program program = method.getProgram();
if (program == null) {
continue;
}
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i);
for (Instruction insn : block) {
if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction) insn;
if (invoke.getType() == InvocationType.VIRTUAL) {
virtualMethods.add(invoke.getMethod());
}
} else if (insn instanceof CloneArrayInstruction) {
virtualMethods.add(new MethodReference(Object.class, "clone", Object.class));
}
}
}
}
}
return virtualMethods;
}
private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
IncludeManager includes = new SimpleIncludeManager(context.getFileNames(), writer);
includes.init("special.c");

View File

@ -0,0 +1,25 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.model.MethodReference;
public interface BaseWasmFunctionRepository {
WasmFunction forStaticMethod(MethodReference methodReference);
WasmFunction forInstanceMethod(MethodReference methodReference);
}

View File

@ -1,323 +0,0 @@
/*
* Copyright 2016 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;
import java.lang.reflect.Array;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.teavm.model.ValueType;
public final class Example {
private Example() {
}
public static void main(String[] args) {
testFibonacci();
testClasses();
testVirtualCall();
testInstanceOf();
testPrimitiveArray();
testLazyInitialization();
testHashCode();
testArrayList();
testArrayCopy();
testArrayIsObject();
testIsAssignableFrom();
testExceptions();
testArrayReflection();
testBigInteger();
testGC();
}
private static void testBigInteger() {
System.out.println("Running BigInteger benchmark");
BigInteger result = BigInteger.ONE;
for (int j = 0; j < 100; ++j) {
long start = System.currentTimeMillis();
for (int k = 0; k < 5000; ++k) {
BigInteger a = BigInteger.ZERO;
BigInteger b = BigInteger.ONE;
for (int i = 0; i < 1000; ++i) {
BigInteger c = a.add(b);
a = b;
b = c;
}
result = a;
}
long end = System.currentTimeMillis();
System.out.println("Operation took " + (end - start) + " milliseconds");
}
System.out.println(result.toString());
}
private static void testFibonacci() {
int a = 0;
int b = 1;
System.out.println("Fibonacci numbers:");
for (int i = 0; i < 30; ++i) {
int c = a + b;
a = b;
b = c;
System.out.println(String.valueOf(a));
}
}
private static void testClasses() {
System.out.println("A(2) + A(3) = " + (new A(2).getValue() + new A(3).getValue()));
}
private static void testVirtualCall() {
for (int i = 0; i < 4; ++i) {
System.out.println("instance(" + i + ") = " + instance(i).foo());
}
Base[] array = { new Derived1(), new Derived2() };
System.out.println("array.length = " + array.length);
for (Base elem : array) {
System.out.println("array[i] = " + elem.foo());
}
}
private static void testInstanceOf() {
System.out.println("Derived2 instanceof Base = " + (new Derived2() instanceof Base));
System.out.println("Derived3 instanceof Base = " + (new Derived3() instanceof Base));
System.out.println("Derived2 instanceof Derived1 = " + ((Object) new Derived2() instanceof Derived1));
System.out.println("Derived2 instanceof A = " + ((Object) new Derived2() instanceof A));
System.out.println("A instanceof Base = " + (new A(23) instanceof Base));
}
private static void testPrimitiveArray() {
byte[] bytes = { 5, 6, 10, 15 };
for (byte bt : bytes) {
System.out.println("bytes[i] = " + bt);
}
}
private static void testLazyInitialization() {
Initialized.foo();
Initialized.foo();
Initialized.foo();
}
private static void testHashCode() {
Object o = new Object();
System.out.println("hashCode1 = " + o.hashCode());
System.out.println("hashCode1 = " + o.hashCode());
System.out.println("hashCode2 = " + new Object().hashCode());
System.out.println("hashCode3 = " + new Object().hashCode());
}
private static void testArrayList() {
List<Integer> list = new ArrayList<>(Arrays.asList(333, 444, 555));
list.add(1234);
list.remove((Integer) 444);
for (int item : list) {
System.out.println("list[i] = " + item);
}
}
private static void testArrayCopy() {
byte[] array = new byte[100];
byte[] negatives = new byte[100];
for (int i = 0; i < array.length; ++i) {
array[i] = (byte) i;
negatives[i] = (byte) (-i - 1);
}
System.arraycopy(negatives, 0, array, 4, 12);
System.arraycopy(negatives, 1, array, 21, 12);
System.arraycopy(negatives, 2, array, 35, 12);
System.arraycopy(negatives, 1, array, 8, 3);
System.arraycopy(array, 50, array, 54, 12);
StringBuilder sb = new StringBuilder("arrayCopy result:");
for (int i = 0; i < array.length; ++i) {
sb.append(" ").append(array[i]);
}
System.out.println(sb.toString());
}
private static void testArrayIsObject() {
byte[] array = new byte[] { 1, 2, 3 };
byte[] copy = array.clone();
System.out.println("array.hashCode() = " + array.hashCode());
System.out.println("copy.hashCode() = " + copy.hashCode());
System.out.println("array.equals(array) = " + array.equals(array));
System.out.println("array.equals(copy) = " + array.equals(copy));
}
private static void testIsAssignableFrom() {
System.out.println("Object.isAssignableFrom(byte[]) = "
+ Object.class.isAssignableFrom(new byte[0].getClass()));
System.out.println("Object[].isAssignableFrom(Integer[]) = "
+ Object[].class.isAssignableFrom(new Integer[0].getClass()));
System.out.println("Object[].isAssignableFrom(Integer) = "
+ Object[].class.isAssignableFrom(Integer.class));
System.out.println("byte[].isAssignableFrom(Object) = "
+ byte[].class.isAssignableFrom(ValueType.Object.class));
System.out.println("Base.isAssignableFrom(Derived1) = "
+ Base.class.isAssignableFrom(Derived1.class));
System.out.println("Base.isAssignableFrom(A) = "
+ Base.class.isAssignableFrom(A.class));
}
private static void testGC() {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 4000000; ++i) {
list.add(i);
if (list.size() == 1000) {
list = new ArrayList<>();
}
}
System.out.println("GC complete");
}
private static void testExceptions() {
try {
throwsException();
} catch (IllegalStateException e) {
System.out.println("Caught 1: " + e.getMessage());
e.printStackTrace();
}
int x = 0;
try {
x = 1;
doesNotThrow();
x = 2;
throwsException();
x = 3;
} catch (IllegalStateException e) {
System.out.println("Caught 2: " + e.getMessage());
System.out.println("x = " + x);
}
try {
throwsWithFinally();
} catch (IllegalStateException e) {
System.out.println("Caught 3: " + e.getMessage());
}
Object[] objects = { "a", null };
for (Object o : objects) {
try {
System.out.println(o.toString());
} catch (RuntimeException e) {
System.out.println("Caught NPE");
e.printStackTrace();
}
}
}
private static void testArrayReflection() {
Object[] arrays = new Object[] {
new int[] { 23, 42 },
new byte[] { (byte) 1, (byte) 2, (byte) 3 },
new String[] { "foo", "bar" },
};
for (Object array : arrays) {
int sz = Array.getLength(array);
System.out.println("Array type: " + array.getClass().getName() + ", length " + sz);
for (int i = 0; i < sz; ++i) {
Object item = Array.get(array, i);
System.out.println(" [" + i + "] = " + item + ": " + item.getClass().getName());
}
}
}
private static void doesNotThrow() {
}
private static void throwsWithFinally() {
try {
throwsException();
} finally {
System.out.println("Finally reached");
}
}
private static void throwsException() {
throw new IllegalStateException("exception message");
}
private static Base instance(int index) {
switch (index) {
case 0:
return new Derived1();
case 1:
return new Derived2();
case 2:
return new Derived3();
default:
return new Derived4();
}
}
private static class A {
private int value;
public A(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
interface Base {
int foo();
}
static class Derived1 implements Base {
@Override
public int foo() {
return 234;
}
}
static class Derived2 implements Base {
@Override
public int foo() {
return 345;
}
}
static class Derived3 extends Derived2 {
}
static class Derived4 extends Derived1 {
@Override
public int foo() {
return 123;
}
}
static class Initialized {
static {
System.out.println("Initialized.<clinit>()");
}
public static void foo() {
System.out.println("Initialized.foo()");
}
}
}

View File

@ -27,7 +27,7 @@ import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class WasmFunctionRepository {
public class WasmFunctionRepository implements BaseWasmFunctionRepository {
private WasmModule module;
private WasmFunctionTypes functionTypes;
private NameProvider nameProvider;
@ -50,6 +50,7 @@ public class WasmFunctionRepository {
return isStatic ? forStaticMethod(reference) : forInstanceMethod(reference);
}
@Override
public WasmFunction forStaticMethod(MethodReference reference) {
return staticMethods.computeIfAbsent(reference, key -> {
var wasmParams = new WasmType[key.parameterCount()];
@ -65,6 +66,7 @@ public class WasmFunctionRepository {
});
}
@Override
public WasmFunction forInstanceMethod(MethodReference reference) {
return instanceMethods.computeIfAbsent(reference, key -> {
var wasmParams = new WasmType[key.parameterCount() + 1];

View File

@ -0,0 +1,98 @@
/*
* 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;
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCGenerationUtil;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class WasmGCModuleGenerator {
private final WasmGCTarget wasmGCTarget;
private WasmGCDeclarationsGenerator declarationsGenerator;
private WasmFunction initializer;
private WasmGlobal initializerRef;
public WasmGCModuleGenerator(WasmGCTarget wasmGCTarget, WasmGCDeclarationsGenerator declarationsGenerator) {
this.wasmGCTarget = wasmGCTarget;
this.declarationsGenerator = declarationsGenerator;
}
public void generate() {
declarationsGenerator.generate();
if (initializer != null) {
fillInitializer();
}
}
private void fillInitializer() {
declarationsGenerator.contributeToInitializer(initializer);
initializer.getBody().add(new WasmReturn());
}
public WasmFunction generateMainFunction(String entryPoint) {
var mainFunction = declarationsGenerator.functions().forStaticMethod(new MethodReference(entryPoint,
"main", ValueType.parse(String[].class), ValueType.VOID));
var mainFunctionCaller = new WasmFunction(declarationsGenerator.functionTypes.of(null));
mainFunctionCaller.getBody().add(callInitializer());
var tempVars = new TemporaryVariablePool(mainFunctionCaller);
var genUtil = new WasmGCGenerationUtil(declarationsGenerator.classInfoProvider(), tempVars);
var stringArrayType = declarationsGenerator.typeMapper()
.mapType(ValueType.parse(String[].class))
.asUnpackedType();
var arrayVar = tempVars.acquire(stringArrayType);
genUtil.allocateArray(ValueType.parse(String.class), new WasmInt32Constant(0), null,
arrayVar, mainFunctionCaller.getBody());
var callToMainFunction = new WasmCall(mainFunction, new WasmGetLocal(arrayVar));
mainFunctionCaller.getBody().add(callToMainFunction);
mainFunctionCaller.getBody().add(new WasmReturn());
tempVars.release(arrayVar);
return mainFunctionCaller;
}
private void createInitializer() {
if (initializer != null) {
return;
}
var functionType = declarationsGenerator.functionTypes.of(null);
initializer = new WasmFunction(functionType);
declarationsGenerator.module.functions.add(initializer);
initializerRef = new WasmGlobal("teavm_initializer", functionType.getReference(),
new WasmFunctionReference(initializer));
declarationsGenerator.module.globals.add(initializerRef);
initializer.getBody().add(new WasmSetGlobal(initializerRef,
new WasmFunctionReference(declarationsGenerator.dummyInitializer())));
}
private WasmExpression callInitializer() {
createInitializer();
return new WasmCallReference(new WasmGetGlobal(initializerRef), declarationsGenerator.functionTypes.of(null));
}
}

View File

@ -0,0 +1,143 @@
/*
* 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;
import java.io.IOException;
import java.util.List;
import org.teavm.backend.wasm.gc.WasmGCDependencies;
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerators;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.render.WasmBinaryRenderer;
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyListener;
import org.teavm.interop.Platforms;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.MethodReader;
import org.teavm.model.Program;
import org.teavm.model.transformation.BoundCheckInsertion;
import org.teavm.model.transformation.NullCheckFilter;
import org.teavm.model.transformation.NullCheckInsertion;
import org.teavm.model.util.VariableCategoryProvider;
import org.teavm.vm.BuildTarget;
import org.teavm.vm.TeaVMTarget;
import org.teavm.vm.TeaVMTargetController;
import org.teavm.vm.spi.TeaVMHostExtension;
public class WasmGCTarget implements TeaVMTarget {
private TeaVMTargetController controller;
private NullCheckInsertion nullCheckInsertion;
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
private boolean obfuscated;
public void setObfuscated(boolean obfuscated) {
this.obfuscated = obfuscated;
}
@Override
public void setController(TeaVMTargetController controller) {
this.controller = controller;
nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
}
@Override
public VariableCategoryProvider variableCategoryProvider() {
return null;
}
@Override
public List<DependencyListener> getDependencyListeners() {
return List.of();
}
@Override
public List<ClassHolderTransformer> getTransformers() {
return List.of();
}
@Override
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
new WasmGCDependencies(dependencyAnalyzer).contribute();
}
@Override
public List<TeaVMHostExtension> getHostExtensions() {
return List.of();
}
@Override
public void beforeOptimizations(Program program, MethodReader method) {
/*
nullCheckInsertion.transformProgram(program, method.getReference());
boundCheckInsertion.transformProgram(program, method.getReference());
*/
}
@Override
public void afterOptimizations(Program program, MethodReader method) {
}
@Override
public String[] getPlatformTags() {
return new String[] { Platforms.WEBASSEMBLY_GC };
}
@Override
public boolean isAsyncSupported() {
return false;
}
@Override
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException {
var module = new WasmModule();
var customGenerators = new WasmGCCustomGenerators();
var declarationsGenerator = new WasmGCDeclarationsGenerator(
module,
classes,
controller::isVirtual,
controller.getClassInitializerInfo(),
controller.getDependencyInfo(),
controller.getDiagnostics(),
customGenerators
);
declarationsGenerator.setFriendlyToDebugger(controller.isFriendlyToDebugger());
var moduleGenerator = new WasmGCModuleGenerator(this, declarationsGenerator);
var mainFunction = moduleGenerator.generateMainFunction(controller.getEntryPoint());
mainFunction.setExportName(controller.getEntryPointName());
mainFunction.setName(controller.getEntryPointName());
moduleGenerator.generate();
emitWasmFile(module, buildTarget, outputName);
}
private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
var binaryWriter = new WasmBinaryWriter();
var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated,
null, null, null, null, WasmBinaryStatsCollector.EMPTY);
binaryRenderer.render(module);
var data = binaryWriter.getData();
if (!outputName.endsWith(".wasm")) {
outputName += ".wasm";
}
try (var output = buildTarget.createResource(outputName)) {
output.write(data);
}
}
}

View File

@ -130,7 +130,6 @@ import org.teavm.interop.Import;
import org.teavm.interop.Platforms;
import org.teavm.interop.StaticInit;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.BasicBlock;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassHolder;
@ -139,7 +138,6 @@ import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor;
@ -152,9 +150,6 @@ import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableBuilder;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.lowlevel.Characteristics;
import org.teavm.model.lowlevel.ClassInitializerEliminator;
import org.teavm.model.lowlevel.ClassInitializerTransformer;
@ -1075,40 +1070,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
var builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes));
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes));
builder.setMethodCalledVirtually(controller::isVirtual);
return builder.build();
}
private Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) {
var virtualMethods = new HashSet<MethodReference>();
for (String className : classes.getClassNames()) {
ClassHolder cls = classes.get(className);
for (MethodHolder method : cls.getMethods()) {
Program program = method.getProgram();
if (program == null) {
continue;
}
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i);
for (Instruction insn : block) {
if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction) insn;
if (invoke.getType() == InvocationType.VIRTUAL) {
virtualMethods.add(invoke.getMethod());
}
} else if (insn instanceof CloneArrayInstruction) {
virtualMethods.add(new MethodReference(Object.class, "clone", Object.class));
}
}
}
}
}
return virtualMethods;
}
@Override
public String[] getPlatformTags() {
return new String[] { Platforms.WEBASSEMBLY, Platforms.LOW_LEVEL };

View File

@ -0,0 +1,129 @@
/*
* 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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.analysis.BaseTypeInference;
public class PreciseTypeInference extends BaseTypeInference<ValueType> {
public static final ValueType OBJECT_TYPE = ValueType.object("java.lang.Object");
private ClassHierarchy hierarchy;
public PreciseTypeInference(Program program, MethodReference reference, ClassHierarchy hierarchy) {
super(program, reference);
this.hierarchy = hierarchy;
}
@Override
protected ValueType mapType(ValueType type) {
return type;
}
@Override
protected ValueType nullType() {
return null;
}
@Override
protected ValueType merge(ValueType a, ValueType b) {
if (a == null) {
return b;
} else if (b == null) {
return a;
} else {
if (a instanceof ValueType.Primitive && b instanceof ValueType.Primitive) {
if (a != b) {
return OBJECT_TYPE;
} else {
return a;
}
} else if (a instanceof ValueType.Array) {
if (b instanceof ValueType.Array) {
var p = ((ValueType.Array) a).getItemType();
var q = ((ValueType.Array) b).getItemType();
return ValueType.arrayOf(merge(p, q));
} else {
return OBJECT_TYPE;
}
} else if (b instanceof ValueType.Array) {
return OBJECT_TYPE;
} else if (a instanceof ValueType.Object) {
if (b instanceof ValueType.Object) {
var p = ((ValueType.Object) a).getClassName();
var q = ((ValueType.Object) b).getClassName();
if (p.equals(q)) {
return a;
}
var first = hierarchy.getClassSource().get(p);
if (first == null) {
p = "java.lang.Object";
}
var second = hierarchy.getClassSource().get(q);
if (second == null) {
q = "java.lang.Object";
}
if (hierarchy.isSuperType(p, q, false)) {
return a;
} else if (hierarchy.isSuperType(q, p, false)) {
return b;
}
return ValueType.object(findCommonSuperclass(first, second));
} else {
return OBJECT_TYPE;
}
} else {
return OBJECT_TYPE;
}
}
}
private String findCommonSuperclass(ClassReader a, ClassReader b) {
var firstPath = findPathToRoot(a);
Collections.reverse(firstPath);
var secondPath = findPathToRoot(b);
Collections.reverse(secondPath);
if (firstPath.get(0) != secondPath.get(0)) {
return "java.lang.Object";
}
var max = Math.max(firstPath.size(), secondPath.size());
var index = 1;
while (index < max && firstPath.get(index) == secondPath.get(index)) {
++index;
}
return firstPath.get(index).getName();
}
private List<ClassReader> findPathToRoot(ClassReader cls) {
var path = new ArrayList<ClassReader>();
while (cls != null) {
path.add(cls);
cls = cls.getParent() != null ? hierarchy.getClassSource().get(cls.getParent()) : null;
}
return path;
}
@Override
protected ValueType elementType(ValueType valueType) {
return ((ValueType.Array) valueType).getItemType();
}
}

View File

@ -0,0 +1,63 @@
/*
* 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;
import java.util.Arrays;
import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.model.MethodReference;
public class WasmGCDependencies {
private DependencyAnalyzer analyzer;
public WasmGCDependencies(DependencyAnalyzer analyzer) {
this.analyzer = analyzer;
}
public void contribute() {
contributeMathUtils();
contributeExceptionUtils();
contributeInitializerUtils();
}
private void contributeMathUtils() {
for (var type : Arrays.asList(int.class, long.class, float.class, double.class)) {
var method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class);
analyzer.linkMethod(method).use();
}
for (var type : Arrays.asList(int.class, long.class)) {
var method = new MethodReference(WasmRuntime.class, "compareUnsigned", type, type, int.class);
analyzer.linkMethod(method).use();
}
for (var type : Arrays.asList(float.class, double.class)) {
var method = new MethodReference(WasmRuntime.class, "remainder", type, type, type);
analyzer.linkMethod(method).use();
}
}
private void contributeExceptionUtils() {
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "npe", NullPointerException.class));
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class));
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class));
}
private void contributeInitializerUtils() {
analyzer.linkMethod(new MethodReference(WasmGCStringPool.class, "nextCharArray", char[].class));
}
}

View File

@ -0,0 +1,45 @@
/*
* 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;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.util.VariableCategoryProvider;
public class WasmGCVariableCategoryProvider implements VariableCategoryProvider {
private ClassHierarchy hierarchy;
private PreciseTypeInference inference;
public WasmGCVariableCategoryProvider(ClassHierarchy hierarchy) {
this.hierarchy = hierarchy;
}
public PreciseTypeInference getTypeInference() {
return inference;
}
@Override
public Object[] getCategories(Program program, MethodReference method) {
inference = new PreciseTypeInference(program, method, hierarchy);
var result = new Object[program.variableCount()];
for (int i = 0; i < program.variableCount(); ++i) {
var type = inference.typeOf(program.variableAt(i));
result[i] = type != null ? type : PreciseTypeInference.OBJECT_TYPE;
}
return result;
}
}

View File

@ -90,7 +90,7 @@ public class WasmGenerationContext implements BaseWasmGenerationContext {
}
@Override
public ClassReaderSource classSource() {
public ClassReaderSource classes() {
return classSource;
}

View File

@ -15,17 +15,17 @@
*/
package org.teavm.backend.wasm.generate.common.methods;
import org.teavm.backend.wasm.WasmFunctionRepository;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.model.ClassReaderSource;
public interface BaseWasmGenerationContext {
WasmFunctionRepository functions();
BaseWasmFunctionRepository functions();
WasmFunctionTypes functionTypes();
WasmTag getExceptionTag();
ClassReaderSource classSource();
ClassReaderSource classes();
}

View File

@ -832,9 +832,11 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) {
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
var method = context.classSource().resolve(expr.getMethod());
var method = context.classes().resolve(expr.getMethod());
var reference = method != null ? method.getReference() : expr.getMethod();
var function = context.functions().forMethod(reference, expr.getType() == InvocationType.STATIC);
var function = expr.getType() == InvocationType.STATIC
? context.functions().forStaticMethod(reference)
: context.functions().forInstanceMethod(reference);
var call = new WasmCall(function);
for (var argument : expr.getArguments()) {

View File

@ -0,0 +1,143 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.generate.gc;
import java.util.List;
import java.util.function.Predicate;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.WasmNameProvider;
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.WasmGCSupertypeFunctionProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCMethodGenerator;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.dependency.DependencyInfo;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.MethodReference;
import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableBuilder;
import org.teavm.model.classes.VirtualTableProvider;
public class WasmGCDeclarationsGenerator {
public final ClassHierarchy hierarchy;
public final WasmModule module;
public final WasmFunctionTypes functionTypes;
private final WasmGCClassGenerator classGenerator;
private final WasmGCMethodGenerator methodGenerator;
public WasmGCDeclarationsGenerator(
WasmModule module,
ListableClassHolderSource classes,
Predicate<MethodReference> virtualMethods,
ClassInitializerInfo classInitializerInfo,
DependencyInfo dependencyInfo,
Diagnostics diagnostics,
WasmGCCustomGeneratorProvider customGenerators
) {
this.module = module;
hierarchy = new ClassHierarchy(classes);
var virtualTables = createVirtualTableProvider(classes, virtualMethods);
functionTypes = new WasmFunctionTypes(module);
var names = new WasmNameProvider();
methodGenerator = new WasmGCMethodGenerator(
module,
hierarchy,
classes,
virtualTables,
classInitializerInfo,
functionTypes,
names,
diagnostics,
customGenerators
);
var tags = new TagRegistry(classes, hierarchy);
var metadataRequirements = new ClassMetadataRequirements(dependencyInfo);
classGenerator = new WasmGCClassGenerator(
module,
classes,
functionTypes,
tags,
metadataRequirements,
virtualTables,
methodGenerator,
names,
classInitializerInfo
);
methodGenerator.setClassInfoProvider(classGenerator);
methodGenerator.setStrings(classGenerator.strings);
methodGenerator.setSupertypeFunctions(classGenerator.getSupertypeProvider());
methodGenerator.setStandardClasses(classGenerator.standardClasses);
methodGenerator.setTypeMapper(classGenerator.typeMapper);
}
public void setFriendlyToDebugger(boolean friendlyToDebugger) {
methodGenerator.setFriendlyToDebugger(friendlyToDebugger);
}
public WasmGCClassInfoProvider classInfoProvider() {
return classGenerator;
}
public WasmGCTypeMapper typeMapper() {
return classGenerator.typeMapper;
}
public BaseWasmFunctionRepository functions() {
return methodGenerator;
}
public WasmGCSupertypeFunctionProvider supertypeFunctions() {
return classGenerator.getSupertypeProvider();
}
public void generate() {
boolean somethingGenerated;
do {
somethingGenerated = false;
somethingGenerated |= methodGenerator.process();
somethingGenerated |= classGenerator.process();
} while (somethingGenerated);
}
public void contributeToInitializer(WasmFunction function) {
classGenerator.contributeToInitializer(function);
var contributors = List.of(classGenerator, classGenerator.strings);
for (var contributor : contributors) {
contributor.contributeToInitializerDefinitions(function);
contributor.contributeToInitializer(function);
}
}
private static VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes,
Predicate<MethodReference> virtualMethods) {
var builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes));
builder.setMethodCalledVirtually(virtualMethods);
return builder.build();
}
public WasmFunction dummyInitializer() {
return methodGenerator.getDummyInitializer();
}
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.generate.gc.initialization;
package org.teavm.backend.wasm.generate.gc;
import org.teavm.backend.wasm.model.WasmFunction;

View File

@ -17,13 +17,18 @@ package org.teavm.backend.wasm.generate.gc.classes;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.function.Consumer;
import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCFunctionProvider;
import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmFunction;
@ -70,17 +75,20 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private TagRegistry tagRegistry;
private ClassMetadataRequirements metadataRequirements;
private VirtualTableProvider virtualTables;
private WasmGCFunctionProvider functionProvider;
private BaseWasmFunctionRepository functionProvider;
private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>();
private Queue<WasmGCClassInfo> classInfoQueue = new ArrayDeque<>();
private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>();
private ObjectIntMap<MethodReference> methodIndexes = new ObjectIntHashMap<>();
private Map<FieldReference, WasmGlobal> staticFieldLocations = new HashMap<>();
private List<Consumer<WasmFunction>> staticFieldInitializers = new ArrayList<>();
private ClassInitializerInfo classInitializerInfo;
public final WasmGCStringPool strings;
public final WasmGCStandardClasses standardClasses;
private final WasmGCTypeMapper typeMapper;
public final WasmGCTypeMapper typeMapper;
private final NameProvider names;
private WasmFunction initializer;
private List<WasmExpression> initializerFunctionStatements = new ArrayList<>();
private WasmFunction createPrimitiveClassFunction;
private WasmFunction createArrayClassFunction;
private final WasmGCSupertypeFunctionGenerator supertypeGenerator;
@ -97,7 +105,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables,
WasmGCFunctionProvider functionProvider, NameProvider names,
BaseWasmFunctionRepository functionProvider, NameProvider names,
ClassInitializerInfo classInitializerInfo) {
this.module = module;
this.classSource = classSource;
@ -114,6 +122,22 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
typeMapper = new WasmGCTypeMapper(this);
}
public WasmGCSupertypeFunctionProvider getSupertypeProvider() {
return supertypeGenerator;
}
public boolean process() {
if (classInfoQueue.isEmpty()) {
return false;
}
while (!classInfoQueue.isEmpty()) {
var classInfo = classInfoQueue.remove();
classInfo.initializer.accept(initializerFunctionStatements);
classInfo.initializer = null;
}
return true;
}
@Override
public void contributeToInitializerDefinitions(WasmFunction function) {
for (var classInfo : classInfoMap.values()) {
@ -125,8 +149,9 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
@Override
public void contributeToInitializer(WasmFunction function) {
var classClass = standardClasses.classClass();
function.getBody().addAll(initializerFunctionStatements);
initializerFunctionStatements.clear();
for (var classInfo : classInfoMap.values()) {
classInfo.initializer.accept(function.getBody());
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset,
new WasmFunctionReference(supertypeFunction)));
@ -134,12 +159,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
new WasmGetGlobal(classClass.pointer)));
if (classInfo.initializerPointer != null) {
var className = ((ValueType.Object) classInfo.getValueType()).getClassName();
var initFunction = functionProvider.getStaticFunction(new MethodReference(className,
var initFunction = functionProvider.forStaticMethod(new MethodReference(className,
CLINIT_METHOD_DESC));
function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer,
new WasmFunctionReference(initFunction)));
}
}
for (var consumer : staticFieldInitializers) {
consumer.accept(function);
}
}
@Override
@ -147,6 +175,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
var classInfo = classInfoMap.get(type);
if (classInfo == null) {
classInfo = new WasmGCClassInfo(type);
classInfoQueue.add(classInfo);
classInfoMap.put(type, classInfo);
if (!(type instanceof ValueType.Primitive)) {
var name = type instanceof ValueType.Object
@ -154,6 +183,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
: null;
classInfo.structure = new WasmStructure(name);
classInfo.structure.getFields().add(standardClasses.classClass().getType().asStorage());
module.types.add(classInfo.structure);
fillFields(classInfo.structure.getFields(), type);
}
var pointerName = names.forClassInstance(type);
@ -241,6 +271,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
var clinitType = functionTypes.of(null);
classInfo.initializerPointer = new WasmGlobal(null, clinitType.getReference(),
new WasmNullConstant(clinitType.getReference()));
module.globals.add(classInfo.initializerPointer);
}
}
classInfo.initializer = target -> {
@ -276,7 +307,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
for (var method : virtualTable.getMethods()) {
var entry = virtualTable.getEntry(method);
if (entry != null && entry.getImplementor() != null) {
var function = functionProvider.getMemberFunction(entry.getImplementor());
var function = functionProvider.forInstanceMethod(entry.getImplementor());
if (!origin.equals(entry.getImplementor().getClassName())) {
var functionType = getFunctionType(virtualTable.getClassName(), method);
var wrapperFunction = new WasmFunction(functionType);
@ -361,6 +392,33 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return result;
}
@Override
public WasmGlobal getStaticFieldLocation(FieldReference fieldRef) {
return staticFieldLocations.computeIfAbsent(fieldRef, this::generateStaticFieldLocation);
}
private WasmGlobal generateStaticFieldLocation(FieldReference fieldRef) {
ValueType javaType = null;
Object initValue = null;
var cls = classSource.get(fieldRef.getClassName());
if (cls != null) {
var field = cls.getField(fieldRef.getFieldName());
if (field != null) {
javaType = field.getType();
initValue = field.getInitialValue();
}
}
if (javaType == null) {
javaType = ValueType.object("java.lang.Object");
}
var type = typeMapper.mapType(javaType).asUnpackedType();
var global = new WasmGlobal(names.forStaticField(fieldRef), type, WasmExpression.defaultValueOfType(type));
module.globals.add(global);
return global;
}
@Override
public int getVirtualMethodIndex(MethodReference methodRef) {
var result = methodIndexes.getOrDefault(methodRef, -1);
@ -442,13 +500,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
);
var function = new WasmFunction(functionType);
function.setName("_teavm_fill_primitive_class_");
module.functions.add(function);
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name");
var kindVar = new WasmLocal(WasmType.INT32, "kind");
function.getLocalVariables().add(targetVar);
function.getLocalVariables().add(nameVar);
function.getLocalVariables().add(kindVar);
function.add(targetVar);
function.add(nameVar);
function.add(kindVar);
var flagsExpr = new WasmIntBinary(
WasmIntType.INT32,
@ -497,12 +556,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
standardClasses.classClass().getType()
);
var function = new WasmFunction(functionType);
module.functions.add(function);
function.setName("_teavm_fill_array_class_");
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item");
function.getLocalVariables().add(targetVar);
function.getLocalVariables().add(itemVar);
function.add(targetVar);
function.add(itemVar);
function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(),
@ -538,15 +598,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
private WasmFunction getInitializer() {
if (initializer == null) {
initializer = new WasmFunction(functionTypes.of(null));
initializer.setName("_teavm_init_classes_");
module.functions.add(initializer);
}
return initializer;
}
private WasmExpression setClassField(WasmGCClassInfo classInfo, int fieldIndex, WasmExpression value) {
return new WasmStructSet(
standardClasses.classClass().getStructure(),

View File

@ -15,6 +15,7 @@
*/
package org.teavm.backend.wasm.generate.gc.classes;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
@ -22,12 +23,15 @@ import org.teavm.model.ValueType;
public interface WasmGCClassInfoProvider {
int CLASS_FIELD_OFFSET = 0;
int MONITOR_FIELD_OFFSET = 1;
int CUSTOM_FIELD_OFFSETS = 2;
int ARRAY_DATA_FIELD_OFFSET = 2;
WasmGCClassInfo getClassInfo(ValueType type);
int getFieldIndex(FieldReference fieldRef);
WasmGlobal getStaticFieldLocation(FieldReference fieldRef);
int getVirtualMethodIndex(MethodReference methodRef);
default WasmGCClassInfo getClassInfo(String name) {

View File

@ -15,8 +15,6 @@
*/
package org.teavm.backend.wasm.generate.gc.classes;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@ -45,8 +43,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.model.ValueType;
import org.teavm.model.classes.TagRegistry;
public class WasmGCSupertypeFunctionGenerator {
private ObjectIntMap<ValueType> tableIndexes = new ObjectIntHashMap<>();
public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunctionProvider {
private Map<ValueType, WasmFunction> functions = new HashMap<>();
private WasmModule module;
private WasmGCClassGenerator classGenerator;
@ -69,6 +66,7 @@ public class WasmGCSupertypeFunctionGenerator {
this.functionTypes = functionTypes;
}
@Override
public WasmFunction getIsSupertypeFunction(ValueType type) {
var result = functions.get(type);
if (result == null) {

View File

@ -0,0 +1,23 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.generate.gc.classes;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.model.ValueType;
public interface WasmGCSupertypeFunctionProvider {
WasmFunction getIsSupertypeFunction(ValueType type);
}

View File

@ -15,11 +15,9 @@
*/
package org.teavm.backend.wasm.generate.gc.methods;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator;
import org.teavm.model.MethodReference;
public interface WasmGCFunctionProvider {
WasmFunction getMemberFunction(MethodReference methodRef);
WasmFunction getStaticFunction(MethodReference methodRef);
public interface WasmGCCustomGeneratorProvider {
WasmGCCustomGenerator get(MethodReference method);
}

View File

@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.generate.gc;
package org.teavm.backend.wasm.generate.gc.methods;
import org.teavm.backend.wasm.WasmFunctionRepository;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
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.WasmGCStandardClasses;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCSupertypeFunctionProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
import org.teavm.backend.wasm.model.WasmFunction;
@ -39,20 +40,33 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private WasmGCStringProvider strings;
private VirtualTableProvider virtualTables;
private WasmGCTypeMapper typeMapper;
private WasmFunctionTypes functionTypes;
private ClassReaderSource classes;
private BaseWasmFunctionRepository functions;
private WasmGCSupertypeFunctionProvider supertypeFunctions;
private WasmGCCustomGeneratorProvider customGenerators;
private WasmFunction npeMethod;
private WasmFunction aaiobeMethod;
private WasmFunction cceMethod;
private WasmGlobal exceptionGlobal;
private WasmTag exceptionTag;
public WasmGCGenerationContext(WasmModule module, WasmGCClassInfoProvider classInfoProvider,
WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, VirtualTableProvider virtualTables,
WasmGCTypeMapper typeMapper) {
public WasmGCGenerationContext(WasmModule module, VirtualTableProvider virtualTables,
WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ClassReaderSource classes,
BaseWasmFunctionRepository functions, WasmGCSupertypeFunctionProvider supertypeFunctions,
WasmGCClassInfoProvider classInfoProvider, WasmGCStandardClasses standardClasses,
WasmGCStringProvider strings, WasmGCCustomGeneratorProvider customGenerators) {
this.module = module;
this.virtualTables = virtualTables;
this.typeMapper = typeMapper;
this.functionTypes = functionTypes;
this.classes = classes;
this.functions = functions;
this.supertypeFunctions = supertypeFunctions;
this.classInfoProvider = classInfoProvider;
this.standardClasses = standardClasses;
this.strings = strings;
this.virtualTables = virtualTables;
this.typeMapper = typeMapper;
this.customGenerators = customGenerators;
}
public WasmGCClassInfoProvider classInfoProvider() {
@ -76,23 +90,31 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
}
@Override
public WasmFunctionRepository functions() {
return null;
public BaseWasmFunctionRepository functions() {
return functions;
}
public WasmGCSupertypeFunctionProvider supertypeFunctions() {
return supertypeFunctions;
}
@Override
public WasmFunctionTypes functionTypes() {
return null;
return functionTypes;
}
@Override
public WasmTag getExceptionTag() {
return null;
if (exceptionTag == null) {
exceptionTag = new WasmTag(functionTypes.of(null));
module.tags.add(exceptionTag);
}
return exceptionTag;
}
@Override
public ClassReaderSource classSource() {
return null;
public ClassReaderSource classes() {
return classes;
}
public WasmFunction npeMethod() {
@ -127,4 +149,8 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
}
return exceptionGlobal;
}
public WasmGCCustomGeneratorProvider customGenerators() {
return customGenerators;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.generate.gc.methods;
import java.util.List;
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault;
import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.model.TextLocation;
import org.teavm.model.ValueType;
public class WasmGCGenerationUtil {
private WasmGCClassInfoProvider classInfoProvider;
private TemporaryVariablePool tempVars;
public WasmGCGenerationUtil(WasmGCClassInfoProvider classInfoProvider, TemporaryVariablePool tempVars) {
this.classInfoProvider = classInfoProvider;
this.tempVars = tempVars;
}
public void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local,
List<WasmExpression> target) {
var classInfo = classInfoProvider.getClassInfo(ValueType.arrayOf(itemType));
var block = new WasmBlock(false);
block.setType(classInfo.getType());
var targetVar = local;
if (targetVar == null) {
targetVar = tempVars.acquire(classInfo.getType());
}
var structNew = new WasmSetLocal(targetVar, new WasmStructNewDefault(classInfo.getStructure()));
structNew.setLocation(location);
target.add(structNew);
var initClassField = new WasmStructSet(classInfo.getStructure(), new WasmGetLocal(targetVar),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, new WasmGetGlobal(classInfo.getPointer()));
initClassField.setLocation(location);
target.add(initClassField);
var wasmArrayType = (WasmType.CompositeReference) classInfo.getStructure().getFields()
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET)
.asUnpackedType();
var wasmArray = (WasmArray) wasmArrayType.composite;
var initArrayField = new WasmStructSet(
classInfo.getStructure(),
new WasmGetLocal(targetVar),
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET,
new WasmArrayNewDefault(wasmArray, length)
);
initArrayField.setLocation(location);
target.add(initArrayField);
if (local == null) {
var getLocal = new WasmGetLocal(targetVar);
getLocal.setLocation(location);
target.add(getLocal);
tempVars.release(targetVar);
}
}
}

View File

@ -23,7 +23,6 @@ import org.teavm.ast.QualificationExpr;
import org.teavm.ast.SubscriptExpr;
import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor;
import org.teavm.backend.wasm.generate.gc.WasmGCGenerationContext;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmFunction;
@ -33,7 +32,6 @@ import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayGet;
import org.teavm.backend.wasm.model.expression.WasmArrayLength;
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault;
import org.teavm.backend.wasm.model.expression.WasmArraySet;
import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmCall;
@ -57,11 +55,13 @@ import org.teavm.model.ValueType;
public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
private WasmGCGenerationContext context;
private WasmGCGenerationUtil generationUtil;
public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function,
int firstVariable, boolean async) {
super(context, function, firstVariable, async);
this.context = context;
generationUtil = new WasmGCGenerationUtil(context.classInfoProvider(), tempVars);
}
@Override
@ -130,20 +130,29 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override
protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) {
accept(qualified);
var target = result;
accept(value);
var wasmValue = result;
if (qualified == null) {
accept(value);
var wasmValue = result;
var global = context.classInfoProvider().getStaticFieldLocation(field);
var result = new WasmSetGlobal(global, wasmValue);
result.setLocation(location);
resultConsumer.add(result);
} else {
accept(qualified);
var target = result;
accept(value);
var wasmValue = result;
target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult();
var struct = (WasmStructure) type.composite;
target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult();
var struct = (WasmStructure) type.composite;
var fieldIndex = context.classInfoProvider().getFieldIndex(field);
var fieldIndex = context.classInfoProvider().getFieldIndex(field);
var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue);
expr.setLocation(location);
resultConsumer.add(expr);
var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue);
expr.setLocation(location);
resultConsumer.add(expr);
}
}
@Override
@ -230,42 +239,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override
protected void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local,
List<WasmExpression> target) {
var classInfo = context.classInfoProvider().getClassInfo(ValueType.arrayOf(itemType));
var block = new WasmBlock(false);
block.setType(classInfo.getType());
var targetVar = local;
if (targetVar == null) {
targetVar = tempVars.acquire(classInfo.getType());
}
var structNew = new WasmSetLocal(targetVar, new WasmStructNewDefault(classInfo.getStructure()));
structNew.setLocation(location);
target.add(structNew);
var initClassField = new WasmStructSet(classInfo.getStructure(), new WasmGetLocal(targetVar),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, new WasmGetGlobal(classInfo.getPointer()));
initClassField.setLocation(location);
target.add(initClassField);
var wasmArrayType = (WasmType.CompositeReference) classInfo.getStructure().getFields()
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET)
.asUnpackedType();
var wasmArray = (WasmArray) wasmArrayType.composite;
var initArrayField = new WasmStructSet(
classInfo.getStructure(),
new WasmGetLocal(targetVar),
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET,
new WasmArrayNewDefault(wasmArray, length)
);
initArrayField.setLocation(location);
target.add(initArrayField);
if (local == null) {
var getLocal = new WasmGetLocal(targetVar);
getLocal.setLocation(location);
target.add(getLocal);
tempVars.release(targetVar);
}
generationUtil.allocateArray(itemType, length, location, local, target);
}
@Override
@ -277,7 +251,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override
protected WasmExpression generateInstanceOf(WasmExpression expression, ValueType type) {
context.classInfoProvider().getClassInfo(type);
var supertypeCall = new WasmCall(context.functions().forSupertype(type));
var supertypeCall = new WasmCall(context.supertypeFunctions().getIsSupertypeFunction(type));
var classRef = new WasmStructGet(
context.standardClasses().objectClass().getStructure(),
expression,
@ -352,17 +326,23 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override
public void visit(QualificationExpr expr) {
accept(expr.getQualified());
var target = result;
if (expr.getQualified() == null) {
var global = context.classInfoProvider().getStaticFieldLocation(expr.getField());
result = new WasmGetGlobal(global);
result.setLocation(expr.getLocation());
} else {
accept(expr.getQualified());
var target = result;
target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult();
var struct = (WasmStructure) type.composite;
target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult();
var struct = (WasmStructure) type.composite;
var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField());
var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField());
result = new WasmStructGet(struct, target, fieldIndex);
result.setLocation(expr.getLocation());
result = new WasmStructGet(struct, target, fieldIndex);
result.setLocation(expr.getLocation());
}
}
private class SimpleCallSite extends CallSiteIdentifier {

View File

@ -0,0 +1,319 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.generate.gc.methods;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.teavm.ast.decompilation.Decompiler;
import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.WasmGCVariableCategoryProvider;
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.WasmGCSupertypeFunctionProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator;
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorContext;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.interop.Import;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassHolderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.util.RegisterAllocator;
public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
private WasmModule module;
private ClassHierarchy hierarchy;
private ClassHolderSource classes;
private VirtualTableProvider virtualTables;
private ClassInitializerInfo classInitInfo;
private WasmFunctionTypes functionTypes;
private WasmGCSupertypeFunctionProvider supertypeFunctions;
private NameProvider names;
private Diagnostics diagnostics;
private WasmGCTypeMapper typeMapper;
private WasmGCCustomGeneratorProvider customGenerators;
private Queue<Runnable> queue = new ArrayDeque<>();
private Map<MethodReference, WasmFunction> staticMethods = new HashMap<>();
private Map<MethodReference, WasmFunction> instanceMethods = new HashMap<>();
private boolean friendlyToDebugger;
private Decompiler decompiler;
private WasmGCGenerationContext context;
private WasmFunction dummyInitializer;
private WasmGCClassInfoProvider classInfoProvider;
private WasmGCStandardClasses standardClasses;
private WasmGCStringProvider strings;
public WasmGCMethodGenerator(
WasmModule module,
ClassHierarchy hierarchy,
ClassHolderSource classes,
VirtualTableProvider virtualTables,
ClassInitializerInfo classInitInfo,
WasmFunctionTypes functionTypes,
NameProvider names,
Diagnostics diagnostics,
WasmGCCustomGeneratorProvider customGenerators
) {
this.module = module;
this.hierarchy = hierarchy;
this.classes = classes;
this.virtualTables = virtualTables;
this.classInitInfo = classInitInfo;
this.functionTypes = functionTypes;
this.names = names;
this.diagnostics = diagnostics;
this.customGenerators = customGenerators;
}
public void setTypeMapper(WasmGCTypeMapper typeMapper) {
this.typeMapper = typeMapper;
}
public void setFriendlyToDebugger(boolean friendlyToDebugger) {
this.friendlyToDebugger = friendlyToDebugger;
}
public void setClassInfoProvider(WasmGCClassInfoProvider classInfoProvider) {
this.classInfoProvider = classInfoProvider;
}
public void setStandardClasses(WasmGCStandardClasses standardClasses) {
this.standardClasses = standardClasses;
}
public void setSupertypeFunctions(WasmGCSupertypeFunctionProvider supertypeFunctions) {
this.supertypeFunctions = supertypeFunctions;
}
public void setStrings(WasmGCStringProvider strings) {
this.strings = strings;
}
public boolean process() {
if (queue.isEmpty()) {
return false;
}
while (!queue.isEmpty()) {
queue.remove().run();
}
return true;
}
@Override
public WasmFunction forStaticMethod(MethodReference methodReference) {
return staticMethods.computeIfAbsent(methodReference, this::createStaticFunction);
}
private WasmFunction createStaticFunction(MethodReference methodReference) {
var returnType = typeMapper.mapType(methodReference.getReturnType()).asUnpackedType();
var parameterTypes = new WasmType[methodReference.parameterCount()];
for (var i = 0; i < parameterTypes.length; ++i) {
parameterTypes[i] = typeMapper.mapType(methodReference.parameterType(i)).asUnpackedType();
}
var function = new WasmFunction(functionTypes.of(returnType, parameterTypes));
function.setName(names.forMethod(methodReference));
module.functions.add(function);
function.setJavaMethod(methodReference);
var cls = classes.get(methodReference.getClassName());
if (cls != null) {
var method = cls.getMethod(methodReference.getDescriptor());
if (method != null && method.hasModifier(ElementModifier.STATIC)) {
queue.add(() -> generateMethodBody(method, function));
}
}
return function;
}
@Override
public WasmFunction forInstanceMethod(MethodReference methodReference) {
return instanceMethods.computeIfAbsent(methodReference, this::createInstanceFunction);
}
private WasmFunction createInstanceFunction(MethodReference methodReference) {
var returnType = typeMapper.mapType(methodReference.getReturnType()).asUnpackedType();
var parameterTypes = new WasmType[methodReference.parameterCount() + 1];
parameterTypes[0] = typeMapper.mapType(ValueType.object(methodReference.getClassName())).asUnpackedType();
for (var i = 0; i < methodReference.parameterCount(); ++i) {
parameterTypes[i + 1] = typeMapper.mapType(methodReference.parameterType(i)).asUnpackedType();
}
var function = new WasmFunction(functionTypes.of(returnType, parameterTypes));
function.setName(names.forMethod(methodReference));
module.functions.add(function);
function.setJavaMethod(methodReference);
var cls = classes.get(methodReference.getClassName());
if (cls != null) {
var method = cls.getMethod(methodReference.getDescriptor());
if (method != null && !method.hasModifier(ElementModifier.STATIC)) {
queue.add(() -> generateMethodBody(method, function));
}
}
return function;
}
private void generateMethodBody(MethodHolder method, WasmFunction function) {
var customGenerator = customGenerators.get(method.getReference());
if (customGenerator != null) {
generateCustomMethodBody(customGenerator, method.getReference(), function);
} else if (!method.hasModifier(ElementModifier.NATIVE)) {
generateRegularMethodBody(method, function);
} else {
generateNativeMethodBody(method, function);
}
}
private void generateCustomMethodBody(WasmGCCustomGenerator customGenerator, MethodReference method,
WasmFunction function) {
customGenerator.apply(method, function, customGeneratorContext);
}
private void generateRegularMethodBody(MethodHolder method, WasmFunction function) {
var decompiler = getDecompiler();
var categoryProvider = new WasmGCVariableCategoryProvider(hierarchy);
var allocator = new RegisterAllocator(categoryProvider);
allocator.allocateRegisters(method.getReference(), method.getProgram(), friendlyToDebugger);
var ast = decompiler.decompileRegular(method);
var firstVar = method.hasModifier(ElementModifier.STATIC) ? 1 : 0;
var typeInference = categoryProvider.getTypeInference();
var registerCount = 0;
for (var i = 0; i < method.getProgram().variableCount(); ++i) {
registerCount = Math.max(registerCount, method.getProgram().variableAt(i).getRegister() + 1);
}
var originalIndexToIndex = new int[registerCount];
Arrays.fill(originalIndexToIndex, -1);
for (var varNode : ast.getVariables()) {
originalIndexToIndex[varNode.getOriginalIndex()] = varNode.getIndex();
}
var variableRepresentatives = new int[registerCount];
Arrays.fill(variableRepresentatives, -1);
for (var i = 0; i < method.getProgram().variableCount(); ++i) {
var variable = method.getProgram().variableAt(i);
var varNodeIndex = variable.getRegister() >= 0 ? originalIndexToIndex[variable.getRegister()] : -1;
if (varNodeIndex >= 0 && variableRepresentatives[varNodeIndex] < 0) {
variableRepresentatives[varNodeIndex] = variable.getIndex();
}
}
for (var i = firstVar; i < ast.getVariables().size(); ++i) {
var localVar = ast.getVariables().get(i);
var representative = method.getProgram().variableAt(variableRepresentatives[i]);
var type = typeMapper.mapType(typeInference.typeOf(representative)).asUnpackedType();
var wasmLocal = new WasmLocal(type, localVar.getName());
function.add(wasmLocal);
}
addInitializerErase(method, function);
var visitor = new WasmGCGenerationVisitor(getGenerationContext(), function, firstVar, false);
visitor.generate(ast.getBody(), function.getBody());
}
private void generateNativeMethodBody(MethodHolder method, WasmFunction function) {
var importAnnot = method.getAnnotations().get(Import.class.getName());
if (importAnnot == null) {
diagnostics.error(new CallLocation(method.getReference()), "Method is not annotated with {{c0}}",
Import.class.getName());
return;
}
function.setImportName(importAnnot.getValue("name").getString());
var moduleName = importAnnot.getValue("module");
function.setImportModule(moduleName != null ? moduleName.getString() : "teavm");
}
private void addInitializerErase(MethodReader method, WasmFunction function) {
if (method.hasModifier(ElementModifier.STATIC) && method.getName().equals("<clinit>")
&& method.parameterCount() == 0 && classInitInfo.isDynamicInitializer(method.getOwnerName())) {
var classInfo = classInfoProvider.getClassInfo(method.getOwnerName());
var erase = new WasmSetGlobal(classInfo.getInitializerPointer(),
new WasmFunctionReference(getDummyInitializer()));
function.getBody().add(erase);
}
}
private Decompiler getDecompiler() {
if (decompiler == null) {
decompiler = new Decompiler(classes, Set.of(), friendlyToDebugger);
}
return decompiler;
}
private WasmGCGenerationContext getGenerationContext() {
if (context == null) {
context = new WasmGCGenerationContext(
module,
virtualTables,
typeMapper,
functionTypes,
classes,
this,
supertypeFunctions,
classInfoProvider,
standardClasses,
strings,
customGenerators
);
}
return context;
}
public WasmFunction getDummyInitializer() {
if (dummyInitializer == null) {
dummyInitializer = new WasmFunction(functionTypes.of(null));
dummyInitializer.getBody().add(new WasmReturn());
dummyInitializer.setName("teavm_dummy_initializer");
module.functions.add(dummyInitializer);
}
return dummyInitializer;
}
private WasmGCCustomGeneratorContext customGeneratorContext = new WasmGCCustomGeneratorContext() {
@Override
public WasmModule module() {
return module;
}
@Override
public WasmFunctionTypes functionTypes() {
return functionTypes;
}
};
}

View File

@ -18,15 +18,20 @@ package org.teavm.backend.wasm.generate.gc.strings;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor;
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.initialization.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCFunctionProvider;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmMemorySegment;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.model.MethodReference;
@ -35,10 +40,10 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
private WasmModule module;
private WasmBinaryWriter binaryWriter = new WasmBinaryWriter();
private Map<String, WasmGCStringConstant> stringMap = new LinkedHashMap<>();
private WasmGCFunctionProvider functionProvider;
private BaseWasmFunctionRepository functionProvider;
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
WasmGCFunctionProvider functionProvider) {
BaseWasmFunctionRepository functionProvider) {
this.standardClasses = standardClasses;
this.module = module;
this.functionProvider = functionProvider;
@ -46,6 +51,9 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
@Override
public void contributeToInitializerDefinitions(WasmFunction function) {
var segment = new WasmMemorySegment();
module.getSegments().add(segment);
segment.setData(binaryWriter.getData());
for (var str : stringMap.values()) {
var newStruct = new WasmStructNewDefault(standardClasses.stringClass().getStructure());
function.getBody().add(new WasmSetGlobal(str.global, newStruct));
@ -54,8 +62,14 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
@Override
public void contributeToInitializer(WasmFunction function) {
var nextCharArrayFunction = functionProvider.getStaticFunction(new MethodReference(WasmGCStringPool.class,
var nextCharArrayFunction = functionProvider.forStaticMethod(new MethodReference(WasmGCStringPool.class,
"nextCharArray", char[].class));
var stringStruct = standardClasses.stringClass().getStructure();
for (var str : stringMap.values()) {
var value = new WasmCall(nextCharArrayFunction);
function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global),
WasmGCClassInfoProvider.CUSTOM_FIELD_OFFSETS, value));
}
}
@Override

View File

@ -0,0 +1,23 @@
/*
* 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.generators.gc;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.model.MethodReference;
public interface WasmGCCustomGenerator {
void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context);
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.generators.gc;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.model.WasmModule;
public interface WasmGCCustomGeneratorContext {
WasmModule module();
WasmFunctionTypes functionTypes();
}

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.generators.gc;
import java.util.HashMap;
import java.util.Map;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
import org.teavm.model.MethodReference;
public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
private Map<MethodReference, WasmGCCustomGenerator> generators = new HashMap<>();
public WasmGCCustomGenerators() {
fillStringPool();
}
private void fillStringPool() {
generators.put(
new MethodReference(WasmGCStringPool.class, "nextByte", byte.class),
new WasmGCStringPoolGenerator()
);
}
@Override
public WasmGCCustomGenerator get(MethodReference method) {
return generators.get(method);
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.generators.gc;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmGlobal;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.model.MethodReference;
public class WasmGCStringPoolGenerator implements WasmGCCustomGenerator {
@Override
public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) {
var module = context.module();
var pointer = new WasmGlobal("teavm_string_pool_pointer", WasmType.INT32, new WasmInt32Constant(0));
module.globals.add(pointer);
var resultLocal = new WasmLocal(WasmType.INT32);
function.add(resultLocal);
var increment = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
new WasmGetGlobal(pointer), new WasmInt32Constant(1));
function.getBody().add(new WasmSetLocal(resultLocal, increment));
function.getBody().add(new WasmReturn(new WasmGetGlobal(pointer)));
}
}

View File

@ -15,10 +15,11 @@
*/
package org.teavm.backend.wasm.model;
import java.util.ArrayList;
import java.util.List;
public class WasmStructure extends WasmCompositeType {
private List<WasmStorageType> fields;
private List<WasmStorageType> fields = new ArrayList<>();
public WasmStructure(String name) {
super(name);

View File

@ -19,6 +19,7 @@ import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmCompositeTypeVisitor;
import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmStorageType;
import org.teavm.backend.wasm.model.WasmStructure;
public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor {
@ -26,17 +27,24 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor
private WasmBinaryWriter section;
public WasmCompositeTypeBinaryRenderer(WasmModule module, WasmBinaryWriter section) {
this.module = module;
this.section = section;
}
@Override
public void visit(WasmStructure type) {
section.writeByte(0x5F);
section.writeLEB(type.getFields().size());
for (var fieldType : type.getFields()) {
writeStorageType(fieldType);
section.writeLEB(0x01); // mutable
}
}
@Override
public void visit(WasmArray type) {
writeStorageType(type.getElementType());
section.writeLEB(0x01); // mutable
}
@Override
@ -53,4 +61,19 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor
section.writeByte(0);
}
}
private void writeStorageType(WasmStorageType storageType) {
if (storageType instanceof WasmStorageType.Packed) {
switch (((WasmStorageType.Packed) storageType).type) {
case INT8:
section.writeByte(0x78);
break;
case INT16:
section.writeByte(0x77);
break;
}
} else {
section.writeType(storageType.asUnpackedType(), module);
}
}
}

View File

@ -15,6 +15,8 @@
*/
package org.teavm.backend.wasm.runtime;
import org.teavm.interop.Import;
public class WasmGCSupport {
private WasmGCSupport() {
}
@ -30,4 +32,10 @@ public class WasmGCSupport {
public static ClassCastException cce() {
return new ClassCastException();
}
@Import(name = "putcharStdout")
public static native void putCharStdout(char c);
@Import(name = "putcharStderr")
public static native void putCharStderr(char c);
}

View File

@ -64,7 +64,7 @@ public class MethodDescriptor implements Serializable {
public ValueType parameterType(int index) {
if (index >= signature.length - 1) {
throw new IndexOutOfBoundsException(String.valueOf(index) + "/" + (signature.length - 1));
throw new IndexOutOfBoundsException(index + "/" + (signature.length - 1));
}
return signature[index];
}

View File

@ -36,10 +36,14 @@ import org.teavm.common.LCATree;
import org.teavm.model.AccessLevel;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.util.GraphColorer;
public class VirtualTableBuilder {
@ -518,4 +522,33 @@ public class VirtualTableBuilder {
IntArrayList colors = new IntArrayList();
List<MethodDescriptor> methods = new ArrayList<>();
}
public static Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) {
var virtualMethods = new HashSet<MethodReference>();
for (var className : classes.getClassNames()) {
var cls = classes.get(className);
for (var method : cls.getMethods()) {
var program = method.getProgram();
if (program == null) {
continue;
}
for (int i = 0; i < program.basicBlockCount(); ++i) {
var block = program.basicBlockAt(i);
for (var insn : block) {
if (insn instanceof InvokeInstruction) {
var invoke = (InvokeInstruction) insn;
if (invoke.getType() == InvocationType.VIRTUAL) {
virtualMethods.add(invoke.getMethod());
}
} else if (insn instanceof CloneArrayInstruction) {
virtualMethods.add(new MethodReference(Object.class, "clone", Object.class));
}
}
}
}
}
return virtualMethods;
}
}

View File

@ -16,11 +16,11 @@
package org.teavm.model.util;
import org.teavm.model.MethodReference;
import org.teavm.model.ProgramReader;
import org.teavm.model.Program;
public class DefaultVariableCategoryProvider implements VariableCategoryProvider {
@Override
public Object[] getCategories(ProgramReader program, MethodReference method) {
public Object[] getCategories(Program program, MethodReference method) {
TypeInferer inferer = new TypeInferer();
inferer.inferTypes(program, method);
var categories = new Object[program.variableCount()];

View File

@ -16,8 +16,8 @@
package org.teavm.model.util;
import org.teavm.model.MethodReference;
import org.teavm.model.ProgramReader;
import org.teavm.model.Program;
public interface VariableCategoryProvider {
Object[] getCategories(ProgramReader program, MethodReference method);
Object[] getCategories(Program program, MethodReference method);
}

View File

@ -60,5 +60,4 @@ public interface TeaVMTargetController {
void addVirtualMethods(Predicate<MethodReference> methods);
ClassInitializerInfo getClassInitializerInfo();
}

View File

@ -23,4 +23,5 @@ public final class Platforms {
public static final String WEBASSEMBLY = "webassembly";
public static final String C = "c";
public static final String LOW_LEVEL = "low_level";
public static final String WEBASSEMBLY_GC = "webassembly-gc";
}