diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index 6f8345417..7fd35a798 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -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 diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java index 4aedda8ee..a02fb81ee 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeSectionListener.java @@ -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()); 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 125f647d2..822be043d 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 @@ -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; + } }; } 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/IntNumIntrinsic.java similarity index 77% rename from core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/LongIntrinsic.java rename to core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/IntNumIntrinsic.java index 1f7eece75..eaed36989 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/LongIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/IntNumIntrinsic.java @@ -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: diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsic.java new file mode 100644 index 000000000..5d84f692e --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsic.java @@ -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); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsics.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsics.java deleted file mode 100644 index 065460c55..000000000 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsics.java +++ /dev/null @@ -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; - } -} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java index 3cc21bbc6..bc60f2c7f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java @@ -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(); } 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 c54b2c74d..fa1b4d1be 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 @@ -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 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); } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java index 9be0d526c..47f462e01 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java @@ -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; diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCast.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCast.java index cdc8cc684..1ee6fd943 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCast.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmCast.java @@ -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); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java index ba53b7e34..ab7a7eae5 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java @@ -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); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java index 09751950f..e05f65d44 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java @@ -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); } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmInt31Get.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmInt31Get.java new file mode 100644 index 000000000..3977888a8 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmInt31Get.java @@ -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); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmInt31Reference.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmInt31Reference.java new file mode 100644 index 000000000..1e202a48a --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmInt31Reference.java @@ -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); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java index 21a709244..2c634203b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java @@ -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())); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmTest.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmTest.java new file mode 100644 index 000000000..14e1ac055 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmTest.java @@ -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); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java index a05fb3b8f..3404eb8d3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java +++ b/core/src/main/java/org/teavm/backend/wasm/optimization/UnusedTypeElimination.java @@ -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) { diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java index 7c7680def..efd168211 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java @@ -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) { + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java index f9fca4ad4..3a7ffce29 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java @@ -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: diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/WasmHollowType.java b/core/src/main/java/org/teavm/backend/wasm/parser/WasmHollowType.java index 84fe76328..ea4df92b6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/WasmHollowType.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/WasmHollowType.java @@ -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 { diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java index 866c3383b..4f2fdd221 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java @@ -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)); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java index e14deb4da..852b9e5a9 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java @@ -73,6 +73,9 @@ public class WasmBinaryWriter { case ARRAY: writeByte(0x6a); break; + case I31: + writeByte(0x6c); + break; } } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java index 1a0b521ee..87d8042d4 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java @@ -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 */"); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java index c36390809..e634b3d9a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java @@ -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(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java index ab15baf88..ab0abcb04 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java @@ -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: 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 29bafd8b4..193b09384 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 @@ -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); diff --git a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java index 017672f4c..484f3ff94 100644 --- a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java +++ b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java @@ -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); + } + } + } } } } diff --git a/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java b/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java index 92c70b2e8..495c719c4 100644 --- a/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java +++ b/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java @@ -291,7 +291,7 @@ public abstract class BaseTypeInference { @Override public void visit(NullConstantInstruction insn) { - types[insn.getReceiver().getIndex()] = nullType(); + type(insn.getReceiver(), nullType()); } @Override @@ -454,6 +454,10 @@ public abstract class BaseTypeInference { 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 { 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; } } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 36e0d60d0..ed5950da8 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -388,7 +388,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository { return !cancelled; }); target.contributeDependencies(dependencyAnalyzer); - dependencyAnalyzer.addDependencyListener(new StdlibDependencyListener()); + if (target.needsSystemArrayCopyOptimization()) { + dependencyAnalyzer.addDependencyListener(new StdlibDependencyListener()); + } dependencyAnalyzer.processDependencies(); if (wasCancelled() || !diagnostics.getSevereProblems().isEmpty()) { return;