summary refs log tree commit diff stats
path: root/target/arm/vfp_helper.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2025-02-21 19:09:55 +0000
committerPeter Maydell <peter.maydell@linaro.org>2025-02-25 15:32:58 +0000
commitb9d3dc45532e696f5ee566edd227a4f46bad0f35 (patch)
treed36bc6e93361c98de9d8289bc144d0c1a59ecae8 /target/arm/vfp_helper.c
parente34cfba5e8d7bd631398a09d658dee40b1aef085 (diff)
downloadfocaccia-qemu-b9d3dc45532e696f5ee566edd227a4f46bad0f35.tar.gz
focaccia-qemu-b9d3dc45532e696f5ee566edd227a4f46bad0f35.zip
target/arm: Move softfloat specific FPCR/FPSR handling to tcg/
The softfloat (i.e. TCG) specific handling for the FPCR
and FPSR is abstracted behind five functions:
 arm_set_default_fp_behaviours
 arm_set_ah_fp_behaviours
 vfp_get_fpsr_from_host
 vfp_clear_float_status_exc_flags
 vfp_set_fpsr_to_host

Currently we rely on the first two calling softfloat functions that
work even in a KVM-only compile because they're defined as inline in
the softfloat header file, and we provide stub versions of the last
three in arm/vfp_helper.c if CONFIG_TCG isn't defined.

Move the softfloat-specific versions of these functions to
tcg/vfp_helper.c, and provide the non-TCG stub versions in
tcg-stubs.c.

This lets us drop the softfloat header include and the last
set of CONFIG_TCG ifdefs from arm/vfp_helper.c.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20250221190957.811948-4-peter.maydell@linaro.org
Diffstat (limited to 'target/arm/vfp_helper.c')
-rw-r--r--target/arm/vfp_helper.c248
1 files changed, 0 insertions, 248 deletions
diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c
index 0919acb7b8..cc0f055ef0 100644
--- a/target/arm/vfp_helper.c
+++ b/target/arm/vfp_helper.c
@@ -21,254 +21,6 @@
 #include "cpu.h"
 #include "internals.h"
 #include "cpu-features.h"
-#include "fpu/softfloat.h"
-
-/*
- * Set the float_status behaviour to match the Arm defaults:
- *  * tininess-before-rounding
- *  * 2-input NaN propagation prefers SNaN over QNaN, and then
- *    operand A over operand B (see FPProcessNaNs() pseudocode)
- *  * 3-input NaN propagation prefers SNaN over QNaN, and then
- *    operand C over A over B (see FPProcessNaNs3() pseudocode,
- *    but note that for QEMU muladd is a * b + c, whereas for
- *    the pseudocode function the arguments are in the order c, a, b.
- *  * 0 * Inf + NaN returns the default NaN if the input NaN is quiet,
- *    and the input NaN if it is signalling
- *  * Default NaN has sign bit clear, msb frac bit set
- */
-void arm_set_default_fp_behaviours(float_status *s)
-{
-    set_float_detect_tininess(float_tininess_before_rounding, s);
-    set_float_ftz_detection(float_ftz_before_rounding, s);
-    set_float_2nan_prop_rule(float_2nan_prop_s_ab, s);
-    set_float_3nan_prop_rule(float_3nan_prop_s_cab, s);
-    set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, s);
-    set_float_default_nan_pattern(0b01000000, s);
-}
-
-/*
- * Set the float_status behaviour to match the FEAT_AFP
- * FPCR.AH=1 requirements:
- *  * tininess-after-rounding
- *  * 2-input NaN propagation prefers the first NaN
- *  * 3-input NaN propagation prefers a over b over c
- *  * 0 * Inf + NaN always returns the input NaN and doesn't
- *    set Invalid for a QNaN
- *  * default NaN has sign bit set, msb frac bit set
- */
-void arm_set_ah_fp_behaviours(float_status *s)
-{
-    set_float_detect_tininess(float_tininess_after_rounding, s);
-    set_float_ftz_detection(float_ftz_after_rounding, s);
-    set_float_2nan_prop_rule(float_2nan_prop_ab, s);
-    set_float_3nan_prop_rule(float_3nan_prop_abc, s);
-    set_float_infzeronan_rule(float_infzeronan_dnan_never |
-                              float_infzeronan_suppress_invalid, s);
-    set_float_default_nan_pattern(0b11000000, s);
-}
-
-#ifdef CONFIG_TCG
-
-/* Convert host exception flags to vfp form.  */
-static inline uint32_t vfp_exceptbits_from_host(int host_bits, bool ah)
-{
-    uint32_t target_bits = 0;
-
-    if (host_bits & float_flag_invalid) {
-        target_bits |= FPSR_IOC;
-    }
-    if (host_bits & float_flag_divbyzero) {
-        target_bits |= FPSR_DZC;
-    }
-    if (host_bits & float_flag_overflow) {
-        target_bits |= FPSR_OFC;
-    }
-    if (host_bits & (float_flag_underflow | float_flag_output_denormal_flushed)) {
-        target_bits |= FPSR_UFC;
-    }
-    if (host_bits & float_flag_inexact) {
-        target_bits |= FPSR_IXC;
-    }
-    if (host_bits & float_flag_input_denormal_flushed) {
-        target_bits |= FPSR_IDC;
-    }
-    /*
-     * With FPCR.AH, IDC is set when an input denormal is used,
-     * and flushing an output denormal to zero sets both IXC and UFC.
-     */
-    if (ah && (host_bits & float_flag_input_denormal_used)) {
-        target_bits |= FPSR_IDC;
-    }
-    if (ah && (host_bits & float_flag_output_denormal_flushed)) {
-        target_bits |= FPSR_IXC;
-    }
-    return target_bits;
-}
-
-static uint32_t vfp_get_fpsr_from_host(CPUARMState *env)
-{
-    uint32_t a32_flags = 0, a64_flags = 0;
-
-    a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A32]);
-    a32_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_STD]);
-    /* FZ16 does not generate an input denormal exception.  */
-    a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A32_F16])
-          & ~float_flag_input_denormal_flushed);
-    a32_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_STD_F16])
-          & ~float_flag_input_denormal_flushed);
-
-    a64_flags |= get_float_exception_flags(&env->vfp.fp_status[FPST_A64]);
-    a64_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A64_F16])
-          & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used));
-    /*
-     * We do not merge in flags from FPST_AH or FPST_AH_F16, because
-     * they are used for insns that must not set the cumulative exception bits.
-     */
-
-    /*
-     * Flushing an input denormal *only* because FPCR.FIZ == 1 does
-     * not set FPSR.IDC; if FPCR.FZ is also set then this takes
-     * precedence and IDC is set (see the FPUnpackBase pseudocode).
-     * So squash it unless (FPCR.AH == 0 && FPCR.FZ == 1).
-     * We only do this for the a64 flags because FIZ has no effect
-     * on AArch32 even if it is set.
-     */
-    if ((env->vfp.fpcr & (FPCR_FZ | FPCR_AH)) != FPCR_FZ) {
-        a64_flags &= ~float_flag_input_denormal_flushed;
-    }
-    return vfp_exceptbits_from_host(a64_flags, env->vfp.fpcr & FPCR_AH) |
-        vfp_exceptbits_from_host(a32_flags, false);
-}
-
-static void vfp_clear_float_status_exc_flags(CPUARMState *env)
-{
-    /*
-     * Clear out all the exception-flag information in the float_status
-     * values. The caller should have arranged for env->vfp.fpsr to
-     * be the architecturally up-to-date exception flag information first.
-     */
-    set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32]);
-    set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64]);
-    set_float_exception_flags(0, &env->vfp.fp_status[FPST_A32_F16]);
-    set_float_exception_flags(0, &env->vfp.fp_status[FPST_A64_F16]);
-    set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD]);
-    set_float_exception_flags(0, &env->vfp.fp_status[FPST_STD_F16]);
-    set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH]);
-    set_float_exception_flags(0, &env->vfp.fp_status[FPST_AH_F16]);
-}
-
-static void vfp_sync_and_clear_float_status_exc_flags(CPUARMState *env)
-{
-    /*
-     * Synchronize any pending exception-flag information in the
-     * float_status values into env->vfp.fpsr, and then clear out
-     * the float_status data.
-     */
-    env->vfp.fpsr |= vfp_get_fpsr_from_host(env);
-    vfp_clear_float_status_exc_flags(env);
-}
-
-static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask)
-{
-    uint64_t changed = env->vfp.fpcr;
-
-    changed ^= val;
-    changed &= mask;
-    if (changed & (3 << 22)) {
-        int i = (val >> 22) & 3;
-        switch (i) {
-        case FPROUNDING_TIEEVEN:
-            i = float_round_nearest_even;
-            break;
-        case FPROUNDING_POSINF:
-            i = float_round_up;
-            break;
-        case FPROUNDING_NEGINF:
-            i = float_round_down;
-            break;
-        case FPROUNDING_ZERO:
-            i = float_round_to_zero;
-            break;
-        }
-        set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32]);
-        set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64]);
-        set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32_F16]);
-        set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64_F16]);
-    }
-    if (changed & FPCR_FZ16) {
-        bool ftz_enabled = val & FPCR_FZ16;
-        set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]);
-        set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]);
-        set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]);
-        set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]);
-        set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]);
-        set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]);
-        set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]);
-        set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]);
-    }
-    if (changed & FPCR_FZ) {
-        bool ftz_enabled = val & FPCR_FZ;
-        set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]);
-        set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64]);
-        /* FIZ is A64 only so FZ always makes A32 code flush inputs to zero */
-        set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]);
-    }
-    if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) {
-        /*
-         * A64: Flush denormalized inputs to zero if FPCR.FIZ = 1, or
-         * both FPCR.AH = 0 and FPCR.FZ = 1.
-         */
-        bool fitz_enabled = (val & FPCR_FIZ) ||
-            (val & (FPCR_FZ | FPCR_AH)) == FPCR_FZ;
-        set_flush_inputs_to_zero(fitz_enabled, &env->vfp.fp_status[FPST_A64]);
-    }
-    if (changed & FPCR_DN) {
-        bool dnan_enabled = val & FPCR_DN;
-        set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32]);
-        set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64]);
-        set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A32_F16]);
-        set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_A64_F16]);
-        set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH]);
-        set_default_nan_mode(dnan_enabled, &env->vfp.fp_status[FPST_AH_F16]);
-    }
-    if (changed & FPCR_AH) {
-        bool ah_enabled = val & FPCR_AH;
-
-        if (ah_enabled) {
-            /* Change behaviours for A64 FP operations */
-            arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64]);
-            arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]);
-        } else {
-            arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]);
-            arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]);
-        }
-    }
-    /*
-     * If any bits changed that we look at in vfp_get_fpsr_from_host(),
-     * we must sync the float_status flags into vfp.fpsr now (under the
-     * old regime) before we update vfp.fpcr.
-     */
-    if (changed & (FPCR_FZ | FPCR_AH | FPCR_FIZ)) {
-        vfp_sync_and_clear_float_status_exc_flags(env);
-    }
-}
-
-#else
-
-static uint32_t vfp_get_fpsr_from_host(CPUARMState *env)
-{
-    return 0;
-}
-
-static void vfp_clear_float_status_exc_flags(CPUARMState *env)
-{
-}
-
-static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask)
-{
-}
-
-#endif
 
 uint32_t vfp_get_fpcr(CPUARMState *env)
 {