mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 04:13:53 -06:00

Currently we handle flushing of output denormals in uncanon_normal always before we deal with rounding. This works for architectures that detect tininess before rounding, but is usually not the right place when the architecture detects tininess after rounding. For example, for x86 the SDM states that the MXCSR FTZ control bit causes outputs to be flushed to zero "when it detects a floating-point underflow condition". This means that we mustn't flush to zero if the input is such that after rounding it is no longer tiny. At least one of our guest architectures does underflow detection after rounding but flushing of denormals before rounding (MIPS MSA); this means we need to have a config knob for this that is separate from our existing tininess_before_rounding setting. Add an ftz_detection flag. For consistency with tininess_before_rounding, we make it default to "detect ftz after rounding"; this means that we need to explicitly set the flag to "detect ftz before rounding" on every existing architecture that sets flush_to_zero, so that this commit has no behaviour change. (This means more code change here but for the long term a less confusing API.) For several architectures the current behaviour is either definitely or possibly wrong; annotate those with TODO comments. These architectures are definitely wrong (and should detect ftz after rounding): * x86 * Alpha For these architectures the spec is unclear: * MIPS (for non-MSA) * RX * SH4 PA-RISC makes ftz detection IMPDEF, but we aren't setting the "tininess before rounding" setting that we ought to. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
113 lines
3.8 KiB
C
113 lines
3.8 KiB
C
/*
|
|
* Helpers for emulation of FPU-related MIPS instructions.
|
|
*
|
|
* Copyright (C) 2004-2005 Jocelyn Mayer
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
#include "fpu/softfloat-helpers.h"
|
|
#include "cpu.h"
|
|
|
|
extern const FloatRoundMode ieee_rm[4];
|
|
|
|
uint32_t float_class_s(uint32_t arg, float_status *fst);
|
|
uint64_t float_class_d(uint64_t arg, float_status *fst);
|
|
|
|
static inline void restore_rounding_mode(CPUMIPSState *env)
|
|
{
|
|
set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3],
|
|
&env->active_fpu.fp_status);
|
|
}
|
|
|
|
static inline void restore_flush_mode(CPUMIPSState *env)
|
|
{
|
|
set_flush_to_zero((env->active_fpu.fcr31 & (1 << FCR31_FS)) != 0,
|
|
&env->active_fpu.fp_status);
|
|
}
|
|
|
|
static inline void restore_snan_bit_mode(CPUMIPSState *env)
|
|
{
|
|
bool nan2008 = env->active_fpu.fcr31 & (1 << FCR31_NAN2008);
|
|
FloatInfZeroNaNRule izn_rule;
|
|
Float3NaNPropRule nan3_rule;
|
|
|
|
/*
|
|
* With nan2008, SNaNs are silenced in the usual way.
|
|
* Before that, SNaNs are not silenced; default nans are produced.
|
|
*/
|
|
set_snan_bit_is_one(!nan2008, &env->active_fpu.fp_status);
|
|
set_default_nan_mode(!nan2008, &env->active_fpu.fp_status);
|
|
/*
|
|
* For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan)
|
|
* case sets InvalidOp and returns the default NaN.
|
|
* For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan)
|
|
* case sets InvalidOp and returns the input value 'c'.
|
|
*/
|
|
izn_rule = nan2008 ? float_infzeronan_dnan_never : float_infzeronan_dnan_always;
|
|
set_float_infzeronan_rule(izn_rule, &env->active_fpu.fp_status);
|
|
nan3_rule = nan2008 ? float_3nan_prop_s_cab : float_3nan_prop_s_abc;
|
|
set_float_3nan_prop_rule(nan3_rule, &env->active_fpu.fp_status);
|
|
/*
|
|
* With nan2008, the default NaN value has the sign bit clear and the
|
|
* frac msb set; with the older mode, the sign bit is clear, and all
|
|
* frac bits except the msb are set.
|
|
*/
|
|
set_float_default_nan_pattern(nan2008 ? 0b01000000 : 0b00111111,
|
|
&env->active_fpu.fp_status);
|
|
|
|
}
|
|
|
|
static inline void restore_fp_status(CPUMIPSState *env)
|
|
{
|
|
restore_rounding_mode(env);
|
|
restore_flush_mode(env);
|
|
restore_snan_bit_mode(env);
|
|
}
|
|
|
|
static inline void fp_reset(CPUMIPSState *env)
|
|
{
|
|
restore_fp_status(env);
|
|
|
|
/*
|
|
* According to MIPS specifications, if one of the two operands is
|
|
* a sNaN, a new qNaN has to be generated. This is done in
|
|
* floatXX_silence_nan(). For qNaN inputs the specifications
|
|
* says: "When possible, this QNaN result is one of the operand QNaN
|
|
* values." In practice it seems that most implementations choose
|
|
* the first operand if both operands are qNaN. In short this gives
|
|
* the following rules:
|
|
* 1. A if it is signaling
|
|
* 2. B if it is signaling
|
|
* 3. A (quiet)
|
|
* 4. B (quiet)
|
|
* A signaling NaN is always silenced before returning it.
|
|
*/
|
|
set_float_2nan_prop_rule(float_2nan_prop_s_ab,
|
|
&env->active_fpu.fp_status);
|
|
/*
|
|
* TODO: the spec does't say clearly whether FTZ happens before
|
|
* or after rounding for normal FPU operations.
|
|
*/
|
|
set_float_ftz_detection(float_ftz_before_rounding,
|
|
&env->active_fpu.fp_status);
|
|
}
|
|
|
|
/* MSA */
|
|
|
|
enum CPUMIPSMSADataFormat {
|
|
DF_BYTE = 0,
|
|
DF_HALF,
|
|
DF_WORD,
|
|
DF_DOUBLE
|
|
};
|
|
|
|
static inline void restore_msa_fp_status(CPUMIPSState *env)
|
|
{
|
|
float_status *status = &env->active_tc.msa_fp_status;
|
|
int rounding_mode = (env->active_tc.msacsr & MSACSR_RM_MASK) >> MSACSR_RM;
|
|
bool flush_to_zero = (env->active_tc.msacsr & MSACSR_FS_MASK) != 0;
|
|
|
|
set_float_rounding_mode(ieee_rm[rounding_mode], status);
|
|
set_flush_to_zero(flush_to_zero, status);
|
|
set_flush_inputs_to_zero(flush_to_zero, status);
|
|
}
|