diff mbox series

[committed] d: Merge upstream dmd 09db0c41e, druntime e68a5ae3.

Message ID 20200425002019.25760-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Merge upstream dmd 09db0c41e, druntime e68a5ae3. | expand

Commit Message

Iain Buclaw April 25, 2020, 12:20 a.m. UTC
Hi,

This patch merges the D front-end implementation with upstream dmd
09db0c41e, and the D runtime library with upstream 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.

Bootstrapped and regression tested on x86_64-linux-gnu, committed to
mainline.

Regards
Iain

---
gcc/d/ChangeLog:

	* intrinsics.cc (expand_intrinsic_toprec): New function.
	(maybe_expand_intrinsic): Handle toPrec intrinsics.
	* intrinsics.def (TOPRECF, TOPREC, TOPRECL): Add toPrec intrinsics.
---
 gcc/d/dmd/MERGE                               |   2 +-
 gcc/d/intrinsics.cc                           |  22 +++
 gcc/d/intrinsics.def                          |   3 +
 .../gdc.test/compilable/interpret3.d          |  16 ++
 gcc/testsuite/gdc.test/runnable/builtin.d     |   2 +-
 gcc/testsuite/gdc.test/runnable/complex.d     |   2 +-
 gcc/testsuite/gdc.test/runnable/constfold.d   |   3 +-
 gcc/testsuite/gdc.test/runnable/foreach4.d    |   3 +-
 gcc/testsuite/gdc.test/runnable/ifti.d        |   2 +-
 gcc/testsuite/gdc.test/runnable/implicit.d    |   3 +-
 gcc/testsuite/gdc.test/runnable/inner.d       |   3 +-
 gcc/testsuite/gdc.test/runnable/interpret.d   |  47 ++++-
 gcc/testsuite/gdc.test/runnable/issue8671.d   |   2 +-
 gcc/testsuite/gdc.test/runnable/lazy.d        |   2 +-
 gcc/testsuite/gdc.test/runnable/mars1.d       |   2 +-
 gcc/testsuite/gdc.test/runnable/mixin1.d      |   3 +-
 gcc/testsuite/gdc.test/runnable/mixin2.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/s2ir.d        |   3 +-
 gcc/testsuite/gdc.test/runnable/stress.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/template4.d   |   2 +-
 gcc/testsuite/gdc.test/runnable/template9.d   |   2 +-
 gcc/testsuite/gdc.test/runnable/test10942.d   |   2 +-
 gcc/testsuite/gdc.test/runnable/test11.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/test12.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/test12197.d   |   2 +-
 gcc/testsuite/gdc.test/runnable/test15.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/test22.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/test23.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/test24.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/test27.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/test28.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/test34.d      |   3 +-
 gcc/testsuite/gdc.test/runnable/test37.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/test42.d      |   3 +-
 gcc/testsuite/gdc.test/runnable/test5305.d    |   2 +-
 gcc/testsuite/gdc.test/runnable/test60.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/testaa.d      |   2 +-
 .../gdc.test/runnable/testbitarray.d          |   2 +-
 gcc/testsuite/gdc.test/runnable/testdstress.d |   2 +-
 gcc/testsuite/gdc.test/runnable/testfile.d    |   2 +-
 gcc/testsuite/gdc.test/runnable/testformat.d  |   2 +-
 gcc/testsuite/gdc.test/runnable/testline.d    |   2 +-
 gcc/testsuite/gdc.test/runnable/testmmfile.d  |   2 +-
 gcc/testsuite/gdc.test/runnable/testscope2.d  |   2 +-
 gcc/testsuite/gdc.test/runnable/testsignals.d |   2 +-
 gcc/testsuite/gdc.test/runnable/testsocket.d  |   2 +-
 gcc/testsuite/gdc.test/runnable/teststdio.d   |   2 +-
 gcc/testsuite/gdc.test/runnable/testthread2.d |   2 +-
 gcc/testsuite/gdc.test/runnable/testtypeid.d  |   3 +-
 gcc/testsuite/gdc.test/runnable/traits.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/wc.d          |   2 +-
 gcc/testsuite/gdc.test/runnable/wc2.d         |   2 +-
 gcc/testsuite/gdc.test/runnable/wc3.d         |   2 +-
 gcc/testsuite/gdc.test/runnable/xtest46.d     |   2 +-
 gcc/testsuite/gdc.test/runnable/xtest55.d     |   2 +-
 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 +-
 60 files changed, 329 insertions(+), 121 deletions(-)

Comments

Segher Boessenkool April 25, 2020, 8:50 p.m. UTC | #1
Hi!

On Sat, Apr 25, 2020 at 02:20:19AM +0200, Iain Buclaw via Gcc-patches wrote:
> +            // 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.

That's not correct.  There is no "Unused" field, and the lower fraction
is not always an immediate extension of the higher fraction.

(It's not 1,7,56 -- it is 1,11,52).

A "double double" is really a pair of double precision numbers, both
with sign and exponent fields.  If the first number has maximum
exponent (so, it is infinity or a NaN), the second number is not
significant; otherwise, the sum of the two numbers (taken as exact
numbers, no rounding, no truncation, etc.) is the represented number.
The first number should be that, rounded to double precision.

So the second double does "add fraction bits" somewhat like this, but
there is an implicit leading 1, for normal numbers, and there can be a
gap between the two halves, too (like in 0x1p0 + 0x1p-100).


Segher
Iain Buclaw April 26, 2020, 8:38 a.m. UTC | #2
On 25/04/2020 22:50, Segher Boessenkool wrote:
> Hi!
> 
> On Sat, Apr 25, 2020 at 02:20:19AM +0200, Iain Buclaw via Gcc-patches wrote:
>> +            // 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.
> 
> That's not correct.  There is no "Unused" field, and the lower fraction
> is not always an immediate extension of the higher fraction.
> 
> (It's not 1,7,56 -- it is 1,11,52).
> 

Thanks, I did a quick look-up of where the original might have came
from, and I've found another extended floating point format of IBM.
I'll send the correction upstream.


> A "double double" is really a pair of double precision numbers, both
> with sign and exponent fields.  If the first number has maximum
> exponent (so, it is infinity or a NaN), the second number is not
> significant; otherwise, the sum of the two numbers (taken as exact
> numbers, no rounding, no truncation, etc.) is the represented number.
> The first number should be that, rounded to double precision.
> 
> So the second double does "add fraction bits" somewhat like this, but
> there is an implicit leading 1, for normal numbers, and there can be a
> gap between the two halves, too (like in 0x1p0 + 0x1p-100).
> 
> 

The job of this routine is to pry out the byte representation of real
(long double) values at compile-time for hashing-related purposes.

As zero, infinity and NaN are already handled, unless a mismatch between
compile and run-time computed hashes is found (I haven't seen any
unit-tests trigger failures in the testsuite), I don't think that there
is any immediate problem with the current implementation.

Thanks for the information though.

Iain.
Segher Boessenkool April 26, 2020, 4:41 p.m. UTC | #3
On Sun, Apr 26, 2020 at 10:38:57AM +0200, Iain Buclaw wrote:
> On 25/04/2020 22:50, Segher Boessenkool wrote:
> > On Sat, Apr 25, 2020 at 02:20:19AM +0200, Iain Buclaw via Gcc-patches wrote:
> >> +            // 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.
> > 
> > That's not correct.  There is no "Unused" field, and the lower fraction
> > is not always an immediate extension of the higher fraction.
> > 
> > (It's not 1,7,56 -- it is 1,11,52).
> 
> Thanks, I did a quick look-up of where the original might have came
> from, and I've found another extended floating point format of IBM.
> I'll send the correction upstream.
> 
> > A "double double" is really a pair of double precision numbers, both
> > with sign and exponent fields.  If the first number has maximum
> > exponent (so, it is infinity or a NaN), the second number is not
> > significant; otherwise, the sum of the two numbers (taken as exact
> > numbers, no rounding, no truncation, etc.) is the represented number.
> > The first number should be that, rounded to double precision.
> > 
> > So the second double does "add fraction bits" somewhat like this, but
> > there is an implicit leading 1, for normal numbers, and there can be a
> > gap between the two halves, too (like in 0x1p0 + 0x1p-100).
> 
> The job of this routine is to pry out the byte representation of real
> (long double) values at compile-time for hashing-related purposes.
> 
> As zero, infinity and NaN are already handled, unless a mismatch between
> compile and run-time computed hashes is found (I haven't seen any
> unit-tests trigger failures in the testsuite), I don't think that there
> is any immediate problem with the current implementation.
> 
> Thanks for the information though.

All bits are significant to the value, there are no unused bits, for
most values.  The sign and exponent of the second number are very much
relevant, in general.

I didn't look at your actual implementation, just this comment, but it
sounds like your tests miss a lot of cases, if no problems were found?


Segher
Iain Buclaw April 26, 2020, 8:43 p.m. UTC | #4
On 26/04/2020 18:41, Segher Boessenkool wrote:
> On Sun, Apr 26, 2020 at 10:38:57AM +0200, Iain Buclaw wrote:
>> On 25/04/2020 22:50, Segher Boessenkool wrote:
>>> On Sat, Apr 25, 2020 at 02:20:19AM +0200, Iain Buclaw via Gcc-patches wrote:
>>>> +            // 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.
>>>
>>> That's not correct.  There is no "Unused" field, and the lower fraction
>>> is not always an immediate extension of the higher fraction.
>>>
>>> (It's not 1,7,56 -- it is 1,11,52).
>>
>> Thanks, I did a quick look-up of where the original might have came
>> from, and I've found another extended floating point format of IBM.
>> I'll send the correction upstream.
>>
>>> A "double double" is really a pair of double precision numbers, both
>>> with sign and exponent fields.  If the first number has maximum
>>> exponent (so, it is infinity or a NaN), the second number is not
>>> significant; otherwise, the sum of the two numbers (taken as exact
>>> numbers, no rounding, no truncation, etc.) is the represented number.
>>> The first number should be that, rounded to double precision.
>>>
>>> So the second double does "add fraction bits" somewhat like this, but
>>> there is an implicit leading 1, for normal numbers, and there can be a
>>> gap between the two halves, too (like in 0x1p0 + 0x1p-100).
>>
>> The job of this routine is to pry out the byte representation of real
>> (long double) values at compile-time for hashing-related purposes.
>>
>> As zero, infinity and NaN are already handled, unless a mismatch between
>> compile and run-time computed hashes is found (I haven't seen any
>> unit-tests trigger failures in the testsuite), I don't think that there
>> is any immediate problem with the current implementation.
>>
>> Thanks for the information though.
> 
> All bits are significant to the value, there are no unused bits, for
> most values.  The sign and exponent of the second number are very much
> relevant, in general.
> 
> I didn't look at your actual implementation, just this comment, but it
> sounds like your tests miss a lot of cases, if no problems were found?
> 

All these tests pass.  That is, the computed compile-time byte layout
(the result of this function) is the same as the layout at run-time.

Don't mind that these are all strings, they are all passed to mixin().

/**Test special values*/
testNumberConvert!("-real.infinity");
testNumberConvert!("real.infinity");
testNumberConvert!("-0.0L");
testNumberConvert!("0.0L");
testNumberConvert!("real.nan");

/**Test min and max values values*/
testNumberConvert!("real.min_normal");
testNumberConvert!("real.max");

/**Test common values*/
testNumberConvert!("-0.17L");
testNumberConvert!("3.14L");

/**Test immutable and const*/
testNumberConvert!("cast(const)3.14L");
testNumberConvert!("cast(immutable)3.14L");

/**Test denormalized values*/
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");
testNumberConvert!("real.min_normal/17");

/**Test imaginary values: convert algorithm is same with real values*/
testNumberConvert!("0.0Li");

/**True random values*/
testNumberConvert!("-0x9.0f7ee55df77618fp-13829L");
testNumberConvert!("0x7.36e6e2640120d28p+8797L");
testNumberConvert!("-0x1.05df6ce4702ccf8p+15835L");
testNumberConvert!("0x9.54bb0d88806f714p-7088L");

/**Big overflow or underflow*/
testNumberConvert!("cast(double)-0x9.0f7ee55df77618fp-13829L");
testNumberConvert!("cast(double)0x7.36e6e2640120d28p+8797L");
testNumberConvert!("cast(double)-0x1.05df6ce4702ccf8p+15835L");
testNumberConvert!("cast(double)0x9.54bb0d88806f714p-7088L");
testNumberConvert!("cast(float)-0x9.0f7ee55df77618fp-13829L");
testNumberConvert!("cast(float)0x7.36e6e2640120d28p+8797L");

Iain.
Segher Boessenkool April 28, 2020, 5:59 p.m. UTC | #5
On Sun, Apr 26, 2020 at 10:43:53PM +0200, Iain Buclaw wrote:
> >>>> +            // 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.
> >>>
> >>> That's not correct.  There is no "Unused" field, and the lower fraction
> >>> is not always an immediate extension of the higher fraction.
> >>>
> >>> (It's not 1,7,56 -- it is 1,11,52).

> > All bits are significant to the value, there are no unused bits, for
> > most values.  The sign and exponent of the second number are very much
> > relevant, in general.
> > 
> > I didn't look at your actual implementation, just this comment, but it
> > sounds like your tests miss a lot of cases, if no problems were found?
> 
> All these tests pass.  That is, the computed compile-time byte layout
> (the result of this function) is the same as the layout at run-time.

Then either it tests not nearly enough, or it does not implement what the
comment says.

> // check subnormal storage edge case for Quadruple
> testNumberConvert!("real.min_normal/2UL^^56");
> testNumberConvert!("real.min_normal/19");
> testNumberConvert!("real.min_normal/17");

IBM long double has quite different edge cases than IEEE QP float.

> /**True random values*/
> testNumberConvert!("-0x9.0f7ee55df77618fp-13829L");
> testNumberConvert!("0x7.36e6e2640120d28p+8797L");
> testNumberConvert!("-0x1.05df6ce4702ccf8p+15835L");
> testNumberConvert!("0x9.54bb0d88806f714p-7088L");

None of these are valid double-double numbers (they all underflow or
overflow).

> /**Big overflow or underflow*/
> testNumberConvert!("cast(double)-0x9.0f7ee55df77618fp-13829L");
> testNumberConvert!("cast(double)0x7.36e6e2640120d28p+8797L");
> testNumberConvert!("cast(double)-0x1.05df6ce4702ccf8p+15835L");
> testNumberConvert!("cast(double)0x9.54bb0d88806f714p-7088L");
> testNumberConvert!("cast(float)-0x9.0f7ee55df77618fp-13829L");
> testNumberConvert!("cast(float)0x7.36e6e2640120d28p+8797L");

(Exactly like these).


Segher
diff mbox series

Patch

diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index 155286dd765..a878cb9f42e 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@ 
-62ce36f3737de691217c21f0173f411734eb1d43
+09db0c41ee922502fa0966bde24c1cb9b15ad436
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
diff --git a/gcc/d/intrinsics.cc b/gcc/d/intrinsics.cc
index 9a9fd41727f..c7bde88e5cd 100644
--- a/gcc/d/intrinsics.cc
+++ b/gcc/d/intrinsics.cc
@@ -467,6 +467,25 @@  expand_intrinsic_pow (tree callexp)
 			  base, exponent);
 }
 
+/* Expand a front-end intrinsic call to toPrec().  This takes one argument, the
+   signature to which can be either:
+
+	T toPrec(T)(float f);
+	T toPrec(T)(double f);
+	T toPrec(T)(real f);
+
+    This rounds the argument F to the precision of the specified floating
+    point type T.  The original call expression is held in CALLEXP.  */
+
+static tree
+expand_intrinsic_toprec (tree callexp)
+{
+  tree f = CALL_EXPR_ARG (callexp, 0);
+  tree type = TREE_TYPE (callexp);
+
+  return convert (type, f);
+}
+
 /* Expand a front-end intrinsic call to va_arg().  This takes either one or two
    arguments, the signature to which can be either:
 
@@ -818,6 +837,9 @@  maybe_expand_intrinsic (tree callexp)
 			      CALL_EXPR_ARG (callexp, 1),
 			      CALL_EXPR_ARG (callexp, 2));
 
+    case INTRINSIC_TOPREC:
+      return expand_intrinsic_toprec (callexp);
+
     case INTRINSIC_VA_ARG:
     case INTRINSIC_C_VA_ARG:
       return expand_intrinsic_vaarg (callexp);
diff --git a/gcc/d/intrinsics.def b/gcc/d/intrinsics.def
index 3c8ed101848..1782cd7f507 100644
--- a/gcc/d/intrinsics.def
+++ b/gcc/d/intrinsics.def
@@ -95,6 +95,9 @@  DEF_D_BUILTIN (SIN, SIN, "sin", "core.math", "FNaNbNiNfeZe")
 DEF_D_BUILTIN (SQRTF, SQRTF, "sqrt", "core.math", "FNaNbNiNffZf")
 DEF_D_BUILTIN (SQRT, SQRT, "sqrt", "core.math", "FNaNbNiNfdZd")
 DEF_D_BUILTIN (SQRTL, SQRTL, "sqrt", "core.math", "FNaNbNiNfeZe")
+DEF_D_BUILTIN (TOPRECF, TOPREC, "toPrec", "core.math", "FNaNbNffZI1T")
+DEF_D_BUILTIN (TOPREC, TOPREC, "toPrec", "core.math", "FNaNbNfdZI1T")
+DEF_D_BUILTIN (TOPRECL, TOPREC, "toPrec", "core.math", "FNaNbNfeZI1T")
 
 /* std.math intrinsics.  */
 
diff --git a/gcc/testsuite/gdc.test/compilable/interpret3.d b/gcc/testsuite/gdc.test/compilable/interpret3.d
index 36cdd13148b..14d1a12c240 100644
--- a/gcc/testsuite/gdc.test/compilable/interpret3.d
+++ b/gcc/testsuite/gdc.test/compilable/interpret3.d
@@ -7742,3 +7742,19 @@  struct RBNode(T)
 
 static assert(!__traits(compiles, { alias bug18057 = RBNode!int; }));
 
+/************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=9937
+
+int test9937()
+{
+    import core.math;
+
+    float x = float.max;
+    x *= 2;
+    x = toPrec!float(x);
+    x /= 2;
+    assert(x == float.infinity);
+    return 1;
+}
+
+static assert(test9937());
diff --git a/gcc/testsuite/gdc.test/runnable/builtin.d b/gcc/testsuite/gdc.test/runnable/builtin.d
index d7ac356757f..44817b16ee0 100644
--- a/gcc/testsuite/gdc.test/runnable/builtin.d
+++ b/gcc/testsuite/gdc.test/runnable/builtin.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 
 import std.stdio;
 import std.math;
@@ -116,4 +117,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/complex.d b/gcc/testsuite/gdc.test/runnable/complex.d
index 49bb3097aef..78fe574b310 100644
--- a/gcc/testsuite/gdc.test/runnable/complex.d
+++ b/gcc/testsuite/gdc.test/runnable/complex.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 import std.stdio;
@@ -460,4 +461,3 @@  int main(char[][] args)
     return 0;
 }
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/constfold.d b/gcc/testsuite/gdc.test/runnable/constfold.d
index d56f6c1357c..42406ea44a5 100644
--- a/gcc/testsuite/gdc.test/runnable/constfold.d
+++ b/gcc/testsuite/gdc.test/runnable/constfold.d
@@ -1,5 +1,5 @@ 
 #! blah
-
+// RUNNABLE_PHOBOS_TEST
 static assert(__LINE__ == 3); // fails as __LINE__ is 2
 
 import std.stdio;
@@ -672,4 +672,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/foreach4.d b/gcc/testsuite/gdc.test/runnable/foreach4.d
index bf8eab45b0e..8c9d4218d6e 100644
--- a/gcc/testsuite/gdc.test/runnable/foreach4.d
+++ b/gcc/testsuite/gdc.test/runnable/foreach4.d
@@ -1,4 +1,4 @@ 
-
+// RUNNABLE_PHOBOS_TEST
 import core.stdc.stdio;
 import std.stdio;
 
@@ -928,4 +928,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/ifti.d b/gcc/testsuite/gdc.test/runnable/ifti.d
index 0e4edef8983..6d669753123 100644
--- a/gcc/testsuite/gdc.test/runnable/ifti.d
+++ b/gcc/testsuite/gdc.test/runnable/ifti.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 import std.stdio;
 
 struct S {
@@ -118,4 +119,3 @@  void main() {
     }
 
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/implicit.d b/gcc/testsuite/gdc.test/runnable/implicit.d
index 89e6ac11b9f..9170b04adb0 100644
--- a/gcc/testsuite/gdc.test/runnable/implicit.d
+++ b/gcc/testsuite/gdc.test/runnable/implicit.d
@@ -1,4 +1,4 @@ 
-
+// RUNNABLE_PHOBOS_TEST
 import std.stdio;
 
 /***********************************/
@@ -479,4 +479,3 @@  void main()
 
     writefln("Success");
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/inner.d b/gcc/testsuite/gdc.test/runnable/inner.d
index 40eb27d5799..e1be7b9cbad 100644
--- a/gcc/testsuite/gdc.test/runnable/inner.d
+++ b/gcc/testsuite/gdc.test/runnable/inner.d
@@ -1,4 +1,4 @@ 
-
+// RUNNABLE_PHOBOS_TEST
 import std.stdio;
 
 /*******************************************************/
@@ -916,4 +916,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/interpret.d b/gcc/testsuite/gdc.test/runnable/interpret.d
index eee3930d34b..b822cadfcd0 100644
--- a/gcc/testsuite/gdc.test/runnable/interpret.d
+++ b/gcc/testsuite/gdc.test/runnable/interpret.d
@@ -1,4 +1,4 @@ 
-
+// RUNNABLE_PHOBOS_TEST
 import std.stdio;
 
 template Tuple(A...)
@@ -3480,6 +3480,50 @@  void test15681()
     assert(s2.values[1].value == 1);        // OK
 }
 
+/************************************************/
+// toPrec
+
+void testToPrec()
+{
+    import core.math;
+
+    enum real ctpir = 0xc.90fdaa22168c235p-2;
+    enum double ctpid = 0x1.921fb54442d18p+1;
+    enum float ctpif = 0x1.921fb6p+1;
+    static assert(toPrec!float(ctpir) == ctpif);
+    static assert(toPrec!double(ctpir) == ctpid);
+    static assert(toPrec!real(ctpir) == ctpir);
+    static assert(toPrec!float(ctpid) == ctpif);
+    static assert(toPrec!double(ctpid) == ctpid);
+    static assert(toPrec!real(ctpid) == ctpid);
+    static assert(toPrec!float(ctpif) == ctpif);
+    static assert(toPrec!double(ctpif) == ctpif);
+    static assert(toPrec!real(ctpif) == ctpif);
+
+    assert(toPrec!float(ctpir) == ctpif);
+    assert(toPrec!double(ctpir) == ctpid);
+    assert(toPrec!real(ctpir) == ctpir);
+    assert(toPrec!float(ctpid) == ctpif);
+    assert(toPrec!double(ctpid) == ctpid);
+    assert(toPrec!real(ctpid) == ctpid);
+    assert(toPrec!float(ctpif) == ctpif);
+    assert(toPrec!double(ctpif) == ctpif);
+    assert(toPrec!real(ctpif) == ctpif);
+
+    static real rtpir = 0xc.90fdaa22168c235p-2;
+    static double rtpid = 0x1.921fb54442d18p+1;
+    static float rtpif = 0x1.921fb6p+1;
+    assert(toPrec!float(rtpir) == rtpif);
+    assert(toPrec!double(rtpir) == rtpid);
+    assert(toPrec!real(rtpir) == rtpir);
+    assert(toPrec!float(rtpid) == rtpif);
+    assert(toPrec!double(rtpid) == rtpid);
+    assert(toPrec!real(rtpid) == rtpid);
+    assert(toPrec!float(rtpif) == rtpif);
+    assert(toPrec!double(rtpif) == rtpif);
+    assert(toPrec!real(rtpif) == rtpif);
+}
+
 /************************************************/
 
 int main()
@@ -3609,4 +3653,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/issue8671.d b/gcc/testsuite/gdc.test/runnable/issue8671.d
index 8097e79d6bf..c28e63b275b 100644
--- a/gcc/testsuite/gdc.test/runnable/issue8671.d
+++ b/gcc/testsuite/gdc.test/runnable/issue8671.d
@@ -1,6 +1,6 @@ 
+// RUNNABLE_PHOBOS_TEST
 import std.random;
 void main()
 {
     double t = 1.0 - uniform(0.0, 1.0);
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/lazy.d b/gcc/testsuite/gdc.test/runnable/lazy.d
index 741877cb970..b9d0fd2afc5 100644
--- a/gcc/testsuite/gdc.test/runnable/lazy.d
+++ b/gcc/testsuite/gdc.test/runnable/lazy.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 import core.vararg;
 import std.stdio;
 
@@ -308,4 +309,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/mars1.d b/gcc/testsuite/gdc.test/runnable/mars1.d
index b240745a513..4d17d33aebf 100644
--- a/gcc/testsuite/gdc.test/runnable/mars1.d
+++ b/gcc/testsuite/gdc.test/runnable/mars1.d
@@ -1,4 +1,5 @@ 
 /*
+RUNNABLE_PHOBOS_TEST
 REQUIRED_ARGS: -mcpu=native
 PERMUTE_ARGS: -O -inline
 */
@@ -1723,4 +1724,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/mixin1.d b/gcc/testsuite/gdc.test/runnable/mixin1.d
index c16d943d20f..9d88fbd2841 100644
--- a/gcc/testsuite/gdc.test/runnable/mixin1.d
+++ b/gcc/testsuite/gdc.test/runnable/mixin1.d
@@ -1,4 +1,4 @@ 
-
+// RUNNABLE_PHOBOS_TEST
 module mixin1;
 
 import std.stdio;
@@ -1468,4 +1468,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/mixin2.d b/gcc/testsuite/gdc.test/runnable/mixin2.d
index 53a64d5f40e..3591d1200e0 100644
--- a/gcc/testsuite/gdc.test/runnable/mixin2.d
+++ b/gcc/testsuite/gdc.test/runnable/mixin2.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 import std.stdio;
 
 /*********************************************/
@@ -361,4 +362,3 @@  void main()
 
     writeln("Success");
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/s2ir.d b/gcc/testsuite/gdc.test/runnable/s2ir.d
index 4c969b4b16f..29cfc9669bb 100644
--- a/gcc/testsuite/gdc.test/runnable/s2ir.d
+++ b/gcc/testsuite/gdc.test/runnable/s2ir.d
@@ -1,4 +1,4 @@ 
-
+// RUNNABLE_PHOBOS_TEST
 import std.stdio;
 
 /***********************************/
@@ -95,4 +95,3 @@  int main()
     writefln("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/stress.d b/gcc/testsuite/gdc.test/runnable/stress.d
index 1b7f6457cf0..b15725369be 100644
--- a/gcc/testsuite/gdc.test/runnable/stress.d
+++ b/gcc/testsuite/gdc.test/runnable/stress.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 import core.stdc.stdio : printf;
@@ -725,4 +726,3 @@  void CLASS()
         }
     }
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/template4.d b/gcc/testsuite/gdc.test/runnable/template4.d
index 84792dae6ae..77d6254361a 100644
--- a/gcc/testsuite/gdc.test/runnable/template4.d
+++ b/gcc/testsuite/gdc.test/runnable/template4.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 import std.stdio;
 import core.stdc.stdio;
 
@@ -1164,4 +1165,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/template9.d b/gcc/testsuite/gdc.test/runnable/template9.d
index 0b11c6b4fa3..4f182959cb1 100644
--- a/gcc/testsuite/gdc.test/runnable/template9.d
+++ b/gcc/testsuite/gdc.test/runnable/template9.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 module breaker;
@@ -4965,4 +4966,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test10942.d b/gcc/testsuite/gdc.test/runnable/test10942.d
index 612158fc294..0d48946383d 100644
--- a/gcc/testsuite/gdc.test/runnable/test10942.d
+++ b/gcc/testsuite/gdc.test/runnable/test10942.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // REQUIRED_ARGS: -g
 
 import std.string;
@@ -24,4 +25,3 @@  mixin(getEnum(1087));
 
 void main() { }
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test11.d b/gcc/testsuite/gdc.test/runnable/test11.d
index e62ea97940a..0d916c1af6c 100644
--- a/gcc/testsuite/gdc.test/runnable/test11.d
+++ b/gcc/testsuite/gdc.test/runnable/test11.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // REQUIRED_ARGS:
 
 extern(C) int printf(const char*, ...);
@@ -1390,4 +1391,3 @@  int main(string[] argv)
 }
 
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test12.d b/gcc/testsuite/gdc.test/runnable/test12.d
index c196361a401..eb7e422c28b 100644
--- a/gcc/testsuite/gdc.test/runnable/test12.d
+++ b/gcc/testsuite/gdc.test/runnable/test12.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS: -unittest -O -release -inline -fPIC -g
 
 extern(C) int printf(const char*, ...);
@@ -1250,4 +1251,3 @@  int main(string[] argv)
     return 0;
 }
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test12197.d b/gcc/testsuite/gdc.test/runnable/test12197.d
index 52d44c94a2e..92a0bbddde5 100644
--- a/gcc/testsuite/gdc.test/runnable/test12197.d
+++ b/gcc/testsuite/gdc.test/runnable/test12197.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // import std.math;
 
 void foo(T)(T[] b)
@@ -10,4 +11,3 @@  void main()
     foo(a);
     assert(a[0] == 10000);
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test15.d b/gcc/testsuite/gdc.test/runnable/test15.d
index 75cd11ee8e7..234f50b2749 100644
--- a/gcc/testsuite/gdc.test/runnable/test15.d
+++ b/gcc/testsuite/gdc.test/runnable/test15.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // REQUIRED_ARGS:
 // EXTRA_FILES: extra-files/test15.txt
 
@@ -1439,4 +1440,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test22.d b/gcc/testsuite/gdc.test/runnable/test22.d
index d15db1d86a9..bd0487845b3 100644
--- a/gcc/testsuite/gdc.test/runnable/test22.d
+++ b/gcc/testsuite/gdc.test/runnable/test22.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // REQUIRED_ARGS:
 
 import std.math: poly;
@@ -1306,4 +1307,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test23.d b/gcc/testsuite/gdc.test/runnable/test23.d
index dda2864fb48..abf8e37f83d 100644
--- a/gcc/testsuite/gdc.test/runnable/test23.d
+++ b/gcc/testsuite/gdc.test/runnable/test23.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // REQUIRED_ARGS:
 
 module test;
@@ -1566,4 +1567,3 @@  void main()
 
     printf("Success\n");
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test24.d b/gcc/testsuite/gdc.test/runnable/test24.d
index cc6d6686d7a..2f31d757921 100644
--- a/gcc/testsuite/gdc.test/runnable/test24.d
+++ b/gcc/testsuite/gdc.test/runnable/test24.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // EXTRA_SOURCES: imports/test24a.d imports/test24b.d
 // PERMUTE_ARGS:
 // REQUIRED_ARGS:
@@ -8,4 +9,3 @@  void main()
 {
     string hi = std.string.format("%s", 3);
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test27.d b/gcc/testsuite/gdc.test/runnable/test27.d
index 3e5a462e4a5..a3e76ea8d62 100644
--- a/gcc/testsuite/gdc.test/runnable/test27.d
+++ b/gcc/testsuite/gdc.test/runnable/test27.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // COMPILE_SEPARATELY
 // EXTRA_SOURCES: imports/test27a.d
 // PERMUTE_ARGS:
@@ -11,4 +12,3 @@  int main()
     return 0;
 }
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test28.d b/gcc/testsuite/gdc.test/runnable/test28.d
index 1f7e7e89cf0..5355c2cb23a 100644
--- a/gcc/testsuite/gdc.test/runnable/test28.d
+++ b/gcc/testsuite/gdc.test/runnable/test28.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 module test;
 
 import core.vararg;
@@ -1318,4 +1319,3 @@  void main()
     printf("Success\n");
 }
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test34.d b/gcc/testsuite/gdc.test/runnable/test34.d
index e92f3d6e340..6e2b36846c9 100644
--- a/gcc/testsuite/gdc.test/runnable/test34.d
+++ b/gcc/testsuite/gdc.test/runnable/test34.d
@@ -1,4 +1,4 @@ 
-
+// RUNNABLE_PHOBOS_TEST
 module test34;
 
 import std.stdio;
@@ -1292,4 +1292,3 @@  void main()
 }
 
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test37.d b/gcc/testsuite/gdc.test/runnable/test37.d
index c28d30f2ae9..f4a454747b4 100644
--- a/gcc/testsuite/gdc.test/runnable/test37.d
+++ b/gcc/testsuite/gdc.test/runnable/test37.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 // REQUIRED_ARGS: -Jrunnable/extra-files
 // EXTRA_FILES: extra-files/foo37.txt extra-files/std14198/uni.d
@@ -11,4 +12,3 @@  void main()
     // imports in a subdirectory of the -J path
     writefln(import("std14198/uni.d"));
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test42.d b/gcc/testsuite/gdc.test/runnable/test42.d
index 2bb04580a7d..66b3c04e0bb 100644
--- a/gcc/testsuite/gdc.test/runnable/test42.d
+++ b/gcc/testsuite/gdc.test/runnable/test42.d
@@ -1,5 +1,5 @@ 
+// RUNNABLE_PHOBOS_TEST
 // REQUIRED_ARGS:
-//
 
 module test42;
 
@@ -6438,4 +6438,3 @@  int main()
     return 0;
 }
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test5305.d b/gcc/testsuite/gdc.test/runnable/test5305.d
index 9102cba91f2..ff52936a8dc 100644
--- a/gcc/testsuite/gdc.test/runnable/test5305.d
+++ b/gcc/testsuite/gdc.test/runnable/test5305.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // https://issues.dlang.org/show_bug.cgi?id=5305
 
 import std.math;
@@ -5,4 +6,3 @@  void map(real function(real) f) { }
 int main() { map(&sqrt); return 0; }
 
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/test60.d b/gcc/testsuite/gdc.test/runnable/test60.d
index ab967329f08..cfd92b225ca 100644
--- a/gcc/testsuite/gdc.test/runnable/test60.d
+++ b/gcc/testsuite/gdc.test/runnable/test60.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 import std.stdio;
 import std.algorithm;
 
@@ -20,4 +21,3 @@  void main()
 
     writeln("Success");
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testaa.d b/gcc/testsuite/gdc.test/runnable/testaa.d
index 6e28e894cf2..75e8172ab49 100644
--- a/gcc/testsuite/gdc.test/runnable/testaa.d
+++ b/gcc/testsuite/gdc.test/runnable/testaa.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS: -fPIC
 
 /* Test associative arrays */
@@ -1383,4 +1384,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testbitarray.d b/gcc/testsuite/gdc.test/runnable/testbitarray.d
index 118388bebd1..8a34f886b05 100644
--- a/gcc/testsuite/gdc.test/runnable/testbitarray.d
+++ b/gcc/testsuite/gdc.test/runnable/testbitarray.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 import std.bitmanip;
@@ -14,4 +15,3 @@  void main() {
 }
 
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testdstress.d b/gcc/testsuite/gdc.test/runnable/testdstress.d
index 097fc310d34..f06f93982a5 100644
--- a/gcc/testsuite/gdc.test/runnable/testdstress.d
+++ b/gcc/testsuite/gdc.test/runnable/testdstress.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 module dstress.run.module_01;
@@ -930,4 +931,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testfile.d b/gcc/testsuite/gdc.test/runnable/testfile.d
index e7c3e19a49d..6f568044ea4 100644
--- a/gcc/testsuite/gdc.test/runnable/testfile.d
+++ b/gcc/testsuite/gdc.test/runnable/testfile.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 import std.file;
@@ -22,4 +23,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testformat.d b/gcc/testsuite/gdc.test/runnable/testformat.d
index 2cb0da9fc4d..74f4095c009 100644
--- a/gcc/testsuite/gdc.test/runnable/testformat.d
+++ b/gcc/testsuite/gdc.test/runnable/testformat.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 import std.stdio;
@@ -123,4 +124,3 @@  int main()
     std.stdio.writefln("Success");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testline.d b/gcc/testsuite/gdc.test/runnable/testline.d
index 5001fc2fa68..5b84204b785 100644
--- a/gcc/testsuite/gdc.test/runnable/testline.d
+++ b/gcc/testsuite/gdc.test/runnable/testline.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 // $HeadURL$
@@ -41,4 +42,3 @@  void checkFileSpec(Object o){
 writeln(str);
         assert(str[start .. start+3]=="(1)");
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testmmfile.d b/gcc/testsuite/gdc.test/runnable/testmmfile.d
index 1ae98df9178..6a9d6e9094a 100644
--- a/gcc/testsuite/gdc.test/runnable/testmmfile.d
+++ b/gcc/testsuite/gdc.test/runnable/testmmfile.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 // REQUIRED_ARGS:
 
@@ -117,4 +118,3 @@  int main()
 
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testscope2.d b/gcc/testsuite/gdc.test/runnable/testscope2.d
index cde02ab405f..6c96fb5873d 100644
--- a/gcc/testsuite/gdc.test/runnable/testscope2.d
+++ b/gcc/testsuite/gdc.test/runnable/testscope2.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // REQUIRED_ARGS: -dip25
 
 import core.stdc.stdio;
@@ -246,4 +247,3 @@  void main()
     printf("Success\n");
 }
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testsignals.d b/gcc/testsuite/gdc.test/runnable/testsignals.d
index a43f89295e8..c2fbcee45cb 100644
--- a/gcc/testsuite/gdc.test/runnable/testsignals.d
+++ b/gcc/testsuite/gdc.test/runnable/testsignals.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 import std.stdio;
 import std.signals;
 
@@ -111,4 +112,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testsocket.d b/gcc/testsuite/gdc.test/runnable/testsocket.d
index 00b757a896b..d0619299ece 100644
--- a/gcc/testsuite/gdc.test/runnable/testsocket.d
+++ b/gcc/testsuite/gdc.test/runnable/testsocket.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 import std.stdio;
@@ -48,4 +49,3 @@  int main ()
 }
 
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/teststdio.d b/gcc/testsuite/gdc.test/runnable/teststdio.d
index 854dcb5ae38..d340f21892f 100644
--- a/gcc/testsuite/gdc.test/runnable/teststdio.d
+++ b/gcc/testsuite/gdc.test/runnable/teststdio.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 // EXTRA_FILES: extra-files/teststdio.txt
 
@@ -31,4 +32,3 @@  void main()
     } while (!feof(fp));
     //fclose(fp);
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testthread2.d b/gcc/testsuite/gdc.test/runnable/testthread2.d
index 0ec6e0dbcdc..93aace6c650 100644
--- a/gcc/testsuite/gdc.test/runnable/testthread2.d
+++ b/gcc/testsuite/gdc.test/runnable/testthread2.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 import std.algorithm : map;
@@ -106,4 +107,3 @@  void main() {
     }
 }
 
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/testtypeid.d b/gcc/testsuite/gdc.test/runnable/testtypeid.d
index dcaac731fa3..ebdda30c197 100644
--- a/gcc/testsuite/gdc.test/runnable/testtypeid.d
+++ b/gcc/testsuite/gdc.test/runnable/testtypeid.d
@@ -1,4 +1,4 @@ 
-
+// RUNNABLE_PHOBOS_TEST
 import core.vararg;
 import std.stdio;
 
@@ -684,4 +684,3 @@  int main()
 
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/traits.d b/gcc/testsuite/gdc.test/runnable/traits.d
index 915f16da48e..547976f6598 100644
--- a/gcc/testsuite/gdc.test/runnable/traits.d
+++ b/gcc/testsuite/gdc.test/runnable/traits.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 module traits;
 
@@ -1606,4 +1607,3 @@  int main()
     writeln("Success");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/wc.d b/gcc/testsuite/gdc.test/runnable/wc.d
index e5fd98a0f83..8f847c5938f 100644
--- a/gcc/testsuite/gdc.test/runnable/wc.d
+++ b/gcc/testsuite/gdc.test/runnable/wc.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 // EXECUTE_ARGS: runnable/wc.d
 
@@ -48,4 +49,3 @@  int main (string[] args)
     }
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/wc2.d b/gcc/testsuite/gdc.test/runnable/wc2.d
index 780f5f160ea..a97c6fa8620 100644
--- a/gcc/testsuite/gdc.test/runnable/wc2.d
+++ b/gcc/testsuite/gdc.test/runnable/wc2.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 // EXECUTE_ARGS: runnable/wc2.d
 
@@ -72,4 +73,3 @@  int main (string[] args)
     }
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/wc3.d b/gcc/testsuite/gdc.test/runnable/wc3.d
index 666bfb99113..13beac25aba 100644
--- a/gcc/testsuite/gdc.test/runnable/wc3.d
+++ b/gcc/testsuite/gdc.test/runnable/wc3.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 // EXECUTE_ARGS: runnable/extra-files/alice30.txt
 // EXTRA_FILES: extra-files/alice30.txt
@@ -70,4 +71,3 @@  int main (string[] args)
     }
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/xtest46.d b/gcc/testsuite/gdc.test/runnable/xtest46.d
index e1cdbcd1922..8cba4ed4a80 100644
--- a/gcc/testsuite/gdc.test/runnable/xtest46.d
+++ b/gcc/testsuite/gdc.test/runnable/xtest46.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS: -unittest -O -release -inline -fPIC -g
 
 import std.stdio;
@@ -8283,4 +8284,3 @@  int main()
     printf("Success\n");
     return 0;
 }
-// RUNNABLE_PHOBOS_TEST
diff --git a/gcc/testsuite/gdc.test/runnable/xtest55.d b/gcc/testsuite/gdc.test/runnable/xtest55.d
index 1062a1a77fc..4b295d8c686 100644
--- a/gcc/testsuite/gdc.test/runnable/xtest55.d
+++ b/gcc/testsuite/gdc.test/runnable/xtest55.d
@@ -1,3 +1,4 @@ 
+// RUNNABLE_PHOBOS_TEST
 // PERMUTE_ARGS:
 
 import core.memory, std.stdio;
@@ -22,4 +23,3 @@  int main()
     return 0;
 }
 
-// RUNNABLE_PHOBOS_TEST
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<FloatTraits!T.EXPONENT/8; ++i)
-        {
-            ubyte cur_exp = cast(ubyte)exp;
-            exp >>= 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<FloatTraits!T.EXPONENT/8; ++i)
+            {
+                ubyte cur_exp = cast(ubyte)exp;
+                exp >>= 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;       ///