mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
ARM64: Fix MMI variable-shift + PMADDW voodoo bugs
Second correctness pass over aR5900MMI.cpp found two divergences from the interpreter (MMI.cpp) that the first pass's gtests missed because their oracles were self-consistent with the buggy emitters: - PSLLVW/PSRLVW/PSRAVW emitted four independent 32-bit lane shifts. The interpreter shifts only lanes 0 and 2 and sign-extends each 32-bit result to a full 64-bit doubleword (Rd.SD[k] = (s64)(s32)(Rt.UL[2k] op (Rs.UL[2k]&0x1F))). Rewrote them; corrected the refPSxxVW test oracles which had replicated the bug. - PMADDW division voodoo skipped the (Rt&0x7FFFFFFF)==0 trigger (the Cbz treated a zero result as "skip"). Both ==0 and ==0x7FFFFFFF must add 0x70000000 when Rs!=Rt. Added a voodoo_check_rs label; added MMI_PMADDW_VoodooZeroRt to cover the ==0 path that the shared inA/inB inputs never reach. Verified: unittests 100% (Arm64EmitEE 270/270); pcsx2-qt builds arm64.
This commit is contained in:
@@ -28,6 +28,44 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-05 — Phase 5.4 MMI second correctness pass: variable shifts + PMADDW voodoo
|
||||
|
||||
**Goal:** Re-review `aR5900MMI.cpp` against `MMI.cpp` for any remaining divergences
|
||||
(the prior pass claimed "bit-exact", but the test oracles were only as good as
|
||||
their author's reading of the interpreter).
|
||||
|
||||
**What changed:**
|
||||
- **Emit (`pcsx2/arm64/aR5900MMI.cpp`):**
|
||||
- **PSLLVW/PSRLVW/PSRAVW were wrong.** They emitted four independent 32-bit lane
|
||||
shifts. The interpreter only shifts lanes 0 and 2 and sign-extends each 32-bit
|
||||
result to a full 64-bit doubleword (`Rd.SD[k] = (s64)(s32)(Rt.UL[2k] op (Rs.UL[2k]&0x1F))`).
|
||||
Rewrote all three to two `Sxtw`'d doubleword stores. (Low word of each dword was
|
||||
coincidentally right before; the high words were garbage shifts of Rt.UL[1]/[3].)
|
||||
- **PMADDW division voodoo missed the `(Rt&0x7FFFFFFF)==0` trigger.** The `Cbz`
|
||||
treated a zero result as "skip", but ==0 *and* ==0x7FFFFFFF both trigger the
|
||||
`+0x70000000`. Added a `voodoo_check_rs` label so ==0 falls through to the Rs!=Rt
|
||||
check instead of skipping.
|
||||
- **Tests (`tests/ctest/core/arm64_emit_test.cpp`):**
|
||||
- Fixed the `refPSLLVW/PSRLVW/PSRAVW` oracles — they replicated the *buggy*
|
||||
4-lane model, so the gtests were green against wrong code. Now mirror MMI.cpp.
|
||||
- Added `MMI_PMADDW_VoodooZeroRt` (Rt.UL[0] ∈ {0, 0x80000000}, Rs≠Rt) — the shared
|
||||
inA/inB inputs never make `Rt&0x7FFFFFFF==0`, so the ==0 path had zero coverage.
|
||||
|
||||
**Decisions & rationale:**
|
||||
- Lesson reinforced: a passing gtest only proves the emitter matches its *oracle*.
|
||||
When an oracle is hand-derived from the interpreter, re-derive it independently
|
||||
before trusting "bit-exact". Both bugs hid behind self-consistent-but-wrong tests.
|
||||
|
||||
**Blockers / open questions:** none.
|
||||
|
||||
**Verified:** `unittests` 100% (Arm64EmitEE 270/270, +1 voodoo test); `pcsx2-qt`
|
||||
builds arm64. Live game verification still pending.
|
||||
|
||||
**Next step:** Phase 4.4 recLUT (parked on `armjit-reclut-wip` until BIOS stall
|
||||
solved), or game compatibility testing.
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-05 — Phase 5.4 MMI correctness pass: decode rewrite + emit fixes + tests
|
||||
|
||||
**Goal:** Review the 5 unpushed MMI commits + the uncommitted misc-ops batch for
|
||||
|
||||
+12
-2
@@ -30,8 +30,18 @@ untested, and the committed test file did not even compile):
|
||||
- **QFSRV** stays on the interpreter (its shift amount is the runtime SA register
|
||||
`cpuRegs.sa`, not an instruction immediate) — the only intentional MMI fallback.
|
||||
|
||||
**Verified:** `pcsx2-qt` builds arm64; unittests 100% (Arm64EmitEE 269/269, core
|
||||
354/354). Live game verification still pending.
|
||||
**Second correctness pass (2026-06-05):** a re-review found two more bugs that the
|
||||
first pass's tests missed because the oracles were self-consistent with the buggy code:
|
||||
- **PSLLVW/PSRLVW/PSRAVW** emitted four independent 32-bit lane shifts. The interpreter
|
||||
shifts only lanes 0 and 2 and sign-extends each 32-bit result to a full 64-bit
|
||||
doubleword. Rewrote them; the `refPSxxVW` test oracles (which replicated the bug) were
|
||||
corrected to mirror MMI.cpp.
|
||||
- **PMADDW division voodoo** skipped the `(Rt&0x7FFFFFFF)==0` trigger (only ==0x7FFFFFFF
|
||||
was handled). Fixed; added `MMI_PMADDW_VoodooZeroRt` to cover the ==0 path that inA/inB
|
||||
never reach.
|
||||
|
||||
**Verified:** `pcsx2-qt` builds arm64; unittests 100% (Arm64EmitEE 270/270). Live game
|
||||
verification still pending.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+42
-75
@@ -430,112 +430,75 @@ void armEmitPEXCW(u32 rd, u32 rt)
|
||||
// =============================================================================
|
||||
// Parallel variable shifts (Phase 5.4 continuation)
|
||||
// =============================================================================
|
||||
// These shift each 32-bit lane by the amount specified in the corresponding
|
||||
// lane of GPR[rs]. The shift amount is masked to 5 bits per lane (& 0x1F).
|
||||
// IMPORTANT: despite the "VW" name, the interpreter (MMI.cpp PSLLVW/PSRLVW/
|
||||
// PSRAVW) does NOT shift four independent 32-bit lanes. It shifts only lanes 0
|
||||
// and 2 of Rt (each by the matching lane of Rs, masked to 5 bits) and writes the
|
||||
// 32-bit result *sign-extended to a full 64-bit doubleword*:
|
||||
//
|
||||
// ARM64 NEON does not have a direct "shift each lane by unsigned vector amount"
|
||||
// instruction for 32-bit lanes. We use scalar GPR operations for correctness:
|
||||
// load each 32-bit lane, shift by the corresponding amount, and store back.
|
||||
// Rd.SD[0] = (s64)(s32)(Rt.UL[0] <</>> (Rs.UL[0] & 0x1F)); // fills Rd.UD[0]
|
||||
// Rd.SD[1] = (s64)(s32)(Rt.UL[2] <</>> (Rs.UL[2] & 0x1F)); // fills Rd.UD[1]
|
||||
//
|
||||
// The GPR stores 4 x 32-bit lanes in a 128-bit register. We pack two 32-bit
|
||||
// results into each 64-bit store (SD[0] = {UL[0], UL[1]}, SD[1] = {UL[2], UL[3]}).
|
||||
// So each doubleword's high word is the sign fill of its low word, NOT a shift of
|
||||
// Rt.UL[1]/Rt.UL[3]. We compute each lane in a w-register (the variable shift form
|
||||
// already masks the amount mod 32 == & 0x1F), Sxtw it to 64 bits, and store the
|
||||
// whole doubleword.
|
||||
//
|
||||
// Caller-saved GPRs used as scratch: x9-x12 (avoiding x16 which is VIXL scratch).
|
||||
// Caller-saved GPRs used as scratch: x9-x10 (avoiding x16 which is VIXL scratch).
|
||||
|
||||
// --- PSLLVW: parallel logical shift left by GPR[rs] -------------------------
|
||||
// Output[i] = Rt[i] << (Rs[i] & 0x1F) for 32-bit lanes i=0..3
|
||||
void armEmitPSLLVW(u32 rd, u32 rs, u32 rt)
|
||||
{
|
||||
if (rd == 0)
|
||||
return;
|
||||
|
||||
// Process lanes 0 and 1 -> pack into SD[0]
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt)));
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 4));
|
||||
armAsm->Ldr(a64::w11, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs)));
|
||||
armAsm->Ldr(a64::w12, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 4));
|
||||
|
||||
armAsm->Lsl(a64::w9, a64::w9, a64::w11);
|
||||
armAsm->Lsl(a64::w10, a64::w10, a64::w12);
|
||||
|
||||
// Pack two 32-bit results into one 64-bit register: result[0] | (result[1] << 32)
|
||||
armAsm->Bfi(a64::x9, a64::x10, 32, 32);
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt))); // Rt.UL[0]
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs))); // Rs.UL[0]
|
||||
armAsm->Lsl(a64::w9, a64::w9, a64::w10);
|
||||
armAsm->Sxtw(a64::x9, a64::w9); // sign-extend to UD[0]
|
||||
armAsm->Str(a64::x9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rd)));
|
||||
|
||||
// Process lanes 2 and 3 -> pack into SD[1]
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 8));
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 12));
|
||||
armAsm->Ldr(a64::w11, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 8));
|
||||
armAsm->Ldr(a64::w12, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 12));
|
||||
|
||||
armAsm->Lsl(a64::w9, a64::w9, a64::w11);
|
||||
armAsm->Lsl(a64::w10, a64::w10, a64::w12);
|
||||
|
||||
armAsm->Bfi(a64::x9, a64::x10, 32, 32);
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 8)); // Rt.UL[2]
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 8)); // Rs.UL[2]
|
||||
armAsm->Lsl(a64::w9, a64::w9, a64::w10);
|
||||
armAsm->Sxtw(a64::x9, a64::w9); // sign-extend to UD[1]
|
||||
armAsm->Str(a64::x9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rd) + 8));
|
||||
}
|
||||
|
||||
// --- PSRLVW: parallel logical (unsigned) shift right by GPR[rs] -------------
|
||||
// Output[i] = Rt[i] >> (Rs[i] & 0x1F) (zero-fill from left)
|
||||
void armEmitPSRLVW(u32 rd, u32 rs, u32 rt)
|
||||
{
|
||||
if (rd == 0)
|
||||
return;
|
||||
|
||||
// Process lanes 0 and 1 -> pack into SD[0]
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt)));
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 4));
|
||||
armAsm->Ldr(a64::w11, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs)));
|
||||
armAsm->Ldr(a64::w12, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 4));
|
||||
|
||||
armAsm->Lsr(a64::w9, a64::w9, a64::w11);
|
||||
armAsm->Lsr(a64::w10, a64::w10, a64::w12);
|
||||
|
||||
armAsm->Bfi(a64::x9, a64::x10, 32, 32);
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt))); // Rt.UL[0]
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs))); // Rs.UL[0]
|
||||
armAsm->Lsr(a64::w9, a64::w9, a64::w10);
|
||||
armAsm->Sxtw(a64::x9, a64::w9); // sign-extend to UD[0]
|
||||
armAsm->Str(a64::x9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rd)));
|
||||
|
||||
// Process lanes 2 and 3 -> pack into SD[1]
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 8));
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 12));
|
||||
armAsm->Ldr(a64::w11, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 8));
|
||||
armAsm->Ldr(a64::w12, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 12));
|
||||
|
||||
armAsm->Lsr(a64::w9, a64::w9, a64::w11);
|
||||
armAsm->Lsr(a64::w10, a64::w10, a64::w12);
|
||||
|
||||
armAsm->Bfi(a64::x9, a64::x10, 32, 32);
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 8)); // Rt.UL[2]
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 8)); // Rs.UL[2]
|
||||
armAsm->Lsr(a64::w9, a64::w9, a64::w10);
|
||||
armAsm->Sxtw(a64::x9, a64::w9); // sign-extend to UD[1]
|
||||
armAsm->Str(a64::x9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rd) + 8));
|
||||
}
|
||||
|
||||
// --- PSRAVW: parallel arithmetic (signed) shift right by GPR[rs] ------------
|
||||
// Output[i] = Rt[i] >> (Rs[i] & 0x1F) (sign-extend from left)
|
||||
void armEmitPSRAVW(u32 rd, u32 rs, u32 rt)
|
||||
{
|
||||
if (rd == 0)
|
||||
return;
|
||||
|
||||
// Process lanes 0 and 1 -> pack into SD[0]
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt)));
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 4));
|
||||
armAsm->Ldr(a64::w11, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs)));
|
||||
armAsm->Ldr(a64::w12, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 4));
|
||||
|
||||
armAsm->Asr(a64::w9, a64::w9, a64::w11);
|
||||
armAsm->Asr(a64::w10, a64::w10, a64::w12);
|
||||
|
||||
armAsm->Bfi(a64::x9, a64::x10, 32, 32);
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt))); // Rt.SL[0]
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs))); // Rs.UL[0]
|
||||
armAsm->Asr(a64::w9, a64::w9, a64::w10);
|
||||
armAsm->Sxtw(a64::x9, a64::w9); // sign-extend to UD[0]
|
||||
armAsm->Str(a64::x9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rd)));
|
||||
|
||||
// Process lanes 2 and 3 -> pack into SD[1]
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 8));
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 12));
|
||||
armAsm->Ldr(a64::w11, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 8));
|
||||
armAsm->Ldr(a64::w12, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 12));
|
||||
|
||||
armAsm->Asr(a64::w9, a64::w9, a64::w11);
|
||||
armAsm->Asr(a64::w10, a64::w10, a64::w12);
|
||||
|
||||
armAsm->Bfi(a64::x9, a64::x10, 32, 32);
|
||||
armAsm->Ldr(a64::w9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rt) + 8)); // Rt.SL[2]
|
||||
armAsm->Ldr(a64::w10, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rs) + 8)); // Rs.UL[2]
|
||||
armAsm->Asr(a64::w9, a64::w9, a64::w10);
|
||||
armAsm->Sxtw(a64::x9, a64::w9); // sign-extend to UD[1]
|
||||
armAsm->Str(a64::x9, a64::MemOperand(RESTATEPTR, EE_GPR_OFFSET(rd) + 8));
|
||||
}
|
||||
|
||||
@@ -693,12 +656,16 @@ static void emitPMADDWLane(u32 rd, u32 rs, u32 rt, u32 srcOff, u32 loOff, u32 hi
|
||||
if (voodoo)
|
||||
{
|
||||
// Condition: ((Rt&0x7FFFFFFF)==0 || ==0x7FFFFFFF) && Rs != Rt
|
||||
// Both ==0 and ==0x7FFFFFFF are triggers, so a zero result must fall
|
||||
// through to the Rs!=Rt check, not skip the add.
|
||||
a64::Label voodoo_check_rs;
|
||||
armAsm->And(a64::w12, a64::w10, 0x7FFFFFFF);
|
||||
armAsm->Cbz(a64::w12, &voodoo_done);
|
||||
armAsm->Cbz(a64::w12, &voodoo_check_rs); // ==0 -> still a trigger
|
||||
armAsm->Cmp(a64::w12, 0x7FFFFFFF);
|
||||
armAsm->B(&voodoo_done, a64::ne);
|
||||
armAsm->B(&voodoo_done, a64::ne); // neither 0 nor 0x7FFFFFFF -> no voodoo
|
||||
armAsm->Bind(&voodoo_check_rs);
|
||||
armAsm->Cmp(a64::w9, a64::w10);
|
||||
armAsm->B(&voodoo_done, a64::eq);
|
||||
armAsm->B(&voodoo_done, a64::eq); // Rs == Rt -> no voodoo
|
||||
armAsm->Mov(a64::w12, 0x70000000);
|
||||
armAsm->Add(a64::x11, a64::x11, a64::x12); // temp2 += 0x70000000
|
||||
armAsm->Bind(&voodoo_done);
|
||||
|
||||
@@ -2426,9 +2426,11 @@ namespace mmiref
|
||||
static MQ refPSRAW(MQ t, u32 sa) { MQ d{}; for (int n = 0; n < 4; n++) d.ul[n] = (u32)(t.sl[n] >> (sa & 0x1F)); return d; }
|
||||
|
||||
// Parallel variable shifts (amount from GPR[rs], 5-bit masked per lane).
|
||||
static MQ refPSLLVW(MQ s, MQ t) { MQ d{}; for (int n = 0; n < 4; n++) d.ul[n] = t.ul[n] << (s.ul[n] & 0x1F); return d; }
|
||||
static MQ refPSRLVW(MQ s, MQ t) { MQ d{}; for (int n = 0; n < 4; n++) d.ul[n] = t.ul[n] >> (s.ul[n] & 0x1F); return d; }
|
||||
static MQ refPSRAVW(MQ s, MQ t) { MQ d{}; for (int n = 0; n < 4; n++) d.ul[n] = (u32)(t.sl[n] >> (s.ul[n] & 0x1F)); return d; }
|
||||
// PxxVW operate on lanes 0 and 2 only, sign-extending each 32-bit result to a
|
||||
// full 64-bit doubleword (MMI.cpp: Rd.SD[k] = (s64)(s32)(Rt.UL[2k] op (Rs.UL[2k] & 0x1F))).
|
||||
static MQ refPSLLVW(MQ s, MQ t) { MQ d{}; d.ud[0] = (uint64_t)(int64_t)(int32_t)(t.ul[0] << (s.ul[0] & 0x1F)); d.ud[1] = (uint64_t)(int64_t)(int32_t)(t.ul[2] << (s.ul[2] & 0x1F)); return d; }
|
||||
static MQ refPSRLVW(MQ s, MQ t) { MQ d{}; d.ud[0] = (uint64_t)(int64_t)(int32_t)(t.ul[0] >> (s.ul[0] & 0x1F)); d.ud[1] = (uint64_t)(int64_t)(int32_t)(t.ul[2] >> (s.ul[2] & 0x1F)); return d; }
|
||||
static MQ refPSRAVW(MQ s, MQ t) { MQ d{}; d.ud[0] = (uint64_t)(int64_t)(t.sl[0] >> (s.ul[0] & 0x1F)); d.ud[1] = (uint64_t)(int64_t)(t.sl[2] >> (s.ul[2] & 0x1F)); return d; }
|
||||
|
||||
// Lane permutes (Phase 5.4 continuation).
|
||||
// PINTH: interleave low half of Rt with high half of Rs (halfwords)
|
||||
@@ -2978,6 +2980,29 @@ MMI_MACACC_TEST(PMSUBH)
|
||||
MMI_MACACC_TEST(PHMADH)
|
||||
MMI_MACACC_TEST(PHMSBH)
|
||||
|
||||
// PMADDW lane-0 division voodoo: the (Rt & 0x7FFFFFFF) == 0 trigger (Rt == 0 or
|
||||
// Rt == 0x80000000, with Rs != Rt) must add 0x70000000 just like ==0x7FFFFFFF.
|
||||
// The shared inA/inB inputs never make Rt.UL[0] & 0x7FFFFFFF == 0, so cover it here.
|
||||
TEST(Arm64EmitEE, MMI_PMADDW_VoodooZeroRt)
|
||||
{
|
||||
using mmiref::MQ;
|
||||
auto check = [](uint32_t rtLane0) {
|
||||
MQ s{}, t{}, loIn{}, hiIn{};
|
||||
s.ul[0] = 0x12345678; t.ul[0] = rtLane0; // lane 0: Rs != Rt, (Rt&0x7FFFFFFF)==0
|
||||
s.ul[2] = 0x0000abcd; t.ul[2] = 0x00000003; // lane 1: ordinary
|
||||
hiIn.ul[0] = 0x000000ff; hiIn.ul[2] = 0x00000011;
|
||||
loIn.ul[0] = 0x00000007; loIn.ul[2] = 0x00000009;
|
||||
AccResult j = runMMIMACAcc(armEmitPMADDW, s, t, loIn, hiIn);
|
||||
MQ rlo{}, rhi{}, rrd{};
|
||||
mmiref::refPMADDW(s, t, loIn, hiIn, rlo, rhi, rrd);
|
||||
EXPECT_TRUE(eqMQ(j.lo, rlo)) << "PMADDW LO (Rt lane0 0x" << std::hex << rtLane0 << ")";
|
||||
EXPECT_TRUE(eqMQ(j.hi, rhi)) << "PMADDW HI (Rt lane0 0x" << std::hex << rtLane0 << ")";
|
||||
EXPECT_TRUE(eqMQ(j.rd, rrd)) << "PMADDW rd (Rt lane0 0x" << std::hex << rtLane0 << ")";
|
||||
};
|
||||
check(0x00000000);
|
||||
check(0x80000000);
|
||||
}
|
||||
|
||||
// Misc MMI ops.
|
||||
TEST(Arm64EmitEE, MMI_PLZCW)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user