hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 30f4370c24502f80c84f681d366e25a6710a9269
parent 58498b4d24667dd39a342c2555424229feb0e636
Author: Bor Grošelj Simić <bor.groseljsimic@telemach.net>
Date:   Thu, 10 Feb 2022 01:32:50 +0100

math: floating point exceptions and rounding modes

Signed-off-by: Bor Grošelj Simić <bor.groseljsimic@telemach.net>

Diffstat:
Amath/fenv+aarch64.ha | 191+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amath/fenv+x86_64.ha | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amath/fenv_func.ha | 19+++++++++++++++++++
Art/+aarch64/fenv.s | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mrt/+test/run.ha | 5+++++
Art/+x86_64/fenv.s | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Art/fenv_defs.ha | 8++++++++
Mscripts/gen-stdlib | 16+++++++++++++++-
Mstdlib.mk | 20++++++++++++++++++++
9 files changed, 637 insertions(+), 1 deletion(-)

diff --git a/math/fenv+aarch64.ha b/math/fenv+aarch64.ha @@ -0,0 +1,191 @@ +// Defines flags characterizing types of floating point exceptions, +// Each of the flags is only defined when the target platform supports handling +// the corresponding exception. Flags NONE and ALL are always +// defined and correspond to a bitwise OR of none and all defined flags +// respectively. Platforms may define additional nonstandard flags. +// +// Examples: +// math::raiseexcept(math::fexcept::UNDERFLOW); // raise UNDERFLOW +// math::clearexcept(math::fexcept::ALL); // clear all exceptions +// +// // e will be math::fexcept::INVALID +// math::clearexcept(math::fexcept::ALL); +// let a = 0.0/0.0; +// let e = math::testexcept(math::fexcept::INVALID | math::fexcept::INEXACT); +export type fexcept = enum uint { + // No flags set + NONE = 0, + // Occurs when there is no well-defined result of an operation, such as + // with 0/0 or sqrt(-1) + INVALID = 1 << 0, + // Occurs when an operation on finite numbers produces infinity + DIVBYZERO = 1 << 1, + // Occurs when the result of an operation is much bigger (by + // absolute value) than the biggest representable finite number + OVERFLOW = 1 << 2, + // Occurs when the result of an operation is too small (by + // absolute value) to be stored as a normalized number + UNDERFLOW = 1 << 3, + // Occurs when the result of an operation is rounded to a + // value that differs from the infinite precision result. + INEXACT = 1 << 4, + // Combination of all flags + ALL = INVALID | DIVBYZERO | OVERFLOW | UNDERFLOW | INEXACT, +}; + +// Defines values characterizing different floating point rounding behaviors. +// Each of the values is only definined when the target platform supports the +// corresponding rounding mode. +export type fround = enum uint { + // Round towards nearest integer, with ties rounding to even + TONEAREST = 0, + // Round towards negative infinity + DOWNWARD = 0x800000, + // Round towards positive infinity + UPWARD = 0x400000, + // Round towards zero + TOWARDZERO = 0xC00000, +}; + +@test fn fexcept() void = { + assert(testexcept(fexcept::ALL) == fexcept::NONE); + assert(testexcept(fexcept::NONE) == fexcept::NONE); + + raiseexcept(fexcept::INEXACT | fexcept::DIVBYZERO); + + assert(testexcept(fexcept::INEXACT) == fexcept::INEXACT); + assert(testexcept(fexcept::DIVBYZERO) == fexcept::DIVBYZERO); + assert(testexcept(fexcept::UNDERFLOW) == fexcept::NONE); + assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT) + == fexcept::DIVBYZERO | fexcept::INEXACT); + assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT | fexcept::INVALID) + == fexcept::DIVBYZERO | fexcept::INEXACT); + + clearexcept(fexcept::INEXACT); + + assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT) == fexcept::DIVBYZERO); + + raiseexcept(fexcept::ALL); + + assert(testexcept(fexcept::ALL) == fexcept::ALL); + assert(testexcept(fexcept::NONE) == fexcept::NONE); + + clearexcept(fexcept::ALL); + + assert(testexcept(fexcept::ALL) == fexcept::NONE); + assert(testexcept(fexcept::NONE) == fexcept::NONE); +}; + +@test fn fround() void = { + // from musl's testsuite + let f = &f64frombits; + + assert(getround() == fround::TONEAREST); + assert(isnan(nearbyintf64(f(0x7ff8000000000000)))); + assert(isposinf(nearbyintf64(f(0x7ff0000000000000)))); + assert(isneginf(nearbyintf64(f(0xfff0000000000000)))); + assert(nearbyintf64(f(0x0)) == f(0x0)); + assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x0)); + assert(nearbyintf64(f(0xbfe0000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0001000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3feffff000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbfeffff000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x39b0000000000000)) == f(0x0)); + assert(nearbyintf64(f(0xb9b0000000000000)) == f(0x8000000000000000)); + + setround(fround::DOWNWARD); + assert(getround() == fround::DOWNWARD); + assert(isnan(nearbyintf64(f(0x7ff8000000000000)))); + assert(isposinf(nearbyintf64(f(0x7ff0000000000000)))); + assert(isneginf(nearbyintf64(f(0xfff0000000000000)))); + assert(nearbyintf64(f(0x0)) == f(0x0)); + assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x0)); + assert(nearbyintf64(f(0xbfe0000000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0001000000000)) == f(0xc000000000000000)); + assert(nearbyintf64(f(0x3feffff000000000)) == f(0x0)); + assert(nearbyintf64(f(0xbfeffff000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x39b0000000000000)) == f(0x0)); + assert(nearbyintf64(f(0xb9b0000000000000)) == f(0xbff0000000000000)); + + setround(fround::UPWARD); + assert(getround() == fround::UPWARD); + assert(isnan(nearbyintf64(f(0x7ff8000000000000)))); + assert(isposinf(nearbyintf64(f(0x7ff0000000000000)))); + assert(isneginf(nearbyintf64(f(0xfff0000000000000)))); + assert(nearbyintf64(f(0x0)) == f(0x0)); + assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbfe0000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x4000000000000000)); + assert(nearbyintf64(f(0xbff0001000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3feffff000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbfeffff000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x39b0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xb9b0000000000000)) == f(0x8000000000000000)); + + let f = &f32frombits; + + setround(fround::TONEAREST); + assert(getround() == fround::TONEAREST); + assert(isnan(nearbyintf32(f(0x7fc00000)))); + assert(isposinf(nearbyintf32(f(0x7f800000)))); + assert(isneginf(nearbyintf32(f(0xff800000)))); + assert(nearbyintf32(f(0x0)) == f(0x0)); + assert(nearbyintf32(f(0x80000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f000000)) == f(0x0)); + assert(nearbyintf32(f(0xbf000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800080)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800080)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f7fff80)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf7fff80)) == f(0xbf800000)); + assert(nearbyintf32(f(0xd800000)) == f(0x0)); + assert(nearbyintf32(f(0x8d800000)) == f(0x80000000)); + + setround(fround::DOWNWARD); + assert(getround() == fround::DOWNWARD); + assert(isnan(nearbyintf32(f(0x7fc00000)))); + assert(isposinf(nearbyintf32(f(0x7f800000)))); + assert(isneginf(nearbyintf32(f(0xff800000)))); + assert(nearbyintf32(f(0x0)) == f(0x0)); + assert(nearbyintf32(f(0x80000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f000000)) == f(0x0)); + assert(nearbyintf32(f(0xbf000000)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f800080)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800080)) == f(0xc0000000)); + assert(nearbyintf32(f(0x3f7fff80)) == f(0x0)); + assert(nearbyintf32(f(0xbf7fff80)) == f(0xbf800000)); + assert(nearbyintf32(f(0xd800000)) == f(0x0)); + assert(nearbyintf32(f(0x8d800000)) == f(0xbf800000)); + + setround(fround::UPWARD); + assert(getround() == fround::UPWARD); + assert(isnan(nearbyintf32(f(0x7fc00000)))); + assert(isposinf(nearbyintf32(f(0x7f800000)))); + assert(isneginf(nearbyintf32(f(0xff800000)))); + assert(nearbyintf32(f(0x0)) == f(0x0)); + assert(nearbyintf32(f(0x80000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f000000)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800080)) == f(0x40000000)); + assert(nearbyintf32(f(0xbf800080)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f7fff80)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf7fff80)) == f(0x80000000)); + assert(nearbyintf32(f(0xd800000)) == f(0x3f800000)); + assert(nearbyintf32(f(0x8d800000)) == f(0x80000000)); +}; diff --git a/math/fenv+x86_64.ha b/math/fenv+x86_64.ha @@ -0,0 +1,192 @@ +// Defines flags characterizing types of floating point exceptions, +// Each of the flags is only defined when the target platform supports handling +// the corresponding exception. Flags NONE and ALL are always +// defined and correspond to a bitwise OR of none and all defined flags +// respectively. Platforms may define additional nonstandard flags. +// +// Examples: +// math::raiseexcept(math::fexcept::UNDERFLOW); // raise UNDERFLOW +// math::clearexcept(math::fexcept::ALL); // clear all exceptions +// +// // e will be math::fexcept::INVALID +// math::clearexcept(math::fexcept::ALL); +// let a = 0.0/0.0; +// let e = math::testexcept(math::fexcept::INVALID | math::fexcept::INEXACT); +export type fexcept = enum uint { + // No flags set + NONE = 0, + // Occurs when there is no well-defined result of an operation, such as + // with 0/0 or sqrt(-1) + INVALID = 1 << 0, + __DENORM = 1 << 1, // arch-specific + // Occurs when an operation on finite numbers produces infinity + DIVBYZERO = 1 << 2, + // Occurs when the result of an operation is much bigger (by + // absolute value) than the biggest representable finite number + OVERFLOW = 1 << 3, + // Occurs when the result of an operation is too small (by + // absolute value) to be stored as a normalized number + UNDERFLOW = 1 << 4, + // Occurs when the result of an operation is rounded to a + // value that differs from the infinite precision result. + INEXACT = 1 << 5, + // Combination of all flags + ALL = INVALID | __DENORM | DIVBYZERO | OVERFLOW | UNDERFLOW | INEXACT, +}; + +// Defines values characterizing different floating point rounding behaviors. +// Each of the values is only definined when the target platform supports the +// corresponding rounding mode. +export type fround = enum uint { + // Round towards nearest integer, with ties rounding to even + TONEAREST = 0, + // Round towards negative infinity + DOWNWARD = 0x400, + // Round towards positive infinity + UPWARD = 0x800, + // Round towards zero + TOWARDZERO = 0xC00, +}; + +@test fn fexcept() void = { + assert(testexcept(fexcept::ALL) == fexcept::NONE); + assert(testexcept(fexcept::NONE) == fexcept::NONE); + + raiseexcept(fexcept::INEXACT | fexcept::DIVBYZERO); + + assert(testexcept(fexcept::INEXACT) == fexcept::INEXACT); + assert(testexcept(fexcept::DIVBYZERO) == fexcept::DIVBYZERO); + assert(testexcept(fexcept::UNDERFLOW) == fexcept::NONE); + assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT) + == fexcept::DIVBYZERO | fexcept::INEXACT); + assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT | fexcept::INVALID) + == fexcept::DIVBYZERO | fexcept::INEXACT); + + clearexcept(fexcept::INEXACT); + + assert(testexcept(fexcept::DIVBYZERO | fexcept::INEXACT) == fexcept::DIVBYZERO); + + raiseexcept(fexcept::ALL); + + assert(testexcept(fexcept::ALL) == fexcept::ALL); + assert(testexcept(fexcept::NONE) == fexcept::NONE); + + clearexcept(fexcept::ALL); + + assert(testexcept(fexcept::ALL) == fexcept::NONE); + assert(testexcept(fexcept::NONE) == fexcept::NONE); +}; + +@test fn fround() void = { + // from musl's testsuite + let f = &f64frombits; + + assert(getround() == fround::TONEAREST); + assert(isnan(nearbyintf64(f(0x7ff8000000000000)))); + assert(isposinf(nearbyintf64(f(0x7ff0000000000000)))); + assert(isneginf(nearbyintf64(f(0xfff0000000000000)))); + assert(nearbyintf64(f(0x0)) == f(0x0)); + assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x0)); + assert(nearbyintf64(f(0xbfe0000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0001000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3feffff000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbfeffff000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x39b0000000000000)) == f(0x0)); + assert(nearbyintf64(f(0xb9b0000000000000)) == f(0x8000000000000000)); + + setround(fround::DOWNWARD); + assert(getround() == fround::DOWNWARD); + assert(isnan(nearbyintf64(f(0x7ff8000000000000)))); + assert(isposinf(nearbyintf64(f(0x7ff0000000000000)))); + assert(isneginf(nearbyintf64(f(0xfff0000000000000)))); + assert(nearbyintf64(f(0x0)) == f(0x0)); + assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x0)); + assert(nearbyintf64(f(0xbfe0000000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0001000000000)) == f(0xc000000000000000)); + assert(nearbyintf64(f(0x3feffff000000000)) == f(0x0)); + assert(nearbyintf64(f(0xbfeffff000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x39b0000000000000)) == f(0x0)); + assert(nearbyintf64(f(0xb9b0000000000000)) == f(0xbff0000000000000)); + + setround(fround::UPWARD); + assert(getround() == fround::UPWARD); + assert(isnan(nearbyintf64(f(0x7ff8000000000000)))); + assert(isposinf(nearbyintf64(f(0x7ff0000000000000)))); + assert(isneginf(nearbyintf64(f(0xfff0000000000000)))); + assert(nearbyintf64(f(0x0)) == f(0x0)); + assert(nearbyintf64(f(0x8000000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbff0000000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3fe0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbfe0000000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x3ff0001000000000)) == f(0x4000000000000000)); + assert(nearbyintf64(f(0xbff0001000000000)) == f(0xbff0000000000000)); + assert(nearbyintf64(f(0x3feffff000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xbfeffff000000000)) == f(0x8000000000000000)); + assert(nearbyintf64(f(0x39b0000000000000)) == f(0x3ff0000000000000)); + assert(nearbyintf64(f(0xb9b0000000000000)) == f(0x8000000000000000)); + + let f = &f32frombits; + + setround(fround::TONEAREST); + assert(getround() == fround::TONEAREST); + assert(isnan(nearbyintf32(f(0x7fc00000)))); + assert(isposinf(nearbyintf32(f(0x7f800000)))); + assert(isneginf(nearbyintf32(f(0xff800000)))); + assert(nearbyintf32(f(0x0)) == f(0x0)); + assert(nearbyintf32(f(0x80000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f000000)) == f(0x0)); + assert(nearbyintf32(f(0xbf000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800080)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800080)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f7fff80)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf7fff80)) == f(0xbf800000)); + assert(nearbyintf32(f(0xd800000)) == f(0x0)); + assert(nearbyintf32(f(0x8d800000)) == f(0x80000000)); + + setround(fround::DOWNWARD); + assert(getround() == fround::DOWNWARD); + assert(isnan(nearbyintf32(f(0x7fc00000)))); + assert(isposinf(nearbyintf32(f(0x7f800000)))); + assert(isneginf(nearbyintf32(f(0xff800000)))); + assert(nearbyintf32(f(0x0)) == f(0x0)); + assert(nearbyintf32(f(0x80000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f000000)) == f(0x0)); + assert(nearbyintf32(f(0xbf000000)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f800080)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800080)) == f(0xc0000000)); + assert(nearbyintf32(f(0x3f7fff80)) == f(0x0)); + assert(nearbyintf32(f(0xbf7fff80)) == f(0xbf800000)); + assert(nearbyintf32(f(0xd800000)) == f(0x0)); + assert(nearbyintf32(f(0x8d800000)) == f(0xbf800000)); + + setround(fround::UPWARD); + assert(getround() == fround::UPWARD); + assert(isnan(nearbyintf32(f(0x7fc00000)))); + assert(isposinf(nearbyintf32(f(0x7f800000)))); + assert(isneginf(nearbyintf32(f(0xff800000)))); + assert(nearbyintf32(f(0x0)) == f(0x0)); + assert(nearbyintf32(f(0x80000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800000)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf800000)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f000000)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf000000)) == f(0x80000000)); + assert(nearbyintf32(f(0x3f800080)) == f(0x40000000)); + assert(nearbyintf32(f(0xbf800080)) == f(0xbf800000)); + assert(nearbyintf32(f(0x3f7fff80)) == f(0x3f800000)); + assert(nearbyintf32(f(0xbf7fff80)) == f(0x80000000)); + assert(nearbyintf32(f(0xd800000)) == f(0x3f800000)); + assert(nearbyintf32(f(0x8d800000)) == f(0x80000000)); +}; diff --git a/math/fenv_func.ha b/math/fenv_func.ha @@ -0,0 +1,19 @@ +use rt; + +// Accepts a set of flags from [[fexcept]] ORed together and clears +// exceptions corresponding to the given flags. +export fn clearexcept(ex: fexcept) void = rt::feclearexcept(ex: uint); + +// Accepts a set of flags from [[fexcept]] ORed together and raises +// exceptions corresponding to the given flags. +export fn raiseexcept(ex: fexcept) void = rt::feraiseexcept(ex: uint); + +// Accepts a set of flags from [[except]] ORed together and returns +// the subset that is currently raised +export fn testexcept(ex: fexcept) fexcept = rt::fetestexcept(ex: uint): fexcept; + +// Returns the value corresponding to the current rounding mode +export fn getround() fround = rt::fegetround(): fround; + +// Sets the rounding mode to the specified value +export fn setround(mode: fround) void = rt::fesetround(mode: uint); diff --git a/rt/+aarch64/fenv.s b/rt/+aarch64/fenv.s @@ -0,0 +1,92 @@ +# This file is vendored from musl and is licensed under MIT license: +# +# ---------------------------------------------------------------------- +# Copyright © 2005-2020 Rich Felker, et al. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# ---------------------------------------------------------------------- + +.global rt.fegetround +.type rt.fegetround,%function +rt.fegetround: + mrs x0, fpcr + and w0, w0, #0xc00000 + ret + +.global rt.fesetround +.type rt.fesetround,%function +rt.fesetround: + mrs x1, fpcr + bic w1, w1, #0xc00000 + orr w1, w1, w0 + msr fpcr, x1 + mov w0, #0 + ret + +.global rt.fetestexcept +.type rt.fetestexcept,%function +rt.fetestexcept: + and w0, w0, #0x1f + mrs x1, fpsr + and w0, w0, w1 + ret + +.global rt.feclearexcept +.type rt.feclearexcept,%function +rt.feclearexcept: + and w0, w0, #0x1f + mrs x1, fpsr + bic w1, w1, w0 + msr fpsr, x1 + mov w0, #0 + ret + +.global rt.feraiseexcept +.type rt.feraiseexcept,%function +rt.feraiseexcept: + and w0, w0, #0x1f + mrs x1, fpsr + orr w1, w1, w0 + msr fpsr, x1 + mov w0, #0 + ret + +.global rt.fegetenv +.type rt.fegetenv,%function +rt.fegetenv: + mrs x1, fpcr + mrs x2, fpsr + stp w1, w2, [x0] + mov w0, #0 + ret + +// TODO preserve some bits +.global rt.fesetenv +.type rt.fesetenv,%function +rt.fesetenv: + mov x1, #0 + mov x2, #0 + cmn x0, #1 + b.eq 1f + ldp w1, w2, [x0] +1: msr fpcr, x1 + msr fpsr, x2 + mov w0, #0 + ret diff --git a/rt/+test/run.ha b/rt/+test/run.ha @@ -25,6 +25,7 @@ export fn tests_main() size = { let failures: [](str, abort_reason) = []; let npass = 0z, nfail = 0z; + let default_round = fegetround(); print("Running "); print(ztos(ntest)); print(" tests:\n\n"); @@ -43,6 +44,10 @@ export fn tests_main() size = { print("FAIL\n"); continue; }; + + fesetround(default_round); + feclearexcept(~0u); + test_start[i].func(); npass += 1; diff --git a/rt/+x86_64/fenv.s b/rt/+x86_64/fenv.s @@ -0,0 +1,95 @@ +# This file is vendored from musl and is licensed under MIT license: +# +# ---------------------------------------------------------------------- +# Copyright © 2005-2020 Rich Felker, et al. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# ---------------------------------------------------------------------- + +.global rt.feclearexcept +.type rt.feclearexcept,@function +rt.feclearexcept: + # maintain exceptions in the sse mxcsr, clear x87 exceptions + mov %edi,%ecx + and $0x3f,%ecx + fnstsw %ax + test %eax,%ecx + jz 1f + fnclex +1: stmxcsr -8(%rsp) + and $0x3f,%eax + or %eax,-8(%rsp) + test %ecx,-8(%rsp) + jz 1f + not %ecx + and %ecx,-8(%rsp) + ldmxcsr -8(%rsp) +1: xor %eax,%eax + ret + +.global rt.feraiseexcept +.type rt.feraiseexcept,@function +rt.feraiseexcept: + and $0x3f,%edi + stmxcsr -8(%rsp) + or %edi,-8(%rsp) + ldmxcsr -8(%rsp) + xor %eax,%eax + ret + +.global rt.fesetround +.type rt.fesetround,@function +rt.fesetround: + push %rax + xor %eax,%eax + mov %edi,%ecx + fnstcw (%rsp) + andb $0xf3,1(%rsp) + or %ch,1(%rsp) + fldcw (%rsp) + stmxcsr (%rsp) + shl $3,%ch + andb $0x9f,1(%rsp) + or %ch,1(%rsp) + ldmxcsr (%rsp) + pop %rcx + ret + +.global rt.fegetround +.type rt.fegetround,@function +rt.fegetround: + push %rax + stmxcsr (%rsp) + pop %rax + shr $3,%eax + and $0xc00,%eax + ret + +.global rt.fetestexcept +.type rt.fetestexcept,@function +rt.fetestexcept: + and $0x3f,%edi + push %rax + stmxcsr (%rsp) + pop %rsi + fnstsw %ax + or %esi,%eax + and %edi,%eax + ret diff --git a/rt/fenv_defs.ha b/rt/fenv_defs.ha @@ -0,0 +1,8 @@ +export fn feclearexcept(ex: uint) void; +export fn feraiseexcept(ex: uint) void; +export fn fetestexcept(ex: uint) uint; + +export fn fegetround() uint; +export fn fesetround(mode: uint) void; + +// TODO fesetenv/fegetenv? diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -34,6 +34,7 @@ gensrcs_rt() { +linux/socket.ha \ '+$(ARCH)'/jmp.ha \ '+$(ARCH)'/backtrace.ha \ + fenv_defs.ha \ ensure.ha \ jmp.ha \ malloc.ha \ @@ -55,6 +56,7 @@ gensrcs_rt() { +freebsd/types.ha \ +'$(ARCH)'/jmp.ha \ +'$(ARCH)'/backtrace.ha \ + fenv_defs.ha \ ensure.ha \ jmp.ha \ malloc.ha \ @@ -95,6 +97,7 @@ ${stdlib}_asm=\$($cache)/rt/syscall.o \\ \$($cache)/rt/longjmp.o \\ \$($cache)/rt/restore.o \\ \$($cache)/rt/getfp.o \\ + \$($cache)/rt/fenv.o \\ \$($cache)/rt/start.o \$($cache)/rt/syscall.o: \$(STDLIB)/rt/+\$(PLATFORM)/syscall+\$(ARCH).s @@ -117,6 +120,11 @@ ${stdlib}_asm=\$($cache)/rt/syscall.o \\ @mkdir -p \$($cache)/rt @\$(AS) -o \$@ \$< +\$($cache)/rt/fenv.o: \$(STDLIB)/rt/+\$(ARCH)/fenv.s + @printf 'AS \t\$@\n' + @mkdir -p \$($cache)/rt + @\$(AS) -o \$@ \$< + \$($cache)/rt/getfp.o: \$(STDLIB)/rt/+\$(ARCH)/getfp.s @printf 'AS \t\$@\n' @mkdir -p \$($cache)/rt @@ -786,7 +794,13 @@ linux_vdso() { gensrcs_math() { gen_srcs math \ - math.ha floats.ha ints.ha uints.ha trig.ha \ + math.ha \ + fenv_func.ha \ + 'fenv+$(ARCH).ha' \ + floats.ha \ + ints.ha \ + uints.ha \ + trig.ha \ $* } diff --git a/stdlib.mk b/stdlib.mk @@ -18,6 +18,7 @@ stdlib_rt_linux_srcs= \ $(STDLIB)/rt/+linux/socket.ha \ $(STDLIB)/rt/+$(ARCH)/jmp.ha \ $(STDLIB)/rt/+$(ARCH)/backtrace.ha \ + $(STDLIB)/rt/fenv_defs.ha \ $(STDLIB)/rt/ensure.ha \ $(STDLIB)/rt/jmp.ha \ $(STDLIB)/rt/malloc.ha \ @@ -42,6 +43,7 @@ stdlib_rt_freebsd_srcs= \ $(STDLIB)/rt/+freebsd/types.ha \ $(STDLIB)/rt/+$(ARCH)/jmp.ha \ $(STDLIB)/rt/+$(ARCH)/backtrace.ha \ + $(STDLIB)/rt/fenv_defs.ha \ $(STDLIB)/rt/ensure.ha \ $(STDLIB)/rt/jmp.ha \ $(STDLIB)/rt/malloc.ha \ @@ -74,6 +76,7 @@ stdlib_asm=$(HARECACHE)/rt/syscall.o \ $(HARECACHE)/rt/longjmp.o \ $(HARECACHE)/rt/restore.o \ $(HARECACHE)/rt/getfp.o \ + $(HARECACHE)/rt/fenv.o \ $(HARECACHE)/rt/start.o $(HARECACHE)/rt/syscall.o: $(STDLIB)/rt/+$(PLATFORM)/syscall+$(ARCH).s @@ -96,6 +99,11 @@ $(HARECACHE)/rt/restore.o: $(STDLIB)/rt/+$(ARCH)/restore.s @mkdir -p $(HARECACHE)/rt @$(AS) -o $@ $< +$(HARECACHE)/rt/fenv.o: $(STDLIB)/rt/+$(ARCH)/fenv.s + @printf 'AS \t$@\n' + @mkdir -p $(HARECACHE)/rt + @$(AS) -o $@ $< + $(HARECACHE)/rt/getfp.o: $(STDLIB)/rt/+$(ARCH)/getfp.s @printf 'AS \t$@\n' @mkdir -p $(HARECACHE)/rt @@ -1307,6 +1315,8 @@ $(HARECACHE)/linux/vdso/linux_vdso-linux.ssa: $(stdlib_linux_vdso_linux_srcs) $( # math (+any) stdlib_math_any_srcs= \ $(STDLIB)/math/math.ha \ + $(STDLIB)/math/fenv_func.ha \ + $(STDLIB)/math/fenv+$(ARCH).ha \ $(STDLIB)/math/floats.ha \ $(STDLIB)/math/ints.ha \ $(STDLIB)/math/uints.ha \ @@ -1826,6 +1836,7 @@ testlib_rt_linux_srcs= \ $(STDLIB)/rt/+linux/socket.ha \ $(STDLIB)/rt/+$(ARCH)/jmp.ha \ $(STDLIB)/rt/+$(ARCH)/backtrace.ha \ + $(STDLIB)/rt/fenv_defs.ha \ $(STDLIB)/rt/ensure.ha \ $(STDLIB)/rt/jmp.ha \ $(STDLIB)/rt/malloc.ha \ @@ -1854,6 +1865,7 @@ testlib_rt_freebsd_srcs= \ $(STDLIB)/rt/+freebsd/types.ha \ $(STDLIB)/rt/+$(ARCH)/jmp.ha \ $(STDLIB)/rt/+$(ARCH)/backtrace.ha \ + $(STDLIB)/rt/fenv_defs.ha \ $(STDLIB)/rt/ensure.ha \ $(STDLIB)/rt/jmp.ha \ $(STDLIB)/rt/malloc.ha \ @@ -1890,6 +1902,7 @@ testlib_asm=$(TESTCACHE)/rt/syscall.o \ $(TESTCACHE)/rt/longjmp.o \ $(TESTCACHE)/rt/restore.o \ $(TESTCACHE)/rt/getfp.o \ + $(TESTCACHE)/rt/fenv.o \ $(TESTCACHE)/rt/start.o $(TESTCACHE)/rt/syscall.o: $(STDLIB)/rt/+$(PLATFORM)/syscall+$(ARCH).s @@ -1912,6 +1925,11 @@ $(TESTCACHE)/rt/restore.o: $(STDLIB)/rt/+$(ARCH)/restore.s @mkdir -p $(TESTCACHE)/rt @$(AS) -o $@ $< +$(TESTCACHE)/rt/fenv.o: $(STDLIB)/rt/+$(ARCH)/fenv.s + @printf 'AS \t$@\n' + @mkdir -p $(TESTCACHE)/rt + @$(AS) -o $@ $< + $(TESTCACHE)/rt/getfp.o: $(STDLIB)/rt/+$(ARCH)/getfp.s @printf 'AS \t$@\n' @mkdir -p $(TESTCACHE)/rt @@ -3158,6 +3176,8 @@ $(TESTCACHE)/linux/vdso/linux_vdso-linux.ssa: $(testlib_linux_vdso_linux_srcs) $ # math (+any) testlib_math_any_srcs= \ $(STDLIB)/math/math.ha \ + $(STDLIB)/math/fenv_func.ha \ + $(STDLIB)/math/fenv+$(ARCH).ha \ $(STDLIB)/math/floats.ha \ $(STDLIB)/math/ints.ha \ $(STDLIB)/math/uints.ha \