libphobos: Merge upstream phobos fb4f6a713

Improves the versioning of IeeeFlags and FloatingPointControl code and
unit-tests, making it clearer which targets can and cannot support it.

Reviewed-on: https://github.com/dlang/phobos/pull/7435
This commit is contained in:
Iain Buclaw 2020-04-07 16:14:30 +02:00
parent 7e5367f34d
commit f1a6150ecb
3 changed files with 152 additions and 144 deletions

View File

@ -1,4 +1,4 @@
6c45dd3a6523a21887cb9a883eeb3abd40375dc1 c9c209e2c62ce43a2c08ddd61d647730716b2d0f
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the dlang/druntime repository. merge done from the dlang/druntime repository.

View File

@ -1,4 +1,4 @@
68cc18adbcdbf2a62cb85a5cb2a34236af2ab05a fb4f6a713f5b78742f93e072cff6a6c4ecf9323d
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository. merge done from the dlang/phobos repository.

View File

@ -195,6 +195,38 @@ else version (X86)
private alias haveSSE = core.cpuid.sse; private alias haveSSE = core.cpuid.sse;
} }
version (D_SoftFloat)
{
// Some soft float implementations may support IEEE floating flags.
// The implementation here supports hardware flags only and is so currently
// only available for supported targets.
}
else version (X86_Any) version = IeeeFlagsSupport;
else version (PPC_Any) version = IeeeFlagsSupport;
else version (RISCV_Any) version = IeeeFlagsSupport;
else version (MIPS_Any) version = IeeeFlagsSupport;
else version (ARM_Any) version = IeeeFlagsSupport;
// Struct FloatingPointControl is only available if hardware FP units are available.
version (D_HardFloat)
{
// FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport
version (IeeeFlagsSupport) version = FloatingPointControlSupport;
}
version (GNU)
{
// The compiler can unexpectedly rearrange floating point operations and
// access to the floating point status flags when optimizing. This means
// ieeeFlags tests cannot be reliably checked in optimized code.
// See https://github.com/ldc-developers/ldc/issues/888
}
else
{
version = IeeeFlagsUnittest;
version = FloatingPointControlUnittest;
}
version (unittest) version (unittest)
{ {
import core.stdc.stdio; // : sprintf; import core.stdc.stdio; // : sprintf;
@ -1817,19 +1849,9 @@ real exp(real x) @trusted pure nothrow @nogc
if (isNaN(x)) if (isNaN(x))
return x; return x;
if (x > OF) if (x > OF)
{ return real.infinity;
if (__ctfe)
return real.infinity;
else
return real.max * copysign(real.max, real.infinity);
}
if (x < UF) if (x < UF)
{ return 0.0;
if (__ctfe)
return 0.0;
else
return real.min_normal * copysign(real.min_normal, 0.0);
}
// Express: e^^x = e^^g * 2^^n // Express: e^^x = e^^g * 2^^n
// = e^^g * e^^(n * LOG2E) // = e^^g * e^^(n * LOG2E)
@ -2102,12 +2124,7 @@ L_largenegative:
// Special cases. Raises an overflow flag, except in the case // Special cases. Raises an overflow flag, except in the case
// for CTFE, where there are no hardware controls. // for CTFE, where there are no hardware controls.
if (x > OF) if (x > OF)
{ return real.infinity;
if (__ctfe)
return real.infinity;
else
return real.max * copysign(real.max, real.infinity);
}
if (x == 0.0) if (x == 0.0)
return x; return x;
if (x < UF) if (x < UF)
@ -2402,19 +2419,9 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow
if (isNaN(x)) if (isNaN(x))
return x; return x;
if (x > OF) if (x > OF)
{ return real.infinity;
if (__ctfe)
return real.infinity;
else
return real.max * copysign(real.max, real.infinity);
}
if (x < UF) if (x < UF)
{ return 0.0;
if (__ctfe)
return 0.0;
else
return real.min_normal * copysign(real.min_normal, 0.0);
}
// Separate into integer and fractional parts. // Separate into integer and fractional parts.
int n = cast(int) floor(x + 0.5); int n = cast(int) floor(x + 0.5);
@ -2453,10 +2460,13 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow
@system unittest @system unittest
{ {
FloatingPointControl ctrl; version (FloatingPointControlSupport)
if (FloatingPointControl.hasExceptionTraps) {
ctrl.disableExceptions(FloatingPointControl.allExceptions); FloatingPointControl ctrl;
ctrl.rounding = FloatingPointControl.roundToNearest; if (FloatingPointControl.hasExceptionTraps)
ctrl.disableExceptions(FloatingPointControl.allExceptions);
ctrl.rounding = FloatingPointControl.roundToNearest;
}
static if (real.mant_dig == 113) static if (real.mant_dig == 113)
{ {
@ -2519,49 +2529,42 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow
const minEqualDecimalDigits = real.dig - 3; const minEqualDecimalDigits = real.dig - 3;
real x; real x;
IeeeFlags f; version (IeeeFlagsSupport) IeeeFlags f;
foreach (ref pair; exptestpoints) foreach (ref pair; exptestpoints)
{ {
resetIeeeFlags(); version (IeeeFlagsSupport) resetIeeeFlags();
x = exp(pair[0]); x = exp(pair[0]);
f = ieeeFlags;
assert(equalsDigit(x, pair[1], minEqualDecimalDigits)); assert(equalsDigit(x, pair[1], minEqualDecimalDigits));
version (IeeeFlagsSupport)
{
// Check the overflow bit
if (x == real.infinity)
{
// don't care about the overflow bit if input was inf
// (e.g., the LLVM intrinsic doesn't set it on Linux x86_64)
assert(pair[0] == real.infinity || f.overflow);
}
else
assert(!f.overflow);
// Check the underflow bit
assert(f.underflow == (fabs(x) < real.min_normal));
// Invalid and div by zero shouldn't be affected.
assert(!f.invalid);
assert(!f.divByZero);
}
} }
// Ideally, exp(0) would not set the inexact flag. // Ideally, exp(0) would not set the inexact flag.
// Unfortunately, fldl2e sets it! // Unfortunately, fldl2e sets it!
// So it's not realistic to avoid setting it. // So it's not realistic to avoid setting it.
assert(exp(0.0L) == 1.0); assert(exp(0.0L) == 1.0);
// NaN propagation. Doesn't set flags, bcos was already NaN. // NaN propagation. Doesn't set flags, bcos was already NaN.
resetIeeeFlags(); version (IeeeFlagsSupport)
x = exp(real.nan); {
f = ieeeFlags; resetIeeeFlags();
assert(isIdentical(abs(x), real.nan)); x = exp(real.nan);
assert(f.flags == 0); f = ieeeFlags;
assert(isIdentical(abs(x), real.nan));
assert(f.flags == 0);
resetIeeeFlags(); resetIeeeFlags();
x = exp(-real.nan); x = exp(-real.nan);
f = ieeeFlags; f = ieeeFlags;
assert(isIdentical(abs(x), real.nan)); assert(isIdentical(abs(x), real.nan));
assert(f.flags == 0); assert(f.flags == 0);
}
else
{
x = exp(real.nan);
assert(isIdentical(abs(x), real.nan));
x = exp(-real.nan);
assert(isIdentical(abs(x), real.nan));
}
x = exp(NaN(0x123)); x = exp(NaN(0x123));
assert(isIdentical(x, NaN(0x123))); assert(isIdentical(x, NaN(0x123)));
@ -4678,6 +4681,10 @@ real remquo(real x, real y, out int n) @trusted nothrow @nogc /// ditto
assert(0, "remquo not implemented"); assert(0, "remquo not implemented");
} }
version (IeeeFlagsSupport)
{
/** IEEE exception status flags ('sticky bits') /** IEEE exception status flags ('sticky bits')
These flags indicate that an exceptional floating-point condition has occurred. These flags indicate that an exceptional floating-point condition has occurred.
@ -4813,13 +4820,14 @@ private:
else else
assert(0, "Not yet supported"); assert(0, "Not yet supported");
} }
static void resetIeeeFlags() @nogc static void resetIeeeFlags() @nogc
{ {
version (GNU) version (GNU)
{ {
version (X86_Any) version (X86_Any)
{ {
asm pure nothrow @nogc asm nothrow @nogc
{ {
"fnclex"; "fnclex";
} }
@ -4828,12 +4836,12 @@ private:
if (haveSSE) if (haveSSE)
{ {
uint mxcsr; uint mxcsr;
asm pure nothrow @nogc asm nothrow @nogc
{ {
"stmxcsr %0" : "=m" (mxcsr); "stmxcsr %0" : "=m" (mxcsr);
} }
mxcsr &= ~EXCEPTIONS_MASK; mxcsr &= ~EXCEPTIONS_MASK;
asm pure nothrow @nogc asm nothrow @nogc
{ {
"ldmxcsr %0" : : "m" (mxcsr); "ldmxcsr %0" : : "m" (mxcsr);
} }
@ -4847,7 +4855,7 @@ private:
{ {
uint old = FloatingPointControl.getControlState(); uint old = FloatingPointControl.getControlState();
old &= ~0b11111; // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0408i/Chdfifdc.html old &= ~0b11111; // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0408i/Chdfifdc.html
asm pure nothrow @nogc asm nothrow @nogc
{ {
"vmsr FPSCR, %0" : : "r" (old); "vmsr FPSCR, %0" : : "r" (old);
} }
@ -4860,7 +4868,7 @@ private:
else else
{ {
uint newValues = 0x0; uint newValues = 0x0;
asm pure nothrow @nogc asm nothrow @nogc
{ {
"fsflags %0" : : "r" (newValues); "fsflags %0" : : "r" (newValues);
} }
@ -4872,7 +4880,7 @@ private:
else else
version (InlineAsm_X86_Any) version (InlineAsm_X86_Any)
{ {
asm pure nothrow @nogc asm nothrow @nogc
{ {
fnclex; fnclex;
} }
@ -4935,27 +4943,25 @@ public:
} }
/// ///
version (GNU) version (IeeeFlagsUnittest)
{
// ieeeFlags test disabled, see LDC Issue #888.
}
else
@system unittest @system unittest
{ {
static void func() { static void func() {
int a = 10 * 10; int a = 10 * 10;
} }
pragma(inline, false) static void blockopt(ref real x) {}
real a=3.5; real a = 3.5;
// Set all the flags to zero // Set all the flags to zero
resetIeeeFlags(); resetIeeeFlags();
assert(!ieeeFlags.divByZero); assert(!ieeeFlags.divByZero);
blockopt(a); // avoid constant propagation by the optimizer
// Perform a division by zero. // Perform a division by zero.
a/=0.0L; a /= 0.0L;
assert(a == real.infinity); assert(a == real.infinity);
assert(ieeeFlags.divByZero); assert(ieeeFlags.divByZero);
blockopt(a); // avoid constant propagation by the optimizer
// Create a NaN // Create a NaN
a*=0.0L; a *= 0.0L;
assert(ieeeFlags.invalid); assert(ieeeFlags.invalid);
assert(isNaN(a)); assert(isNaN(a));
@ -4966,11 +4972,7 @@ else
assert(ieeeFlags == f); assert(ieeeFlags == f);
} }
version (GNU) version (IeeeFlagsUnittest)
{
// ieeeFlags test disabled, see LDC Issue #888.
}
else
@system unittest @system unittest
{ {
import std.meta : AliasSeq; import std.meta : AliasSeq;
@ -5017,27 +5019,6 @@ else
} }
} }
version (X86_Any)
{
version = IeeeFlagsSupport;
}
else version (PPC_Any)
{
version = IeeeFlagsSupport;
}
else version (RISCV_Any)
{
version = IeeeFlagsSupport;
}
else version (MIPS_Any)
{
version = IeeeFlagsSupport;
}
else version (ARM_Any)
{
version = IeeeFlagsSupport;
}
/// Set all of the floating-point status flags to false. /// Set all of the floating-point status flags to false.
void resetIeeeFlags() @nogc { IeeeFlags.resetIeeeFlags(); } void resetIeeeFlags() @nogc { IeeeFlags.resetIeeeFlags(); }
@ -5047,6 +5028,12 @@ void resetIeeeFlags() @nogc { IeeeFlags.resetIeeeFlags(); }
return IeeeFlags(IeeeFlags.getIeeeFlags()); return IeeeFlags(IeeeFlags.getIeeeFlags());
} }
} // IeeeFlagsSupport
version (FloatingPointControlSupport)
{
/** Control the Floating point hardware /** Control the Floating point hardware
Change the IEEE754 floating-point rounding mode and the floating-point Change the IEEE754 floating-point rounding mode and the floating-point
@ -5418,7 +5405,10 @@ private:
// Clear all pending exceptions // Clear all pending exceptions
static void clearExceptions() @nogc static void clearExceptions() @nogc
{ {
resetIeeeFlags(); version (IeeeFlagsSupport)
resetIeeeFlags();
else
static assert(false, "Not implemented for this architecture");
} }
// Read from the control register // Read from the control register
@ -5479,7 +5469,7 @@ private:
version (D_InlineAsm_X86) version (D_InlineAsm_X86)
{ {
short cont; short cont;
asm nothrow @nogc asm pure nothrow @nogc
{ {
xor EAX, EAX; xor EAX, EAX;
fstcw cont; fstcw cont;
@ -5490,7 +5480,7 @@ private:
version (D_InlineAsm_X86_64) version (D_InlineAsm_X86_64)
{ {
short cont; short cont;
asm nothrow @nogc asm pure nothrow @nogc
{ {
xor RAX, RAX; xor RAX, RAX;
fstcw cont; fstcw cont;
@ -5508,7 +5498,7 @@ private:
{ {
version (X86_Any) version (X86_Any)
{ {
asm pure nothrow @nogc asm nothrow @nogc
{ {
"fclex; fldcw %0" : : "m" (newState); "fclex; fldcw %0" : : "m" (newState);
} }
@ -5517,7 +5507,7 @@ private:
if (haveSSE) if (haveSSE)
{ {
uint mxcsr; uint mxcsr;
asm pure nothrow @nogc asm nothrow @nogc
{ {
"stmxcsr %0" : "=m" (mxcsr); "stmxcsr %0" : "=m" (mxcsr);
} }
@ -5532,7 +5522,7 @@ private:
mxcsr &= ~(allExceptions << 7); // delete old masks mxcsr &= ~(allExceptions << 7); // delete old masks
mxcsr |= (newState & allExceptions) << 7; // write new exception masks mxcsr |= (newState & allExceptions) << 7; // write new exception masks
asm pure nothrow @nogc asm nothrow @nogc
{ {
"ldmxcsr %0" : : "m" (mxcsr); "ldmxcsr %0" : : "m" (mxcsr);
} }
@ -5540,7 +5530,7 @@ private:
} }
else version (AArch64) else version (AArch64)
{ {
asm pure nothrow @nogc asm nothrow @nogc
{ {
"msr FPCR, %0;" : : "r" (newState); "msr FPCR, %0;" : : "r" (newState);
} }
@ -5551,7 +5541,7 @@ private:
return; return;
else else
{ {
asm pure nothrow @nogc asm nothrow @nogc
{ {
"vmsr FPSCR, %0" : : "r" (newState); "vmsr FPSCR, %0" : : "r" (newState);
} }
@ -5563,7 +5553,7 @@ private:
return; return;
else else
{ {
asm pure nothrow @nogc asm nothrow @nogc
{ {
"fscsr %0" : : "r" (newState); "fscsr %0" : : "r" (newState);
} }
@ -5605,7 +5595,7 @@ private:
} }
} }
version (D_HardFloat) @system unittest @system unittest
{ {
void ensureDefaults() void ensureDefaults()
{ {
@ -5642,46 +5632,64 @@ version (D_HardFloat) @system unittest
ensureDefaults(); ensureDefaults();
} }
version (D_HardFloat) @system unittest // rounding version (FloatingPointControlUnittest)
@system unittest // rounding
{ {
import std.meta : AliasSeq; import std.meta : AliasSeq;
foreach (T; AliasSeq!(float, double, real)) foreach (T; AliasSeq!(float, double, real))
{ {
FloatingPointControl fpctrl; /* Be careful with changing the rounding mode, it interferes
* with common subexpressions. Changing rounding modes should
* be done with separate functions that are not inlined.
*/
fpctrl.rounding = FloatingPointControl.roundUp; {
T u = 1; static T addRound(T)(uint rm)
u += 0.1; {
pragma(inline, false) static void blockopt(ref T x) {}
pragma(inline, false);
FloatingPointControl fpctrl;
fpctrl.rounding = rm;
T x = 1;
blockopt(x); // avoid constant propagation by the optimizer
x += 0.1;
return x;
}
fpctrl.rounding = FloatingPointControl.roundDown; T u = addRound!(T)(FloatingPointControl.roundUp);
T d = 1; T d = addRound!(T)(FloatingPointControl.roundDown);
d += 0.1; T z = addRound!(T)(FloatingPointControl.roundToZero);
fpctrl.rounding = FloatingPointControl.roundToZero; assert(u > d);
T z = 1; assert(z == d);
z += 0.1; }
assert(u > d); {
assert(z == d); static T subRound(T)(uint rm)
{
pragma(inline, false) static void blockopt(ref T x) {}
pragma(inline, false);
FloatingPointControl fpctrl;
fpctrl.rounding = rm;
T x = -1;
blockopt(x); // avoid constant propagation by the optimizer
x -= 0.1;
return x;
}
fpctrl.rounding = FloatingPointControl.roundUp; T u = subRound!(T)(FloatingPointControl.roundUp);
u = -1; T d = subRound!(T)(FloatingPointControl.roundDown);
u -= 0.1; T z = subRound!(T)(FloatingPointControl.roundToZero);
fpctrl.rounding = FloatingPointControl.roundDown; assert(u > d);
d = -1; assert(z == u);
d -= 0.1; }
fpctrl.rounding = FloatingPointControl.roundToZero;
z = -1;
z -= 0.1;
assert(u > d);
assert(z == u);
} }
} }
} // FloatingPointControlSupport
/********************************* /*********************************
* Determines if $(D_PARAM x) is NaN. * Determines if $(D_PARAM x) is NaN.