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; return false;
} }
@PlatformMarker(Platforms.WEBASSEMBLY_GC)
public static boolean isWebAssemblyGC() {
return false;
}
@PlatformMarker(Platforms.JAVASCRIPT) @PlatformMarker(Platforms.JAVASCRIPT)
public static boolean isJavaScript() { public static boolean isJavaScript() {
return false; return false;

View File

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

View File

@ -15,12 +15,20 @@
*/ */
package org.teavm.classlib.impl.console; package org.teavm.classlib.impl.console;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.classlib.PlatformDetector;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
public class JSStderrPrintStream extends JsConsolePrintStream { public class JSStderrPrintStream extends JsConsolePrintStream {
@Override @Override
public void print(String s) { 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);") @JSBody(params = "b", script = "$rt_putStderr(b);")

View File

@ -15,12 +15,20 @@
*/ */
package org.teavm.classlib.impl.console; package org.teavm.classlib.impl.console;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.classlib.PlatformDetector;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
public class JSStdoutPrintStream extends JsConsolePrintStream { public class JSStdoutPrintStream extends JsConsolePrintStream {
@Override @Override
public void print(String s) { 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);") @JSBody(params = "b", script = "$rt_putStdout(b);")

View File

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

View File

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

View File

@ -92,18 +92,15 @@ import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyListener;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.Platforms; import org.teavm.interop.Platforms;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.FieldReader; import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program; 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.TagRegistry;
import org.teavm.model.classes.VirtualTableBuilder; import org.teavm.model.classes.VirtualTableBuilder;
import org.teavm.model.classes.VirtualTableProvider; 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.CallSiteDescriptor;
import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.Characteristics;
import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerEliminator;
@ -623,40 +617,11 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
VirtualTableBuilder builder = new VirtualTableBuilder(classes); VirtualTableBuilder builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes)); builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes));
builder.setMethodCalledVirtually(controller::isVirtual); builder.setMethodCalledVirtually(controller::isVirtual);
return builder.build(); 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) { private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
IncludeManager includes = new SimpleIncludeManager(context.getFileNames(), writer); IncludeManager includes = new SimpleIncludeManager(context.getFileNames(), writer);
includes.init("special.c"); 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.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
public class WasmFunctionRepository { public class WasmFunctionRepository implements BaseWasmFunctionRepository {
private WasmModule module; private WasmModule module;
private WasmFunctionTypes functionTypes; private WasmFunctionTypes functionTypes;
private NameProvider nameProvider; private NameProvider nameProvider;
@ -50,6 +50,7 @@ public class WasmFunctionRepository {
return isStatic ? forStaticMethod(reference) : forInstanceMethod(reference); return isStatic ? forStaticMethod(reference) : forInstanceMethod(reference);
} }
@Override
public WasmFunction forStaticMethod(MethodReference reference) { public WasmFunction forStaticMethod(MethodReference reference) {
return staticMethods.computeIfAbsent(reference, key -> { return staticMethods.computeIfAbsent(reference, key -> {
var wasmParams = new WasmType[key.parameterCount()]; var wasmParams = new WasmType[key.parameterCount()];
@ -65,6 +66,7 @@ public class WasmFunctionRepository {
}); });
} }
@Override
public WasmFunction forInstanceMethod(MethodReference reference) { public WasmFunction forInstanceMethod(MethodReference reference) {
return instanceMethods.computeIfAbsent(reference, key -> { return instanceMethods.computeIfAbsent(reference, key -> {
var wasmParams = new WasmType[key.parameterCount() + 1]; 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.Platforms;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.model.AnnotationHolder; import org.teavm.model.AnnotationHolder;
import org.teavm.model.BasicBlock;
import org.teavm.model.CallLocation; import org.teavm.model.CallLocation;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
@ -139,7 +138,6 @@ import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor; 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.TagRegistry;
import org.teavm.model.classes.VirtualTableBuilder; import org.teavm.model.classes.VirtualTableBuilder;
import org.teavm.model.classes.VirtualTableProvider; 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.Characteristics;
import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerEliminator;
import org.teavm.model.lowlevel.ClassInitializerTransformer; import org.teavm.model.lowlevel.ClassInitializerTransformer;
@ -1075,40 +1070,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
var builder = new VirtualTableBuilder(classes); var builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes)); builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes));
builder.setMethodCalledVirtually(controller::isVirtual); builder.setMethodCalledVirtually(controller::isVirtual);
return builder.build(); 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 @Override
public String[] getPlatformTags() { public String[] getPlatformTags() {
return new String[] { Platforms.WEBASSEMBLY, Platforms.LOW_LEVEL }; 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 @Override
public ClassReaderSource classSource() { public ClassReaderSource classes() {
return classSource; return classSource;
} }

View File

@ -15,17 +15,17 @@
*/ */
package org.teavm.backend.wasm.generate.common.methods; 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.WasmFunctionTypes;
import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
public interface BaseWasmGenerationContext { public interface BaseWasmGenerationContext {
WasmFunctionRepository functions(); BaseWasmFunctionRepository functions();
WasmFunctionTypes functionTypes(); WasmFunctionTypes functionTypes();
WasmTag getExceptionTag(); 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) { protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) {
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { 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 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); var call = new WasmCall(function);
for (var argument : expr.getArguments()) { 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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; 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.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap; import com.carrotsearch.hppc.ObjectIntMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Queue;
import java.util.function.Consumer;
import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor; import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCFunctionProvider;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool; import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
@ -70,17 +75,20 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private TagRegistry tagRegistry; private TagRegistry tagRegistry;
private ClassMetadataRequirements metadataRequirements; private ClassMetadataRequirements metadataRequirements;
private VirtualTableProvider virtualTables; private VirtualTableProvider virtualTables;
private WasmGCFunctionProvider functionProvider; private BaseWasmFunctionRepository functionProvider;
private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>(); private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>();
private Queue<WasmGCClassInfo> classInfoQueue = new ArrayDeque<>();
private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>(); private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>();
private ObjectIntMap<MethodReference> methodIndexes = 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; private ClassInitializerInfo classInitializerInfo;
public final WasmGCStringPool strings; public final WasmGCStringPool strings;
public final WasmGCStandardClasses standardClasses; public final WasmGCStandardClasses standardClasses;
private final WasmGCTypeMapper typeMapper; public final WasmGCTypeMapper typeMapper;
private final NameProvider names; private final NameProvider names;
private WasmFunction initializer; private List<WasmExpression> initializerFunctionStatements = new ArrayList<>();
private WasmFunction createPrimitiveClassFunction; private WasmFunction createPrimitiveClassFunction;
private WasmFunction createArrayClassFunction; private WasmFunction createArrayClassFunction;
private final WasmGCSupertypeFunctionGenerator supertypeGenerator; private final WasmGCSupertypeFunctionGenerator supertypeGenerator;
@ -97,7 +105,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
WasmFunctionTypes functionTypes, TagRegistry tagRegistry, WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables, ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables,
WasmGCFunctionProvider functionProvider, NameProvider names, BaseWasmFunctionRepository functionProvider, NameProvider names,
ClassInitializerInfo classInitializerInfo) { ClassInitializerInfo classInitializerInfo) {
this.module = module; this.module = module;
this.classSource = classSource; this.classSource = classSource;
@ -114,6 +122,22 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
typeMapper = new WasmGCTypeMapper(this); 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 @Override
public void contributeToInitializerDefinitions(WasmFunction function) { public void contributeToInitializerDefinitions(WasmFunction function) {
for (var classInfo : classInfoMap.values()) { for (var classInfo : classInfoMap.values()) {
@ -125,8 +149,9 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
@Override @Override
public void contributeToInitializer(WasmFunction function) { public void contributeToInitializer(WasmFunction function) {
var classClass = standardClasses.classClass(); var classClass = standardClasses.classClass();
function.getBody().addAll(initializerFunctionStatements);
initializerFunctionStatements.clear();
for (var classInfo : classInfoMap.values()) { for (var classInfo : classInfoMap.values()) {
classInfo.initializer.accept(function.getBody());
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType()); var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset,
new WasmFunctionReference(supertypeFunction))); new WasmFunctionReference(supertypeFunction)));
@ -134,12 +159,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
new WasmGetGlobal(classClass.pointer))); new WasmGetGlobal(classClass.pointer)));
if (classInfo.initializerPointer != null) { if (classInfo.initializerPointer != null) {
var className = ((ValueType.Object) classInfo.getValueType()).getClassName(); var className = ((ValueType.Object) classInfo.getValueType()).getClassName();
var initFunction = functionProvider.getStaticFunction(new MethodReference(className, var initFunction = functionProvider.forStaticMethod(new MethodReference(className,
CLINIT_METHOD_DESC)); CLINIT_METHOD_DESC));
function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer, function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer,
new WasmFunctionReference(initFunction))); new WasmFunctionReference(initFunction)));
} }
} }
for (var consumer : staticFieldInitializers) {
consumer.accept(function);
}
} }
@Override @Override
@ -147,6 +175,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
var classInfo = classInfoMap.get(type); var classInfo = classInfoMap.get(type);
if (classInfo == null) { if (classInfo == null) {
classInfo = new WasmGCClassInfo(type); classInfo = new WasmGCClassInfo(type);
classInfoQueue.add(classInfo);
classInfoMap.put(type, classInfo); classInfoMap.put(type, classInfo);
if (!(type instanceof ValueType.Primitive)) { if (!(type instanceof ValueType.Primitive)) {
var name = type instanceof ValueType.Object var name = type instanceof ValueType.Object
@ -154,6 +183,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
: null; : null;
classInfo.structure = new WasmStructure(name); classInfo.structure = new WasmStructure(name);
classInfo.structure.getFields().add(standardClasses.classClass().getType().asStorage()); classInfo.structure.getFields().add(standardClasses.classClass().getType().asStorage());
module.types.add(classInfo.structure);
fillFields(classInfo.structure.getFields(), type); fillFields(classInfo.structure.getFields(), type);
} }
var pointerName = names.forClassInstance(type); var pointerName = names.forClassInstance(type);
@ -241,6 +271,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
var clinitType = functionTypes.of(null); var clinitType = functionTypes.of(null);
classInfo.initializerPointer = new WasmGlobal(null, clinitType.getReference(), classInfo.initializerPointer = new WasmGlobal(null, clinitType.getReference(),
new WasmNullConstant(clinitType.getReference())); new WasmNullConstant(clinitType.getReference()));
module.globals.add(classInfo.initializerPointer);
} }
} }
classInfo.initializer = target -> { classInfo.initializer = target -> {
@ -276,7 +307,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
for (var method : virtualTable.getMethods()) { for (var method : virtualTable.getMethods()) {
var entry = virtualTable.getEntry(method); var entry = virtualTable.getEntry(method);
if (entry != null && entry.getImplementor() != null) { if (entry != null && entry.getImplementor() != null) {
var function = functionProvider.getMemberFunction(entry.getImplementor()); var function = functionProvider.forInstanceMethod(entry.getImplementor());
if (!origin.equals(entry.getImplementor().getClassName())) { if (!origin.equals(entry.getImplementor().getClassName())) {
var functionType = getFunctionType(virtualTable.getClassName(), method); var functionType = getFunctionType(virtualTable.getClassName(), method);
var wrapperFunction = new WasmFunction(functionType); var wrapperFunction = new WasmFunction(functionType);
@ -361,6 +392,33 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return result; 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 @Override
public int getVirtualMethodIndex(MethodReference methodRef) { public int getVirtualMethodIndex(MethodReference methodRef) {
var result = methodIndexes.getOrDefault(methodRef, -1); var result = methodIndexes.getOrDefault(methodRef, -1);
@ -442,13 +500,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
); );
var function = new WasmFunction(functionType); var function = new WasmFunction(functionType);
function.setName("_teavm_fill_primitive_class_"); function.setName("_teavm_fill_primitive_class_");
module.functions.add(function);
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target"); var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name"); var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name");
var kindVar = new WasmLocal(WasmType.INT32, "kind"); var kindVar = new WasmLocal(WasmType.INT32, "kind");
function.getLocalVariables().add(targetVar); function.add(targetVar);
function.getLocalVariables().add(nameVar); function.add(nameVar);
function.getLocalVariables().add(kindVar); function.add(kindVar);
var flagsExpr = new WasmIntBinary( var flagsExpr = new WasmIntBinary(
WasmIntType.INT32, WasmIntType.INT32,
@ -497,12 +556,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
standardClasses.classClass().getType() standardClasses.classClass().getType()
); );
var function = new WasmFunction(functionType); var function = new WasmFunction(functionType);
module.functions.add(function);
function.setName("_teavm_fill_array_class_"); function.setName("_teavm_fill_array_class_");
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target"); var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item"); var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item");
function.getLocalVariables().add(targetVar); function.add(targetVar);
function.getLocalVariables().add(itemVar); function.add(itemVar);
function.getBody().add(new WasmStructSet( function.getBody().add(new WasmStructSet(
standardClasses.classClass().getStructure(), 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) { private WasmExpression setClassField(WasmGCClassInfo classInfo, int fieldIndex, WasmExpression value) {
return new WasmStructSet( return new WasmStructSet(
standardClasses.classClass().getStructure(), standardClasses.classClass().getStructure(),

View File

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

View File

@ -15,8 +15,6 @@
*/ */
package org.teavm.backend.wasm.generate.gc.classes; 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.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; 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.ValueType;
import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.TagRegistry;
public class WasmGCSupertypeFunctionGenerator { public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunctionProvider {
private ObjectIntMap<ValueType> tableIndexes = new ObjectIntHashMap<>();
private Map<ValueType, WasmFunction> functions = new HashMap<>(); private Map<ValueType, WasmFunction> functions = new HashMap<>();
private WasmModule module; private WasmModule module;
private WasmGCClassGenerator classGenerator; private WasmGCClassGenerator classGenerator;
@ -69,6 +66,7 @@ public class WasmGCSupertypeFunctionGenerator {
this.functionTypes = functionTypes; this.functionTypes = functionTypes;
} }
@Override
public WasmFunction getIsSupertypeFunction(ValueType type) { public WasmFunction getIsSupertypeFunction(ValueType type) {
var result = functions.get(type); var result = functions.get(type);
if (result == null) { 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; 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; import org.teavm.model.MethodReference;
public interface WasmGCFunctionProvider { public interface WasmGCCustomGeneratorProvider {
WasmFunction getMemberFunction(MethodReference methodRef); WasmGCCustomGenerator get(MethodReference method);
WasmFunction getStaticFunction(MethodReference methodRef);
} }

View File

@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext; import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses; import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
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.classes.WasmGCTypeMapper;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider; import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
@ -39,20 +40,33 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private WasmGCStringProvider strings; private WasmGCStringProvider strings;
private VirtualTableProvider virtualTables; private VirtualTableProvider virtualTables;
private WasmGCTypeMapper typeMapper; private WasmGCTypeMapper typeMapper;
private WasmFunctionTypes functionTypes;
private ClassReaderSource classes;
private BaseWasmFunctionRepository functions;
private WasmGCSupertypeFunctionProvider supertypeFunctions;
private WasmGCCustomGeneratorProvider customGenerators;
private WasmFunction npeMethod; private WasmFunction npeMethod;
private WasmFunction aaiobeMethod; private WasmFunction aaiobeMethod;
private WasmFunction cceMethod; private WasmFunction cceMethod;
private WasmGlobal exceptionGlobal; private WasmGlobal exceptionGlobal;
private WasmTag exceptionTag;
public WasmGCGenerationContext(WasmModule module, WasmGCClassInfoProvider classInfoProvider, public WasmGCGenerationContext(WasmModule module, VirtualTableProvider virtualTables,
WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, VirtualTableProvider virtualTables, WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ClassReaderSource classes,
WasmGCTypeMapper typeMapper) { BaseWasmFunctionRepository functions, WasmGCSupertypeFunctionProvider supertypeFunctions,
WasmGCClassInfoProvider classInfoProvider, WasmGCStandardClasses standardClasses,
WasmGCStringProvider strings, WasmGCCustomGeneratorProvider customGenerators) {
this.module = module; 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.classInfoProvider = classInfoProvider;
this.standardClasses = standardClasses; this.standardClasses = standardClasses;
this.strings = strings; this.strings = strings;
this.virtualTables = virtualTables; this.customGenerators = customGenerators;
this.typeMapper = typeMapper;
} }
public WasmGCClassInfoProvider classInfoProvider() { public WasmGCClassInfoProvider classInfoProvider() {
@ -76,23 +90,31 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
} }
@Override @Override
public WasmFunctionRepository functions() { public BaseWasmFunctionRepository functions() {
return null; return functions;
}
public WasmGCSupertypeFunctionProvider supertypeFunctions() {
return supertypeFunctions;
} }
@Override @Override
public WasmFunctionTypes functionTypes() { public WasmFunctionTypes functionTypes() {
return null; return functionTypes;
} }
@Override @Override
public WasmTag getExceptionTag() { public WasmTag getExceptionTag() {
return null; if (exceptionTag == null) {
exceptionTag = new WasmTag(functionTypes.of(null));
module.tags.add(exceptionTag);
}
return exceptionTag;
} }
@Override @Override
public ClassReaderSource classSource() { public ClassReaderSource classes() {
return null; return classes;
} }
public WasmFunction npeMethod() { public WasmFunction npeMethod() {
@ -127,4 +149,8 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
} }
return exceptionGlobal; 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.SubscriptExpr;
import org.teavm.ast.UnwrapArrayExpr; import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor; 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.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmFunction; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayGet; import org.teavm.backend.wasm.model.expression.WasmArrayGet;
import org.teavm.backend.wasm.model.expression.WasmArrayLength; 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.WasmArraySet;
import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
@ -57,11 +55,13 @@ import org.teavm.model.ValueType;
public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
private WasmGCGenerationContext context; private WasmGCGenerationContext context;
private WasmGCGenerationUtil generationUtil;
public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function, public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function,
int firstVariable, boolean async) { int firstVariable, boolean async) {
super(context, function, firstVariable, async); super(context, function, firstVariable, async);
this.context = context; this.context = context;
generationUtil = new WasmGCGenerationUtil(context.classInfoProvider(), tempVars);
} }
@Override @Override
@ -130,20 +130,29 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override @Override
protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) { protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) {
accept(qualified); if (qualified == null) {
var target = result; accept(value);
accept(value); var wasmValue = result;
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); target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult(); var type = (WasmType.CompositeReference) typeInference.getResult();
var struct = (WasmStructure) type.composite; 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); var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue);
expr.setLocation(location); expr.setLocation(location);
resultConsumer.add(expr); resultConsumer.add(expr);
}
} }
@Override @Override
@ -230,42 +239,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override @Override
protected void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local, protected void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local,
List<WasmExpression> target) { List<WasmExpression> target) {
var classInfo = context.classInfoProvider().getClassInfo(ValueType.arrayOf(itemType)); generationUtil.allocateArray(itemType, length, location, local, target);
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);
}
} }
@Override @Override
@ -277,7 +251,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override @Override
protected WasmExpression generateInstanceOf(WasmExpression expression, ValueType type) { protected WasmExpression generateInstanceOf(WasmExpression expression, ValueType type) {
context.classInfoProvider().getClassInfo(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( var classRef = new WasmStructGet(
context.standardClasses().objectClass().getStructure(), context.standardClasses().objectClass().getStructure(),
expression, expression,
@ -352,17 +326,23 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override @Override
public void visit(QualificationExpr expr) { public void visit(QualificationExpr expr) {
accept(expr.getQualified()); if (expr.getQualified() == null) {
var target = result; 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); target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult(); var type = (WasmType.CompositeReference) typeInference.getResult();
var struct = (WasmStructure) type.composite; 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 = new WasmStructGet(struct, target, fieldIndex);
result.setLocation(expr.getLocation()); result.setLocation(expr.getLocation());
}
} }
private class SimpleCallSite extends CallSiteIdentifier { 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.nio.charset.StandardCharsets;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; 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.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.WasmFunction;
import org.teavm.backend.wasm.model.WasmGlobal; 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.WasmModule;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmExpression; 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.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.render.WasmBinaryWriter; import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -35,10 +40,10 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
private WasmModule module; private WasmModule module;
private WasmBinaryWriter binaryWriter = new WasmBinaryWriter(); private WasmBinaryWriter binaryWriter = new WasmBinaryWriter();
private Map<String, WasmGCStringConstant> stringMap = new LinkedHashMap<>(); private Map<String, WasmGCStringConstant> stringMap = new LinkedHashMap<>();
private WasmGCFunctionProvider functionProvider; private BaseWasmFunctionRepository functionProvider;
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module, public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
WasmGCFunctionProvider functionProvider) { BaseWasmFunctionRepository functionProvider) {
this.standardClasses = standardClasses; this.standardClasses = standardClasses;
this.module = module; this.module = module;
this.functionProvider = functionProvider; this.functionProvider = functionProvider;
@ -46,6 +51,9 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
@Override @Override
public void contributeToInitializerDefinitions(WasmFunction function) { public void contributeToInitializerDefinitions(WasmFunction function) {
var segment = new WasmMemorySegment();
module.getSegments().add(segment);
segment.setData(binaryWriter.getData());
for (var str : stringMap.values()) { for (var str : stringMap.values()) {
var newStruct = new WasmStructNewDefault(standardClasses.stringClass().getStructure()); var newStruct = new WasmStructNewDefault(standardClasses.stringClass().getStructure());
function.getBody().add(new WasmSetGlobal(str.global, newStruct)); function.getBody().add(new WasmSetGlobal(str.global, newStruct));
@ -54,8 +62,14 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
@Override @Override
public void contributeToInitializer(WasmFunction function) { public void contributeToInitializer(WasmFunction function) {
var nextCharArrayFunction = functionProvider.getStaticFunction(new MethodReference(WasmGCStringPool.class, var nextCharArrayFunction = functionProvider.forStaticMethod(new MethodReference(WasmGCStringPool.class,
"nextCharArray", char[].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 @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; package org.teavm.backend.wasm.model;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class WasmStructure extends WasmCompositeType { public class WasmStructure extends WasmCompositeType {
private List<WasmStorageType> fields; private List<WasmStorageType> fields = new ArrayList<>();
public WasmStructure(String name) { public WasmStructure(String name) {
super(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.WasmCompositeTypeVisitor;
import org.teavm.backend.wasm.model.WasmFunctionType; import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmStorageType;
import org.teavm.backend.wasm.model.WasmStructure; import org.teavm.backend.wasm.model.WasmStructure;
public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor { public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor {
@ -26,17 +27,24 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor
private WasmBinaryWriter section; private WasmBinaryWriter section;
public WasmCompositeTypeBinaryRenderer(WasmModule module, WasmBinaryWriter section) { public WasmCompositeTypeBinaryRenderer(WasmModule module, WasmBinaryWriter section) {
this.module = module;
this.section = section; this.section = section;
} }
@Override @Override
public void visit(WasmStructure type) { 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 @Override
public void visit(WasmArray type) { public void visit(WasmArray type) {
writeStorageType(type.getElementType());
section.writeLEB(0x01); // mutable
} }
@Override @Override
@ -53,4 +61,19 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor
section.writeByte(0); 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; package org.teavm.backend.wasm.runtime;
import org.teavm.interop.Import;
public class WasmGCSupport { public class WasmGCSupport {
private WasmGCSupport() { private WasmGCSupport() {
} }
@ -30,4 +32,10 @@ public class WasmGCSupport {
public static ClassCastException cce() { public static ClassCastException cce() {
return new ClassCastException(); 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) { public ValueType parameterType(int index) {
if (index >= signature.length - 1) { if (index >= signature.length - 1) {
throw new IndexOutOfBoundsException(String.valueOf(index) + "/" + (signature.length - 1)); throw new IndexOutOfBoundsException(index + "/" + (signature.length - 1));
} }
return signature[index]; return signature[index];
} }

View File

@ -36,10 +36,14 @@ import org.teavm.common.LCATree;
import org.teavm.model.AccessLevel; import org.teavm.model.AccessLevel;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; 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; import org.teavm.model.util.GraphColorer;
public class VirtualTableBuilder { public class VirtualTableBuilder {
@ -518,4 +522,33 @@ public class VirtualTableBuilder {
IntArrayList colors = new IntArrayList(); IntArrayList colors = new IntArrayList();
List<MethodDescriptor> methods = new ArrayList<>(); 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; package org.teavm.model.util;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ProgramReader; import org.teavm.model.Program;
public class DefaultVariableCategoryProvider implements VariableCategoryProvider { public class DefaultVariableCategoryProvider implements VariableCategoryProvider {
@Override @Override
public Object[] getCategories(ProgramReader program, MethodReference method) { public Object[] getCategories(Program program, MethodReference method) {
TypeInferer inferer = new TypeInferer(); TypeInferer inferer = new TypeInferer();
inferer.inferTypes(program, method); inferer.inferTypes(program, method);
var categories = new Object[program.variableCount()]; var categories = new Object[program.variableCount()];

View File

@ -16,8 +16,8 @@
package org.teavm.model.util; package org.teavm.model.util;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ProgramReader; import org.teavm.model.Program;
public interface VariableCategoryProvider { 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); void addVirtualMethods(Predicate<MethodReference> methods);
ClassInitializerInfo getClassInitializerInfo(); ClassInitializerInfo getClassInitializerInfo();
} }

View File

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