wasm gc: implement intrinsics for Long and Class classes

This commit is contained in:
Alexey Andreev 2024-08-18 19:55:25 +02:00
parent a97e6574ac
commit 5eb1e7d9bc
10 changed files with 219 additions and 32 deletions

View File

@ -117,7 +117,20 @@ public final class TClass<T> 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<T> 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<T> extends TObject implements TAnnotatedElement, TType
}
public boolean isArray() {
if (PlatformDetector.isWebAssemblyGC()) {
return getComponentType() != null;
}
return Platform.getArrayItem(platformClass) != null;
}

View File

@ -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);

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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<MethodReference, WasmGCIntrinsic> 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);

View File

@ -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());
}
}
}

View File

@ -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();
}
}