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:
parent
7e5367f34d
commit
f1a6150ecb
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
/*********************************
|
||||
|
|
Loading…
Reference in New Issue