From ff42928673d18bf286ef1169be45f52ce5600327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 14 Oct 2026 20:49:31 +0200 Subject: [PATCH] RISC-V and LoongArch64 JITs: Don't fuse the multiply-adds in Vec4Dot A fused multiply-add rounds once where the IR interpreter rounds twice, so vdot, vhdp, vtfm and vmmul gave different low bits. Multiply and add separately, in the IR interpreter's order. (LoongArch's LSX path already did, in the pairwise order of the x86 and ARM64 JITs.) Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/MIPS/LoongArch64/LoongArch64CompVec.cpp | 21 +++++---------- Core/MIPS/RiscV/RiscVCompVec.cpp | 27 ++++++++------------ 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/Core/MIPS/LoongArch64/LoongArch64CompVec.cpp b/Core/MIPS/LoongArch64/LoongArch64CompVec.cpp index a12804157f..54472014fb 100644 --- a/Core/MIPS/LoongArch64/LoongArch64CompVec.cpp +++ b/Core/MIPS/LoongArch64/LoongArch64CompVec.cpp @@ -370,21 +370,12 @@ void LoongArch64JitBackend::CompIR_VecHoriz(IRInst inst) { // Do we need care about upper 96 bits? VFADD_S(regs_.V(inst.dest), regs_.V(inst.dest), EncodeRegToV(SCRATCHF1)); } else { - if (Overlap(inst.dest, 1, inst.src1, 4) || Overlap(inst.dest, 1, inst.src2, 4)) { - // This means inst.dest overlaps one of src1 or src2. We have to do that one first. - // Technically this may impact -0.0 and such, but dots accurately need to be aligned anyway. - for (int i = 0; i < 4; ++i) { - if (inst.dest == inst.src1 + i || inst.dest == inst.src2 + i) - FMUL_S(regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i)); - } - for (int i = 0; i < 4; ++i) { - if (inst.dest != inst.src1 + i && inst.dest != inst.src2 + i) - FMADD_S(regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i), regs_.F(inst.dest)); - } - } else { - FMUL_S(regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); - for (int i = 1; i < 4; ++i) - FMADD_S(regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i), regs_.F(inst.dest)); + // Multiply and add separately in the IR interpreter's order: FMADD rounds differently. + // The sum builds up in a scratch reg, so dest may be one of the inputs. + FMUL_S(SCRATCHF1, regs_.F(inst.src1), regs_.F(inst.src2)); + for (int i = 1; i < 4; ++i) { + FMUL_S(SCRATCHF2, regs_.F(inst.src1 + i), regs_.F(inst.src2 + i)); + FADD_S(i == 3 ? regs_.F(inst.dest) : SCRATCHF1, SCRATCHF1, SCRATCHF2); } } break; diff --git a/Core/MIPS/RiscV/RiscVCompVec.cpp b/Core/MIPS/RiscV/RiscVCompVec.cpp index c68b0877ef..a4e5a1c8e6 100644 --- a/Core/MIPS/RiscV/RiscVCompVec.cpp +++ b/Core/MIPS/RiscV/RiscVCompVec.cpp @@ -293,24 +293,19 @@ void RiscVJitBackend::CompIR_VecHoriz(IRInst inst) { switch (inst.op) { case IROp::Vec4Dot: - regs_.Map(inst); - if (Overlap(inst.dest, 1, inst.src1, 4) || Overlap(inst.dest, 1, inst.src2, 4)) { - // This means inst.dest overlaps one of src1 or src2. We have to do that one first. - // Technically this may impact -0.0 and such, but dots accurately need to be aligned anyway. - for (int i = 0; i < 4; ++i) { - if (inst.dest == inst.src1 + i || inst.dest == inst.src2 + i) - FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i)); - } - for (int i = 0; i < 4; ++i) { - if (inst.dest != inst.src1 + i && inst.dest != inst.src2 + i) - FMADD(32, regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i), regs_.F(inst.dest)); - } - } else { - FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); - for (int i = 1; i < 4; ++i) - FMADD(32, regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i), regs_.F(inst.dest)); + { + // Multiply and add separately in the IR interpreter's order: FMADD rounds differently. + // The sum builds up in dest, so it can't be one of the inputs. + if (Overlap(inst.dest, 1, inst.src1, 4) || Overlap(inst.dest, 1, inst.src2, 4)) + DISABLE; + RiscVReg tempReg = regs_.MapWithFPRTemp(inst); + FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); + for (int i = 1; i < 4; ++i) { + FMUL(32, tempReg, regs_.F(inst.src1 + i), regs_.F(inst.src2 + i)); + FADD(32, regs_.F(inst.dest), regs_.F(inst.dest), tempReg); } break; + } case IROp::Vec4VfpuDot: // Through the IR interpreter for now.