From 5eb1e7d9bc192653b672a3df9612bef7950e6111 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 18 Aug 2024 19:55:25 +0200 Subject: [PATCH] wasm gc: implement intrinsics for Long and Class classes --- .../org/teavm/classlib/java/lang/TClass.java | 22 +++++- .../backend/wasm/gc/WasmGCDependencies.java | 17 +++++ .../gc/methods/WasmGCGenerationVisitor.java | 1 + .../wasm/generators/gc/ClassGenerators.java | 12 --- .../generators/gc/WasmGCCustomGenerators.java | 1 - .../wasm/intrinsics/gc/ClassIntrinsics.java | 19 +++++ .../wasm/intrinsics/gc/LongIntrinsic.java | 50 ++++++++++++ .../wasm/intrinsics/gc/WasmGCIntrinsics.java | 36 ++++++++- .../intrinsics/gc/WasmRuntimeIntrinsic.java | 76 +++++++++++++++++++ .../org/teavm/model/classes/VirtualTable.java | 17 +---- 10 files changed, 219 insertions(+), 32 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/LongIntrinsic.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmRuntimeIntrinsic.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index e594edb56..413b1b5b5 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -117,7 +117,20 @@ public final class TClass extends TObject implements TAnnotatedElement, TType } public String getName() { - if (PlatformDetector.isLowLevel()) { + if (PlatformDetector.isWebAssemblyGC()) { + var result = getNameImpl(); + if (result == null) { + if (isArray()) { + var componentType = getComponentType(); + String componentName = componentType.getName(); + if (componentName != null) { + result = componentType.isArray() ? "[" + componentName : "[L" + componentName + ";"; + setNameImpl(result); + } + } + } + return result; + } else if (PlatformDetector.isLowLevel()) { String result = getNameCache(this); if (result == null) { result = Platform.getName(platformClass); @@ -141,6 +154,10 @@ public final class TClass extends TObject implements TAnnotatedElement, TType } } + private native String getNameImpl(); + + private native void setNameImpl(String name); + public String getSimpleName() { String simpleName = getSimpleNameCache(this); if (simpleName == null) { @@ -274,6 +291,9 @@ public final class TClass extends TObject implements TAnnotatedElement, TType } public boolean isArray() { + if (PlatformDetector.isWebAssemblyGC()) { + return getComponentType() != null; + } return Platform.getArrayItem(platformClass) != null; } 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 index b4397a4ff..721da8436 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java @@ -16,6 +16,7 @@ package org.teavm.backend.wasm.gc; import java.util.Arrays; +import java.util.List; import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.runtime.WasmGCSupport; import org.teavm.dependency.DependencyAnalyzer; @@ -29,6 +30,7 @@ public class WasmGCDependencies { } public void contribute() { + contributeWasmRuntime(); contributeMathUtils(); contributeExceptionUtils(); contributeInitializerUtils(); @@ -52,6 +54,21 @@ public class WasmGCDependencies { .use(); } + private void contributeWasmRuntime() { + for (var cls : List.of(int.class, long.class, float.class, double.class)) { + analyzer.linkMethod(new MethodReference(WasmRuntime.class, "lt", cls, cls, boolean.class)).use(); + analyzer.linkMethod(new MethodReference(WasmRuntime.class, "gt", cls, cls, boolean.class)).use(); + } + for (var cls : List.of(int.class, long.class)) { + analyzer.linkMethod(new MethodReference(WasmRuntime.class, "ltu", cls, cls, boolean.class)).use(); + analyzer.linkMethod(new MethodReference(WasmRuntime.class, "gtu", cls, cls, boolean.class)).use(); + } + for (var cls : List.of(float.class, double.class)) { + analyzer.linkMethod(new MethodReference(WasmRuntime.class, "min", cls, cls, cls)).use(); + analyzer.linkMethod(new MethodReference(WasmRuntime.class, "max", cls, cls, cls)).use(); + } + } + 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); 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 9237d5a92..162205647 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 @@ -455,6 +455,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { var intrinsic = context.intrinsics().get(expr.getMethod()); if (intrinsic != null) { var resultExpr = intrinsic.apply(expr, intrinsicContext); + resultExpr.setLocation(expr.getLocation()); if (resultConsumer != null) { if (willDrop) { var drop = new WasmDrop(resultExpr); diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java index 90714d453..bff4f0596 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/ClassGenerators.java @@ -36,9 +36,6 @@ public class ClassGenerators implements WasmGCCustomGenerator { case "isInstance": generateIsInstance(function, context); break; - case "getName": - generateGetName(function, context); - break; default: throw new IllegalArgumentException("Unsupported method: " + method); } @@ -69,13 +66,4 @@ public class ClassGenerators implements WasmGCCustomGenerator { function.getBody().add(new WasmReturn(conditional)); } - private void generateGetName(WasmFunction function, WasmGCCustomGeneratorContext context) { - var classCls = context.classInfoProvider().getClassInfo("java.lang.Class"); - var thisVar = new WasmLocal(classCls.getType()); - function.add(thisVar); - - var nameRef = new WasmStructGet(classCls.getStructure(), new WasmGetLocal(thisVar), - context.classInfoProvider().getClassNameOffset()); - function.getBody().add(new WasmReturn(nameRef)); - } } 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 index 5e2a1ff7d..2236d6a95 100644 --- 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 @@ -35,7 +35,6 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider { private void fillClass() { var classGenerators = new ClassGenerators(); generators.put(new MethodReference(Class.class, "isInstance", Object.class, boolean.class), classGenerators); - generators.put(new MethodReference(Class.class, "getName", String.class), classGenerators); } private void fillStringPool() { diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsics.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsics.java index b8bbcc166..def726133 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsics.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsics.java @@ -18,6 +18,7 @@ package org.teavm.backend.wasm.intrinsics.gc; import org.teavm.ast.InvocationExpr; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.backend.wasm.model.expression.WasmStructSet; public class ClassIntrinsics implements WasmGCIntrinsic { @Override @@ -30,8 +31,26 @@ public class ClassIntrinsics implements WasmGCIntrinsic { context.classInfoProvider().getClassArrayItemOffset()); result.setLocation(invocation.getLocation()); return result; + case "getNameImpl": + return generateGetName(invocation, context); + case "setNameImpl": + return generateSetName(invocation, context); default: throw new IllegalArgumentException("Unsupported invocation method: " + invocation.getMethod()); } } + + private WasmExpression generateGetName(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var classCls = context.classInfoProvider().getClassInfo("java.lang.Class"); + var arg = context.generate(invocation.getArguments().get(0)); + return new WasmStructGet(classCls.getStructure(), arg, context.classInfoProvider().getClassNameOffset()); + } + + private WasmExpression generateSetName(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var classCls = context.classInfoProvider().getClassInfo("java.lang.Class"); + var arg = context.generate(invocation.getArguments().get(0)); + var value = context.generate(invocation.getArguments().get(1)); + return new WasmStructSet(classCls.getStructure(), arg, context.classInfoProvider().getClassNameOffset(), + value); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/LongIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/LongIntrinsic.java new file mode 100644 index 000000000..1f7eece75 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/LongIntrinsic.java @@ -0,0 +1,50 @@ +/* + * 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.intrinsics.gc; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.WasmRuntime; +import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmExpression; +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.model.MethodReference; + +public class LongIntrinsic implements WasmGCIntrinsic { + private static final MethodReference COMPARE_UNSIGNED = new MethodReference(WasmRuntime.class, + "compareUnsigned", long.class, long.class, int.class); + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + switch (invocation.getMethod().getName()) { + case "divideUnsigned": + return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.DIV_UNSIGNED, + context.generate(invocation.getArguments().get(0)), + context.generate(invocation.getArguments().get(1))); + case "remainderUnsigned": + return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.REM_UNSIGNED, + context.generate(invocation.getArguments().get(0)), + context.generate(invocation.getArguments().get(1))); + case "compareUnsigned": + return new WasmCall(context.functions().forStaticMethod(COMPARE_UNSIGNED), + context.generate(invocation.getArguments().get(0)), + context.generate(invocation.getArguments().get(1))); + default: + throw new AssertionError(); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java index f016c5133..c54b2c74d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java @@ -16,7 +16,9 @@ package org.teavm.backend.wasm.intrinsics.gc; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.generate.gc.methods.WasmGCIntrinsicProvider; import org.teavm.model.MethodReference; @@ -24,9 +26,27 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { private Map intrinsics = new HashMap<>(); public WasmGCIntrinsics() { + fillWasmRuntime(); fillObject(); fillClass(); fillSystem(); + fillLong(); + } + + private void fillWasmRuntime() { + var intrinsic = new WasmRuntimeIntrinsic(); + for (var cls : List.of(int.class, long.class, float.class, double.class)) { + intrinsics.put(new MethodReference(WasmRuntime.class, "lt", cls, cls, boolean.class), intrinsic); + intrinsics.put(new MethodReference(WasmRuntime.class, "gt", cls, cls, boolean.class), intrinsic); + } + for (var cls : List.of(int.class, long.class)) { + intrinsics.put(new MethodReference(WasmRuntime.class, "ltu", cls, cls, boolean.class), intrinsic); + intrinsics.put(new MethodReference(WasmRuntime.class, "gtu", cls, cls, boolean.class), intrinsic); + } + for (var cls : List.of(float.class, double.class)) { + intrinsics.put(new MethodReference(WasmRuntime.class, "min", cls, cls, cls), intrinsic); + intrinsics.put(new MethodReference(WasmRuntime.class, "max", cls, cls, cls), intrinsic); + } } private void fillObject() { @@ -35,8 +55,10 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { } private void fillClass() { - var classIntrinsics = new ClassIntrinsics(); - intrinsics.put(new MethodReference(Class.class, "getComponentType", Class.class), classIntrinsics); + var intrinsic = new ClassIntrinsics(); + intrinsics.put(new MethodReference(Class.class, "getComponentType", Class.class), intrinsic); + intrinsics.put(new MethodReference(Class.class, "getNameImpl", String.class), intrinsic); + intrinsics.put(new MethodReference(Class.class, "setNameImpl", String.class, void.class), intrinsic); } private void fillSystem() { @@ -44,6 +66,16 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { int.class, int.class, void.class), new SystemArrayCopyIntrinsic()); } + private void fillLong() { + var intrinsic = new LongIntrinsic(); + intrinsics.put(new MethodReference(Long.class, "divideUnsigned", long.class, long.class, long.class), + intrinsic); + intrinsics.put(new MethodReference(Long.class, "remainderUnsigned", long.class, long.class, long.class), + intrinsic); + intrinsics.put(new MethodReference(Long.class, "compareUnsigned", long.class, long.class, int.class), + intrinsic); + } + @Override public WasmGCIntrinsic get(MethodReference method) { return intrinsics.get(method); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmRuntimeIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmRuntimeIntrinsic.java new file mode 100644 index 000000000..9d5d59f45 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmRuntimeIntrinsic.java @@ -0,0 +1,76 @@ +/* + * 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.intrinsics.gc; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.generate.WasmGeneratorUtil; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmFloatBinary; +import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmFloatType; +import org.teavm.backend.wasm.model.expression.WasmIntBinary; +import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmIntType; + +public class WasmRuntimeIntrinsic implements WasmGCIntrinsic { + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + switch (invocation.getMethod().getName()) { + case "lt": + return comparison(WasmIntBinaryOperation.LT_SIGNED, WasmFloatBinaryOperation.LT, + invocation, context); + case "gt": + return comparison(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.GT, + invocation, context); + case "ltu": + return comparison(WasmIntBinaryOperation.LT_UNSIGNED, WasmFloatBinaryOperation.LT, + invocation, context); + case "gtu": + return comparison(WasmIntBinaryOperation.GT_UNSIGNED, WasmFloatBinaryOperation.GT, + invocation, context); + case "min": + return comparison(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.MIN, + invocation, context); + case "max": + return comparison(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.MAX, + invocation, context); + default: + throw new IllegalArgumentException(invocation.getMethod().getName()); + } + } + + private static WasmExpression comparison(WasmIntBinaryOperation intOp, WasmFloatBinaryOperation floatOp, + InvocationExpr invocation, WasmGCIntrinsicContext context) { + var type = (WasmType.Number) WasmGeneratorUtil.mapType(invocation.getMethod().parameterType(0)); + + WasmExpression first = context.generate(invocation.getArguments().get(0)); + WasmExpression second = context.generate(invocation.getArguments().get(1)); + + switch (type.number) { + case INT32: + return new WasmIntBinary(WasmIntType.INT32, intOp, first, second); + case INT64: + return new WasmIntBinary(WasmIntType.INT64, intOp, first, second); + case FLOAT32: + return new WasmFloatBinary(WasmFloatType.FLOAT32, floatOp, first, second); + case FLOAT64: + return new WasmFloatBinary(WasmFloatType.FLOAT64, floatOp, first, second); + default: + throw new IllegalArgumentException(type.toString()); + } + } +} diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTable.java b/core/src/main/java/org/teavm/model/classes/VirtualTable.java index 362ded592..6af3624c8 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTable.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTable.java @@ -74,21 +74,6 @@ public class VirtualTable { } public boolean hasValidEntries() { - if (!hasValidEntriesComputed) { - hasValidEntriesComputed = true; - hasValidEntries = false; - if (entryMap != null) { - for (var entry : entryMap.values()) { - if (entry.getImplementor() != null) { - hasValidEntries = true; - break; - } - } - } - if (parent != null && parent.hasValidEntries()) { - hasValidEntries = true; - } - } - return hasValidEntries; + return !methods.isEmpty(); } }