Commit 055b0d88ee for qemu.org
commit 055b0d88eee38e823f10c60569d6a60ee7ccd68d
Author: Max Chou <max.chou@sifive.com>
Date: Thu Jun 11 18:50:36 2026 +0800
target/riscv: Set mstatus.FS dirty when scalar FP raises exceptions
According to the RISC-V privileged spec 3.1.6, any instruction that
modifies FP extension state (FP CSRs including fflags, or f registers)
must set mstatus.FS to Dirty. Raising fflags bits is modifying fcsr
(an FP CSR).
Scalar FP instructions that write integer registers (FP comparisons and
FP-to-integer conversions) never call mark_fs_dirty at translation time
to set mstatus.FS to dirty. However, they can raise FP exception flags
via softfloat functions, which modifies fflags without any mechanism to
dirty mstatus.FS.
The affected helpers:
- Comparisons: fle/fleq/flt/fltq/feq
— raise NV on NaN operands
- FP-to-integer: fcvt.[w|wu|l|lu]/fcvtmod.w.d
— raise NX on inexact or NV on out-of-range
Fix this issue by
1. Save float_exception_flags before the softfloat operation
2. Perform the operation
3. If any new exception bits are set, set fs to dirty
Signed-off-by: Max Chou <max.chou@sifive.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20260611105037.157773-2-max.chou@sifive.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index eec5acea5f..7582874c35 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -676,6 +676,7 @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env,
uint8_t riscv_cpu_get_fflags(CPURISCVState *env);
void riscv_cpu_set_fflags(CPURISCVState *env, uint8_t);
+void riscv_cpu_check_fflags(CPURISCVState *env, FloatExceptionFlags);
#ifndef CONFIG_USER_ONLY
void cpu_set_exception_base(int vp_index, uint64_t address);
diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c
index e6d1ffb1d6..db1475271b 100644
--- a/target/riscv/fpu_helper.c
+++ b/target/riscv/fpu_helper.c
@@ -50,6 +50,22 @@ void riscv_cpu_set_fflags(CPURISCVState *env, uint8_t hard)
set_float_exception_flags(soft, &env->fp_status);
}
+#ifndef CONFIG_USER_ONLY
+void riscv_cpu_check_fflags(CPURISCVState *env,
+ FloatExceptionFlags pre_fflag)
+{
+ if (get_float_exception_flags(&env->fp_status) & ~pre_fflag) {
+ env->mstatus |= MSTATUS_FS;
+ if (env->virt_enabled) {
+ env->mstatus_hs |= MSTATUS_FS;
+ }
+ }
+}
+#else
+void riscv_cpu_check_fflags(CPURISCVState *env,
+ FloatExceptionFlags pre_fflag) {}
+#endif
+
void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm)
{
FloatRoundMode softrm;
@@ -286,59 +302,86 @@ target_ulong helper_fle_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float32 frs1 = check_nanbox_s(env, rs1);
float32 frs2 = check_nanbox_s(env, rs2);
- return float32_le(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float32_le(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fleq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float32 frs1 = check_nanbox_s(env, rs1);
float32 frs2 = check_nanbox_s(env, rs2);
- return float32_le_quiet(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float32_le_quiet(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float32 frs1 = check_nanbox_s(env, rs1);
float32 frs2 = check_nanbox_s(env, rs2);
- return float32_lt(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float32_lt(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fltq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float32 frs1 = check_nanbox_s(env, rs1);
float32 frs2 = check_nanbox_s(env, rs2);
- return float32_lt_quiet(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float32_lt_quiet(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_feq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float32 frs1 = check_nanbox_s(env, rs1);
float32 frs2 = check_nanbox_s(env, rs2);
- return float32_eq_quiet(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float32_eq_quiet(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_w_s(CPURISCVState *env, uint64_t rs1)
{
float32 frs1 = check_nanbox_s(env, rs1);
- return float32_to_int32(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float32_to_int32(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_wu_s(CPURISCVState *env, uint64_t rs1)
{
float32 frs1 = check_nanbox_s(env, rs1);
- return (int32_t)float32_to_uint32(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = (int32_t)float32_to_uint32(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_l_s(CPURISCVState *env, uint64_t rs1)
{
float32 frs1 = check_nanbox_s(env, rs1);
- return float32_to_int64(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float32_to_int64(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_lu_s(CPURISCVState *env, uint64_t rs1)
{
float32 frs1 = check_nanbox_s(env, rs1);
- return float32_to_uint64(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float32_to_uint64(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
uint64_t helper_fcvt_s_w(CPURISCVState *env, target_ulong rs1)
@@ -453,52 +496,83 @@ uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1)
target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
{
- return float64_le(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float64_le(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fleq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
{
- return float64_le_quiet(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float64_le_quiet(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
{
- return float64_lt(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float64_lt(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fltq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
{
- return float64_lt_quiet(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float64_lt_quiet(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
{
- return float64_eq_quiet(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float64_eq_quiet(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1)
{
- return float64_to_int32(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float64_to_int32(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
uint64_t helper_fcvtmod_w_d(CPURISCVState *env, uint64_t value)
{
- return float64_to_int32_modulo(value, float_round_to_zero, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ uint64_t ret = float64_to_int32_modulo(value, float_round_to_zero,
+ &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1)
{
- return (int32_t)float64_to_uint32(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = (int32_t)float64_to_uint32(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_l_d(CPURISCVState *env, uint64_t frs1)
{
- return float64_to_int64(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float64_to_int64(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_lu_d(CPURISCVState *env, uint64_t frs1)
{
- return float64_to_uint64(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float64_to_uint64(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
uint64_t helper_fcvt_d_w(CPURISCVState *env, target_ulong rs1)
@@ -619,35 +693,50 @@ target_ulong helper_fle_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float16 frs1 = check_nanbox_h(env, rs1);
float16 frs2 = check_nanbox_h(env, rs2);
- return float16_le(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float16_le(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fleq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float16 frs1 = check_nanbox_h(env, rs1);
float16 frs2 = check_nanbox_h(env, rs2);
- return float16_le_quiet(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float16_le_quiet(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float16 frs1 = check_nanbox_h(env, rs1);
float16 frs2 = check_nanbox_h(env, rs2);
- return float16_lt(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float16_lt(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fltq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float16 frs1 = check_nanbox_h(env, rs1);
float16 frs2 = check_nanbox_h(env, rs2);
- return float16_lt_quiet(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float16_lt_quiet(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_feq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
{
float16 frs1 = check_nanbox_h(env, rs1);
float16 frs2 = check_nanbox_h(env, rs2);
- return float16_eq_quiet(frs1, frs2, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float16_eq_quiet(frs1, frs2, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fclass_h(CPURISCVState *env, uint64_t rs1)
@@ -683,25 +772,37 @@ uint64_t helper_froundnx_h(CPURISCVState *env, uint64_t rs1)
target_ulong helper_fcvt_w_h(CPURISCVState *env, uint64_t rs1)
{
float16 frs1 = check_nanbox_h(env, rs1);
- return float16_to_int32(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float16_to_int32(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_wu_h(CPURISCVState *env, uint64_t rs1)
{
float16 frs1 = check_nanbox_h(env, rs1);
- return (int32_t)float16_to_uint32(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = (int32_t)float16_to_uint32(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_l_h(CPURISCVState *env, uint64_t rs1)
{
float16 frs1 = check_nanbox_h(env, rs1);
- return float16_to_int64(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float16_to_int64(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
target_ulong helper_fcvt_lu_h(CPURISCVState *env, uint64_t rs1)
{
float16 frs1 = check_nanbox_h(env, rs1);
- return float16_to_uint64(frs1, &env->fp_status);
+ FloatExceptionFlags pre_fflag = get_float_exception_flags(&env->fp_status);
+ target_ulong ret = float16_to_uint64(frs1, &env->fp_status);
+ riscv_cpu_check_fflags(env, pre_fflag);
+ return ret;
}
uint64_t helper_fcvt_h_w(CPURISCVState *env, target_ulong rs1)