wasm gc: fix issie with type inference for array set, implement some intrinsics

This commit is contained in:
Alexey Andreev 2024-08-30 13:44:23 +02:00
parent 1ba1dcfc09
commit f19c211b2a
9 changed files with 271 additions and 12 deletions

View File

@ -263,9 +263,11 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, WasmExpression value, protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, Expr value,
ArrayType type) { 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) { private static WasmExpression storeArrayItem(WasmExpression array, WasmExpression value, ArrayType type) {

View File

@ -513,12 +513,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var array = result; var array = result;
leftValue.getIndex().acceptVisitor(this); leftValue.getIndex().acceptVisitor(this);
var index = result; var index = result;
rightValue.acceptVisitor(this); resultConsumer.add(storeArrayItem(array, index, rightValue, leftValue.getType()));
var value = result;
resultConsumer.add(storeArrayItem(array, index, value, leftValue.getType()));
} }
protected abstract WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, WasmExpression value, protected abstract WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, Expr value,
ArrayType type); ArrayType type);
@Override @Override
@ -1081,9 +1079,9 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
block.getBody()); block.getBody());
for (int i = 0; i < expr.getData().size(); ++i) { for (int i = 0; i < expr.getData().size(); ++i) {
expr.getData().get(i).acceptVisitor(this);
var arrayData = unwrapArray(new WasmGetLocal(array)); 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)); block.getBody().add(new WasmGetLocal(array));

View File

@ -562,7 +562,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private WasmFunction generateArrayGetPrimitiveFunction(PrimitiveType type) { private WasmFunction generateArrayGetPrimitiveFunction(PrimitiveType type) {
var function = new WasmFunction(getArrayGetType()); 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")); + ">::get"));
module.functions.add(function); module.functions.add(function);
function.setReferenced(true); function.setReferenced(true);

View File

@ -151,12 +151,14 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
} }
@Override @Override
protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, WasmExpression value, protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, Expr value,
ArrayType type) { ArrayType type) {
array.acceptVisitor(typeInference); array.acceptVisitor(typeInference);
var arrayRefType = (WasmType.CompositeReference) typeInference.getResult(); var arrayRefType = (WasmType.CompositeReference) typeInference.getResult();
var arrayType = (WasmArray) arrayRefType.composite; 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 @Override

View File

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

View File

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

View File

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

View File

@ -34,6 +34,8 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
fillClass(); fillClass();
fillSystem(); fillSystem();
fillLongAndInteger(); fillLongAndInteger();
fillFloat();
fillDouble();
fillArray(); fillArray();
} }
@ -76,6 +78,8 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
private void fillSystem() { private void fillSystem() {
intrinsics.put(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class, intrinsics.put(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class,
int.class, int.class, void.class), new SystemArrayCopyIntrinsic()); int.class, int.class, void.class), new SystemArrayCopyIntrinsic());
intrinsics.put(new MethodReference(System.class, "currentTimeMillis", long.class),
new SystemIntrinsic());
} }
private void fillLongAndInteger() { private void fillLongAndInteger() {
@ -93,6 +97,26 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
intrinsic); 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() { private void fillArray() {
var intrinsic = new ArrayIntrinsic(); var intrinsic = new ArrayIntrinsic();
intrinsics.put(new MethodReference(Array.class, "getLength", Object.class, int.class), intrinsic); intrinsics.put(new MethodReference(Array.class, "getLength", Object.class, int.class), intrinsic);

View File

@ -60,8 +60,10 @@ public class WasmFunction extends WasmEntity {
public void setImportName(String importName) { public void setImportName(String importName) {
this.importName = importName; this.importName = importName;
if (collection != null) {
collection.invalidateIndexes(); collection.invalidateIndexes();
} }
}
public String getImportModule() { public String getImportModule() {
return importModule; return importModule;