commit bf5d4596abea7fefd630b56a28c168ed2c2fe185
parent b19e1ecac90d85a895a8601af25e7c6949e0a395
Author: Alexey Yerin <yyp@disroot.org>
Date: Thu, 17 Mar 2022 22:18:06 +0300
rt,math+riscv64: implement fenv functions
With updated QBE and this in place, Hare now successfully builds on
RISC-V again!
Signed-off-by: Alexey Yerin <yyp@disroot.org>
Diffstat:
2 files changed, 238 insertions(+), 0 deletions(-)
diff --git a/math/fenv+riscv64.ha b/math/fenv+riscv64.ha
@@ -0,0 +1,195 @@
+// License: MPL-2.0
+// (c) 2022 Alexey Yerin <yyp@disroot.org>
+// (c) 2022 Bor Grošelj Simić <bor.groseljsimic@telemach.net>
+
+// 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 << 3,
+ // 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 << 1,
+ // 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 = 0b010,
+ // Round towards positive infinity
+ UPWARD = 0b011,
+ // Round towards zero
+ TOWARDZERO = 0b001,
+};
+
+@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/rt/+riscv64/fenv.s b/rt/+riscv64/fenv.s
@@ -0,0 +1,43 @@
+# License: MPL-2.0
+# (c) 2022 Alexey Yerin <yyp@disroot.org>
+
+.global rt.feclearexcept
+.type rt.feclearexcept,@function
+rt.feclearexcept:
+ and a0, a0, 0x1f
+ # fflags = fflags & ~a0
+ frcsr t0
+ not a0, a0
+ and t0, t0, a0
+ fscsr t0
+ ret
+
+.global rt.feraiseexcept
+.type rt.feraiseexcept,@function
+rt.feraiseexcept:
+ and a0, a0, 0x1f
+ # fflags = fflags | a0
+ frcsr t0
+ or t0, t0, a0
+ fscsr t0
+ ret
+
+.global rt.fesetround
+.type rt.fesetround,@function
+rt.fesetround:
+ fsrm a0
+ ret
+
+.global rt.fegetround
+.type rt.fegetround,@function
+rt.fegetround:
+ frrm a0
+ ret
+
+.global rt.fetestexcept
+.type rt.fetestexcept,@function
+rt.fetestexcept:
+ and a0, a0, 0x1f
+ frcsr t0
+ and a0, a0, t0
+ ret