From 018730326d878d98b85b1256ff220e76665ed97e Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Sat, 25 Apr 2020 02:19:04 +0200 Subject: d: Merge upstream dmd 09db0c41e, druntime e68a5ae3. * New core.math.toPrec templates have been added as an intrinsic. Some floating point algorithms, such as Kahan-Babuska-Neumaier Summation, require rounding to specific precisions. Rounding to precision after every operation, however, loses overall precision in the general case and is a runtime performance problem. Adding these functions guarantee the rounding at required points in the code, and document where in the algorithm the requirement exists. * Support IBM long double types in core.internal.convert. * Add missing aliases for 64-bit vectors in core.simd. * RUNNABLE_PHOBOS_TEST directive has been properly integrated into the D2 language testsuite. Reviewed-on: https://github.com/dlang/druntime/pull/3063 https://github.com/dlang/dmd/pull/11054 gcc/d/ChangeLog: * intrinsics.cc (expand_intrinsic_toprec): New function. (maybe_expand_intrinsic): Handle toPrec intrinsics. * intrinsics.def (TOPRECF, TOPREC, TOPRECL): Add toPrec intrinsics. --- libphobos/libdruntime/MERGE | 2 +- libphobos/libdruntime/core/cpuid.d | 2 +- libphobos/libdruntime/core/internal/convert.d | 170 ++++++++++++++++++-------- libphobos/libdruntime/core/math.d | 71 +++++++++++ libphobos/libdruntime/core/simd.d | 6 +- 5 files changed, 192 insertions(+), 59 deletions(-) (limited to 'libphobos') diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 18d479d54ff..8b461f76ad0 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -c9c209e2c62ce43a2c08ddd61d647730716b2d0f +e68a5ae36790fa9dc5bab6155bc450eb6bf8c12c The first line of this file holds the git revision number of the last merge done from the dlang/druntime repository. diff --git a/libphobos/libdruntime/core/cpuid.d b/libphobos/libdruntime/core/cpuid.d index d35e7d5449f..839605aee1e 100644 --- a/libphobos/libdruntime/core/cpuid.d +++ b/libphobos/libdruntime/core/cpuid.d @@ -822,7 +822,7 @@ void cpuidX86() { asm pure nothrow @nogc { "cpuid" : "=a" (pnb[0]), "=b" (pnb[1]), "=c" (pnb[ 2]), "=d" (pnb[ 3]) : "a" (0x8000_0002); - "cpuid" : "=a" (pnb[4]), "=b" (pnb[5]), "=c" (pnb[ 6]), "=d" (pnb[ 7]) : "a" (0x8000_0003); + "cpuid" : "=a" (pnb[4]), "=b" (pnb[5]), "=c" (pnb[ 6]), "=d" (pnb[ 7]) : "a" (0x8000_0003); "cpuid" : "=a" (pnb[8]), "=b" (pnb[9]), "=c" (pnb[10]), "=d" (pnb[11]) : "a" (0x8000_0004); } } diff --git a/libphobos/libdruntime/core/internal/convert.d b/libphobos/libdruntime/core/internal/convert.d index 3b82010ab9c..3d2cb59a64a 100644 --- a/libphobos/libdruntime/core/internal/convert.d +++ b/libphobos/libdruntime/core/internal/convert.d @@ -37,76 +37,138 @@ private ubyte[] ctfe_alloc()(size_t n) 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 = ctfe_alloc(arr.length); - foreach (k, v; arr) - { - buff[$-k-1] = v; - } - return buff; - } if (__ctfe) { - auto parsed = parse(val); - - ulong mantissa = parsed.mantissa; - uint exp = parsed.exponent; - uint sign = parsed.sign; - - 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 < mantissaMax/8; ++off_bytes) + static if (T.mant_dig == float.mant_dig || T.mant_dig == double.mant_dig) { - buff[off_bytes] = cast(ubyte)mantissa; - mantissa >>= 8; + static if (is(T : ireal)) // https://issues.dlang.org/show_bug.cgi?id=19932 + const f = val.im; + else + alias f = val; + static if (T.sizeof == uint.sizeof) + uint bits = *cast(const uint*) &f; + else static if (T.sizeof == ulong.sizeof) + ulong bits = *cast(const ulong*) &f; + ubyte[] result = ctfe_alloc(T.sizeof); + version (BigEndian) + { + foreach_reverse (ref b; result) + { + b = cast(ubyte) bits; + bits >>= 8; + } + } + else + { + foreach (ref b; result) + { + b = cast(ubyte) bits; + bits >>= 8; + } + } + return result; } - - static if (floatFormat!T == FloatFormat.Quadruple) + else static if (floatFormat!T == FloatFormat.DoubleDouble) { - 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) + // Parse DoubleDoubles as a pair of doubles. + // The layout of the type is: + // + // [1| 7 | 56 ][ 8 | 56 ] + // [S| Exp | Fraction (hi) ][ Unused | Fraction (low) ] + // + // We can get the least significant bits by subtracting the IEEE + // double precision portion from the real value. + + import core.math : toPrec; + + ubyte[] buff = ctfe_alloc(T.sizeof); + enum msbSize = double.sizeof; + + double hi = toPrec!double(val); + buff[0 .. msbSize] = toUbyte(hi)[]; + + if (val is cast(T)0.0 || val is cast(T)-0.0 || + val is T.nan || val is -T.nan || + val is T.infinity || val > T.max || + val is -T.infinity || val < -T.max) + { + // Zero, NaN, and Inf are all representable as doubles, so the + // least significant part can be 0.0. + buff[msbSize .. $] = 0; + } + else { - buff[off_bytes] = cast(ubyte)mantissa2; - mantissa2 >>= 8; + double low = toPrec!double(val - hi); + buff[msbSize .. $] = toUbyte(low)[]; } + + // Arrays don't index differently between little and big-endian targets. + return buff; } else { - off_bits = FloatTraits!T.MANTISSA%8; - buff[off_bytes] = cast(ubyte)mantissa; - } + auto parsed = parse(val); - for (size_t i=0; i>= 8; - buff[off_bytes] |= (cur_exp << off_bits); - ++off_bytes; - buff[off_bytes] |= cur_exp >> 8 - off_bits; - } + ulong mantissa = parsed.mantissa; + uint exp = parsed.exponent; + uint sign = parsed.sign; + 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; - exp <<= 8 - FloatTraits!T.EXPONENT%8 - 1; - buff[off_bytes] |= exp; - sign <<= 7; - buff[off_bytes] |= sign; + for (; off_bytes < mantissaMax/8; ++off_bytes) + { + buff[off_bytes] = cast(ubyte)mantissa; + mantissa >>= 8; + } - version (LittleEndian) - { + 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>= 8; + buff[off_bytes] |= (cur_exp << off_bits); + ++off_bytes; + buff[off_bytes] |= cur_exp >> 8 - off_bits; + } + + + exp <<= 8 - FloatTraits!T.EXPONENT%8 - 1; + buff[off_bytes] |= exp; + sign <<= 7; + buff[off_bytes] |= sign; + + version (BigEndian) + { + for (size_t left = 0, right = buff.length - 1; left < right; left++, right--) + { + const swap = buff[left]; + buff[left] = buff[right]; + buff[right] = swap; + } + } return buff; } - else - { - return reverse_(buff); - } } else { diff --git a/libphobos/libdruntime/core/math.d b/libphobos/libdruntime/core/math.d index 1b661d365c9..878623258cd 100644 --- a/libphobos/libdruntime/core/math.d +++ b/libphobos/libdruntime/core/math.d @@ -164,3 +164,74 @@ unittest } } +/************************************* + * Round argument to a specific precision. + * + * D language types specify a minimum precision, not a maximum. The + * `toPrec()` function forces rounding of the argument `f` to the + * precision of the specified floating point type `T`. + * + * Params: + * T = precision type to round to + * f = value to convert + * Returns: + * f in precision of type `T` + */ +@safe pure nothrow +T toPrec(T:float)(float f) { pragma(inline, false); return f; } +/// ditto +@safe pure nothrow +T toPrec(T:float)(double f) { pragma(inline, false); return cast(T) f; } +/// ditto +@safe pure nothrow +T toPrec(T:float)(real f) { pragma(inline, false); return cast(T) f; } +/// ditto +@safe pure nothrow +T toPrec(T:double)(float f) { pragma(inline, false); return f; } +/// ditto +@safe pure nothrow +T toPrec(T:double)(double f) { pragma(inline, false); return f; } +/// ditto +@safe pure nothrow +T toPrec(T:double)(real f) { pragma(inline, false); return cast(T) f; } +/// ditto +@safe pure nothrow +T toPrec(T:real)(float f) { pragma(inline, false); return f; } +/// ditto +@safe pure nothrow +T toPrec(T:real)(double f) { pragma(inline, false); return f; } +/// ditto +@safe pure nothrow +T toPrec(T:real)(real f) { pragma(inline, false); return f; } + +@safe unittest +{ + static float f = 1.1f; + static double d = 1.1; + static real r = 1.1L; + f = toPrec!float(f + f); + f = toPrec!float(d + d); + f = toPrec!float(r + r); + d = toPrec!double(f + f); + d = toPrec!double(d + d); + d = toPrec!double(r + r); + r = toPrec!real(f + f); + r = toPrec!real(d + d); + r = toPrec!real(r + r); + + /+ Uncomment these once compiler support has been added. + enum real PIR = 0xc.90fdaa22168c235p-2; + enum double PID = 0x1.921fb54442d18p+1; + enum float PIF = 0x1.921fb6p+1; + + assert(toPrec!float(PIR) == PIF); + assert(toPrec!double(PIR) == PID); + assert(toPrec!real(PIR) == PIR); + assert(toPrec!float(PID) == PIF); + assert(toPrec!double(PID) == PID); + assert(toPrec!real(PID) == PID); + assert(toPrec!float(PIF) == PIF); + assert(toPrec!double(PIF) == PIF); + assert(toPrec!real(PIF) == PIF); + +/ +} diff --git a/libphobos/libdruntime/core/simd.d b/libphobos/libdruntime/core/simd.d index 780db37c993..32e2aaf5cfd 100644 --- a/libphobos/libdruntime/core/simd.d +++ b/libphobos/libdruntime/core/simd.d @@ -10,9 +10,6 @@ * Authors: $(WEB digitalmars.com, Walter Bright), */ -/* NOTE: This file has been patched from the original DMD distribution to - * work with the GDC compiler. - */ module core.simd; pure: @@ -42,6 +39,7 @@ template Vector(T) /* Handy aliases */ static if (is(Vector!(void[8]))) alias Vector!(void[8]) void8; /// +static if (is(Vector!(double[1]))) alias Vector!(double[1]) double1; /// static if (is(Vector!(float[2]))) alias Vector!(float[2]) float2; /// static if (is(Vector!(byte[8]))) alias Vector!(byte[8]) byte8; /// static if (is(Vector!(ubyte[8]))) alias Vector!(ubyte[8]) ubyte8; /// @@ -49,6 +47,8 @@ static if (is(Vector!(short[4]))) alias Vector!(short[4]) short4; /// static if (is(Vector!(ushort[4]))) alias Vector!(ushort[4]) ushort4; /// static if (is(Vector!(int[2]))) alias Vector!(int[2]) int2; /// static if (is(Vector!(uint[2]))) alias Vector!(uint[2]) uint2; /// +static if (is(Vector!(long[1]))) alias Vector!(long[1]) long1; /// +static if (is(Vector!(ulong[1]))) alias Vector!(ulong[1]) ulong1; /// static if (is(Vector!(void[16]))) alias Vector!(void[16]) void16; /// static if (is(Vector!(double[2]))) alias Vector!(double[2]) double2; /// -- cgit v1.2.3