This patch fixes incorrect results for [xv]fnm{add,sub}.{s,d} instructions when rounding toward zero, postive, negative.
According to the LoongArch ISA specification, these instructions perform a fused multiply-add followed by a negation of the final result. Previously, the sign inversion was applied before fused operation, which interfered with rounding decisions that depend on the result sign -- leading to deviations from the expected behavior. This patch corrects the implementation by applying the negation after fused multiply-add, ensuring that rounding is performed on the correct intermediate result. Reported-by: mengqinggang <mengqingg...@loongson.cn> Signed-off-by: WANG Rui <wang...@loongson.cn> --- target/loongarch/tcg/fpu_helper.c | 8 ++++++++ target/loongarch/tcg/vec_helper.c | 7 ++++++- tests/tcg/loongarch64/Makefile.target | 2 ++ tests/tcg/loongarch64/test_fnmsub.c | 20 ++++++++++++++++++++ tests/tcg/loongarch64/test_vfnmsub.c | 27 +++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/loongarch64/test_fnmsub.c create mode 100644 tests/tcg/loongarch64/test_vfnmsub.c diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c index fc3fd0561e..970b88ac56 100644 --- a/target/loongarch/tcg/fpu_helper.c +++ b/target/loongarch/tcg/fpu_helper.c @@ -389,9 +389,13 @@ uint64_t helper_fmuladd_s(CPULoongArchState *env, uint64_t fj, uint64_t fk, uint64_t fa, uint32_t flag) { uint64_t fd; + uint32_t neg_res; + neg_res = flag & float_muladd_negate_result; + flag ^= neg_res; fd = nanbox_s(float32_muladd((uint32_t)fj, (uint32_t)fk, (uint32_t)fa, flag, &env->fp_status)); + fd |= neg_res ? 0x80000000ULL : 0; update_fcsr0(env, GETPC()); return fd; } @@ -400,8 +404,12 @@ uint64_t helper_fmuladd_d(CPULoongArchState *env, uint64_t fj, uint64_t fk, uint64_t fa, uint32_t flag) { uint64_t fd; + uint32_t neg_res; + neg_res = flag & float_muladd_negate_result; + flag ^= neg_res; fd = float64_muladd(fj, fk, fa, flag, &env->fp_status); + fd |= neg_res ? 0x8000000000000000ULL : 0; update_fcsr0(env, GETPC()); return fd; } diff --git a/target/loongarch/tcg/vec_helper.c b/target/loongarch/tcg/vec_helper.c index 3faf52cbc4..c48e3662d1 100644 --- a/target/loongarch/tcg/vec_helper.c +++ b/target/loongarch/tcg/vec_helper.c @@ -2446,10 +2446,15 @@ void HELPER(NAME)(void *vd, void *vj, void *vk, void *va, \ VReg *Vk = (VReg *)vk; \ VReg *Va = (VReg *)va; \ int oprsz = simd_oprsz(desc); \ + uint32_t flag = flags; \ + uint32_t neg_res; \ \ + neg_res = flag & float_muladd_negate_result; \ + flag ^= neg_res; \ vec_clear_cause(env); \ for (i = 0; i < oprsz / (BIT / 8); i++) { \ - Vd->E(i) = FN(Vj->E(i), Vk->E(i), Va->E(i), flags, &env->fp_status); \ + Vd->E(i) = FN(Vj->E(i), Vk->E(i), Va->E(i), flag, &env->fp_status); \ + Vd->E(i) |= neg_res ? (1ULL << (BIT - 1)) : 0; \ vec_update_fcsr0(env, GETPC()); \ } \ } diff --git a/tests/tcg/loongarch64/Makefile.target b/tests/tcg/loongarch64/Makefile.target index 00030a1026..e3554a500e 100644 --- a/tests/tcg/loongarch64/Makefile.target +++ b/tests/tcg/loongarch64/Makefile.target @@ -16,5 +16,7 @@ LOONGARCH64_TESTS += test_fclass LOONGARCH64_TESTS += test_fpcom LOONGARCH64_TESTS += test_pcadd LOONGARCH64_TESTS += test_fcsr +LOONGARCH64_TESTS += test_fnmsub +LOONGARCH64_TESTS += test_vfnmsub TESTS += $(LOONGARCH64_TESTS) diff --git a/tests/tcg/loongarch64/test_fnmsub.c b/tests/tcg/loongarch64/test_fnmsub.c new file mode 100644 index 0000000000..0c8514f33d --- /dev/null +++ b/tests/tcg/loongarch64/test_fnmsub.c @@ -0,0 +1,20 @@ +#include <assert.h> +#include <stdint.h> +#include <fenv.h> + +int main() +{ + double x, y, z; + + *(uint64_t *)&x = 0x4ff0000000000000UL; + *(uint64_t *)&y = 0x4ff0000000000000UL; + *(uint64_t *)&z = 0x2ff0000000000000UL; + + fesetround(FE_DOWNWARD); + asm("fnmsub.d %[x], %[x], %[y], %[z]\n\t" + :[x]"+f"(x) + :[y]"f"(y), [z]"f"(z)); + + assert(*(uint64_t *)&x == 0xdfefffffffffffffUL); + return 0; +} diff --git a/tests/tcg/loongarch64/test_vfnmsub.c b/tests/tcg/loongarch64/test_vfnmsub.c new file mode 100644 index 0000000000..8c332674ae --- /dev/null +++ b/tests/tcg/loongarch64/test_vfnmsub.c @@ -0,0 +1,27 @@ +#include <assert.h> +#include <stdint.h> +#include <fenv.h> + +int main() +{ + uint64_t x, y, z; + + x = 0x4ff0000000000000UL; + y = 0x4ff0000000000000UL; + z = 0x2ff0000000000000UL; + + fesetround(FE_DOWNWARD); + asm("vreplgr2vr.d $vr0, %[x]\n\t" + "vreplgr2vr.d $vr1, %[y]\n\t" + "vreplgr2vr.d $vr2, %[z]\n\t" + "vfnmsub.d $vr0, $vr0, $vr1, $vr2\n\t" + "vpickve2gr.d %[x], $vr0, 0\n\t" + "vpickve2gr.d %[y], $vr0, 1\n\t" + :[x]"+&r"(x), [y]"+&r"(y) + :[z]"r"(z) + :"$f0", "$f1", "$f2"); + + assert(x == 0xdfefffffffffffffUL); + assert(y == 0xdfefffffffffffffUL); + return 0; +} -- 2.49.0