[PATCH] target/alpha: do not trap on Inf inputs to bare FP arithmetic

Matt Turner posted 1 patch 8 hours ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260726005601.2513662-1-mattst88@gmail.com
Maintainers: Richard Henderson <richard.henderson@linaro.org>
target/alpha/fpu_helper.c       |   7 +-
tests/tcg/alpha/Makefile.target |   2 +-
tests/tcg/alpha/test-fp-ieee.c  | 173 ++++++++++++++++++++++++++++++++
3 files changed, 177 insertions(+), 5 deletions(-)
create mode 100644 tests/tcg/alpha/test-fp-ieee.c
[PATCH] target/alpha: do not trap on Inf inputs to bare FP arithmetic
Posted by Matt Turner 8 hours ago
helper_ieee_input is called for each operand of bare (non-/S) T-floating
arithmetic instructions.  It was raising EXC_M_INV for any value with
exp == 0x7ff, which covers both NaN and Infinity.

This is wrong for Infinity.  Infinity is a valid arithmetic operand;
operations like Inf + finite, Inf + Inf, or Inf * finite produce well-
defined IEEE results with no invalid-operation exception.  Only NaN
inputs and indeterminate forms (Inf - Inf, 0 * Inf) are invalid.

Indeterminate forms are already handled: the softfloat helpers set
float_flag_invalid in those cases, which soft_to_fpcr_exc converts to
FPCR_INV in env->error_code, and helper_fp_exc_raise then raises the
trap.  No pre-operation check for Inf is needed.

Change the exp == 0x7ff check to also require frac != 0, matching the
existing behavior of helper_ieee_input_cmp.

Fixes: b99e80694cc6 ("target-alpha: Raise EXC_M_INV properly for fp inputs")
Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 target/alpha/fpu_helper.c       |   7 +-
 tests/tcg/alpha/Makefile.target |   2 +-
 tests/tcg/alpha/test-fp-ieee.c  | 173 ++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 5 deletions(-)
 create mode 100644 tests/tcg/alpha/test-fp-ieee.c

diff --git ./target/alpha/fpu_helper.c ./target/alpha/fpu_helper.c
index 5b7e4bd5eb..6c26e3349f 100644
--- ./target/alpha/fpu_helper.c
+++ ./target/alpha/fpu_helper.c
@@ -105,8 +105,7 @@ void helper_fp_exc_raise_s(CPUAlphaState *env, uint32_t ignore, uint32_t regno)
     }
 }
 
-/* Input handing without software completion.  Trap for all
-   non-finite numbers.  */
+/* Input handling without software completion.  Trap for NaN and denormals.  */
 void helper_ieee_input(CPUAlphaState *env, uint64_t val)
 {
     uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
@@ -117,8 +116,8 @@ void helper_ieee_input(CPUAlphaState *env, uint64_t val)
         if (frac != 0) {
             arith_excp(env, GETPC(), EXC_M_INV, 0);
         }
-    } else if (exp == 0x7ff) {
-        /* Infinity or NaN.  */
+    } else if (exp == 0x7ff && frac) {
+        /* NaN.  */
         env->fpcr |= FPCR_INV;
         arith_excp(env, GETPC(), EXC_M_INV, 0);
     }
diff --git ./tests/tcg/alpha/Makefile.target ./tests/tcg/alpha/Makefile.target
index 36d8ed1eae..2636921aeb 100644
--- ./tests/tcg/alpha/Makefile.target
+++ ./tests/tcg/alpha/Makefile.target
@@ -5,7 +5,7 @@
 ALPHA_SRC=$(SRC_PATH)/tests/tcg/alpha
 VPATH+=$(ALPHA_SRC)
 
-ALPHA_TESTS=hello-alpha test-cond test-cmov test-ovf test-cvttq
+ALPHA_TESTS=hello-alpha test-cond test-cmov test-ovf test-cvttq test-fp-ieee
 TESTS+=$(ALPHA_TESTS)
 
 test-cmov: EXTRA_CFLAGS=-DTEST_CMOV
diff --git ./tests/tcg/alpha/test-fp-ieee.c ./tests/tcg/alpha/test-fp-ieee.c
new file mode 100644
index 0000000000..caeeaea7a3
--- /dev/null
+++ ./tests/tcg/alpha/test-fp-ieee.c
@@ -0,0 +1,173 @@
+/*
+ * Test IEEE T-floating (double) arithmetic exception behavior.
+ */
+#include <signal.h>
+#include <setjmp.h>
+#include <stdio.h>
+
+#define FPCR_INED       (1UL << 62)
+#define FPCR_UNFD       (1UL << 61)
+#define FPCR_DYN_NORMAL (2UL << 58)
+#define FPCR_IOV        (1UL << 57)
+#define FPCR_INE        (1UL << 56)
+#define FPCR_UNF        (1UL << 55)
+#define FPCR_OVF        (1UL << 54)
+#define FPCR_DZE        (1UL << 53)
+#define FPCR_INV        (1UL << 52)
+#define FPCR_OVFD       (1UL << 51)
+#define FPCR_DZED       (1UL << 50)
+#define FPCR_INVD       (1UL << 49)
+#define FPCR_STATUS_MASK (FPCR_IOV | FPCR_INE | FPCR_UNF \
+                          | FPCR_OVF | FPCR_DZE | FPCR_INV)
+
+static long run_addt_su(long *exc_out, double a, double b)
+{
+    unsigned long reset = FPCR_INED | FPCR_UNFD | FPCR_OVFD | FPCR_DZED
+                          | FPCR_INVD | FPCR_DYN_NORMAL;
+    double r;
+    long e;
+
+    asm ("excb\n\t"
+         "mt_fpcr %4\n\t"
+         "excb\n\t"
+         "addt/su %2,%3,%0\n\t"
+         "excb\n\t"
+         "mf_fpcr %1\n\t"
+         "excb\n\t"
+         : "=f"(r), "=f"(e)
+         : "f"(a), "f"(b), "f"(reset));
+
+    *exc_out = e & FPCR_STATUS_MASK;
+    long ri;
+    __builtin_memcpy(&ri, &r, 8);
+    return ri;
+}
+
+static long run_mult_su(long *exc_out, double a, double b)
+{
+    unsigned long reset = FPCR_INED | FPCR_UNFD | FPCR_OVFD | FPCR_DZED
+                          | FPCR_INVD | FPCR_DYN_NORMAL;
+    double r;
+    long e;
+
+    asm ("excb\n\t"
+         "mt_fpcr %4\n\t"
+         "excb\n\t"
+         "mult/su %2,%3,%0\n\t"
+         "excb\n\t"
+         "mf_fpcr %1\n\t"
+         "excb\n\t"
+         : "=f"(r), "=f"(e)
+         : "f"(a), "f"(b), "f"(reset));
+
+    *exc_out = e & FPCR_STATUS_MASK;
+    long ri;
+    __builtin_memcpy(&ri, &r, 8);
+    return ri;
+}
+
+static sigjmp_buf jmpbuf;
+static volatile int got_sigfpe;
+
+static void sigfpe_handler(int sig)
+{
+    got_sigfpe = 1;
+    siglongjmp(jmpbuf, 1);
+}
+
+static int test_bare_inf_add_finite(void)
+{
+    double a = __builtin_inf();
+    double b = 1.0;
+    double r = 0.0;
+
+    got_sigfpe = 0;
+    signal(SIGFPE, sigfpe_handler);
+    if (sigsetjmp(jmpbuf, 1) == 0) {
+        asm volatile ("addt %1,%2,%0" : "=f"(r) : "f"(a), "f"(b));
+    }
+    signal(SIGFPE, SIG_DFL);
+
+    if (got_sigfpe) {
+        printf("FAIL bare addt Inf+1.0: spurious SIGFPE\n");
+        return 1;
+    }
+    unsigned long ri;
+    __builtin_memcpy(&ri, &r, 8);
+    if (ri != 0x7ff0000000000000ul) {
+        printf("FAIL bare addt Inf+1.0: result %016lx (expected 7ff0000000000000)\n", ri);
+        return 1;
+    }
+    return 0;
+}
+
+static int test_su_inf_add_finite(void)
+{
+    double a = __builtin_inf();
+    double b = 1.0;
+    double r = 0.0;
+
+    got_sigfpe = 0;
+    signal(SIGFPE, sigfpe_handler);
+    if (sigsetjmp(jmpbuf, 1) == 0) {
+        asm volatile ("addt/su %1,%2,%0" : "=f"(r) : "f"(a), "f"(b));
+    }
+    signal(SIGFPE, SIG_DFL);
+
+    if (got_sigfpe) {
+        printf("FAIL addt/su Inf+1.0: spurious SIGFPE\n");
+        return 1;
+    }
+    unsigned long ri;
+    __builtin_memcpy(&ri, &r, 8);
+    if (ri != 0x7ff0000000000000ul) {
+        printf("FAIL addt/su Inf+1.0: result %016lx (expected 7ff0000000000000)\n", ri);
+        return 1;
+    }
+    return 0;
+}
+
+int main(void)
+{
+    static const struct {
+        double a, b;
+        long r;           /* expected result bit pattern; 0 when result is NaN */
+        long e;           /* expected FPCR exception bits */
+        long (*func)(long *, double, double);
+    } T[] = {
+        /* addt/su: Inf + finite = Inf, no exception */
+        { __builtin_inf(),  1.0,             0x7ff0000000000000l, 0,        run_addt_su },
+        /* addt/su: Inf + Inf = Inf, no exception */
+        { __builtin_inf(),  __builtin_inf(), 0x7ff0000000000000l, 0,        run_addt_su },
+        /* addt/su: Inf + (-Inf) = NaN, invalid operation */
+        { __builtin_inf(), -__builtin_inf(), 0,                   FPCR_INV, run_addt_su },
+        /* mult/su: Inf * finite = Inf, no exception */
+        { __builtin_inf(),  2.0,             0x7ff0000000000000l, 0,        run_mult_su },
+        /* mult/su: 0 * Inf = NaN, invalid operation */
+        { 0.0,              __builtin_inf(), 0,                   FPCR_INV, run_mult_su },
+    };
+
+    int i, err = 0;
+
+    err |= test_bare_inf_add_finite();
+    err |= test_su_inf_add_finite();
+
+    for (i = 0; i < sizeof(T) / sizeof(T[0]); i++) {
+        long e, r = T[i].func(&e, T[i].a, T[i].b);
+
+        /* NaN payload is implementation-defined; skip result check. */
+        int result_ok = (T[i].e & FPCR_INV) ? 1 : (r == T[i].r);
+        if (!result_ok || e != T[i].e) {
+            printf("Fail %a %a: expect (%016lx : %04lx) got (%016lx : %04lx)\n",
+                   T[i].a, T[i].b,
+                   T[i].r, T[i].e >> 48,
+                   r, e >> 48);
+            err = 1;
+        }
+    }
+
+    if (!err) {
+        puts("OK");
+    }
+    return err;
+}
-- 
2.54.0