mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-19 09:19:05 +02:00
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) <noreply@anthropic.com>
This commit is contained in:
1 parent
21010a497f
commit
ff42928673
2 files changed
+17
-31
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user