From f19c211b2afef35f8ace70a3fc06bd71b8930bea Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 30 Aug 2024 13:44:23 +0200 Subject: [PATCH] wasm gc: fix issie with type inference for array set, implement some intrinsics --- .../wasm/generate/WasmGenerationVisitor.java | 6 +- .../methods/BaseWasmGenerationVisitor.java | 10 +- .../gc/classes/WasmGCClassGenerator.java | 2 +- .../gc/methods/WasmGCGenerationVisitor.java | 6 +- .../wasm/intrinsics/gc/DoubleIntrinsic.java | 95 +++++++++++++++++++ .../wasm/intrinsics/gc/FloatIntrinsic.java | 95 +++++++++++++++++++ .../wasm/intrinsics/gc/SystemIntrinsic.java | 41 ++++++++ .../wasm/intrinsics/gc/WasmGCIntrinsics.java | 24 +++++ .../backend/wasm/model/WasmFunction.java | 4 +- 9 files changed, 271 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/DoubleIntrinsic.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/FloatIntrinsic.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemIntrinsic.java diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 3c8fbc15a..6857d55d8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -263,9 +263,11 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { } @Override - protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, WasmExpression value, + protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, Expr value, ArrayType type) { - return storeArrayItem(getArrayElementPointer(array, index, type), value, type); + accept(value); + var wasmValue = result; + return storeArrayItem(getArrayElementPointer(array, index, type), wasmValue, type); } private static WasmExpression storeArrayItem(WasmExpression array, WasmExpression value, ArrayType type) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java index d116bef26..1411a449d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java @@ -513,12 +513,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp var array = result; leftValue.getIndex().acceptVisitor(this); var index = result; - rightValue.acceptVisitor(this); - var value = result; - resultConsumer.add(storeArrayItem(array, index, value, leftValue.getType())); + resultConsumer.add(storeArrayItem(array, index, rightValue, leftValue.getType())); } - protected abstract WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, WasmExpression value, + protected abstract WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, Expr value, ArrayType type); @Override @@ -1081,9 +1079,9 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp block.getBody()); for (int i = 0; i < expr.getData().size(); ++i) { - expr.getData().get(i).acceptVisitor(this); var arrayData = unwrapArray(new WasmGetLocal(array)); - block.getBody().add(storeArrayItem(arrayData, new WasmInt32Constant(i), result, arrayType)); + block.getBody().add(storeArrayItem(arrayData, new WasmInt32Constant(i), expr.getData().get(i), + arrayType)); } block.getBody().add(new WasmGetLocal(array)); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index 992c6051f..902a9b4d8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -562,7 +562,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private WasmFunction generateArrayGetPrimitiveFunction(PrimitiveType type) { var function = new WasmFunction(getArrayGetType()); - arrayGetObjectFunction.setName(names.topLevel("Array<" + names.suggestForType(ValueType.primitive(type)) + function.setName(names.topLevel("Array<" + names.suggestForType(ValueType.primitive(type)) + ">::get")); module.functions.add(function); function.setReferenced(true); 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 0f4ee4df9..02f86a3ce 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 @@ -151,12 +151,14 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { } @Override - protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, WasmExpression value, + protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, Expr value, ArrayType type) { array.acceptVisitor(typeInference); var arrayRefType = (WasmType.CompositeReference) typeInference.getResult(); var arrayType = (WasmArray) arrayRefType.composite; - return new WasmArraySet(arrayType, array, index, value); + accept(value, arrayType.getElementType().asUnpackedType()); + var wasmValue = result; + return new WasmArraySet(arrayType, array, index, wasmValue); } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/DoubleIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/DoubleIntrinsic.java new file mode 100644 index 000000000..d684ec00f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/DoubleIntrinsic.java @@ -0,0 +1,95 @@ +/* + * Copyright 2018 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.model.WasmNumType; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmBlock; +import org.teavm.backend.wasm.model.expression.WasmConversion; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; +import org.teavm.backend.wasm.model.expression.WasmFloatBinary; +import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmFloatType; +import org.teavm.backend.wasm.model.expression.WasmInt64Constant; +import org.teavm.backend.wasm.model.expression.WasmIntBinary; +import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmIntType; + +public class DoubleIntrinsic implements WasmGCIntrinsic { + private static final long EXPONENT_BITS = 0x7FF0000000000000L; + private static final long FRACTION_BITS = 0x000FFFFFFFFFFFFFL; + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + switch (invocation.getMethod().getName()) { + case "getNaN": + return new WasmFloat64Constant(Double.NaN); + case "isNaN": + return testNaN(context.generate(invocation.getArguments().get(0)), context); + case "isInfinite": + return testIsInfinite(context.generate(invocation.getArguments().get(0))); + case "isFinite": + return testIsFinite(context.generate(invocation.getArguments().get(0))); + case "doubleToRawLongBits": { + WasmConversion conversion = new WasmConversion(WasmNumType.FLOAT64, WasmNumType.INT64, false, + context.generate(invocation.getArguments().get(0))); + conversion.setReinterpret(true); + return conversion; + } + case "longBitsToDouble": { + WasmConversion conversion = new WasmConversion(WasmNumType.INT64, WasmNumType.FLOAT64, false, + context.generate(invocation.getArguments().get(0))); + conversion.setReinterpret(true); + return conversion; + } + default: + throw new AssertionError(); + } + } + + private WasmExpression testNaN(WasmExpression expression, WasmGCIntrinsicContext context) { + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + var cache = context.exprCache().create(expression, WasmType.FLOAT64, expression.getLocation(), + block.getBody()); + block.getBody().add(new WasmFloatBinary(WasmFloatType.FLOAT64, WasmFloatBinaryOperation.NE, + cache.expr(), cache.expr())); + cache.release(); + return block; + } + + private WasmExpression testIsInfinite(WasmExpression expression) { + var conversion = new WasmConversion(WasmNumType.FLOAT64, WasmNumType.INT64, false, expression); + conversion.setReinterpret(true); + + var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, + conversion, new WasmInt64Constant(EXPONENT_BITS)); + return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.EQ, result, + new WasmInt64Constant(EXPONENT_BITS)); + } + + private WasmExpression testIsFinite(WasmExpression expression) { + var conversion = new WasmConversion(WasmNumType.FLOAT64, WasmNumType.INT64, false, expression); + conversion.setReinterpret(true); + + var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, + conversion, new WasmInt64Constant(EXPONENT_BITS)); + return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.NE, result, + new WasmInt64Constant(EXPONENT_BITS)); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/FloatIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/FloatIntrinsic.java new file mode 100644 index 000000000..9ba5b39d9 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/FloatIntrinsic.java @@ -0,0 +1,95 @@ +/* + * Copyright 2018 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.model.WasmNumType; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmBlock; +import org.teavm.backend.wasm.model.expression.WasmConversion; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmFloat32Constant; +import org.teavm.backend.wasm.model.expression.WasmFloatBinary; +import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmFloatType; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.backend.wasm.model.expression.WasmIntBinary; +import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmIntType; + +public class FloatIntrinsic implements WasmGCIntrinsic { + private static final int EXPONENT_BITS = 0x7F800000; + private static final int FRACTION_BITS = 0x007FFFFF; + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext manager) { + switch (invocation.getMethod().getName()) { + case "getNaN": + return new WasmFloat32Constant(Float.NaN); + case "isNaN": + return testNaN(manager.generate(invocation.getArguments().get(0)), manager); + case "isInfinite": + return testIsInfinite(manager.generate(invocation.getArguments().get(0))); + case "isFinite": + return testIsFinite(manager.generate(invocation.getArguments().get(0))); + case "floatToRawIntBits": { + var conversion = new WasmConversion(WasmNumType.FLOAT32, WasmNumType.INT32, false, + manager.generate(invocation.getArguments().get(0))); + conversion.setReinterpret(true); + return conversion; + } + case "intBitsToFloat": { + var conversion = new WasmConversion(WasmNumType.INT32, WasmNumType.FLOAT32, false, + manager.generate(invocation.getArguments().get(0))); + conversion.setReinterpret(true); + return conversion; + } + default: + throw new AssertionError(); + } + } + + private WasmExpression testNaN(WasmExpression expression, WasmGCIntrinsicContext context) { + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + var cache = context.exprCache().create(expression, WasmType.FLOAT32, expression.getLocation(), + block.getBody()); + block.getBody().add(new WasmFloatBinary(WasmFloatType.FLOAT32, WasmFloatBinaryOperation.NE, + cache.expr(), cache.expr())); + cache.release(); + return block; + } + + private WasmExpression testIsInfinite(WasmExpression expression) { + var conversion = new WasmConversion(WasmNumType.FLOAT32, WasmNumType.INT32, false, expression); + conversion.setReinterpret(true); + + var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, + conversion, new WasmInt32Constant(EXPONENT_BITS)); + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, result, + new WasmInt32Constant(EXPONENT_BITS)); + } + + private WasmExpression testIsFinite(WasmExpression expression) { + var conversion = new WasmConversion(WasmNumType.FLOAT32, WasmNumType.INT32, false, expression); + conversion.setReinterpret(true); + + var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, + conversion, new WasmInt32Constant(EXPONENT_BITS)); + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, result, + new WasmInt32Constant(EXPONENT_BITS)); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemIntrinsic.java new file mode 100644 index 000000000..d6bbfcf3e --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/SystemIntrinsic.java @@ -0,0 +1,41 @@ +/* + * Copyright 2024 konsoletyper. + * + * 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.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmNumType; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmConversion; +import org.teavm.backend.wasm.model.expression.WasmExpression; + +public class SystemIntrinsic implements WasmGCIntrinsic { + private WasmFunction workerFunction; + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + if (workerFunction == null) { + workerFunction = new WasmFunction(context.functionTypes().of(WasmType.FLOAT64)); + workerFunction.setName("teavm@currentTimeMillis"); + workerFunction.setImportName("currentTimeMillis"); + workerFunction.setImportModule("teavm"); + context.module().functions.add(workerFunction); + } + var call = new WasmCall(workerFunction); + return new WasmConversion(WasmNumType.FLOAT64, WasmNumType.INT64, true, call); + } +} 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 5de25c989..29cd8f21d 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 @@ -34,6 +34,8 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { fillClass(); fillSystem(); fillLongAndInteger(); + fillFloat(); + fillDouble(); fillArray(); } @@ -76,6 +78,8 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { private void fillSystem() { intrinsics.put(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class), new SystemArrayCopyIntrinsic()); + intrinsics.put(new MethodReference(System.class, "currentTimeMillis", long.class), + new SystemIntrinsic()); } private void fillLongAndInteger() { @@ -93,6 +97,26 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { intrinsic); } + private void fillFloat() { + var intrinsic = new FloatIntrinsic(); + intrinsics.put(new MethodReference(Float.class, "getNaN", float.class), intrinsic); + intrinsics.put(new MethodReference(Float.class, "isNaN", float.class, boolean.class), intrinsic); + intrinsics.put(new MethodReference(Float.class, "isInfinite", float.class, boolean.class), intrinsic); + intrinsics.put(new MethodReference(Float.class, "isFinite", float.class, boolean.class), intrinsic); + intrinsics.put(new MethodReference(Float.class, "floatToRawIntBits", float.class, int.class), intrinsic); + intrinsics.put(new MethodReference(Float.class, "intBitsToFloat", int.class, float.class), intrinsic); + } + + private void fillDouble() { + var intrinsic = new DoubleIntrinsic(); + intrinsics.put(new MethodReference(Double.class, "getNaN", double.class), intrinsic); + intrinsics.put(new MethodReference(Double.class, "isNaN", double.class, boolean.class), intrinsic); + intrinsics.put(new MethodReference(Double.class, "isInfinite", double.class, boolean.class), intrinsic); + intrinsics.put(new MethodReference(Double.class, "isFinite", double.class, boolean.class), intrinsic); + intrinsics.put(new MethodReference(Double.class, "doubleToRawLongBits", double.class, long.class), intrinsic); + intrinsics.put(new MethodReference(Double.class, "longBitsToDouble", long.class, double.class), intrinsic); + } + private void fillArray() { var intrinsic = new ArrayIntrinsic(); intrinsics.put(new MethodReference(Array.class, "getLength", Object.class, int.class), intrinsic); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java index 935b44584..72ad2513b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmFunction.java @@ -60,7 +60,9 @@ public class WasmFunction extends WasmEntity { public void setImportName(String importName) { this.importName = importName; - collection.invalidateIndexes(); + if (collection != null) { + collection.invalidateIndexes(); + } } public String getImportModule() {