wasm gc: implement System.identityHashCode() and some Integer intrinsics

This commit is contained in:
Alexey Andreev 2024-08-21 20:54:03 +02:00
parent 5d109236d9
commit 335e2da4cf
29 changed files with 662 additions and 54 deletions

View File

@ -15,6 +15,7 @@
*/
package org.teavm.classlib.java.lang;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.classlib.PlatformDetector;
import org.teavm.dependency.PluggableDependency;
import org.teavm.interop.Address;
@ -246,7 +247,22 @@ public class TObject {
}
final int identity() {
if (PlatformDetector.isLowLevel()) {
if (PlatformDetector.isWebAssemblyGC()) {
var identity = wasmGCIdentity();
if (identity < 0) {
var monitor = this.monitor;
if (monitor != null) {
if (monitor.id < 0) {
monitor.id = WasmGCSupport.nextObjectId() & 0x7ffffff;
}
return monitor.id;
} else {
identity = WasmGCSupport.nextObjectId();
setWasmGCIdentity(identity);
}
}
return identity;
} else if (PlatformDetector.isLowLevel()) {
Monitor monitor = this.monitor;
if (monitor == null) {
int hashCode = hashCodeLowLevel(this);
@ -271,6 +287,10 @@ public class TObject {
return Platform.getPlatformObject(this).getId();
}
private native int wasmGCIdentity();
private native void setWasmGCIdentity(int identity);
@DelegateTo("hashCodeLowLevelImpl")
@NoSideEffects
@Unmanaged

View File

@ -135,6 +135,8 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
return "extern";
case STRUCT:
return "struct";
case I31:
return "i31";
default:
throw new IllegalArgumentException();
}
@ -829,8 +831,21 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
}
@Override
public void cast(WasmHollowType.Reference type) {
writer.address(address).write("ref.cast ").write(typeToString(type)).eol();
public void cast(WasmHollowType.Reference type, boolean nullable) {
writer.address(address).write("ref.cast (ref ");
if (!nullable) {
writer.write("null ");
}
writer.write(typeToString(type)).write(")").eol();
}
@Override
public void test(WasmHollowType.Reference type, boolean nullable) {
writer.address(address).write("ref.test (ref ");
if (!nullable) {
writer.write("null ");
}
writer.write(typeToString(type)).write(")").eol();
}
@Override
@ -890,6 +905,16 @@ public class DisassemblyCodeSectionListener implements AddressListener, CodeSect
writer.address(address).write("ref.func ").write(Integer.toString(functionIndex)).eol();
}
@Override
public void int31Reference() {
writer.address(address).write("ref.i31").eol();
}
@Override
public void int31Get(WasmSignedType signedType) {
writer.address(address).write("ref.i31_").write(signedType == WasmSignedType.SIGNED ? "s" : "u").eol();
}
public static void main(String[] args) throws IOException {
var file = new File(args[0]);
var bytes = Files.readAllBytes(file.toPath());

View File

@ -26,6 +26,7 @@ import org.teavm.ast.SubscriptExpr;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.PreciseTypeInference;
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
@ -587,5 +588,10 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
public WasmGCClassInfoProvider classInfoProvider() {
return context.classInfoProvider();
}
@Override
public TemporaryVariablePool tempVars() {
return tempVars;
}
};
}

View File

@ -24,23 +24,28 @@ 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);
public class IntNumIntrinsic implements WasmGCIntrinsic {
private final MethodReference compareUnsigned;
private final WasmIntType wasmType;
public IntNumIntrinsic(Class<?> javaType, WasmIntType wasmType) {
compareUnsigned = new MethodReference(WasmRuntime.class, "compareUnsigned", javaType, javaType, int.class);
this.wasmType = wasmType;
}
@Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
switch (invocation.getMethod().getName()) {
case "divideUnsigned":
return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.DIV_UNSIGNED,
return new WasmIntBinary(wasmType, 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,
return new WasmIntBinary(wasmType, 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),
return new WasmCall(context.functions().forStaticMethod(compareUnsigned),
context.generate(invocation.getArguments().get(0)),
context.generate(invocation.getArguments().get(1)));
default:

View File

@ -0,0 +1,130 @@
/*
* 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.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch;
import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmDrop;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmInt31Get;
import org.teavm.backend.wasm.model.expression.WasmInt31Reference;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary;
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation;
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmSignedType;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmTest;
import org.teavm.model.ValueType;
public class ObjectIntrinsic implements WasmGCIntrinsic {
@Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
switch (invocation.getMethod().getName()) {
case "getClass":
return generateGetClass(invocation, context);
case "getMonitor":
return generateGetMonitor(invocation, context);
case "setMonitor":
return generateSetMonitor(invocation, context);
case "wasmGCIdentity":
return generateGetIdentity(invocation, context);
case "setWasmGCIdentity":
return generateSetIdentity(invocation, context);
default:
throw new IllegalArgumentException();
}
}
private WasmExpression generateGetClass(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var obj = context.generate(invocation.getArguments().get(0));
var objectStruct = context.classInfoProvider().getClassInfo("java.lang.Object").getStructure();
var result = new WasmStructGet(objectStruct, obj, WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
result.setLocation(invocation.getLocation());
return result;
}
private WasmExpression generateGetMonitor(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var monitorType = context.classInfoProvider().getClassInfo(ValueType.object("java.lang.Object$Monitor"))
.getStructure().getReference();
var objectStruct = context.classInfoProvider().getClassInfo(ValueType.object("java.lang.Object"))
.getStructure();
var block = new WasmBlock(false);
block.setType(monitorType);
var tmpVar = context.tempVars().acquire(WasmType.Reference.ANY);
var instance = context.generate(invocation.getArguments().get(0));
block.getBody().add(new WasmSetLocal(tmpVar, new WasmStructGet(objectStruct, instance,
WasmGCClassInfoProvider.MONITOR_FIELD_OFFSET)));
WasmExpression test = new WasmTest(new WasmGetLocal(tmpVar), monitorType, false);
test = new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, test);
var branch = new WasmBranch(test, block);
branch.setResult(new WasmNullConstant(monitorType));
block.getBody().add(new WasmDrop(branch));
block.getBody().add(new WasmCast(new WasmGetLocal(tmpVar), monitorType));
context.tempVars().release(tmpVar);
return block;
}
private WasmExpression generateSetMonitor(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var objectStruct = context.classInfoProvider().getClassInfo(ValueType.object("java.lang.Object"))
.getStructure();
var instance = context.generate(invocation.getArguments().get(0));
var monitor = context.generate(invocation.getArguments().get(1));
return new WasmStructSet(objectStruct, instance, WasmGCClassInfoProvider.MONITOR_FIELD_OFFSET, monitor);
}
private WasmExpression generateGetIdentity(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var objectStruct = context.classInfoProvider().getClassInfo(ValueType.object("java.lang.Object"))
.getStructure();
var block = new WasmBlock(false);
block.setType(WasmType.INT32);
var tmpVar = context.tempVars().acquire(WasmType.Reference.ANY);
var instance = context.generate(invocation.getArguments().get(0));
block.getBody().add(new WasmSetLocal(tmpVar, new WasmStructGet(objectStruct, instance,
WasmGCClassInfoProvider.MONITOR_FIELD_OFFSET)));
WasmExpression test = new WasmTest(new WasmGetLocal(tmpVar), WasmType.Reference.I31, false);
test = new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, test);
var branch = new WasmBranch(test, block);
branch.setResult(new WasmInt32Constant(-1));
block.getBody().add(new WasmDrop(branch));
var i31ref = new WasmCast(new WasmGetLocal(tmpVar), WasmType.Reference.I31);
block.getBody().add(new WasmInt31Get(i31ref, WasmSignedType.UNSIGNED));
context.tempVars().release(tmpVar);
return block;
}
private WasmExpression generateSetIdentity(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var objectStruct = context.classInfoProvider().getClassInfo(ValueType.object("java.lang.Object"))
.getStructure();
var instance = context.generate(invocation.getArguments().get(0));
var identity = context.generate(invocation.getArguments().get(1));
var identityWrapper = new WasmInt31Reference(identity);
return new WasmStructSet(objectStruct, instance, WasmGCClassInfoProvider.MONITOR_FIELD_OFFSET,
identityWrapper);
}
}

View File

@ -1,32 +0,0 @@
/*
* 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.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
public class ObjectIntrinsics implements WasmGCIntrinsic {
@Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var obj = context.generate(invocation.getArguments().get(0));
var objectStruct = context.classInfoProvider().getClassInfo("java.lang.Object").getStructure();
var result = new WasmStructGet(objectStruct, obj, WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
result.setLocation(invocation.getLocation());
return result;
}
}

View File

@ -19,6 +19,7 @@ import org.teavm.ast.Expr;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.PreciseTypeInference;
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
import org.teavm.backend.wasm.model.WasmModule;
@ -41,4 +42,6 @@ public interface WasmGCIntrinsicContext {
WasmGCTypeMapper typeMapper();
WasmGCClassInfoProvider classInfoProvider();
TemporaryVariablePool tempVars();
}

View File

@ -20,7 +20,9 @@ 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.backend.wasm.model.expression.WasmIntType;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
private Map<MethodReference, WasmGCIntrinsic> intrinsics = new HashMap<>();
@ -30,7 +32,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
fillObject();
fillClass();
fillSystem();
fillLong();
fillLongAndInteger();
}
private void fillWasmRuntime() {
@ -50,8 +52,16 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
}
private void fillObject() {
var objectIntrinsics = new ObjectIntrinsics();
var objectIntrinsics = new ObjectIntrinsic();
intrinsics.put(new MethodReference(Object.class, "getClass", Class.class), objectIntrinsics);
intrinsics.put(new MethodReference(Object.class.getName(), "getMonitor",
ValueType.object("java.lang.Object$Monitor")), objectIntrinsics);
intrinsics.put(new MethodReference(Object.class.getName(), "setMonitor",
ValueType.object("java.lang.Object$Monitor"), ValueType.VOID), objectIntrinsics);
intrinsics.put(new MethodReference(Object.class.getName(), "wasmGCIdentity", ValueType.INTEGER),
objectIntrinsics);
intrinsics.put(new MethodReference(Object.class.getName(), "setWasmGCIdentity", ValueType.INTEGER,
ValueType.VOID), objectIntrinsics);
}
private void fillClass() {
@ -66,13 +76,18 @@ 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),
private void fillLongAndInteger() {
fillIntNum(int.class, Integer.class, WasmIntType.INT32);
fillIntNum(long.class, Long.class, WasmIntType.INT64);
}
private void fillIntNum(Class<?> javaClass, Class<?> wrapperClass, WasmIntType wasmType) {
var intrinsic = new IntNumIntrinsic(javaClass, wasmType);
intrinsics.put(new MethodReference(wrapperClass, "divideUnsigned", javaClass, javaClass, javaClass),
intrinsic);
intrinsics.put(new MethodReference(Long.class, "remainderUnsigned", long.class, long.class, long.class),
intrinsics.put(new MethodReference(wrapperClass, "remainderUnsigned", javaClass, javaClass, javaClass),
intrinsic);
intrinsics.put(new MethodReference(Long.class, "compareUnsigned", long.class, long.class, int.class),
intrinsics.put(new MethodReference(wrapperClass, "compareUnsigned", javaClass, javaClass, int.class),
intrinsic);
}

View File

@ -62,6 +62,7 @@ public abstract class WasmType {
public static final SpecialReference EXTERN = SpecialReferenceKind.EXTERN.asType();
public static final SpecialReference STRUCT = SpecialReferenceKind.STRUCT.asType();
public static final SpecialReference ARRAY = SpecialReferenceKind.ARRAY.asType();
public static final SpecialReference I31 = SpecialReferenceKind.I31.asType();
}
public static final class CompositeReference extends Reference {
@ -85,7 +86,8 @@ public abstract class WasmType {
ANY,
EXTERN,
STRUCT,
ARRAY;
ARRAY,
I31;
private SpecialReference type;

View File

@ -21,10 +21,16 @@ import org.teavm.backend.wasm.model.WasmType;
public class WasmCast extends WasmExpression {
private WasmExpression value;
private WasmType.Reference targetType;
private boolean nullable;
public WasmCast(WasmExpression value, WasmType.Reference targetType) {
this(value, targetType, true);
}
public WasmCast(WasmExpression value, WasmType.Reference targetType, boolean nullable) {
this.value = Objects.requireNonNull(value);
this.targetType = Objects.requireNonNull(targetType);
this.nullable = nullable;
}
public WasmExpression getValue() {
@ -43,6 +49,14 @@ public class WasmCast extends WasmExpression {
this.targetType = Objects.requireNonNull(targetType);
}
public boolean isNullable() {
return nullable;
}
public void setNullable(boolean nullable) {
this.nullable = nullable;
}
@Override
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);

View File

@ -251,6 +251,11 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
expression.getValue().acceptVisitor(this);
}
@Override
public void visit(WasmTest expression) {
expression.getValue().acceptVisitor(this);
}
@Override
public void visit(WasmStructNew expression) {
for (var initializer : expression.getInitializers()) {
@ -308,4 +313,14 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
@Override
public void visit(WasmFunctionReference expression) {
}
@Override
public void visit(WasmInt31Reference expression) {
expression.getValue().acceptVisitor(this);
}
@Override
public void visit(WasmInt31Get expression) {
expression.getValue().acceptVisitor(this);
}
}

View File

@ -96,6 +96,8 @@ public interface WasmExpressionVisitor {
void visit(WasmCast expression);
void visit(WasmTest expression);
void visit(WasmStructNew expression);
void visit(WasmStructNewDefault expression);
@ -115,4 +117,8 @@ public interface WasmExpressionVisitor {
void visit(WasmArrayCopy expression);
void visit(WasmFunctionReference expression);
void visit(WasmInt31Reference expression);
void visit(WasmInt31Get expression);
}

View File

@ -0,0 +1,49 @@
/*
* 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.model.expression;
import java.util.Objects;
public class WasmInt31Get extends WasmExpression {
private WasmSignedType signedType;
private WasmExpression value;
public WasmInt31Get(WasmExpression value, WasmSignedType signedType) {
this.signedType = Objects.requireNonNull(signedType);
this.value = Objects.requireNonNull(value);
}
public WasmSignedType getSignedType() {
return signedType;
}
public void setSignedType(WasmSignedType signedType) {
this.signedType = Objects.requireNonNull(signedType);
}
public WasmExpression getValue() {
return value;
}
public void setValue(WasmExpression value) {
this.value = Objects.requireNonNull(value);
}
@Override
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.model.expression;
import java.util.Objects;
public class WasmInt31Reference extends WasmExpression {
private WasmExpression value;
public WasmInt31Reference(WasmExpression value) {
this.value = Objects.requireNonNull(value);
}
public WasmExpression getValue() {
return value;
}
public void setValue(WasmExpression value) {
this.value = Objects.requireNonNull(value);
}
@Override
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -300,6 +300,12 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
expression.setValue(mapper.apply(expression.getValue()));
}
@Override
public void visit(WasmTest expression) {
expression.getValue().acceptVisitor(this);
expression.setValue(mapper.apply(expression.getValue()));
}
@Override
public void visit(WasmStructNew expression) {
replaceExpressions(expression.getInitializers());
@ -378,4 +384,16 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor {
@Override
public void visit(WasmFunctionReference expression) {
}
@Override
public void visit(WasmInt31Reference expression) {
expression.getValue().acceptVisitor(this);
expression.setValue(mapper.apply(expression.getValue()));
}
@Override
public void visit(WasmInt31Get expression) {
expression.getValue().acceptVisitor(this);
expression.setValue(mapper.apply(expression.getValue()));
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.model.expression;
import java.util.Objects;
import org.teavm.backend.wasm.model.WasmType;
public class WasmTest extends WasmExpression {
private WasmExpression value;
private WasmType.Reference testType;
private boolean nullable;
public WasmTest(WasmExpression value, WasmType.Reference testType) {
this(value, testType, true);
}
public WasmTest(WasmExpression value, WasmType.Reference testType, boolean nullable) {
this.value = Objects.requireNonNull(value);
this.testType = Objects.requireNonNull(testType);
this.nullable = nullable;
}
public WasmExpression getValue() {
return value;
}
public void setValue(WasmExpression value) {
this.value = Objects.requireNonNull(value);
}
public WasmType.Reference getTestType() {
return testType;
}
public void setTestType(WasmType.Reference testType) {
this.testType = Objects.requireNonNull(testType);
}
public boolean isNullable() {
return nullable;
}
public void setNullable(boolean nullable) {
this.nullable = nullable;
}
@Override
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -41,6 +41,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmTest;
public class UnusedTypeElimination {
private WasmModule module;
@ -103,6 +104,11 @@ public class UnusedTypeElimination {
use(expression.getTargetType());
}
@Override
public void visit(WasmTest expression) {
super.visit(expression);
use(expression.getTestType());
}
@Override
public void visit(WasmArrayGet expression) {

View File

@ -135,7 +135,10 @@ public interface CodeListener {
default void nullConstant(WasmHollowType.Reference type) {
}
default void cast(WasmHollowType.Reference type) {
default void cast(WasmHollowType.Reference type, boolean nullable) {
}
default void test(WasmHollowType.Reference type, boolean nullable) {
}
default void structNew(int typeIndex) {
@ -173,4 +176,10 @@ public interface CodeListener {
default void functionReference(int functionIndex) {
}
default void int31Reference() {
}
default void int31Get(WasmSignedType signedType) {
}
}

View File

@ -727,8 +727,31 @@ public class CodeSectionParser {
codeListener.arrayCopy(readLEB(), readLEB());
return true;
case 20:
codeListener.test(readHeapType(), false);
return true;
case 21:
codeListener.test(readHeapType(), true);
return true;
case 22:
codeListener.cast(readHeapType(), false);
return true;
case 23:
codeListener.cast(readHeapType());
codeListener.cast(readHeapType(), true);
return true;
case 28:
codeListener.int31Reference();
return true;
case 29:
codeListener.int31Get(WasmSignedType.SIGNED);
return true;
case 30:
codeListener.int31Get(WasmSignedType.UNSIGNED);
return true;
default:
@ -868,6 +891,8 @@ public class CodeSectionParser {
return WasmHollowType.Reference.EXTERN;
case 0x6E:
return WasmHollowType.Reference.ANY;
case 0x6C:
return WasmHollowType.Reference.I31;
case 0x6B:
return WasmHollowType.Reference.STRUCT;
case 0x6A:

View File

@ -61,6 +61,7 @@ public class WasmHollowType {
public static final SpecialReference EXTERN = new SpecialReference(WasmType.SpecialReferenceKind.EXTERN);
public static final SpecialReference STRUCT = new SpecialReference(WasmType.SpecialReferenceKind.STRUCT);
public static final SpecialReference ARRAY = new SpecialReference(WasmType.SpecialReferenceKind.ARRAY);
public static final SpecialReference I31 = new SpecialReference(WasmType.SpecialReferenceKind.I31);
}
public static final class CompositeReference extends WasmHollowType.Reference {

View File

@ -54,6 +54,8 @@ 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.WasmIndirectCall;
import org.teavm.backend.wasm.model.expression.WasmInt31Get;
import org.teavm.backend.wasm.model.expression.WasmInt31Reference;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
@ -68,6 +70,7 @@ import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmSignedType;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat64;
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
@ -77,6 +80,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmTest;
import org.teavm.backend.wasm.model.expression.WasmThrow;
import org.teavm.backend.wasm.model.expression.WasmTry;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
@ -1071,11 +1075,21 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
pushLocation(expression);
expression.getValue().acceptVisitor(this);
writer.writeByte(0xfb);
writer.writeByte(23);
writer.writeByte(expression.isNullable() ? 23 : 22);
writer.writeHeapType(expression.getTargetType(), module);
popLocation();
}
@Override
public void visit(WasmTest expression) {
pushLocation(expression);
expression.getValue().acceptVisitor(this);
writer.writeByte(0xfb);
writer.writeByte(expression.isNullable() ? 21 : 20);
writer.writeHeapType(expression.getTestType(), module);
popLocation();
}
@Override
public void visit(WasmStructNew expression) {
pushLocation(expression);
@ -1207,6 +1221,24 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
popLocation();
}
@Override
public void visit(WasmInt31Reference expression) {
pushLocation(expression);
expression.getValue().acceptVisitor(this);
writer.writeByte(0xfb);
writer.writeByte(28);
popLocation();
}
@Override
public void visit(WasmInt31Get expression) {
pushLocation(expression);
expression.getValue().acceptVisitor(this);
writer.writeByte(0xfb);
writer.writeByte(expression.getSignedType() == WasmSignedType.SIGNED ? 29 : 30);
popLocation();
}
private int alignment(int value) {
return 31 - Integer.numberOfLeadingZeros(Math.max(1, value));
}

View File

@ -73,6 +73,9 @@ public class WasmBinaryWriter {
case ARRAY:
writeByte(0x6a);
break;
case I31:
writeByte(0x6c);
break;
}
}

View File

@ -54,6 +54,8 @@ 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.WasmIndirectCall;
import org.teavm.backend.wasm.model.expression.WasmInt31Get;
import org.teavm.backend.wasm.model.expression.WasmInt31Reference;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
@ -78,6 +80,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmTest;
import org.teavm.backend.wasm.model.expression.WasmThrow;
import org.teavm.backend.wasm.model.expression.WasmTry;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
@ -1170,6 +1173,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
unsupported();
}
@Override
public void visit(WasmTest expression) {
unsupported();
}
@Override
public void visit(WasmStructNew expression) {
unsupported();
@ -1220,6 +1228,16 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor {
unsupported();
}
@Override
public void visit(WasmInt31Reference expression) {
unsupported();
}
@Override
public void visit(WasmInt31Get expression) {
unsupported();
}
private void unsupported() {
value = new CExpression("/* unsupported */");
}

View File

@ -53,6 +53,8 @@ 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.WasmIndirectCall;
import org.teavm.backend.wasm.model.expression.WasmInt31Get;
import org.teavm.backend.wasm.model.expression.WasmInt31Reference;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
@ -70,6 +72,7 @@ import org.teavm.backend.wasm.model.expression.WasmReferencesEqual;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmSignedType;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat64;
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
@ -79,6 +82,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmTest;
import org.teavm.backend.wasm.model.expression.WasmThrow;
import org.teavm.backend.wasm.model.expression.WasmTry;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
@ -700,6 +704,13 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
close();
}
@Override
public void visit(WasmTest expression) {
open().append("ref.test ").append(type(expression.getTestType()));
line(expression.getValue());
close();
}
@Override
public void visit(WasmStructNew expression) {
open().append("struct.new ");
@ -811,6 +822,20 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
close();
}
@Override
public void visit(WasmInt31Reference expression) {
open().append("ref.i31 ");
line(expression.getValue());
close();
}
@Override
public void visit(WasmInt31Get expression) {
open().append("i31.get_" + (expression.getSignedType() == WasmSignedType.SIGNED ? "s" : "u"));
line(expression.getValue());
close();
}
private String type(WasmType type) {
if (type instanceof WasmType.Number) {
return type(((WasmType.Number) type).number);
@ -826,6 +851,8 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
return "funcref";
case ARRAY:
return "arrayref";
case I31:
return "i31ref";
default:
throw new IllegalArgumentException();
}

View File

@ -42,6 +42,8 @@ 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.WasmIndirectCall;
import org.teavm.backend.wasm.model.expression.WasmInt31Get;
import org.teavm.backend.wasm.model.expression.WasmInt31Reference;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
@ -66,6 +68,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructNew;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmTest;
import org.teavm.backend.wasm.model.expression.WasmThrow;
import org.teavm.backend.wasm.model.expression.WasmTry;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
@ -278,6 +281,11 @@ public class WasmTypeInference implements WasmExpressionVisitor {
result = expression.getTargetType();
}
@Override
public void visit(WasmTest expression) {
result = WasmType.INT32;
}
@Override
public void visit(WasmStructNew expression) {
result = expression.getType().getReference();
@ -328,6 +336,16 @@ public class WasmTypeInference implements WasmExpressionVisitor {
result = expression.getFunction().getType().getReference();
}
@Override
public void visit(WasmInt31Get expression) {
result = WasmType.INT32;
}
@Override
public void visit(WasmInt31Reference expression) {
result = WasmType.Reference.I31;
}
private static WasmType map(WasmIntType type) {
switch (type) {
case INT32:

View File

@ -18,6 +18,8 @@ package org.teavm.backend.wasm.runtime;
import org.teavm.interop.Import;
public class WasmGCSupport {
private static int lastObjectId = 1831433054;
private WasmGCSupport() {
}
@ -37,6 +39,15 @@ public class WasmGCSupport {
return new CloneNotSupportedException();
}
public static int nextObjectId() {
var x = lastObjectId;
x ^= x << 13;
x ^= x >>> 17;
x ^= x << 5;
lastObjectId = x;
return x;
}
@Import(name = "putcharStdout")
public static native void putCharStdout(char c);

View File

@ -16,13 +16,29 @@
package org.teavm.backend.wasm.transformation.gc;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.model.AccessLevel;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.emit.ProgramEmitter;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
public class BaseClassesTransformation implements ClassHolderTransformer {
private static final MethodReference GET_MONITOR = new MethodReference(Object.class.getName(),
"getMonitor", ValueType.object("java.lang.Object$Monitor"));
private static final MethodReference SET_MONITOR = new MethodReference(Object.class.getName(),
"setMonitor", ValueType.object("java.lang.Object$Monitor"), ValueType.VOID);
private static final FieldReference MONITOR = new FieldReference(Object.class.getName(), "monitor");
@Override
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
if (cls.getName().equals("java.lang.Object")) {
@ -37,8 +53,22 @@ public class BaseClassesTransformation implements ClassHolderTransformer {
em.invoke(WasmGCSupport.class, "cnse", CloneNotSupportedException.class).raise();
break;
}
default:
if (method.getProgram() != null) {
transformMonitorFieldAccess(method.getProgram());
}
break;
}
}
var getMonitorMethod = new MethodHolder(GET_MONITOR.getDescriptor());
getMonitorMethod.setLevel(AccessLevel.PRIVATE);
getMonitorMethod.getModifiers().add(ElementModifier.NATIVE);
cls.addMethod(getMonitorMethod);
var setMonitorMethod = new MethodHolder(SET_MONITOR.getDescriptor());
setMonitorMethod.setLevel(AccessLevel.PRIVATE);
setMonitorMethod.getModifiers().add(ElementModifier.NATIVE);
cls.addMethod(setMonitorMethod);
} else if (cls.getName().equals("java.lang.Class")) {
for (var method : cls.getMethods()) {
switch (method.getName()) {
@ -49,6 +79,45 @@ public class BaseClassesTransformation implements ClassHolderTransformer {
break;
}
}
} else if (cls.getName().equals("java.lang.System")) {
for (var method : cls.getMethods()) {
switch (method.getName()) {
case "arraycopy":
method.setProgram(null);
method.getModifiers().add(ElementModifier.NATIVE);
break;
}
}
}
}
private void transformMonitorFieldAccess(Program program) {
for (var block : program.getBasicBlocks()) {
for (var instruction : block) {
if (instruction instanceof GetFieldInstruction) {
var getField = (GetFieldInstruction) instruction;
if (getField.getField().equals(MONITOR)) {
var invocation = new InvokeInstruction();
invocation.setType(InvocationType.SPECIAL);
invocation.setInstance(getField.getInstance());
invocation.setMethod(GET_MONITOR);
invocation.setReceiver(getField.getReceiver());
invocation.setLocation(getField.getLocation());
getField.replace(invocation);
}
} else if (instruction instanceof PutFieldInstruction) {
var putField = (PutFieldInstruction) instruction;
if (putField.getField().equals(MONITOR)) {
var invocation = new InvokeInstruction();
invocation.setType(InvocationType.SPECIAL);
invocation.setInstance(putField.getInstance());
invocation.setMethod(SET_MONITOR);
invocation.setArguments(putField.getValue());
invocation.setLocation(putField.getLocation());
putField.replace(invocation);
}
}
}
}
}
}

View File

@ -291,7 +291,7 @@ public abstract class BaseTypeInference<T> {
@Override
public void visit(NullConstantInstruction insn) {
types[insn.getReceiver().getIndex()] = nullType();
type(insn.getReceiver(), nullType());
}
@Override
@ -454,6 +454,10 @@ public abstract class BaseTypeInference<T> {
if (target != null) {
var t = mapType(type);
if (t != null) {
if (types[target.getIndex()] != null) {
//noinspection unchecked
t = merge((T) types[target.getIndex()], t);
}
types[target.getIndex()] = t;
}
}
@ -461,6 +465,10 @@ public abstract class BaseTypeInference<T> {
void type(Variable target, T type) {
if (target != null && type != null) {
if (types[target.getIndex()] != null) {
//noinspection unchecked
type = merge((T) types[target.getIndex()], type);
}
types[target.getIndex()] = type;
}
}

View File

@ -388,7 +388,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
return !cancelled;
});
target.contributeDependencies(dependencyAnalyzer);
if (target.needsSystemArrayCopyOptimization()) {
dependencyAnalyzer.addDependencyListener(new StdlibDependencyListener());
}
dependencyAnalyzer.processDependencies();
if (wasCancelled() || !diagnostics.getSevereProblems().isEmpty()) {
return;