Skip to content

Commit

Permalink
wasm gc: implement intrinsics for Long and Class classes
Browse files Browse the repository at this point in the history
  • Loading branch information
konsoletyper committed Aug 18, 2024
1 parent a97e657 commit 5eb1e7d
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 32 deletions.
22 changes: 21 additions & 1 deletion classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,20 @@ private boolean isAssignableFromLowLevel(RuntimeClass other) {
}

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);
Expand All @@ -141,6 +154,10 @@ public String getName() {
}
}

private native String getNameImpl();

private native void setNameImpl(String name);

public String getSimpleName() {
String simpleName = getSimpleNameCache(this);
if (simpleName == null) {
Expand Down Expand Up @@ -274,6 +291,9 @@ public boolean isPrimitive() {
}

public boolean isArray() {
if (PlatformDetector.isWebAssemblyGC()) {
return getComponentType() != null;
}
return Platform.getArrayItem(platformClass) != null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,6 +30,7 @@ public WasmGCDependencies(DependencyAnalyzer analyzer) {
}

public void contribute() {
contributeWasmRuntime();
contributeMathUtils();
contributeExceptionUtils();
contributeInitializerUtils();
Expand All @@ -52,6 +54,21 @@ public void contributeStandardExports() {
.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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ protected WasmExpression invocation(InvocationExpr expr, List<WasmExpression> re
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ public void apply(MethodReference method, WasmFunction function, WasmGCCustomGen
case "isInstance":
generateIsInstance(function, context);
break;
case "getName":
generateGetName(function, context);
break;
default:
throw new IllegalArgumentException("Unsupported method: " + method);
}
Expand Down Expand Up @@ -69,13 +66,4 @@ private void generateIsInstance(WasmFunction function, WasmGCCustomGeneratorCont
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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public WasmGCCustomGenerators() {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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
Expand All @@ -30,8 +31,26 @@ public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext co
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);
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,37 @@
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;

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() {
Expand All @@ -35,15 +55,27 @@ private void fillObject() {
}

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() {
intrinsics.put(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class,
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
}
17 changes: 1 addition & 16 deletions core/src/main/java/org/teavm/model/classes/VirtualTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,6 @@ public int size() {
}

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

0 comments on commit 5eb1e7d

Please sign in to comment.