ldc icon indicating copy to clipboard operation
ldc copied to clipboard

std.math ieeeFlags cannot be reliably tested

Open smolt opened this issue 10 years ago • 2 comments

The compiler can unexpectedly rearrange floating point operations and access to the floating point status flags when optimizing. This means std.math ieeeFlags cannot be reliably checked in optimized code. Consider:

void main()
{
    import std.math;
    static real zero = 0.0;
    real x = 3.5;

    resetIeeeFlags();
    assert(!ieeeFlags.invalid);

    x /= zero;
    x *= zero;
    assert(ieeeFlags.invalid);  // optimizer may moves this before div and mult
    assert(isNaN(x));
}

Here, optimizer moves function call ieeeFlags() before math ops, and invalid is not set as expected. This has been observed with LLVM 3.6 targeting arm and x86-64, and other combinations may be affected to. Right now it is cause a new 2.067 unittest in std.math to fail.

C99 invented "#pragma STDC FENV_ACCESS ON" to prevent optimizer from reordering instructions that affect float environment. See note [2] here: http://en.wikipedia.org/wiki/C99#Example

clang does not support this pragma and LLVM IR has no way of expressing: https://llvm.org/bugs/show_bug.cgi?id=10409

This also could be an issue for gdc as this reference says gcc doesn't support pragma FENV_ACCESS: http://wiki.musl-libc.org/wiki/Mathematical_Library#Fenv_and_error_handling

Work around in C is to use volatile vars to force ordering and libm.h has a FORCE_EVAL macro to do that.

smolt avatar Apr 05 '15 05:04 smolt

This has been largely worked around via the really handy @optStrategy("none") UDA in the meantime. There's still 1-2 disabled std.math tests, IIRC because the UDA cannot be applied to delegate literals.

kinke avatar May 12 '18 23:05 kinke

LLVM have support for the aforementioned pragma: https://github.com/llvm/llvm-project/commit/2e204e23911b1f8bd1463535da40c6e48747a138

Geod24 avatar Oct 20 '21 06:10 Geod24

This change to the unittests should help on that front. https://github.com/dlang/phobos/pull/8642

Taking the original code example, the tests have now been rewritten to:

void main()
{
    import std.math;
    static real zero = 0.0;
    real x = 3.5;

    resetIeeeFlags();
    assert(!ieeeFlags.invalid);

    x = forceDiv(x, zero);  // pragma(inline, false) T forceDiv(T)
    x = forceMul(x, zero); // pragma(inline, false) T forceMul(T)
    assert(ieeeFlags.invalid);
    assert(isNaN(x));
}

ibuclaw avatar Dec 03 '22 08:12 ibuclaw