diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java index 1a72e0315..4b2d23cc2 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java @@ -93,13 +93,9 @@ public class TFloat extends TNumber implements TComparable { @Import(module = "teavm", name = "isnan") public static native boolean isNaN(float v); - public static boolean isInfinite(float v) { - return !isFinite(v); - } - - @JSBody(params = "v", script = "return isFinite(v);") - @Import(module = "teavm", name = "isfinite") - private static native boolean isFinite(float v); + @JSBody(params = "v", script = "return !isFinite(v);") + @Import(module = "teavm", name = "isinf") + public static native boolean isInfinite(float v); @JSBody(script = "return NaN;") @Import(module = "teavm", name = "TeaVM_getNaN") diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 93230f438..83400693b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -44,7 +44,9 @@ import org.teavm.backend.wasm.generators.WasmMethodGeneratorContext; import org.teavm.backend.wasm.intrinsics.AddressIntrinsic; import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic; import org.teavm.backend.wasm.intrinsics.ClassIntrinsic; +import org.teavm.backend.wasm.intrinsics.DoubleIntrinsic; import org.teavm.backend.wasm.intrinsics.ExceptionHandlingIntrinsic; +import org.teavm.backend.wasm.intrinsics.FloatIntrinsic; import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic; import org.teavm.backend.wasm.intrinsics.GCIntrinsic; import org.teavm.backend.wasm.intrinsics.MutatorIntrinsic; @@ -334,6 +336,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { context.addIntrinsic(new PlatformClassMetadataIntrinsic()); context.addIntrinsic(new ClassIntrinsic()); context.addIntrinsic(new RuntimeClassIntrinsic()); + context.addIntrinsic(new FloatIntrinsic()); + context.addIntrinsic(new DoubleIntrinsic()); context.addGenerator(new ArrayGenerator()); IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext(); 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 28f09c6bf..340385a70 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 @@ -1517,6 +1517,16 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { public NameProvider getNames() { return context.names; } + + @Override + public WasmLocal getTemporary(WasmType type) { + return WasmGenerationVisitor.this.getTemporary(type); + } + + @Override + public void releaseTemporary(WasmLocal local) { + WasmGenerationVisitor.this.releaseTemporary(local); + } }; private WasmLocal getTemporary(WasmType type) { diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java new file mode 100644 index 000000000..e6756d460 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java @@ -0,0 +1,112 @@ +/* + * 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; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.model.WasmLocal; +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.WasmConversion; +import org.teavm.backend.wasm.model.expression.WasmDrop; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +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; +import org.teavm.backend.wasm.model.expression.WasmSetLocal; +import org.teavm.model.MethodReference; + +public class DoubleIntrinsic implements WasmIntrinsic { + private static final long EXPONENT_BITS = 0x7FF0000000000000L; + private static final long FRACTION_BITS = 0x000FFFFFFFFFFFFFL; + + @Override + public boolean isApplicable(MethodReference methodReference) { + if (!methodReference.getClassName().equals(Double.class.getName())) { + return false; + } + + switch (methodReference.getName()) { + case "getNaN": + case "isNaN": + case "isInfinite": + case "doubleToLongBits": + case "longBitsToDouble": + return true; + default: + return false; + } + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + switch (invocation.getMethod().getName()) { + case "getNaN": + return new WasmFloat64Constant(Double.NaN); + case "isNaN": + return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, + WasmIntBinaryOperation.NE); + case "isInfinite": + return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, + WasmIntBinaryOperation.EQ); + case "doubleToLongBits": { + WasmConversion conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, + manager.generate(invocation.getArguments().get(0))); + conversion.setReinterpret(true); + return conversion; + } + case "longBitsToDouble": { + WasmConversion conversion = new WasmConversion(WasmType.INT64, WasmType.FLOAT64, false, + manager.generate(invocation.getArguments().get(0))); + conversion.setReinterpret(true); + return conversion; + } + default: + throw new AssertionError(); + } + } + + private WasmExpression testSpecialIEEE(WasmExpression expression, WasmIntrinsicManager manager, + WasmIntBinaryOperation fractionOp) { + WasmLocal bitsVar = manager.getTemporary(WasmType.INT64); + WasmBlock block = new WasmBlock(false); + block.setType(WasmType.INT32); + + WasmConversion conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, expression); + conversion.setReinterpret(true); + block.getBody().add(new WasmSetLocal(bitsVar, conversion)); + + WasmExpression exponentBits = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, + new WasmGetLocal(bitsVar), new WasmInt64Constant(EXPONENT_BITS)); + WasmExpression fractionBits = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, + new WasmGetLocal(bitsVar), new WasmInt64Constant(FRACTION_BITS)); + WasmExpression testExponent = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.NE, + exponentBits, new WasmInt64Constant(EXPONENT_BITS)); + WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT64, fractionOp, + fractionBits, new WasmInt64Constant(0)); + + WasmBranch breakIfWrongExponent = new WasmBranch(testExponent, block); + breakIfWrongExponent.setResult(new WasmInt32Constant(0)); + block.getBody().add(new WasmDrop(breakIfWrongExponent)); + + block.getBody().add(testFraction); + return block; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java new file mode 100644 index 000000000..bf7c4076e --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java @@ -0,0 +1,111 @@ +/* + * 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; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.model.WasmLocal; +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.WasmConversion; +import org.teavm.backend.wasm.model.expression.WasmDrop; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmFloat32Constant; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +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; +import org.teavm.backend.wasm.model.expression.WasmSetLocal; +import org.teavm.model.MethodReference; + +public class FloatIntrinsic implements WasmIntrinsic { + private static final int EXPONENT_BITS = 0x7F800000; + private static final int FRACTION_BITS = 0x007FFFFF; + + @Override + public boolean isApplicable(MethodReference methodReference) { + if (!methodReference.getClassName().equals(Float.class.getName())) { + return false; + } + + switch (methodReference.getName()) { + case "getNaN": + case "isNaN": + case "isInfinite": + case "floatToIntBits": + case "intBitsToFloat": + return true; + default: + return false; + } + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + switch (invocation.getMethod().getName()) { + case "getNaN": + return new WasmFloat32Constant(Float.NaN); + case "isNaN": + return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, + WasmIntBinaryOperation.NE); + case "isInfinite": + return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, + WasmIntBinaryOperation.EQ); + case "floatToIntBits": { + WasmConversion conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, + manager.generate(invocation.getArguments().get(0))); + conversion.setReinterpret(true); + return conversion; + } + case "intBitsToFloat": { + WasmConversion conversion = new WasmConversion(WasmType.INT32, WasmType.FLOAT32, false, + manager.generate(invocation.getArguments().get(0))); + conversion.setReinterpret(true); + return conversion; + } + default: + throw new AssertionError(); + } + } + + private WasmExpression testSpecialIEEE(WasmExpression expression, WasmIntrinsicManager manager, + WasmIntBinaryOperation fractionOp) { + WasmLocal bitsVar = manager.getTemporary(WasmType.INT32); + WasmBlock block = new WasmBlock(false); + block.setType(WasmType.INT32); + + WasmConversion conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, expression); + conversion.setReinterpret(true); + block.getBody().add(new WasmSetLocal(bitsVar, conversion)); + + WasmExpression exponentBits = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, + new WasmGetLocal(bitsVar), new WasmInt32Constant(EXPONENT_BITS)); + WasmExpression fractionBits = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, + new WasmGetLocal(bitsVar), new WasmInt32Constant(FRACTION_BITS)); + WasmExpression testExponent = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, + exponentBits, new WasmInt32Constant(EXPONENT_BITS)); + WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT32, fractionOp, + fractionBits, new WasmInt32Constant(0)); + + WasmBranch breakIfWrongExponent = new WasmBranch(testExponent, block); + breakIfWrongExponent.setResult(new WasmInt32Constant(0)); + block.getBody().add(new WasmDrop(breakIfWrongExponent)); + + block.getBody().add(testFraction); + return block; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java index a3eb9eed9..9025fa08c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java @@ -19,6 +19,8 @@ import org.teavm.ast.Expr; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.generate.NameProvider; import org.teavm.backend.wasm.generate.WasmStringPool; +import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.diagnostics.Diagnostics; @@ -32,4 +34,8 @@ public interface WasmIntrinsicManager { Diagnostics getDiagnostics(); NameProvider getNames(); + + WasmLocal getTemporary(WasmType type); + + void releaseTemporary(WasmLocal local); } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConversion.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConversion.java index df2a0a169..22f765bc4 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConversion.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConversion.java @@ -23,6 +23,7 @@ public class WasmConversion extends WasmExpression { private WasmType targetType; private boolean signed; private WasmExpression operand; + private boolean reinterpret; public WasmConversion(WasmType sourceType, WasmType targetType, boolean signed, WasmExpression operand) { Objects.requireNonNull(sourceType); @@ -60,6 +61,14 @@ public class WasmConversion extends WasmExpression { this.signed = signed; } + public boolean isReinterpret() { + return reinterpret; + } + + public void setReinterpret(boolean reinterpret) { + this.reinterpret = reinterpret; + } + public WasmExpression getOperand() { return operand; } 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 f91284f31..f8e9fc918 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 @@ -569,10 +569,6 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmConversion expression) { expression.getOperand().acceptVisitor(this); - render0xD(expression); - } - - private void render0xD(WasmConversion expression) { switch (expression.getSourceType()) { case INT32: switch (expression.getTargetType()) { @@ -582,7 +578,11 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { writer.writeByte(expression.isSigned() ? 0xAC : 0xAD); break; case FLOAT32: - writer.writeByte(expression.isSigned() ? 0xB2 : 0xB3); + if (expression.isReinterpret()) { + writer.writeByte(0xBE); + } else { + writer.writeByte(expression.isSigned() ? 0xB2 : 0xB3); + } break; case FLOAT64: writer.writeByte(expression.isSigned() ? 0xB7 : 0xB8); @@ -600,14 +600,22 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { writer.writeByte(expression.isSigned() ? 0xB4 : 0xB5); break; case FLOAT64: - writer.writeByte(expression.isSigned() ? 0xB9 : 0xBA); + if (expression.isReinterpret()) { + writer.writeByte(0xBF); + } else { + writer.writeByte(expression.isSigned() ? 0xB9 : 0xBA); + } break; } break; case FLOAT32: switch (expression.getTargetType()) { case INT32: - writer.writeByte(expression.isSigned() ? 0xA8 : 0xA9); + if (expression.isReinterpret()) { + writer.writeByte(0xBC); + } else { + writer.writeByte(expression.isSigned() ? 0xA8 : 0xA9); + } break; case INT64: writer.writeByte(expression.isSigned() ? 0xAE : 0xAF); @@ -625,7 +633,11 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { writer.writeByte(expression.isSigned() ? 0xAA : 0xAB); break; case INT64: - writer.writeByte(expression.isSigned() ? 0xB0 : 0xB1); + if (expression.isReinterpret()) { + writer.writeByte(0xBD); + } else { + writer.writeByte(expression.isSigned() ? 0xB0 : 0xB1); + } break; case FLOAT32: writer.writeByte(0xB6); 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 13128b27e..dcaa3b1d7 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 @@ -323,7 +323,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { break; case FLOAT32: case FLOAT64: - name = expression.isSigned() ? "convert_s" : "convert_u"; + if (expression.isReinterpret()) { + name = "reinterpret"; + } else { + name = expression.isSigned() ? "convert_s" : "convert_u"; + } break; } break; @@ -336,7 +340,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { break; case FLOAT32: case FLOAT64: - name = expression.isSigned() ? "convert_s" : "convert_u"; + if (expression.isReinterpret()) { + name = "reinterpret"; + } else { + name = expression.isSigned() ? "convert_s" : "convert_u"; + } break; } break; @@ -344,7 +352,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { switch (expression.getTargetType()) { case INT32: case INT64: - name = expression.isSigned() ? "trunc_s" : "trunc_u"; + if (expression.isReinterpret()) { + name = "reinterpret"; + } else { + name = expression.isSigned() ? "trunc_s" : "trunc_u"; + } break; case FLOAT32: break; @@ -357,7 +369,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { switch (expression.getTargetType()) { case INT32: case INT64: - name = expression.isSigned() ? "trunc_s" : "trunc_u"; + if (expression.isReinterpret()) { + name = "reinterpret"; + } else { + name = expression.isSigned() ? "trunc_s" : "trunc_u"; + } break; case FLOAT32: name = "demote";