From a0a57b074f79dbe5e01745da9cfbd08a52fa8cfd Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Fri, 16 Nov 2018 21:17:33 +0000 Subject: [PATCH] libphobos: Add IEEE quadruple support to core.internal.convert Backport from upstream druntime 2.083 for AArch64. Reviewed-on: https://github.com/dlang/druntime/pull/2257 From-SVN: r266222 --- libphobos/libdruntime/core/internal/convert.d | 135 +++++++++++++++--- 1 file changed, 114 insertions(+), 21 deletions(-) diff --git a/libphobos/libdruntime/core/internal/convert.d b/libphobos/libdruntime/core/internal/convert.d index a612707e2672..c4745b6f5a7f 100644 --- a/libphobos/libdruntime/core/internal/convert.d +++ b/libphobos/libdruntime/core/internal/convert.d @@ -10,13 +10,36 @@ module core.internal.convert; import core.internal.traits : Unqual; +/+ +A @nogc function can allocate memory during CTFE. ++/ +@nogc nothrow pure @trusted +private ubyte[] ctfe_alloc()(size_t n) +{ + if (!__ctfe) + { + assert(0, "CTFE only"); + } + else + { + static ubyte[] alloc(size_t x) nothrow pure + { + if (__ctfe) // Needed to prevent _d_newarray from appearing in compiled prorgam. + return new ubyte[x]; + else + assert(0); + } + return (cast(ubyte[] function(size_t) @nogc nothrow pure) &alloc)(n); + } +} + @trusted pure nothrow -const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real) || +const(ubyte)[] toUbyte(T)(const ref T val) if (is(Unqual!T == float) || is(Unqual!T == double) || is(Unqual!T == real) || is(Unqual!T == ifloat) || is(Unqual!T == idouble) || is(Unqual!T == ireal)) { static const(ubyte)[] reverse_(const(ubyte)[] arr) { - ubyte[] buff = new ubyte[arr.length]; + ubyte[] buff = ctfe_alloc(arr.length); foreach (k, v; arr) { buff[$-k-1] = v; @@ -31,17 +54,35 @@ const(ubyte)[] toUbyte(T)(ref T val) if (is(Unqual!T == float) || is(Unqual!T == uint exp = parsed.exponent; uint sign = parsed.sign; - ubyte[T.sizeof] buff; + ubyte[] buff = ctfe_alloc(T.sizeof); size_t off_bytes = 0; size_t off_bits = 0; + // Quadruples won't fit in one ulong, so check for that. + enum mantissaMax = FloatTraits!T.MANTISSA < ulong.sizeof*8 ? + FloatTraits!T.MANTISSA : ulong.sizeof*8; - for (; off_bytes < FloatTraits!T.MANTISSA/8; ++off_bytes) + for (; off_bytes < mantissaMax/8; ++off_bytes) { buff[off_bytes] = cast(ubyte)mantissa; mantissa >>= 8; } - off_bits = FloatTraits!T.MANTISSA%8; - buff[off_bytes] = cast(ubyte)mantissa; + + static if (floatFormat!T == FloatFormat.Quadruple) + { + ulong mantissa2 = parsed.mantissa2; + off_bytes--; // go back one, since mantissa only stored data in 56 + // bits, ie 7 bytes + for(; off_bytes < FloatTraits!T.MANTISSA/8; ++off_bytes) + { + buff[off_bytes] = cast(ubyte)mantissa2; + mantissa2 >>= 8; + } + } + else + { + off_bits = FloatTraits!T.MANTISSA%8; + buff[off_bytes] = cast(ubyte)mantissa; + } for (size_t i=0; i> pow; + return Float(fl.mantissa >> pow, 0, sign); } @safe pure nothrow -private ulong denormalizedMantissa(T)(T x) if (floatFormat!T != FloatFormat.Real80) +private Float denormalizedMantissa(T)(T x, uint sign) + if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double) { x *= 2.0L^^FloatTraits!T.MANTISSA; auto fl = parse!true(x); ulong mant = fl.mantissa >> (FloatTraits!T.MANTISSA - fl.exponent); - return shiftrRound(mant); + return Float(shiftrRound(mant), 0, sign); +} + +@safe pure nothrow +private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Quadruple) +{ + x *= 2.0L^^FloatTraits!T.MANTISSA; + auto fl = parse!true(x); + uint offset = FloatTraits!T.MANTISSA - fl.exponent + 1; + enum mantissaSize = (ulong.sizeof - 1) * 8; + + if (offset < mantissaSize) + { // Create a new mantissa ulong with the trailing mantissa2 bits that + // need to be shifted into mantissa, by shifting the needed bits left, + // zeroing out the first byte, and then ORing it with mantissa shifted + // right by offset. + + ulong shiftedMantissa = ((fl.mantissa2 << (mantissaSize - offset)) & + 0x00FFFFFFFFFFFFFFUL) | fl.mantissa >> offset; + return Float(shiftedMantissa, 0, sign, fl.mantissa2 >> offset); + } + else if (offset > mantissaSize) + return Float(fl.mantissa2 >> offset - mantissaSize , 0, sign, 0); + else + // Handle special case mentioned in parse() above by zeroing out the + // 57'th bit of mantissa2, "shifting" it into mantissa, and setting the + // first bit of mantissa2. + return Float(fl.mantissa2 & 0x00FFFFFFFFFFFFFFUL , 0, sign, 1); } version (unittest) @@ -403,6 +494,8 @@ version (unittest) testNumberConvert!("real.min_normal/2"); testNumberConvert!("real.min_normal/2UL^^63"); + // check subnormal storage edge case for Quadruple + testNumberConvert!("real.min_normal/2UL^^56"); //testNumberConvert!("real.min_normal/19"); // XGDC: ct[0] == 0, rt[0] == 27 //testNumberConvert!("real.min_normal/17"); // XGDC: ct[0= == 128, rt[0] == 136