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
This commit is contained in:
parent
e34cfba5e8
commit
b9d3dc4553
@ -1833,5 +1833,14 @@ int alle1_tlbmask(CPUARMState *env);
|
|||||||
void arm_set_default_fp_behaviours(float_status *s);
|
void arm_set_default_fp_behaviours(float_status *s);
|
||||||
/* Set the float_status behaviour to match Arm FPCR.AH=1 behaviour */
|
/* Set the float_status behaviour to match Arm FPCR.AH=1 behaviour */
|
||||||
void arm_set_ah_fp_behaviours(float_status *s);
|
void arm_set_ah_fp_behaviours(float_status *s);
|
||||||
|
/* Read the float_status info and return the appropriate FPSR value */
|
||||||
|
uint32_t vfp_get_fpsr_from_host(CPUARMState *env);
|
||||||
|
/* Clear the exception status flags from all float_status fields */
|
||||||
|
void vfp_clear_float_status_exc_flags(CPUARMState *env);
|
||||||
|
/*
|
||||||
|
* Update float_status fields to handle the bits of the FPCR
|
||||||
|
* specified by mask changing to the values in val.
|
||||||
|
*/
|
||||||
|
void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,3 +30,25 @@ void assert_hflags_rebuild_correctly(CPUARMState *env)
|
|||||||
void define_tlb_insn_regs(ARMCPU *cpu)
|
void define_tlb_insn_regs(ARMCPU *cpu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* With KVM, we never use float_status, so these can be no-ops */
|
||||||
|
void arm_set_default_fp_behaviours(float_status *s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void arm_set_ah_fp_behaviours(float_status *s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t vfp_get_fpsr_from_host(CPUARMState *env)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vfp_clear_float_status_exc_flags(CPUARMState *env)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -25,6 +25,234 @@
|
|||||||
#include "fpu/softfloat.h"
|
#include "fpu/softfloat.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* VFP support. We follow the convention used for VFP instructions:
|
* VFP support. We follow the convention used for VFP instructions:
|
||||||
* Single precision routines have a "s" suffix, double precision a
|
* Single precision routines have a "s" suffix, double precision a
|
||||||
|
@ -21,254 +21,6 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "internals.h"
|
#include "internals.h"
|
||||||
#include "cpu-features.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)
|
uint32_t vfp_get_fpcr(CPUARMState *env)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user