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
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
merge done from the dlang/phobos repository.

View File

@ -195,6 +195,38 @@ else version (X86)
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)
{
import core.stdc.stdio; // : sprintf;
@ -1817,19 +1849,9 @@ real exp(real x) @trusted pure nothrow @nogc
if (isNaN(x))
return x;
if (x > OF)
{
if (__ctfe)
return real.infinity;
else
return real.max * copysign(real.max, real.infinity);
}
if (x < UF)
{
if (__ctfe)
return 0.0;
else
return real.min_normal * copysign(real.min_normal, 0.0);
}
// Express: e^^x = e^^g * 2^^n
// = e^^g * e^^(n * LOG2E)
@ -2102,12 +2124,7 @@ L_largenegative:
// Special cases. Raises an overflow flag, except in the case
// for CTFE, where there are no hardware controls.
if (x > OF)
{
if (__ctfe)
return real.infinity;
else
return real.max * copysign(real.max, real.infinity);
}
if (x == 0.0)
return x;
if (x < UF)
@ -2402,19 +2419,9 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow
if (isNaN(x))
return x;
if (x > OF)
{
if (__ctfe)
return real.infinity;
else
return real.max * copysign(real.max, real.infinity);
}
if (x < UF)
{
if (__ctfe)
return 0.0;
else
return real.min_normal * copysign(real.min_normal, 0.0);
}
// Separate into integer and fractional parts.
int n = cast(int) floor(x + 0.5);
@ -2452,11 +2459,14 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow
}
@system unittest
{
version (FloatingPointControlSupport)
{
FloatingPointControl ctrl;
if (FloatingPointControl.hasExceptionTraps)
ctrl.disableExceptions(FloatingPointControl.allExceptions);
ctrl.rounding = FloatingPointControl.roundToNearest;
}
static if (real.mant_dig == 113)
{
@ -2519,38 +2529,22 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow
const minEqualDecimalDigits = real.dig - 3;
real x;
IeeeFlags f;
version (IeeeFlagsSupport) IeeeFlags f;
foreach (ref pair; exptestpoints)
{
resetIeeeFlags();
version (IeeeFlagsSupport) resetIeeeFlags();
x = exp(pair[0]);
f = ieeeFlags;
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.
// Unfortunately, fldl2e sets it!
// So it's not realistic to avoid setting it.
assert(exp(0.0L) == 1.0);
// NaN propagation. Doesn't set flags, bcos was already NaN.
version (IeeeFlagsSupport)
{
resetIeeeFlags();
x = exp(real.nan);
f = ieeeFlags;
@ -2562,6 +2556,15 @@ private real exp2Impl(real x) @nogc @trusted pure nothrow
f = ieeeFlags;
assert(isIdentical(abs(x), real.nan));
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));
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");
}
version (IeeeFlagsSupport)
{
/** IEEE exception status flags ('sticky bits')
These flags indicate that an exceptional floating-point condition has occurred.
@ -4813,13 +4820,14 @@ private:
else
assert(0, "Not yet supported");
}
static void resetIeeeFlags() @nogc
{
version (GNU)
{
version (X86_Any)
{
asm pure nothrow @nogc
asm nothrow @nogc
{
"fnclex";
}
@ -4828,12 +4836,12 @@ private:
if (haveSSE)
{
uint mxcsr;
asm pure nothrow @nogc
asm nothrow @nogc
{
"stmxcsr %0" : "=m" (mxcsr);
}
mxcsr &= ~EXCEPTIONS_MASK;
asm pure nothrow @nogc
asm nothrow @nogc
{
"ldmxcsr %0" : : "m" (mxcsr);
}
@ -4847,7 +4855,7 @@ private:
{
uint old = FloatingPointControl.getControlState();
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);
}
@ -4860,7 +4868,7 @@ private:
else
{
uint newValues = 0x0;
asm pure nothrow @nogc
asm nothrow @nogc
{
"fsflags %0" : : "r" (newValues);
}
@ -4872,7 +4880,7 @@ private:
else
version (InlineAsm_X86_Any)
{
asm pure nothrow @nogc
asm nothrow @nogc
{
fnclex;
}
@ -4935,25 +4943,23 @@ public:
}
///
version (GNU)
{
// ieeeFlags test disabled, see LDC Issue #888.
}
else
version (IeeeFlagsUnittest)
@system unittest
{
static void func() {
int a = 10 * 10;
}
pragma(inline, false) static void blockopt(ref real x) {}
real a = 3.5;
// Set all the flags to zero
resetIeeeFlags();
assert(!ieeeFlags.divByZero);
blockopt(a); // avoid constant propagation by the optimizer
// Perform a division by zero.
a /= 0.0L;
assert(a == real.infinity);
assert(ieeeFlags.divByZero);
blockopt(a); // avoid constant propagation by the optimizer
// Create a NaN
a *= 0.0L;
assert(ieeeFlags.invalid);
@ -4966,11 +4972,7 @@ else
assert(ieeeFlags == f);
}
version (GNU)
{
// ieeeFlags test disabled, see LDC Issue #888.
}
else
version (IeeeFlagsUnittest)
@system unittest
{
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.
void resetIeeeFlags() @nogc { IeeeFlags.resetIeeeFlags(); }
@ -5047,6 +5028,12 @@ void resetIeeeFlags() @nogc { IeeeFlags.resetIeeeFlags(); }
return IeeeFlags(IeeeFlags.getIeeeFlags());
}
} // IeeeFlagsSupport
version (FloatingPointControlSupport)
{
/** Control the Floating point hardware
Change the IEEE754 floating-point rounding mode and the floating-point
@ -5418,7 +5405,10 @@ private:
// Clear all pending exceptions
static void clearExceptions() @nogc
{
version (IeeeFlagsSupport)
resetIeeeFlags();
else
static assert(false, "Not implemented for this architecture");
}
// Read from the control register
@ -5479,7 +5469,7 @@ private:
version (D_InlineAsm_X86)
{
short cont;
asm nothrow @nogc
asm pure nothrow @nogc
{
xor EAX, EAX;
fstcw cont;
@ -5490,7 +5480,7 @@ private:
version (D_InlineAsm_X86_64)
{
short cont;
asm nothrow @nogc
asm pure nothrow @nogc
{
xor RAX, RAX;
fstcw cont;
@ -5508,7 +5498,7 @@ private:
{
version (X86_Any)
{
asm pure nothrow @nogc
asm nothrow @nogc
{
"fclex; fldcw %0" : : "m" (newState);
}
@ -5517,7 +5507,7 @@ private:
if (haveSSE)
{
uint mxcsr;
asm pure nothrow @nogc
asm nothrow @nogc
{
"stmxcsr %0" : "=m" (mxcsr);
}
@ -5532,7 +5522,7 @@ private:
mxcsr &= ~(allExceptions << 7); // delete old masks
mxcsr |= (newState & allExceptions) << 7; // write new exception masks
asm pure nothrow @nogc
asm nothrow @nogc
{
"ldmxcsr %0" : : "m" (mxcsr);
}
@ -5540,7 +5530,7 @@ private:
}
else version (AArch64)
{
asm pure nothrow @nogc
asm nothrow @nogc
{
"msr FPCR, %0;" : : "r" (newState);
}
@ -5551,7 +5541,7 @@ private:
return;
else
{
asm pure nothrow @nogc
asm nothrow @nogc
{
"vmsr FPSCR, %0" : : "r" (newState);
}
@ -5563,7 +5553,7 @@ private:
return;
else
{
asm pure nothrow @nogc
asm nothrow @nogc
{
"fscsr %0" : : "r" (newState);
}
@ -5605,7 +5595,7 @@ private:
}
}
version (D_HardFloat) @system unittest
@system unittest
{
void ensureDefaults()
{
@ -5642,45 +5632,63 @@ version (D_HardFloat) @system unittest
ensureDefaults();
}
version (D_HardFloat) @system unittest // rounding
version (FloatingPointControlUnittest)
@system unittest // rounding
{
import std.meta : AliasSeq;
foreach (T; AliasSeq!(float, double, real))
{
/* 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.
*/
{
static T addRound(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 = 1;
u += 0.1;
fpctrl.rounding = FloatingPointControl.roundDown;
T d = 1;
d += 0.1;
fpctrl.rounding = FloatingPointControl.roundToZero;
T z = 1;
z += 0.1;
T u = addRound!(T)(FloatingPointControl.roundUp);
T d = addRound!(T)(FloatingPointControl.roundDown);
T z = addRound!(T)(FloatingPointControl.roundToZero);
assert(u > d);
assert(z == d);
}
fpctrl.rounding = FloatingPointControl.roundUp;
u = -1;
u -= 0.1;
{
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.roundDown;
d = -1;
d -= 0.1;
fpctrl.rounding = FloatingPointControl.roundToZero;
z = -1;
z -= 0.1;
T u = subRound!(T)(FloatingPointControl.roundUp);
T d = subRound!(T)(FloatingPointControl.roundDown);
T z = subRound!(T)(FloatingPointControl.roundToZero);
assert(u > d);
assert(z == u);
}
}
}
} // FloatingPointControlSupport
/*********************************