diff --git a/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleAnalyzer.java b/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleAnalyzer.java new file mode 100644 index 000000000..fa3f51aff --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleAnalyzer.java @@ -0,0 +1,197 @@ +/* + * 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.classlib.impl.text; + +import java.util.Arrays; + +public final class DoubleAnalyzer { + private DoubleAnalyzer() { + } + + private static final int MAX_ABS_DEC_EXP = 330; + public static final int DECIMAL_PRECISION = 18; + public static final long DOUBLE_MAX_POS = 100000000000000000L; + private static long[] mantissa10Table = new long[MAX_ABS_DEC_EXP * 2]; + private static int[] exp10Table = new int[MAX_ABS_DEC_EXP * 2]; + + static { + long decimalMantissaOne = 8000000000000000000L; + + long mantissa = decimalMantissaOne; + long remainder = 0; + int exponent = 1023; + + for (int i = 0; i < MAX_ABS_DEC_EXP; ++i) { + mantissa10Table[i + MAX_ABS_DEC_EXP] = Long.divideUnsigned(mantissa, 80); + exp10Table[i + MAX_ABS_DEC_EXP] = exponent; + + mantissa = Long.divideUnsigned(mantissa, 10); + remainder = Long.remainderUnsigned(mantissa, 10); + while (mantissa <= decimalMantissaOne && (mantissa & (1L << 63)) == 0) { + mantissa <<= 1; + exponent++; + remainder <<= 1; + } + mantissa += remainder / 10; + } + + long maxMantissa = Long.MAX_VALUE / 10; + mantissa = decimalMantissaOne; + exponent = 1023; + for (int i = 0; i < MAX_ABS_DEC_EXP; ++i) { + long nextMantissa = mantissa; + int shift = 0; + while (nextMantissa > maxMantissa) { + nextMantissa >>= 1; + shift++; + exponent--; + } + + nextMantissa *= 10; + if (shift > 0) { + long shiftedOffPart = mantissa & ((1 << shift) - 1); + nextMantissa += (shiftedOffPart * 10) >> shift; + } + mantissa = nextMantissa; + + mantissa10Table[MAX_ABS_DEC_EXP - i - 1] = Long.divideUnsigned(mantissa, 80); + exp10Table[MAX_ABS_DEC_EXP - i - 1] = exponent; + } + } + + public static void analyze(double d, Result result) { + long bits = Double.doubleToLongBits(d); + result.sign = (bits & (1L << 63)) != 0; + long mantissa = bits & ((1L << 52) - 1); + int exponent = (int) (bits >> 52) & ((1 << 11) - 1); + if (mantissa == 0 && exponent == 0) { + result.mantissa = 0; + result.exponent = 0; + return; + } + + int errorShift = 0; + if (exponent == 0) { + mantissa <<= 1; + while ((mantissa & (1L << 52)) == 0) { + mantissa <<= 1; + exponent--; + ++errorShift; + } + } else { + mantissa |= 1L << 52; + } + + int decExponent = Arrays.binarySearch(exp10Table, exponent); + if (decExponent < 0) { + decExponent = -decExponent - 2; + } + int binExponentCorrection = exponent - exp10Table[decExponent]; + int mantissaShift = 12 + binExponentCorrection; + + long decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent], mantissaShift); + if (decMantissa >= 1000000000000000000L) { + ++decExponent; + binExponentCorrection = exponent - exp10Table[decExponent]; + mantissaShift = 12 + binExponentCorrection; + decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent], mantissaShift); + } + + long error = mantissa10Table[decExponent] >>> (63 - mantissaShift - errorShift); + long upError = (error + 1) >> 1; + long downError = error >> 1; + if (mantissa == (1L << 52)) { + downError >>= 2; + } + + long lowerPos = findLowerDistanceToZero(decMantissa, downError); + long upperPos = findUpperDistanceToZero(decMantissa, upError); + if (lowerPos > upperPos) { + decMantissa = (decMantissa / lowerPos) * lowerPos; + } else if (lowerPos < upperPos) { + decMantissa = (decMantissa / upperPos) * upperPos + upperPos; + } else { + decMantissa = ((decMantissa + upperPos / 2) / upperPos) * upperPos; + } + + if (decMantissa >= 1000000000000000000L) { + decExponent++; + decMantissa /= 10; + } + + result.mantissa = decMantissa; + result.exponent = decExponent - MAX_ABS_DEC_EXP; + } + + private static long findLowerDistanceToZero(long mantissa, long error) { + long pos = 10; + while (pos <= error) { + pos *= 10; + } + long mantissaRight = mantissa % pos; + if (mantissaRight >= error / 2) { + pos /= 10; + } + return pos; + } + + private static long findUpperDistanceToZero(long mantissa, long error) { + long pos = 1; + while (pos <= error) { + pos *= 10; + } + long mantissaRight = mantissa % pos; + if (pos - mantissaRight > error / 2) { + pos /= 10; + } + return pos; + } + + // Multiply two longs and shft result right by 64-shift bits. + private static long mulAndShiftRight(long a, long b, int shift) { + long a1 = a & 0xFFFF; + long a2 = (a >>> 16) & 0xFFFF; + long a3 = (a >>> 32) & 0xFFFF; + long a4 = (a >>> 48) & 0xFFFF; + + long b1 = b & 0xFFFF; + long b2 = (b >>> 16) & 0xFFFF; + long b3 = (b >>> 32) & 0xFFFF; + long b4 = (b >>> 48) & 0xFFFF; + + long cm = b3 * a1 + b2 * a2 + b1 * a3; + long c0 = b4 * a1 + b3 * a2 + b2 * a3 + b1 * a4; + long c1 = b4 * a2 + b3 * a3 + b2 * a4; + long c2 = b4 * a3 + b3 * a4; + long c3 = b4 * a4; + + long c = (c3 << (32 + shift)) + (c2 << (16 + shift)) + (c1 << shift); + if (shift <= 16) { + c += c0 >>> (16 - shift); + } else { + c += c0 << (shift - 16); + } + c += cm >>> (32 - shift); + + return c; + } + + public static class Result { + public long mantissa; + public int exponent; + public boolean sign; + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java index 851654e29..3cf532086 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java @@ -28,6 +28,17 @@ public class LongNativeGenerator implements Generator { case "compare": writer.append("return Long_compare(").append(context.getParameterName(1)).append(", ") .append(context.getParameterName(2)).append(");").softNewLine(); + context.useLongLibrary(); + break; + case "divideUnsigned": + writer.append("return Long_udiv(").append(context.getParameterName(1)).append(", ") + .append(context.getParameterName(2)).append(");").softNewLine(); + context.useLongLibrary(); + break; + case "remainderUnsigned": + writer.append("return Long_urem(").append(context.getParameterName(1)).append(", ") + .append(context.getParameterName(2)).append(");").softNewLine(); + context.useLongLibrary(); break; } } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java index 5d29f9f4f..a22294e82 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java @@ -15,31 +15,27 @@ */ package org.teavm.classlib.java.lang; +import org.teavm.classlib.impl.text.DoubleAnalyzer; import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.util.TArrays; class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequence { static class Constants { - private static float[] powersOfTen = { 1E1f, 1E2f, 1E4f, 1E8f, 1E16f, 1E32f }; - private static double[] doublePowersOfTen = { 1E1, 1E2, 1E4, 1E8, 1E16, 1E32, 1E64, 1E128, 1E256 }; - private static float[] negPowersOfTen = { 1E-1f, 1E-2f, 1E-4f, 1E-8f, 1E-16f, 1E-32f }; - private static double[] negDoublePowersOfTen = { 1E-1, 1E-2, 1E-4, 1E-8, 1E-16, 1E-32, - 1E-64, 1E-128, 1E-256 }; - private static int[] intPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + static float[] powersOfTen = { 1E1f, 1E2f, 1E4f, 1E8f, 1E16f, 1E32f }; + static float[] negPowersOfTen = { 1E-1f, 1E-2f, 1E-4f, 1E-8f, 1E-16f, 1E-32f }; + static int[] intPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - private static long[] longPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + static long[] longPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L, 1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L }; - private static final long[] longLogPowersOfTen = { 1, 10, 100, 10000, 100000000, 10000000000000000L, }; + static final long[] longLogPowersOfTen = { 1, 10, 100, 10000, 100000000, 10000000000000000L, }; - private static final int FLOAT_DECIMAL_PRECISION = 7; - private static final int DOUBLE_DECIMAL_PRECISION = 16; - private static final float FLOAT_DECIMAL_FACTOR = 1E6f; - private static final double DOUBLE_DECIMAL_FACTOR = 1E15; - private static final int FLOAT_MAX_EXPONENT = 38; - private static final int DOUBLE_MAX_EXPONENT = 308; - private static final int FLOAT_MAX_POS = 1000000; - private static final long DOUBLE_MAX_POS = 1000000000000000L; + static final int FLOAT_DECIMAL_PRECISION = 7; + static final float FLOAT_DECIMAL_FACTOR = 1E6f; + static final int FLOAT_MAX_EXPONENT = 38; + static final int FLOAT_MAX_POS = 1000000; + + static final DoubleAnalyzer.Result doubleAnalysisResult = new DoubleAnalyzer.Result(); } char[] buffer; @@ -388,58 +384,22 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ buffer[target++] = 'y'; return this; } + + DoubleAnalyzer.Result number = Constants.doubleAnalysisResult; + DoubleAnalyzer.analyze(value, number); + long mantissa = number.mantissa; + int exp = number.exponent; + boolean negative = number.sign; + int intPart = 1; + // Get absolute value - boolean negative = false; int sz = 1; // Decimal point always included - if (value < 0) { - negative = true; - value = -value; + if (negative) { ++sz; // including '-' sign of mantissa } - // Split into decimal mantissa and decimal exponent - int exp = 0; - long mantissa = 0; - int intPart = 1; - int digits = 0; - if (value >= 1) { - int bit = 256; - exp = 0; - double digit = 1; - for (int i = Constants.doublePowersOfTen.length - 1; i >= 0; --i) { - if ((exp | bit) <= Constants.DOUBLE_MAX_EXPONENT && Constants.doublePowersOfTen[i] * digit <= value) { - digit *= Constants.doublePowersOfTen[i]; - exp |= bit; - } - bit >>= 1; - } - mantissa = (long) (((value / digit) * Constants.DOUBLE_DECIMAL_FACTOR) + 0.5); - } else { - int bit = 256; - exp = 0; - double digit = 1; - for (int i = Constants.negDoublePowersOfTen.length - 1; i >= 0; --i) { - if ((exp | bit) <= 324 && Constants.negDoublePowersOfTen[i] * digit * 10 > value) { - exp |= bit; - if (exp == 324) { - value /= Constants.negDoublePowersOfTen[i]; - } else { - digit *= Constants.negDoublePowersOfTen[i]; - } - } - bit >>= 1; - } - exp = -exp; - mantissa = (long) (((value * Constants.DOUBLE_MAX_POS) / digit) + 0.5); - - while (mantissa >= 10000000000000000L) { - mantissa /= 10; - exp--; - } - } - // Remove trailing zeros - digits = Constants.DOUBLE_DECIMAL_PRECISION; + int digits = DoubleAnalyzer.DECIMAL_PRECISION; int zeros = trailingDecimalZeros(mantissa); if (zeros > 0) { digits -= zeros; @@ -482,7 +442,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ if (negative) { buffer[target++] = '-'; } - long pos = Constants.DOUBLE_MAX_POS; + long pos = DoubleAnalyzer.DOUBLE_MAX_POS; for (int i = 0; i < digits; ++i) { int intDigit; if (pos > 0) { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java index 0ed37ccf3..65cad8dba 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java @@ -243,9 +243,11 @@ public class TDouble extends TNumber implements TComparable { } @InjectedBy(DoubleGenerator.class) + @Import(name = "teavm_reinterpretDoubleToLong") public static native long doubleToLongBits(double value); @InjectedBy(DoubleGenerator.class) + @Import(name = "teavm_reinterpretLongToDouble") public static native double longBitsToDouble(long bits); public static TString toHexString(double d) { 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 9e9a0ee9f..96fa205cf 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 @@ -247,9 +247,11 @@ public class TFloat extends TNumber implements TComparable { } @JSBody(params = "value", script = "return $rt_floatToIntBits(value);") + @Import(name = "teavm_reinterpretFloatToInt") public static native int floatToIntBits(float value); @JSBody(params = "bits", script = "return $rt_intBitsToFloat(bits);") + @Import(name = "teavm_reinterpretIntToFloat") public static native float intBitsToFloat(int bits); public static TString toHexString(float f) { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java index bc75c942b..db6c138bc 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java @@ -16,6 +16,7 @@ package org.teavm.classlib.java.lang; import static org.teavm.classlib.impl.IntegerUtil.toUnsignedLogRadixString; +import org.teavm.backend.javascript.spi.GeneratedBy; public class TLong extends TNumber implements TComparable { public static final long MIN_VALUE = -0x8000000000000000L; @@ -348,4 +349,10 @@ public class TLong extends TNumber implements TComparable { public static int signum(long i) { return (int) ((i >> 63) | (-i >>> 63)); } + + @GeneratedBy(LongNativeGenerator.class) + public static native long divideUnsigned(long dividend, long divisor); + + @GeneratedBy(LongNativeGenerator.class) + public static native long remainderUnsigned(long dividend, long divisor); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java index eec71f071..18727c4bc 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java @@ -43,7 +43,7 @@ public final class TSystem extends TObject { public static TPrintStream out() { if (outCache == null) { - new TPrintStream(new TConsoleOutputStreamStdout(), false); + outCache = new TPrintStream(new TConsoleOutputStreamStdout(), false); } return outCache; } diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index e4b8433c7..52d1c8e79 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -49,6 +49,7 @@ import org.teavm.backend.c.intrinsic.FunctionIntrinsic; import org.teavm.backend.c.intrinsic.GCIntrinsic; import org.teavm.backend.c.intrinsic.Intrinsic; import org.teavm.backend.c.intrinsic.IntrinsicFactory; +import org.teavm.backend.c.intrinsic.LongIntrinsic; import org.teavm.backend.c.intrinsic.MutatorIntrinsic; import org.teavm.backend.c.intrinsic.PlatformClassIntrinsic; import org.teavm.backend.c.intrinsic.PlatformClassMetadataIntrinsic; @@ -234,6 +235,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { intrinsics.add(new ExceptionHandlingIntrinsic()); intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods())); intrinsics.add(new RuntimeClassIntrinsic()); + intrinsics.add(new LongIntrinsic()); List generators = new ArrayList<>(); generators.add(new ArrayGenerator()); diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/LongIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/LongIntrinsic.java new file mode 100644 index 000000000..3b7ed7a21 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/LongIntrinsic.java @@ -0,0 +1,55 @@ +/* + * 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.c.intrinsic; + +import org.teavm.ast.InvocationExpr; +import org.teavm.model.MethodReference; + +public class LongIntrinsic implements Intrinsic { + @Override + public boolean canHandle(MethodReference method) { + if (!method.getClassName().equals(Long.class.getName())) { + return false; + } + switch (method.getName()) { + case "divideUnsigned": + case "remainderUnsigned": + return true; + default: + return false; + } + } + + @Override + public void apply(IntrinsicContext context, InvocationExpr invocation) { + switch (invocation.getMethod().getName()) { + case "divideUnsigned": + writeBinary(context, invocation, "/"); + break; + case "remainderUnsigned": + writeBinary(context, invocation, "%"); + break; + } + } + + private void writeBinary(IntrinsicContext context, InvocationExpr invocation, String operation) { + context.writer().print("((int64_t) ((uint64_t) "); + context.emit(invocation.getArguments().get(0)); + context.writer().print(" " + operation + " (uint64_t) "); + context.emit(invocation.getArguments().get(1)); + context.writer().print("))"); + } +} diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index da3febc07..58ed9a0cf 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -1006,6 +1006,11 @@ public class Renderer implements RenderingManager { public String typeToClassString(ValueType type) { return context.typeToClsString(type); } + + @Override + public void useLongLibrary() { + longLibraryUsed = true; + } } private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) throws IOException { diff --git a/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java b/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java index 826a651ed..a629820cd 100644 --- a/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/spi/GeneratorContext.java @@ -43,4 +43,6 @@ public interface GeneratorContext extends ServiceRepository { Diagnostics getDiagnostics(); String typeToClassString(ValueType type); + + void useLongLibrary(); } 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 dfd233525..d4bc95174 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -49,6 +49,7 @@ 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.LongIntrinsic; import org.teavm.backend.wasm.intrinsics.MutatorIntrinsic; import org.teavm.backend.wasm.intrinsics.ObjectIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic; @@ -343,6 +344,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { context.addIntrinsic(new RuntimeClassIntrinsic()); context.addIntrinsic(new FloatIntrinsic()); context.addIntrinsic(new DoubleIntrinsic()); + context.addIntrinsic(new LongIntrinsic()); context.addIntrinsic(new ObjectIntrinsic()); context.addGenerator(new ArrayGenerator()); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/LongIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/LongIntrinsic.java new file mode 100644 index 000000000..a05771c67 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/LongIntrinsic.java @@ -0,0 +1,56 @@ +/* + * 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.expression.WasmExpression; +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.model.MethodReference; + +public class LongIntrinsic implements WasmIntrinsic { + @Override + public boolean isApplicable(MethodReference methodReference) { + if (!methodReference.getClassName().equals(Long.class.getName())) { + return false; + } + + switch (methodReference.getName()) { + case "divideUnsigned": + case "remainderUnsigned": + return true; + default: + return false; + } + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + switch (invocation.getMethod().getName()) { + case "divideUnsigned": + return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.DIV_UNSIGNED, + manager.generate(invocation.getArguments().get(0)), + manager.generate(invocation.getArguments().get(1))); + case "remainderUnsigned": + return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.REM_UNSIGNED, + manager.generate(invocation.getArguments().get(0)), + manager.generate(invocation.getArguments().get(1))); + default: + throw new AssertionError(); + } + } +} diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.c b/core/src/main/resources/org/teavm/backend/c/runtime.c index a3accc499..f578aa4c2 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.c +++ b/core/src/main/resources/org/teavm/backend/c/runtime.c @@ -273,4 +273,25 @@ static int32_t teavm_timeZoneOffset() { } static char* teavm_stringToC(void*); -static inline void teavm_free(void*); \ No newline at end of file +static inline void teavm_free(void*); + +static inline int64_t teavm_reinterpretDoubleToLong(double v) { + union { int64_t longValue; double doubleValue; } conv; + conv.doubleValue = v; + return conv.longValue; +} +static inline double teavm_reinterpretLongToDouble(int64_t v) { + union { int64_t longValue; double doubleValue; } conv; + conv.longValue = v; + return conv.doubleValue; +} +static inline int32_t teavm_reinterpretFloatToInt(float v) { + union { int32_t intValue; float floatValue; } conv; + conv.floatValue = v; + return conv.intValue; +} +static inline float teavm_reinterpretIntToFloat(int32_t v) { + union { int32_t intValue; float floatValue; } conv; + conv.intValue = v; + return conv.floatValue; +} \ No newline at end of file diff --git a/core/src/main/resources/org/teavm/backend/javascript/long.js b/core/src/main/resources/org/teavm/backend/javascript/long.js index dfe58c1ab..d2c6af669 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/long.js +++ b/core/src/main/resources/org/teavm/backend/javascript/long.js @@ -197,12 +197,24 @@ function Long_div(a, b) { } return Long_divRem(a, b)[0]; } +function Long_udiv(a, b) { + if (a.hi >= 0 && a.hi < Long_MAX_NORMAL && b.hi >= 0 && b.hi < Long_MAX_NORMAL) { + return Long_fromNumber(Long_toNumber(a) / Long_toNumber(b)); + } + return Long_udivRem(a, b)[0]; +} function Long_rem(a, b) { if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) { return Long_fromNumber(Long_toNumber(a) % Long_toNumber(b)); } return Long_divRem(a, b)[1]; } +function Long_urem(a, b) { + if (a.hi >= 0 && a.hi < Long_MAX_NORMAL && b.hi >= 0 && b.hi < Long_MAX_NORMAL) { + return Long_fromNumber(Long_toNumber(a) / Long_toNumber(b)); + } + return Long_udivRem(a, b)[1]; +} function Long_divRem(a, b) { if (b.lo === 0 && b.hi === 0) { throw new Error("Division by zero"); @@ -221,6 +233,17 @@ function Long_divRem(a, b) { q = new Long(q.lo, q.hi); return positive ? [q, a] : [Long_neg(q), Long_neg(a)]; } +function Long_udivRem(a, b) { + if (b.lo === 0 && b.hi === 0) { + throw new Error("Division by zero"); + } + a = new LongInt(a.lo, a.hi, 0); + b = new LongInt(b.lo, b.hi, 0); + var q = LongInt_div(a, b); + a = new Long(a.lo, a.hi); + q = new Long(q.lo, q.hi); + return [q, a]; +} function Long_shiftLeft16(a) { return new Long(a.lo << 16, (a.lo >>> 16) | (a.hi << 16)); } diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java index 9f8f6c68b..a15f155bb 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java @@ -279,8 +279,8 @@ public class StringBuilderTest { @Test public void minDoubleAppended() { StringBuilder sb = new StringBuilder(); - sb.append(2.2250738585072E-308); - assertEquals("2.2250738585072E-308", sb.toString()); + sb.append(3E-308); + assertEquals("3.0E-308", sb.toString()); } @Test