From a281c19363eea3ad57a694e53584e0d8f8614925 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 24 Jul 2024 21:25:32 +0200 Subject: [PATCH] wasm gc: draft Wasm GC backend --- .../org/teavm/classlib/PlatformDetector.java | 5 + .../teavm/classlib/impl/console/Console.java | 9 + .../impl/console/JSStderrPrintStream.java | 10 +- .../impl/console/JSStdoutPrintStream.java | 10 +- .../org/teavm/classlib/java/lang/TSystem.java | 4 +- .../main/java/org/teavm/ast/VariableNode.java | 6 + .../java/org/teavm/backend/c/CTarget.java | 37 +- .../wasm/BaseWasmFunctionRepository.java | 25 ++ .../java/org/teavm/backend/wasm/Example.java | 323 ------------------ .../backend/wasm/WasmFunctionRepository.java | 4 +- .../backend/wasm/WasmGCModuleGenerator.java | 98 ++++++ .../org/teavm/backend/wasm/WasmGCTarget.java | 143 ++++++++ .../org/teavm/backend/wasm/WasmTarget.java | 36 +- .../backend/wasm/gc/PreciseTypeInference.java | 129 +++++++ .../backend/wasm/gc/WasmGCDependencies.java | 63 ++++ .../gc/WasmGCVariableCategoryProvider.java | 45 +++ .../wasm/generate/WasmGenerationContext.java | 2 +- .../methods/BaseWasmGenerationContext.java | 6 +- .../methods/BaseWasmGenerationVisitor.java | 6 +- .../gc/WasmGCDeclarationsGenerator.java | 143 ++++++++ .../WasmGCInitializerContributor.java | 2 +- .../gc/classes/WasmGCClassGenerator.java | 97 ++++-- .../gc/classes/WasmGCClassInfoProvider.java | 4 + .../WasmGCSupertypeFunctionGenerator.java | 6 +- .../WasmGCSupertypeFunctionProvider.java | 23 ++ ...ava => WasmGCCustomGeneratorProvider.java} | 8 +- .../WasmGCGenerationContext.java | 52 ++- .../gc/methods/WasmGCGenerationUtil.java | 83 +++++ .../gc/methods/WasmGCGenerationVisitor.java | 96 +++--- .../gc/methods/WasmGCMethodGenerator.java | 319 +++++++++++++++++ .../generate/gc/strings/WasmGCStringPool.java | 24 +- .../generators/gc/WasmGCCustomGenerator.java | 23 ++ .../gc/WasmGCCustomGeneratorContext.java | 25 ++ .../generators/gc/WasmGCCustomGenerators.java | 42 +++ .../gc/WasmGCStringPoolGenerator.java | 46 +++ .../backend/wasm/model/WasmStructure.java | 3 +- .../WasmCompositeTypeBinaryRenderer.java | 27 +- .../backend/wasm/runtime/WasmGCSupport.java | 8 + .../org/teavm/model/MethodDescriptor.java | 2 +- .../model/classes/VirtualTableBuilder.java | 33 ++ .../util/DefaultVariableCategoryProvider.java | 4 +- .../model/util/VariableCategoryProvider.java | 4 +- .../org/teavm/vm/TeaVMTargetController.java | 1 - .../java/org/teavm/interop/Platforms.java | 1 + 44 files changed, 1514 insertions(+), 523 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/BaseWasmFunctionRepository.java delete mode 100644 core/src/main/java/org/teavm/backend/wasm/Example.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java rename core/src/main/java/org/teavm/backend/wasm/generate/gc/{initialization => }/WasmGCInitializerContributor.java (93%) create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionProvider.java rename core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/{WasmGCFunctionProvider.java => WasmGCCustomGeneratorProvider.java} (76%) rename core/src/main/java/org/teavm/backend/wasm/generate/gc/{ => methods}/WasmGCGenerationContext.java (69%) create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerator.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCStringPoolGenerator.java diff --git a/classlib/src/main/java/org/teavm/classlib/PlatformDetector.java b/classlib/src/main/java/org/teavm/classlib/PlatformDetector.java index 88abf06f7..bc89a9e79 100644 --- a/classlib/src/main/java/org/teavm/classlib/PlatformDetector.java +++ b/classlib/src/main/java/org/teavm/classlib/PlatformDetector.java @@ -32,6 +32,11 @@ public final class PlatformDetector { return false; } + @PlatformMarker(Platforms.WEBASSEMBLY_GC) + public static boolean isWebAssemblyGC() { + return false; + } + @PlatformMarker(Platforms.JAVASCRIPT) public static boolean isJavaScript() { return false; diff --git a/classlib/src/main/java/org/teavm/classlib/impl/console/Console.java b/classlib/src/main/java/org/teavm/classlib/impl/console/Console.java index 5c489d692..310533d8a 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/console/Console.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/console/Console.java @@ -16,6 +16,7 @@ package org.teavm.classlib.impl.console; import org.teavm.backend.c.intrinsic.RuntimeInclude; +import org.teavm.backend.wasm.runtime.WasmGCSupport; import org.teavm.backend.wasm.runtime.WasmSupport; import org.teavm.classlib.PlatformDetector; import org.teavm.interop.Address; @@ -35,6 +36,10 @@ public final class Console { } } else if (PlatformDetector.isWebAssembly()) { WasmSupport.putCharsStderr(Address.ofData(data).add(off), len); + } else if (PlatformDetector.isWebAssemblyGC()) { + for (var i = 0; i < len; ++i) { + WasmGCSupport.putCharStderr((char) data[off + i]); + } } else { for (int i = 0; i < len; ++i) { byte b = data[i + off]; @@ -51,6 +56,10 @@ public final class Console { } } else if (PlatformDetector.isWebAssembly()) { WasmSupport.putCharsStdout(Address.ofData(data).add(off), len); + } else if (PlatformDetector.isWebAssemblyGC()) { + for (var i = 0; i < len; ++i) { + WasmGCSupport.putCharStdout((char) data[off + i]); + } } else { for (int i = 0; i < len; ++i) { byte b = data[i + off]; diff --git a/classlib/src/main/java/org/teavm/classlib/impl/console/JSStderrPrintStream.java b/classlib/src/main/java/org/teavm/classlib/impl/console/JSStderrPrintStream.java index 3fa17681c..18a737dba 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/console/JSStderrPrintStream.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/console/JSStderrPrintStream.java @@ -15,12 +15,20 @@ */ package org.teavm.classlib.impl.console; +import org.teavm.backend.wasm.runtime.WasmGCSupport; +import org.teavm.classlib.PlatformDetector; import org.teavm.jso.JSBody; public class JSStderrPrintStream extends JsConsolePrintStream { @Override public void print(String s) { - writeJs(s); + if (PlatformDetector.isWebAssemblyGC()) { + for (int i = 0; i < s.length(); ++i) { + WasmGCSupport.putCharStderr(s.charAt(i)); + } + } else { + writeJs(s); + } } @JSBody(params = "b", script = "$rt_putStderr(b);") diff --git a/classlib/src/main/java/org/teavm/classlib/impl/console/JSStdoutPrintStream.java b/classlib/src/main/java/org/teavm/classlib/impl/console/JSStdoutPrintStream.java index 449895dd3..9707e83f6 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/console/JSStdoutPrintStream.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/console/JSStdoutPrintStream.java @@ -15,12 +15,20 @@ */ package org.teavm.classlib.impl.console; +import org.teavm.backend.wasm.runtime.WasmGCSupport; +import org.teavm.classlib.PlatformDetector; import org.teavm.jso.JSBody; public class JSStdoutPrintStream extends JsConsolePrintStream { @Override public void print(String s) { - writeJs(s); + if (PlatformDetector.isWebAssemblyGC()) { + for (int i = 0; i < s.length(); ++i) { + WasmGCSupport.putCharStderr(s.charAt(i)); + } + } else { + writeJs(s); + } } @JSBody(params = "b", script = "$rt_putStdout(b);") diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java index 5446675ba..f1930b715 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java @@ -56,7 +56,7 @@ public final class TSystem extends TObject { public static TPrintStream out() { if (outCache == null) { - if (PlatformDetector.isJavaScript()) { + if (PlatformDetector.isJavaScript() || PlatformDetector.isWebAssemblyGC()) { outCache = (TPrintStream) (Object) new JSStdoutPrintStream(); } else { outCache = new TPrintStream(StdoutOutputStream.INSTANCE, false); @@ -67,7 +67,7 @@ public final class TSystem extends TObject { public static TPrintStream err() { if (errCache == null) { - if (PlatformDetector.isJavaScript()) { + if (PlatformDetector.isJavaScript() || PlatformDetector.isWebAssemblyGC()) { errCache = (TPrintStream) (Object) new JSStderrPrintStream(); } else { errCache = new TPrintStream(StderrOutputStream.INSTANCE, false); diff --git a/core/src/main/java/org/teavm/ast/VariableNode.java b/core/src/main/java/org/teavm/ast/VariableNode.java index 2cc1fc28c..2c7d915b3 100644 --- a/core/src/main/java/org/teavm/ast/VariableNode.java +++ b/core/src/main/java/org/teavm/ast/VariableNode.java @@ -19,14 +19,20 @@ import org.teavm.model.util.VariableType; public class VariableNode { private int index; + private int originalIndex; private VariableType type; private String name; public VariableNode(int index, VariableType type) { this.index = index; + this.originalIndex = index; this.type = type; } + public int getOriginalIndex() { + return originalIndex; + } + public int getIndex() { return index; } diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index dd981a6da..22da2f497 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -92,18 +92,15 @@ import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyListener; import org.teavm.interop.Address; import org.teavm.interop.Platforms; -import org.teavm.model.BasicBlock; import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassReader; import org.teavm.model.FieldReader; import org.teavm.model.FieldReference; -import org.teavm.model.Instruction; import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodDescriptor; -import org.teavm.model.MethodHolder; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.Program; @@ -111,9 +108,6 @@ import org.teavm.model.ValueType; import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.VirtualTableBuilder; import org.teavm.model.classes.VirtualTableProvider; -import org.teavm.model.instructions.CloneArrayInstruction; -import org.teavm.model.instructions.InvocationType; -import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.lowlevel.CallSiteDescriptor; import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.ClassInitializerEliminator; @@ -623,40 +617,11 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { VirtualTableBuilder builder = new VirtualTableBuilder(classes); - builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes)); + builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes)); builder.setMethodCalledVirtually(controller::isVirtual); return builder.build(); } - private Set getMethodsUsedOnCallSites(ListableClassHolderSource classes) { - Set virtualMethods = new HashSet<>(); - - for (String className : classes.getClassNames()) { - ClassHolder cls = classes.get(className); - for (MethodHolder method : cls.getMethods()) { - Program program = method.getProgram(); - if (program == null) { - continue; - } - for (int i = 0; i < program.basicBlockCount(); ++i) { - BasicBlock block = program.basicBlockAt(i); - for (Instruction insn : block) { - if (insn instanceof InvokeInstruction) { - InvokeInstruction invoke = (InvokeInstruction) insn; - if (invoke.getType() == InvocationType.VIRTUAL) { - virtualMethods.add(invoke.getMethod()); - } - } else if (insn instanceof CloneArrayInstruction) { - virtualMethods.add(new MethodReference(Object.class, "clone", Object.class)); - } - } - } - } - } - - return virtualMethods; - } - private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) { IncludeManager includes = new SimpleIncludeManager(context.getFileNames(), writer); includes.init("special.c"); diff --git a/core/src/main/java/org/teavm/backend/wasm/BaseWasmFunctionRepository.java b/core/src/main/java/org/teavm/backend/wasm/BaseWasmFunctionRepository.java new file mode 100644 index 000000000..16e5926ec --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/BaseWasmFunctionRepository.java @@ -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); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/Example.java b/core/src/main/java/org/teavm/backend/wasm/Example.java deleted file mode 100644 index 7cb485ee9..000000000 --- a/core/src/main/java/org/teavm/backend/wasm/Example.java +++ /dev/null @@ -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 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 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.()"); - } - - public static void foo() { - System.out.println("Initialized.foo()"); - } - } -} diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmFunctionRepository.java b/core/src/main/java/org/teavm/backend/wasm/WasmFunctionRepository.java index ab849841f..a48dda336 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmFunctionRepository.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmFunctionRepository.java @@ -27,7 +27,7 @@ import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; -public class WasmFunctionRepository { +public class WasmFunctionRepository implements BaseWasmFunctionRepository { private WasmModule module; private WasmFunctionTypes functionTypes; private NameProvider nameProvider; @@ -50,6 +50,7 @@ public class WasmFunctionRepository { return isStatic ? forStaticMethod(reference) : forInstanceMethod(reference); } + @Override public WasmFunction forStaticMethod(MethodReference reference) { return staticMethods.computeIfAbsent(reference, key -> { var wasmParams = new WasmType[key.parameterCount()]; @@ -65,6 +66,7 @@ public class WasmFunctionRepository { }); } + @Override public WasmFunction forInstanceMethod(MethodReference reference) { return instanceMethods.computeIfAbsent(reference, key -> { var wasmParams = new WasmType[key.parameterCount() + 1]; diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java new file mode 100644 index 000000000..4e2d2e18d --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCModuleGenerator.java @@ -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)); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java new file mode 100644 index 000000000..687aa50d7 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -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 getDependencyListeners() { + return List.of(); + } + + @Override + public List getTransformers() { + return List.of(); + } + + @Override + public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) { + new WasmGCDependencies(dependencyAnalyzer).contribute(); + } + + @Override + public List 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); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 9f9340c0b..61dc1d1e8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -130,7 +130,6 @@ import org.teavm.interop.Import; import org.teavm.interop.Platforms; import org.teavm.interop.StaticInit; import org.teavm.model.AnnotationHolder; -import org.teavm.model.BasicBlock; import org.teavm.model.CallLocation; import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHolder; @@ -139,7 +138,6 @@ import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; -import org.teavm.model.Instruction; import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodDescriptor; @@ -152,9 +150,6 @@ import org.teavm.model.analysis.ClassMetadataRequirements; import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.VirtualTableBuilder; import org.teavm.model.classes.VirtualTableProvider; -import org.teavm.model.instructions.CloneArrayInstruction; -import org.teavm.model.instructions.InvocationType; -import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerTransformer; @@ -1075,40 +1070,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { var builder = new VirtualTableBuilder(classes); - builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes)); + builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes)); builder.setMethodCalledVirtually(controller::isVirtual); return builder.build(); } - private Set getMethodsUsedOnCallSites(ListableClassHolderSource classes) { - var 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; - } - @Override public String[] getPlatformTags() { return new String[] { Platforms.WEBASSEMBLY, Platforms.LOW_LEVEL }; diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java b/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java new file mode 100644 index 000000000..3f85d5bd6 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java @@ -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 { + 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 findPathToRoot(ClassReader cls) { + var path = new ArrayList(); + 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(); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java new file mode 100644 index 000000000..fa37c67f9 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java @@ -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)); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java new file mode 100644 index 000000000..004302f2c --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java @@ -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; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java index d1664a704..cf2ce40cf 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java @@ -90,7 +90,7 @@ public class WasmGenerationContext implements BaseWasmGenerationContext { } @Override - public ClassReaderSource classSource() { + public ClassReaderSource classes() { return classSource; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationContext.java index 9fa58b3cd..784d60e23 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationContext.java @@ -15,17 +15,17 @@ */ package org.teavm.backend.wasm.generate.common.methods; -import org.teavm.backend.wasm.WasmFunctionRepository; +import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.model.WasmTag; import org.teavm.model.ClassReaderSource; public interface BaseWasmGenerationContext { - WasmFunctionRepository functions(); + BaseWasmFunctionRepository functions(); WasmFunctionTypes functionTypes(); WasmTag getExceptionTag(); - ClassReaderSource classSource(); + ClassReaderSource classes(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java index f8079aa44..9ac77329f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java @@ -832,9 +832,11 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) { if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { - var method = context.classSource().resolve(expr.getMethod()); + var method = context.classes().resolve(expr.getMethod()); var reference = method != null ? method.getReference() : expr.getMethod(); - var function = context.functions().forMethod(reference, expr.getType() == InvocationType.STATIC); + var function = expr.getType() == InvocationType.STATIC + ? context.functions().forStaticMethod(reference) + : context.functions().forInstanceMethod(reference); var call = new WasmCall(function); for (var argument : expr.getArguments()) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java new file mode 100644 index 000000000..068f31142 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java @@ -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 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 virtualMethods) { + var builder = new VirtualTableBuilder(classes); + builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes)); + builder.setMethodCalledVirtually(virtualMethods); + return builder.build(); + } + + public WasmFunction dummyInitializer() { + return methodGenerator.getDummyInitializer(); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/initialization/WasmGCInitializerContributor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCInitializerContributor.java similarity index 93% rename from core/src/main/java/org/teavm/backend/wasm/generate/gc/initialization/WasmGCInitializerContributor.java rename to core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCInitializerContributor.java index 9cb4a0b46..eeda9fead 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/initialization/WasmGCInitializerContributor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCInitializerContributor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.wasm.generate.gc.initialization; +package org.teavm.backend.wasm.generate.gc; import org.teavm.backend.wasm.model.WasmFunction; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index d67642ad8..56c8e374a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -17,13 +17,18 @@ package org.teavm.backend.wasm.generate.gc.classes; import com.carrotsearch.hppc.ObjectIntHashMap; import com.carrotsearch.hppc.ObjectIntMap; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.function.Consumer; import org.teavm.backend.lowlevel.generate.NameProvider; +import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; -import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor; -import org.teavm.backend.wasm.generate.gc.methods.WasmGCFunctionProvider; +import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor; import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool; import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmFunction; @@ -70,17 +75,20 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private TagRegistry tagRegistry; private ClassMetadataRequirements metadataRequirements; private VirtualTableProvider virtualTables; - private WasmGCFunctionProvider functionProvider; + private BaseWasmFunctionRepository functionProvider; private Map classInfoMap = new LinkedHashMap<>(); + private Queue classInfoQueue = new ArrayDeque<>(); private ObjectIntMap fieldIndexes = new ObjectIntHashMap<>(); private ObjectIntMap methodIndexes = new ObjectIntHashMap<>(); + private Map staticFieldLocations = new HashMap<>(); + private List> staticFieldInitializers = new ArrayList<>(); private ClassInitializerInfo classInitializerInfo; public final WasmGCStringPool strings; public final WasmGCStandardClasses standardClasses; - private final WasmGCTypeMapper typeMapper; + public final WasmGCTypeMapper typeMapper; private final NameProvider names; - private WasmFunction initializer; + private List initializerFunctionStatements = new ArrayList<>(); private WasmFunction createPrimitiveClassFunction; private WasmFunction createArrayClassFunction; private final WasmGCSupertypeFunctionGenerator supertypeGenerator; @@ -97,7 +105,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, WasmFunctionTypes functionTypes, TagRegistry tagRegistry, ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables, - WasmGCFunctionProvider functionProvider, NameProvider names, + BaseWasmFunctionRepository functionProvider, NameProvider names, ClassInitializerInfo classInitializerInfo) { this.module = module; this.classSource = classSource; @@ -114,6 +122,22 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit typeMapper = new WasmGCTypeMapper(this); } + public WasmGCSupertypeFunctionProvider getSupertypeProvider() { + return supertypeGenerator; + } + + public boolean process() { + if (classInfoQueue.isEmpty()) { + return false; + } + while (!classInfoQueue.isEmpty()) { + var classInfo = classInfoQueue.remove(); + classInfo.initializer.accept(initializerFunctionStatements); + classInfo.initializer = null; + } + return true; + } + @Override public void contributeToInitializerDefinitions(WasmFunction function) { for (var classInfo : classInfoMap.values()) { @@ -125,8 +149,9 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit @Override public void contributeToInitializer(WasmFunction function) { var classClass = standardClasses.classClass(); + function.getBody().addAll(initializerFunctionStatements); + initializerFunctionStatements.clear(); for (var classInfo : classInfoMap.values()) { - classInfo.initializer.accept(function.getBody()); var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType()); function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, new WasmFunctionReference(supertypeFunction))); @@ -134,12 +159,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit new WasmGetGlobal(classClass.pointer))); if (classInfo.initializerPointer != null) { var className = ((ValueType.Object) classInfo.getValueType()).getClassName(); - var initFunction = functionProvider.getStaticFunction(new MethodReference(className, + var initFunction = functionProvider.forStaticMethod(new MethodReference(className, CLINIT_METHOD_DESC)); function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer, new WasmFunctionReference(initFunction))); } } + for (var consumer : staticFieldInitializers) { + consumer.accept(function); + } } @Override @@ -147,6 +175,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var classInfo = classInfoMap.get(type); if (classInfo == null) { classInfo = new WasmGCClassInfo(type); + classInfoQueue.add(classInfo); classInfoMap.put(type, classInfo); if (!(type instanceof ValueType.Primitive)) { var name = type instanceof ValueType.Object @@ -154,6 +183,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit : null; classInfo.structure = new WasmStructure(name); classInfo.structure.getFields().add(standardClasses.classClass().getType().asStorage()); + module.types.add(classInfo.structure); fillFields(classInfo.structure.getFields(), type); } var pointerName = names.forClassInstance(type); @@ -241,6 +271,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var clinitType = functionTypes.of(null); classInfo.initializerPointer = new WasmGlobal(null, clinitType.getReference(), new WasmNullConstant(clinitType.getReference())); + module.globals.add(classInfo.initializerPointer); } } classInfo.initializer = target -> { @@ -276,7 +307,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit for (var method : virtualTable.getMethods()) { var entry = virtualTable.getEntry(method); if (entry != null && entry.getImplementor() != null) { - var function = functionProvider.getMemberFunction(entry.getImplementor()); + var function = functionProvider.forInstanceMethod(entry.getImplementor()); if (!origin.equals(entry.getImplementor().getClassName())) { var functionType = getFunctionType(virtualTable.getClassName(), method); var wrapperFunction = new WasmFunction(functionType); @@ -361,6 +392,33 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return result; } + @Override + public WasmGlobal getStaticFieldLocation(FieldReference fieldRef) { + return staticFieldLocations.computeIfAbsent(fieldRef, this::generateStaticFieldLocation); + } + + private WasmGlobal generateStaticFieldLocation(FieldReference fieldRef) { + ValueType javaType = null; + Object initValue = null; + var cls = classSource.get(fieldRef.getClassName()); + if (cls != null) { + var field = cls.getField(fieldRef.getFieldName()); + if (field != null) { + javaType = field.getType(); + initValue = field.getInitialValue(); + } + } + if (javaType == null) { + javaType = ValueType.object("java.lang.Object"); + } + + var type = typeMapper.mapType(javaType).asUnpackedType(); + var global = new WasmGlobal(names.forStaticField(fieldRef), type, WasmExpression.defaultValueOfType(type)); + module.globals.add(global); + + return global; + } + @Override public int getVirtualMethodIndex(MethodReference methodRef) { var result = methodIndexes.getOrDefault(methodRef, -1); @@ -442,13 +500,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit ); var function = new WasmFunction(functionType); function.setName("_teavm_fill_primitive_class_"); + module.functions.add(function); var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target"); var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name"); var kindVar = new WasmLocal(WasmType.INT32, "kind"); - function.getLocalVariables().add(targetVar); - function.getLocalVariables().add(nameVar); - function.getLocalVariables().add(kindVar); + function.add(targetVar); + function.add(nameVar); + function.add(kindVar); var flagsExpr = new WasmIntBinary( WasmIntType.INT32, @@ -497,12 +556,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit standardClasses.classClass().getType() ); var function = new WasmFunction(functionType); + module.functions.add(function); function.setName("_teavm_fill_array_class_"); var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target"); var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item"); - function.getLocalVariables().add(targetVar); - function.getLocalVariables().add(itemVar); + function.add(targetVar); + function.add(itemVar); function.getBody().add(new WasmStructSet( standardClasses.classClass().getStructure(), @@ -538,15 +598,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit } - private WasmFunction getInitializer() { - if (initializer == null) { - initializer = new WasmFunction(functionTypes.of(null)); - initializer.setName("_teavm_init_classes_"); - module.functions.add(initializer); - } - return initializer; - } - private WasmExpression setClassField(WasmGCClassInfo classInfo, int fieldIndex, WasmExpression value) { return new WasmStructSet( standardClasses.classClass().getStructure(), diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java index ac8201d32..ce34f79e3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm.generate.gc.classes; +import org.teavm.backend.wasm.model.WasmGlobal; import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -22,12 +23,15 @@ import org.teavm.model.ValueType; public interface WasmGCClassInfoProvider { int CLASS_FIELD_OFFSET = 0; int MONITOR_FIELD_OFFSET = 1; + int CUSTOM_FIELD_OFFSETS = 2; int ARRAY_DATA_FIELD_OFFSET = 2; WasmGCClassInfo getClassInfo(ValueType type); int getFieldIndex(FieldReference fieldRef); + WasmGlobal getStaticFieldLocation(FieldReference fieldRef); + int getVirtualMethodIndex(MethodReference methodRef); default WasmGCClassInfo getClassInfo(String name) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java index 5a6794467..8b4a9e209 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java @@ -15,8 +15,6 @@ */ package org.teavm.backend.wasm.generate.gc.classes; -import com.carrotsearch.hppc.ObjectIntHashMap; -import com.carrotsearch.hppc.ObjectIntMap; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -45,8 +43,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructGet; import org.teavm.model.ValueType; import org.teavm.model.classes.TagRegistry; -public class WasmGCSupertypeFunctionGenerator { - private ObjectIntMap tableIndexes = new ObjectIntHashMap<>(); +public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunctionProvider { private Map functions = new HashMap<>(); private WasmModule module; private WasmGCClassGenerator classGenerator; @@ -69,6 +66,7 @@ public class WasmGCSupertypeFunctionGenerator { this.functionTypes = functionTypes; } + @Override public WasmFunction getIsSupertypeFunction(ValueType type) { var result = functions.get(type); if (result == null) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionProvider.java new file mode 100644 index 000000000..9d465de7c --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionProvider.java @@ -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); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCFunctionProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCCustomGeneratorProvider.java similarity index 76% rename from core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCFunctionProvider.java rename to core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCCustomGeneratorProvider.java index 933cf6d12..cd35d5cc8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCFunctionProvider.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCCustomGeneratorProvider.java @@ -15,11 +15,9 @@ */ package org.teavm.backend.wasm.generate.gc.methods; -import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator; import org.teavm.model.MethodReference; -public interface WasmGCFunctionProvider { - WasmFunction getMemberFunction(MethodReference methodRef); - - WasmFunction getStaticFunction(MethodReference methodRef); +public interface WasmGCCustomGeneratorProvider { + WasmGCCustomGenerator get(MethodReference method); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java similarity index 69% rename from core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCGenerationContext.java rename to core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java index f203827dd..be2e049b0 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.wasm.generate.gc; +package org.teavm.backend.wasm.generate.gc.methods; -import org.teavm.backend.wasm.WasmFunctionRepository; +import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses; +import org.teavm.backend.wasm.generate.gc.classes.WasmGCSupertypeFunctionProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider; import org.teavm.backend.wasm.model.WasmFunction; @@ -39,20 +40,33 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { private WasmGCStringProvider strings; private VirtualTableProvider virtualTables; private WasmGCTypeMapper typeMapper; + private WasmFunctionTypes functionTypes; + private ClassReaderSource classes; + private BaseWasmFunctionRepository functions; + private WasmGCSupertypeFunctionProvider supertypeFunctions; + private WasmGCCustomGeneratorProvider customGenerators; private WasmFunction npeMethod; private WasmFunction aaiobeMethod; private WasmFunction cceMethod; private WasmGlobal exceptionGlobal; + private WasmTag exceptionTag; - public WasmGCGenerationContext(WasmModule module, WasmGCClassInfoProvider classInfoProvider, - WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, VirtualTableProvider virtualTables, - WasmGCTypeMapper typeMapper) { + public WasmGCGenerationContext(WasmModule module, VirtualTableProvider virtualTables, + WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ClassReaderSource classes, + BaseWasmFunctionRepository functions, WasmGCSupertypeFunctionProvider supertypeFunctions, + WasmGCClassInfoProvider classInfoProvider, WasmGCStandardClasses standardClasses, + WasmGCStringProvider strings, WasmGCCustomGeneratorProvider customGenerators) { this.module = module; + this.virtualTables = virtualTables; + this.typeMapper = typeMapper; + this.functionTypes = functionTypes; + this.classes = classes; + this.functions = functions; + this.supertypeFunctions = supertypeFunctions; this.classInfoProvider = classInfoProvider; this.standardClasses = standardClasses; this.strings = strings; - this.virtualTables = virtualTables; - this.typeMapper = typeMapper; + this.customGenerators = customGenerators; } public WasmGCClassInfoProvider classInfoProvider() { @@ -76,23 +90,31 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { } @Override - public WasmFunctionRepository functions() { - return null; + public BaseWasmFunctionRepository functions() { + return functions; + } + + public WasmGCSupertypeFunctionProvider supertypeFunctions() { + return supertypeFunctions; } @Override public WasmFunctionTypes functionTypes() { - return null; + return functionTypes; } @Override public WasmTag getExceptionTag() { - return null; + if (exceptionTag == null) { + exceptionTag = new WasmTag(functionTypes.of(null)); + module.tags.add(exceptionTag); + } + return exceptionTag; } @Override - public ClassReaderSource classSource() { - return null; + public ClassReaderSource classes() { + return classes; } public WasmFunction npeMethod() { @@ -127,4 +149,8 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { } return exceptionGlobal; } + + public WasmGCCustomGeneratorProvider customGenerators() { + return customGenerators; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java new file mode 100644 index 000000000..819bbdbde --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java @@ -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 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); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index 29133f3d6..ef22ac79c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -23,7 +23,6 @@ import org.teavm.ast.QualificationExpr; import org.teavm.ast.SubscriptExpr; import org.teavm.ast.UnwrapArrayExpr; import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor; -import org.teavm.backend.wasm.generate.gc.WasmGCGenerationContext; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmFunction; @@ -33,7 +32,6 @@ import org.teavm.backend.wasm.model.WasmStructure; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmArrayGet; import org.teavm.backend.wasm.model.expression.WasmArrayLength; -import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; import org.teavm.backend.wasm.model.expression.WasmArraySet; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmCall; @@ -57,11 +55,13 @@ import org.teavm.model.ValueType; public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { private WasmGCGenerationContext context; + private WasmGCGenerationUtil generationUtil; public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function, int firstVariable, boolean async) { super(context, function, firstVariable, async); this.context = context; + generationUtil = new WasmGCGenerationUtil(context.classInfoProvider(), tempVars); } @Override @@ -130,20 +130,29 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { @Override protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) { - accept(qualified); - var target = result; - accept(value); - var wasmValue = result; + if (qualified == null) { + accept(value); + var wasmValue = result; + var global = context.classInfoProvider().getStaticFieldLocation(field); + var result = new WasmSetGlobal(global, wasmValue); + result.setLocation(location); + resultConsumer.add(result); + } else { + accept(qualified); + var target = result; + accept(value); + var wasmValue = result; - target.acceptVisitor(typeInference); - var type = (WasmType.CompositeReference) typeInference.getResult(); - var struct = (WasmStructure) type.composite; + target.acceptVisitor(typeInference); + var type = (WasmType.CompositeReference) typeInference.getResult(); + var struct = (WasmStructure) type.composite; - var fieldIndex = context.classInfoProvider().getFieldIndex(field); + var fieldIndex = context.classInfoProvider().getFieldIndex(field); - var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue); - expr.setLocation(location); - resultConsumer.add(expr); + var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue); + expr.setLocation(location); + resultConsumer.add(expr); + } } @Override @@ -230,42 +239,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { @Override protected void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local, List target) { - var classInfo = context.classInfoProvider().getClassInfo(ValueType.arrayOf(itemType)); - var block = new WasmBlock(false); - block.setType(classInfo.getType()); - var targetVar = local; - if (targetVar == null) { - targetVar = tempVars.acquire(classInfo.getType()); - } - - var structNew = new WasmSetLocal(targetVar, new WasmStructNewDefault(classInfo.getStructure())); - structNew.setLocation(location); - target.add(structNew); - - var initClassField = new WasmStructSet(classInfo.getStructure(), new WasmGetLocal(targetVar), - WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, new WasmGetGlobal(classInfo.getPointer())); - initClassField.setLocation(location); - target.add(initClassField); - - var wasmArrayType = (WasmType.CompositeReference) classInfo.getStructure().getFields() - .get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET) - .asUnpackedType(); - var wasmArray = (WasmArray) wasmArrayType.composite; - var initArrayField = new WasmStructSet( - classInfo.getStructure(), - new WasmGetLocal(targetVar), - WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET, - new WasmArrayNewDefault(wasmArray, length) - ); - initArrayField.setLocation(location); - target.add(initArrayField); - - if (local == null) { - var getLocal = new WasmGetLocal(targetVar); - getLocal.setLocation(location); - target.add(getLocal); - tempVars.release(targetVar); - } + generationUtil.allocateArray(itemType, length, location, local, target); } @Override @@ -277,7 +251,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { @Override protected WasmExpression generateInstanceOf(WasmExpression expression, ValueType type) { context.classInfoProvider().getClassInfo(type); - var supertypeCall = new WasmCall(context.functions().forSupertype(type)); + var supertypeCall = new WasmCall(context.supertypeFunctions().getIsSupertypeFunction(type)); var classRef = new WasmStructGet( context.standardClasses().objectClass().getStructure(), expression, @@ -352,17 +326,23 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { @Override public void visit(QualificationExpr expr) { - accept(expr.getQualified()); - var target = result; + if (expr.getQualified() == null) { + var global = context.classInfoProvider().getStaticFieldLocation(expr.getField()); + result = new WasmGetGlobal(global); + result.setLocation(expr.getLocation()); + } else { + accept(expr.getQualified()); + var target = result; - target.acceptVisitor(typeInference); - var type = (WasmType.CompositeReference) typeInference.getResult(); - var struct = (WasmStructure) type.composite; + target.acceptVisitor(typeInference); + var type = (WasmType.CompositeReference) typeInference.getResult(); + var struct = (WasmStructure) type.composite; - var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField()); + var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField()); - result = new WasmStructGet(struct, target, fieldIndex); - result.setLocation(expr.getLocation()); + result = new WasmStructGet(struct, target, fieldIndex); + result.setLocation(expr.getLocation()); + } } private class SimpleCallSite extends CallSiteIdentifier { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java new file mode 100644 index 000000000..e712852e3 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java @@ -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 queue = new ArrayDeque<>(); + private Map staticMethods = new HashMap<>(); + private Map 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("") + && 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; + } + }; +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java index cb65a62e8..a3cd66390 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java @@ -18,15 +18,20 @@ package org.teavm.backend.wasm.generate.gc.strings; import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; +import org.teavm.backend.wasm.BaseWasmFunctionRepository; +import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor; +import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses; -import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor; -import org.teavm.backend.wasm.generate.gc.methods.WasmGCFunctionProvider; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmGlobal; +import org.teavm.backend.wasm.model.WasmMemorySegment; import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmGetGlobal; import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; +import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.render.WasmBinaryWriter; import org.teavm.model.MethodReference; @@ -35,10 +40,10 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer private WasmModule module; private WasmBinaryWriter binaryWriter = new WasmBinaryWriter(); private Map stringMap = new LinkedHashMap<>(); - private WasmGCFunctionProvider functionProvider; + private BaseWasmFunctionRepository functionProvider; public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module, - WasmGCFunctionProvider functionProvider) { + BaseWasmFunctionRepository functionProvider) { this.standardClasses = standardClasses; this.module = module; this.functionProvider = functionProvider; @@ -46,6 +51,9 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer @Override public void contributeToInitializerDefinitions(WasmFunction function) { + var segment = new WasmMemorySegment(); + module.getSegments().add(segment); + segment.setData(binaryWriter.getData()); for (var str : stringMap.values()) { var newStruct = new WasmStructNewDefault(standardClasses.stringClass().getStructure()); function.getBody().add(new WasmSetGlobal(str.global, newStruct)); @@ -54,8 +62,14 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer @Override public void contributeToInitializer(WasmFunction function) { - var nextCharArrayFunction = functionProvider.getStaticFunction(new MethodReference(WasmGCStringPool.class, + var nextCharArrayFunction = functionProvider.forStaticMethod(new MethodReference(WasmGCStringPool.class, "nextCharArray", char[].class)); + var stringStruct = standardClasses.stringClass().getStructure(); + for (var str : stringMap.values()) { + var value = new WasmCall(nextCharArrayFunction); + function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global), + WasmGCClassInfoProvider.CUSTOM_FIELD_OFFSETS, value)); + } } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerator.java new file mode 100644 index 000000000..654305c58 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerator.java @@ -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); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java new file mode 100644 index 000000000..802305056 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorContext.java @@ -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(); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java new file mode 100644 index 000000000..1b0f30ac6 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java @@ -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 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); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCStringPoolGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCStringPoolGenerator.java new file mode 100644 index 000000000..86eccd6ba --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCStringPoolGenerator.java @@ -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))); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java index 142cbe8a0..0b242a57d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmStructure.java @@ -15,10 +15,11 @@ */ package org.teavm.backend.wasm.model; +import java.util.ArrayList; import java.util.List; public class WasmStructure extends WasmCompositeType { - private List fields; + private List fields = new ArrayList<>(); public WasmStructure(String name) { super(name); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java index 3ef20aa5e..fb929cd32 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java @@ -19,6 +19,7 @@ import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmCompositeTypeVisitor; import org.teavm.backend.wasm.model.WasmFunctionType; import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmStorageType; import org.teavm.backend.wasm.model.WasmStructure; public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor { @@ -26,17 +27,24 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor private WasmBinaryWriter section; public WasmCompositeTypeBinaryRenderer(WasmModule module, WasmBinaryWriter section) { + this.module = module; this.section = section; } @Override public void visit(WasmStructure type) { - + section.writeByte(0x5F); + section.writeLEB(type.getFields().size()); + for (var fieldType : type.getFields()) { + writeStorageType(fieldType); + section.writeLEB(0x01); // mutable + } } @Override public void visit(WasmArray type) { - + writeStorageType(type.getElementType()); + section.writeLEB(0x01); // mutable } @Override @@ -53,4 +61,19 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor section.writeByte(0); } } + + private void writeStorageType(WasmStorageType storageType) { + if (storageType instanceof WasmStorageType.Packed) { + switch (((WasmStorageType.Packed) storageType).type) { + case INT8: + section.writeByte(0x78); + break; + case INT16: + section.writeByte(0x77); + break; + } + } else { + section.writeType(storageType.asUnpackedType(), module); + } + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java index 44d4237a8..1d2a6b702 100644 --- a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java +++ b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java @@ -15,6 +15,8 @@ */ package org.teavm.backend.wasm.runtime; +import org.teavm.interop.Import; + public class WasmGCSupport { private WasmGCSupport() { } @@ -30,4 +32,10 @@ public class WasmGCSupport { public static ClassCastException cce() { return new ClassCastException(); } + + @Import(name = "putcharStdout") + public static native void putCharStdout(char c); + + @Import(name = "putcharStderr") + public static native void putCharStderr(char c); } diff --git a/core/src/main/java/org/teavm/model/MethodDescriptor.java b/core/src/main/java/org/teavm/model/MethodDescriptor.java index bfc439b52..8c152b134 100644 --- a/core/src/main/java/org/teavm/model/MethodDescriptor.java +++ b/core/src/main/java/org/teavm/model/MethodDescriptor.java @@ -64,7 +64,7 @@ public class MethodDescriptor implements Serializable { public ValueType parameterType(int index) { if (index >= signature.length - 1) { - throw new IndexOutOfBoundsException(String.valueOf(index) + "/" + (signature.length - 1)); + throw new IndexOutOfBoundsException(index + "/" + (signature.length - 1)); } return signature[index]; } diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java b/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java index 7755cb671..09f197332 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java @@ -36,10 +36,14 @@ import org.teavm.common.LCATree; import org.teavm.model.AccessLevel; import org.teavm.model.ClassReader; import org.teavm.model.ElementModifier; +import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; +import org.teavm.model.instructions.CloneArrayInstruction; +import org.teavm.model.instructions.InvocationType; +import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.util.GraphColorer; public class VirtualTableBuilder { @@ -518,4 +522,33 @@ public class VirtualTableBuilder { IntArrayList colors = new IntArrayList(); List methods = new ArrayList<>(); } + + public static Set getMethodsUsedOnCallSites(ListableClassHolderSource classes) { + var virtualMethods = new HashSet(); + + 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; + } } diff --git a/core/src/main/java/org/teavm/model/util/DefaultVariableCategoryProvider.java b/core/src/main/java/org/teavm/model/util/DefaultVariableCategoryProvider.java index 3b08d178a..a381d29ec 100644 --- a/core/src/main/java/org/teavm/model/util/DefaultVariableCategoryProvider.java +++ b/core/src/main/java/org/teavm/model/util/DefaultVariableCategoryProvider.java @@ -16,11 +16,11 @@ package org.teavm.model.util; import org.teavm.model.MethodReference; -import org.teavm.model.ProgramReader; +import org.teavm.model.Program; public class DefaultVariableCategoryProvider implements VariableCategoryProvider { @Override - public Object[] getCategories(ProgramReader program, MethodReference method) { + public Object[] getCategories(Program program, MethodReference method) { TypeInferer inferer = new TypeInferer(); inferer.inferTypes(program, method); var categories = new Object[program.variableCount()]; diff --git a/core/src/main/java/org/teavm/model/util/VariableCategoryProvider.java b/core/src/main/java/org/teavm/model/util/VariableCategoryProvider.java index ce988b8dc..b52af0931 100644 --- a/core/src/main/java/org/teavm/model/util/VariableCategoryProvider.java +++ b/core/src/main/java/org/teavm/model/util/VariableCategoryProvider.java @@ -16,8 +16,8 @@ package org.teavm.model.util; import org.teavm.model.MethodReference; -import org.teavm.model.ProgramReader; +import org.teavm.model.Program; public interface VariableCategoryProvider { - Object[] getCategories(ProgramReader program, MethodReference method); + Object[] getCategories(Program program, MethodReference method); } diff --git a/core/src/main/java/org/teavm/vm/TeaVMTargetController.java b/core/src/main/java/org/teavm/vm/TeaVMTargetController.java index f9191cdfb..9983ecb5d 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTargetController.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTargetController.java @@ -60,5 +60,4 @@ public interface TeaVMTargetController { void addVirtualMethods(Predicate methods); ClassInitializerInfo getClassInitializerInfo(); - } diff --git a/interop/core/src/main/java/org/teavm/interop/Platforms.java b/interop/core/src/main/java/org/teavm/interop/Platforms.java index 521bb0381..c752d451a 100644 --- a/interop/core/src/main/java/org/teavm/interop/Platforms.java +++ b/interop/core/src/main/java/org/teavm/interop/Platforms.java @@ -23,4 +23,5 @@ public final class Platforms { public static final String WEBASSEMBLY = "webassembly"; public static final String C = "c"; public static final String LOW_LEVEL = "low_level"; + public static final String WEBASSEMBLY_GC = "webassembly-gc"; }