target/loongarch/tcg/insn_trans/trans_branch.c.inc | 2 ++ 1 file changed, 2 insertions(+)
gen_cz_bc() loads env->cf[cj] without CHECK_FPE, unlike every other
translator that touches an fcc register (trans_fcmp.c.inc and
trans_fmov.c.inc, for movcf2gr/movgr2cf/movcf2fr/movfr2cf/fsel).
A guest that manages the FPU lazily -- Linux clears CSR.EUEN.FPE in
lose_fpu() on every context switch -- relies on the next fcc access
raising a Floating-Point-Disabled exception so the kernel can restore
that task's fcc. Because bceqz and bcnez never raise it, they branch on
the condition flag left behind by whichever task last owned the FPU.
Real Loongson hardware does raise the exception, so this is TCG-only.
It surfaces as Go binaries dying at startup in runtime.check() with
"fatal error: float64nan1" -- roughly one process start in a thousand
once the guest has more runnable tasks than vCPUs -- and in general as a
conditional branch silently taking the wrong path.
With four tasks each executing 5M bcnez on a 2-vCPU guest, master
mispredicts 89 of 20000000. With this patch, 0 of 100000000 over five
runs; a Loongson-3C5000 is likewise 0 of 600000000.
CHECK_FPE is defined in trans_farith.c.inc, which translate.c includes
before trans_branch.c.inc, so it is already in scope.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4209
Cc: qemu-stable@nongnu.org
Signed-off-by: Jan Mercl <0xjnml@gmail.com>
---
target/loongarch/tcg/insn_trans/trans_branch.c.inc | 2 ++
1 file changed, 2 insertions(+)
diff --git a/target/loongarch/tcg/insn_trans/trans_branch.c.inc b/target/loongarch/tcg/insn_trans/trans_branch.c.inc
index f94c1f3..da07778 100644
--- a/target/loongarch/tcg/insn_trans/trans_branch.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_branch.c.inc
@@ -66,6 +66,8 @@ static bool gen_cz_bc(DisasContext *ctx, arg_c_offs *a, TCGCond cond)
TCGv src1 = tcg_temp_new();
TCGv src2 = tcg_constant_tl(0);
+ CHECK_FPE;
+
tcg_gen_ld8u_tl(src1, tcg_env,
offsetof(CPULoongArchState, cf[a->cj]));
gen_bc(ctx, src1, src2, a->offs, cond);
--
2.47.3
On 8/19/26 06:24, Jan Mercl wrote: > gen_cz_bc() loads env->cf[cj] without CHECK_FPE, unlike every other > translator that touches an fcc register (trans_fcmp.c.inc and > trans_fmov.c.inc, for movcf2gr/movgr2cf/movcf2fr/movfr2cf/fsel). > > A guest that manages the FPU lazily -- Linux clears CSR.EUEN.FPE in > lose_fpu() on every context switch -- relies on the next fcc access > raising a Floating-Point-Disabled exception so the kernel can restore > that task's fcc. Because bceqz and bcnez never raise it, they branch on > the condition flag left behind by whichever task last owned the FPU. > > Real Loongson hardware does raise the exception, so this is TCG-only. > It surfaces as Go binaries dying at startup in runtime.check() with > "fatal error: float64nan1" -- roughly one process start in a thousand > once the guest has more runnable tasks than vCPUs -- and in general as a > conditional branch silently taking the wrong path. > > With four tasks each executing 5M bcnez on a 2-vCPU guest, master > mispredicts 89 of 20000000. With this patch, 0 of 100000000 over five > runs; a Loongson-3C5000 is likewise 0 of 600000000. > > CHECK_FPE is defined in trans_farith.c.inc, which translate.c includes > before trans_branch.c.inc, so it is already in scope. > > Resolves:https://gitlab.com/qemu-project/qemu/-/issues/4209 > Cc:qemu-stable@nongnu.org > Signed-off-by: Jan Mercl<0xjnml@gmail.com> > --- > target/loongarch/tcg/insn_trans/trans_branch.c.inc | 2 ++ > 1 file changed, 2 insertions(+) Reviewed-by: Richard Henderson <richard.henderson@linaro.org> r~
© 2016 - 2026 Red Hat, Inc.