Skip to content

Commit 8a70616

Browse files
committed
iOS: Native MADD family and pipeline-1 HI/LO moves
Port the MMI multiply-accumulate and pipeline-1 HI/LO move codegen from the mac arm64 backend (upstream 673135b) so MADD/MADDU/MADD1/MADDU1 and MFHI1/MTHI1/MFLO1/MTLO1 compile natively instead of falling back to single-stepping through the interpreter. The accumulator math (acc = (HI<<32)|LO plus a widening rs*rt product) fits in the two existing manual scratch registers via Smull/Umull + two Adds, so no extra scratch or register-allocator support is needed. The pipeline-1 forms reuse the proven +8 HI1/LO1 offsets already used by MULT1/DIV1. Semantics are bit-exact against the interpreter (R5900OpcodeImpl / MMI). Wires MMI funct 0x00/0x01 (MADD/MADDU), 0x10-0x13 (the HI1/LO1 moves) and 0x20/0x21 (MADD1/MADDU1) into recTranslateOp's case 0x1C. Const invalidation (recConstApplyNativeEffects case 0x1C) already marks rd unknown, which is correct for every one of these.
1 parent dec1a6d commit 8a70616

3 files changed

Lines changed: 107 additions & 2 deletions

File tree

app/src/main/cpp/pcsx2/arm64/aR5900.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2527,15 +2527,23 @@ static bool recTranslateOp(u32 op)
25272527
default: return false;
25282528
}
25292529

2530-
// MMI — second-pipeline multiply/divide (Phase 3.5). Other MMI ops (SIMD,
2531-
// MFHI1/MFLO1, ...) are not yet implemented and fall through to false.
2530+
// MMI — second-pipeline multiply/divide (Phase 3.5) + multiply-accumulate
2531+
// and the pipeline-1 HI/LO moves. Remaining MMI ops fall through to false.
25322532
case 0x1C:
25332533
switch (funct)
25342534
{
2535+
case 0x00: armEmitMADD(rd, rs, rt); return true; // MADD
2536+
case 0x01: armEmitMADDU(rd, rs, rt); return true; // MADDU
2537+
case 0x10: armEmitMFHI1(rd); return true; // MFHI1
2538+
case 0x11: armEmitMTHI1(rs); return true; // MTHI1
2539+
case 0x12: armEmitMFLO1(rd); return true; // MFLO1
2540+
case 0x13: armEmitMTLO1(rs); return true; // MTLO1
25352541
case 0x18: armEmitMULT1(rd, rs, rt); return true;
25362542
case 0x19: armEmitMULTU1(rd, rs, rt); return true;
25372543
case 0x1A: armEmitDIV1(rs, rt); return true;
25382544
case 0x1B: armEmitDIVU1(rs, rt); return true;
2545+
case 0x20: armEmitMADD1(rd, rs, rt); return true; // MADD1
2546+
case 0x21: armEmitMADDU1(rd, rs, rt); return true; // MADDU1
25392547
// Direct tbl_MMI entries (indexed by funct = op & 0x3F).
25402548
case 0x04: armEmitPLZCW(rd, rs); return true;
25412549
// MMI0/1/2/3 SIMD sub-groups (Phase 5.4); sub-op in `sa`.

app/src/main/cpp/pcsx2/arm64/aR5900.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,19 @@ void armEmitMULTU1(u32 rd, u32 rs, u32 rt);
324324
void armEmitDIV1(u32 rs, u32 rt);
325325
void armEmitDIVU1(u32 rs, u32 rt);
326326

327+
// MMI multiply-accumulate (MADD/MADDU funct 0x00/0x01; MADD1/MADDU1 0x20/0x21):
328+
// (HI:LO) += rs*rt, then Rd = LO. The "1" forms target the upper doubleword.
329+
void armEmitMADD(u32 rd, u32 rs, u32 rt);
330+
void armEmitMADDU(u32 rd, u32 rs, u32 rt);
331+
void armEmitMADD1(u32 rd, u32 rs, u32 rt);
332+
void armEmitMADDU1(u32 rd, u32 rs, u32 rt);
333+
334+
// MMI pipeline-1 HI/LO moves (funct 0x10-0x13): MFHI1/MTHI1/MFLO1/MTLO1.
335+
void armEmitMFHI1(u32 rd);
336+
void armEmitMTHI1(u32 rs);
337+
void armEmitMFLO1(u32 rd);
338+
void armEmitMTLO1(u32 rs);
339+
327340
// --------------------------------------------------------------------------------------
328341
// EE jump opcode generators (Phase 4.1)
329342
// --------------------------------------------------------------------------------------

app/src/main/cpp/pcsx2/arm64/aR5900MultDiv.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,87 @@ void armEmitMULT1(u32 rd, u32 rs, u32 rt) { emitMult(true, rd, rs, rt, EE_LO1_OF
166166
void armEmitMULTU1(u32 rd, u32 rs, u32 rt) { emitMult(false, rd, rs, rt, EE_LO1_OFFSET, EE_HI1_OFFSET); }
167167
void armEmitDIV1(u32 rs, u32 rt) { emitDivS(rs, rt, EE_LO1_OFFSET, EE_HI1_OFFSET); }
168168
void armEmitDIVU1(u32 rs, u32 rt) { emitDivU(rs, rt, EE_LO1_OFFSET, EE_HI1_OFFSET); }
169+
170+
// ------------------------------------------------------------------------
171+
// Multiply-accumulate (MMI funct 0x00/0x01 MADD/MADDU, 0x20/0x21 MADD1/MADDU1).
172+
// acc = (u64)LO.UL[0] | ((u64)HI.UL[0] << 32) (low 32 of each accumulator word)
173+
// temp = acc + (rs * rt) (signed for MADD/MADD1, unsigned for the U forms)
174+
// LO = (s32)(temp & 0xffffffff) (sign-extended to 64)
175+
// HI = (s32)(temp >> 32) (sign-extended to 64)
176+
// if rd != 0: GPR[rd].UD[0] = LO (R5900 3-operand form)
177+
// The two 32-bit accumulator words are added straight onto the 64-bit product
178+
// (HI word << 32, then LO word), so `acc` never needs its own register and the op
179+
// fits in the two manual scratch registers. Result sign-extension is identical for
180+
// the unsigned forms (the interpreter sign-extends LO/HI regardless). The pipeline-1
181+
// forms select the upper doubleword via lo_off/hi_off, exactly like MULT1.
182+
// ------------------------------------------------------------------------
183+
static void emitMadd(bool sign, u32 rd, u32 rs, u32 rt, u32 lo_off, u32 hi_off)
184+
{
185+
armAsm->Ldr(RSCRATCH2W, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs)));
186+
armAsm->Ldr(RSCRATCHW, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt)));
187+
188+
// The live 64-bit product is kept in RSCRATCH (x17), which AsmHelpers removes
189+
// from VIXL's scratch-register list, so it is safe to hold across the macro ops
190+
// below. RSCRATCH2 (x16 == RXVIXLSCRATCH) is VIXL's macro scratch, so it is only
191+
// ever loaded and immediately consumed as a plain operand here, never held across
192+
// a macro.
193+
if (sign)
194+
armAsm->Smull(RSCRATCH, RSCRATCH2W, RSCRATCHW); // x17 = (s64)rs * (s32)rt
195+
else
196+
armAsm->Umull(RSCRATCH, RSCRATCH2W, RSCRATCHW); // x17 = (u64)rs * (u32)rt
197+
198+
// temp = product + (HI.UL[0] << 32) + LO.UL[0]. The w-loads zero-extend, so each
199+
// accumulator word contributes exactly its 32 bits with no stray high bits.
200+
armAsm->Ldr(RSCRATCH2W, a64::MemOperand(RESTATEPTR, hi_off)); // HI accumulator word
201+
armAsm->Add(RSCRATCH, RSCRATCH, a64::Operand(RSCRATCH2, a64::LSL, 32));
202+
armAsm->Ldr(RSCRATCH2W, a64::MemOperand(RESTATEPTR, lo_off)); // LO accumulator word
203+
armAsm->Add(RSCRATCH, RSCRATCH, RSCRATCH2); // RSCRATCH = temp
204+
205+
// LO = sign-extended low 32 bits of temp; also Rd in the R5900 3-operand form.
206+
armAsm->Sxtw(RSCRATCH2, RSCRATCHW);
207+
armAsm->Str(RSCRATCH2, a64::MemOperand(RESTATEPTR, lo_off));
208+
if (rd != 0)
209+
armAsm->Str(RSCRATCH2, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rd)));
210+
211+
// HI = sign-extended high 32 bits (asr #32 sign-extends from bit 63 == bit 31 hi).
212+
armAsm->Asr(RSCRATCH, RSCRATCH, 32);
213+
armAsm->Str(RSCRATCH, a64::MemOperand(RESTATEPTR, hi_off));
214+
}
215+
216+
void armEmitMADD(u32 rd, u32 rs, u32 rt) { emitMadd(true, rd, rs, rt, EE_LO_OFFSET, EE_HI_OFFSET); }
217+
void armEmitMADDU(u32 rd, u32 rs, u32 rt) { emitMadd(false, rd, rs, rt, EE_LO_OFFSET, EE_HI_OFFSET); }
218+
void armEmitMADD1(u32 rd, u32 rs, u32 rt) { emitMadd(true, rd, rs, rt, EE_LO1_OFFSET, EE_HI1_OFFSET); }
219+
void armEmitMADDU1(u32 rd, u32 rs, u32 rt) { emitMadd(false, rd, rs, rt, EE_LO1_OFFSET, EE_HI1_OFFSET); }
220+
221+
// ------------------------------------------------------------------------
222+
// Pipeline-1 HI/LO moves (MMI funct 0x10-0x13: MFHI1/MTHI1/MFLO1/MTLO1).
223+
// Full 64-bit copies to/from the upper doubleword HI1/LO1 — mirror MFHI/MFLO/
224+
// MTHI/MTLO but with the +8 offsets.
225+
// ------------------------------------------------------------------------
226+
void armEmitMFHI1(u32 rd)
227+
{
228+
if (rd == 0)
229+
return;
230+
armAsm->Ldr(RSCRATCH, a64::MemOperand(RESTATEPTR, EE_HI1_OFFSET));
231+
armAsm->Str(RSCRATCH, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rd)));
232+
}
233+
234+
void armEmitMFLO1(u32 rd)
235+
{
236+
if (rd == 0)
237+
return;
238+
armAsm->Ldr(RSCRATCH, a64::MemOperand(RESTATEPTR, EE_LO1_OFFSET));
239+
armAsm->Str(RSCRATCH, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rd)));
240+
}
241+
242+
void armEmitMTHI1(u32 rs)
243+
{
244+
armAsm->Ldr(RSCRATCH, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs)));
245+
armAsm->Str(RSCRATCH, a64::MemOperand(RESTATEPTR, EE_HI1_OFFSET));
246+
}
247+
248+
void armEmitMTLO1(u32 rs)
249+
{
250+
armAsm->Ldr(RSCRATCH, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs)));
251+
armAsm->Str(RSCRATCH, a64::MemOperand(RESTATEPTR, EE_LO1_OFFSET));
252+
}

0 commit comments

Comments
 (0)