Decode the unused bits of shifts and Allegrex bit ops as the PSP does

srl/srlv rotate when the low bit of their unused field (rs or sa) is
set and shift when it's clear, whatever the other bits hold
(cpu/cpu_alu/shift_encodings). The interpreter treated values above 1 as
invalid and the other cores shifted.

The Allegrex bit ops decode only some bits of sa, so all 32 values are
one of wsbh, wsbw, seb, seh and bitrev (cpu/cpu_alu/allegrex_encodings).
AllegrexBitOp() gives the canonical one for every core. Special3
function 0x18 does nothing on the PSP, where it was decoded as an alias
of 0x20.

The interpreter dispatch is regenerated, and its generator now lists
each instruction name once. CPUEquivalence no longer skips the srl/srlv
encodings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-14 10:56:48 +02:00
1 parent 5ca12f0050
commit 4849ef020e
10 files changed
+132 -88

No files matched your search

+6 -6
View File
@@ -519,10 +519,10 @@ namespace MIPSComp
// WARNING : ROTR
switch (op & 0x3f) {
case 0: CompShiftImm(op, ST_LSL, sa); break; //sll
case 2: CompShiftImm(op, rs == 1 ? ST_ROR : ST_LSR, sa); break; //srl
case 2: CompShiftImm(op, (rs & 1) ? ST_ROR : ST_LSR, sa); break; //srl
case 3: CompShiftImm(op, ST_ASR, sa); break; //sra
case 4: CompShiftVar(op, ST_LSL); break; //sllv
case 6: CompShiftVar(op, fd == 1 ? ST_ROR : ST_LSR); break; //srlv
case 6: CompShiftVar(op, (fd & 1) ? ST_ROR : ST_LSR); break; //srlv
case 7: CompShiftVar(op, ST_ASR); break; //srav
default:
@@ -602,7 +602,7 @@ namespace MIPSComp
if (rd == 0)
return;
switch ((op >> 6) & 31) {
switch (AllegrexBitOp(op)) {
case 16: // seb // R(rd) = SignExtend8ToU32(R(rt));
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, SignExtend8ToU32(gpr.GetImm(rt)));
@@ -656,8 +656,8 @@ namespace MIPSComp
if (rd == 0)
return;
switch (op & 0x3ff) {
case 0xA0: //wsbh
switch (AllegrexBitOp(op)) {
case 2: //wsbh
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, ((gpr.GetImm(rt) & 0xFF00FF00) >> 8) | ((gpr.GetImm(rt) & 0x00FF00FF) << 8));
} else {
@@ -665,7 +665,7 @@ namespace MIPSComp
REV16(gpr.R(rd), gpr.R(rt));
}
break;
case 0xE0: //wsbw
case 3: //wsbw
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, swap32(gpr.GetImm(rt)));
} else {
+6 -6
View File
@@ -422,10 +422,10 @@ void Arm64Jit::Comp_ShiftType(MIPSOpcode op) {
// WARNING : ROTR
switch (op & 0x3f) {
case 0: CompShiftImm(op, ST_LSL, sa); break; //sll
case 2: CompShiftImm(op, rs == 1 ? ST_ROR : ST_LSR, sa); break; //srl
case 2: CompShiftImm(op, (rs & 1) ? ST_ROR : ST_LSR, sa); break; //srl
case 3: CompShiftImm(op, ST_ASR, sa); break; //sra
case 4: CompShiftVar(op, ST_LSL); break; //sllv
case 6: CompShiftVar(op, fd == 1 ? ST_ROR : ST_LSR); break; //srlv
case 6: CompShiftVar(op, (fd & 1) ? ST_ROR : ST_LSR); break; //srlv
case 7: CompShiftVar(op, ST_ASR); break; //srav
default:
DISABLE;
@@ -493,7 +493,7 @@ void Arm64Jit::Comp_Allegrex(MIPSOpcode op) {
if (rd == 0)
return;
switch ((op >> 6) & 31) {
switch (AllegrexBitOp(op)) {
case 16: // seb // R(rd) = SignExtend8ToU32(R(rt));
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, SignExtend8ToU32(gpr.GetImm(rt)));
@@ -543,8 +543,8 @@ void Arm64Jit::Comp_Allegrex2(MIPSOpcode op) {
if (rd == 0)
return;
switch (op & 0x3ff) {
case 0xA0: //wsbh
switch (AllegrexBitOp(op)) {
case 2: //wsbh
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, ((gpr.GetImm(rt) & 0xFF00FF00) >> 8) | ((gpr.GetImm(rt) & 0x00FF00FF) << 8));
} else {
@@ -552,7 +552,7 @@ void Arm64Jit::Comp_Allegrex2(MIPSOpcode op) {
REV16(gpr.R(rd), gpr.R(rt));
}
break;
case 0xE0: //wsbw
case 3: //wsbw
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, swap32(gpr.GetImm(rt)));
} else {
+7 -7
View File
@@ -212,13 +212,13 @@ void IRFrontend::Comp_ShiftType(MIPSOpcode op) {
if (rd == MIPS_REG_ZERO)
return;
// WARNING: srl/rotr and srlv/rotrv share encodings (differentiated using unused bits.)
// WARNING: srl/rotr and srlv/rotrv share encodings, told apart by the low bit of the unused field.
switch (op & 0x3f) {
case 0: CompShiftImm(op, IROp::ShlImm, sa); break; //sll
case 2: CompShiftImm(op, (rs == 1 ? IROp::RorImm : IROp::ShrImm), sa); break; //srl
case 2: CompShiftImm(op, ((rs & 1) ? IROp::RorImm : IROp::ShrImm), sa); break; //srl
case 3: CompShiftImm(op, IROp::SarImm, sa); break; //sra
case 4: CompShiftVar(op, IROp::Shl); break; //sllv
case 6: CompShiftVar(op, (sa == 1 ? IROp::Ror : IROp::Shr)); break; //srlv
case 6: CompShiftVar(op, ((sa & 1) ? IROp::Ror : IROp::Shr)); break; //srlv
case 7: CompShiftVar(op, IROp::Sar); break; //srav
default:
@@ -292,7 +292,7 @@ void IRFrontend::Comp_Allegrex(MIPSOpcode op) {
if (rd == MIPS_REG_ZERO)
return;
switch ((op >> 6) & 31) {
switch (AllegrexBitOp(op)) {
case 16: // seb // R(rd) = SignExtend8ToU32(R(rt));
ir.Write(IROp::Ext8to32, rd, rt);
break;
@@ -320,11 +320,11 @@ void IRFrontend::Comp_Allegrex2(MIPSOpcode op) {
if (rd == MIPS_REG_ZERO)
return;
switch (op & 0x3ff) {
case 0xA0: //wsbh
switch (AllegrexBitOp(op)) {
case 2: //wsbh
ir.Write(IROp::BSwap16, rd, rt);
break;
case 0xE0: //wsbw
case 3: //wsbw
ir.Write(IROp::BSwap32, rd, rt);
break;
default:
+14 -22
View File
@@ -967,34 +967,26 @@ namespace MIPSInt {
{
case 0: R(rd) = R(rt) << sa; break; //sll
case 2:
if (_RS == 0) //srl
{
R(rd) = R(rt) >> sa;
break;
}
else if (_RS == 1) //rotr
{
// Only the low bit of the unused rs field counts (cpu/cpu_alu/shift_encodings).
if (_RS & 1) { //rotr
R(rd) = __rotr(R(rt), sa);
break;
} else { //srl
R(rd) = R(rt) >> sa;
}
else
goto wrong;
break;
case 3: R(rd) = (u32)(((s32)R(rt)) >> sa); break; //sra
case 4: R(rd) = R(rt) << (R(rs) & 0x1F); break; //sllv
case 6:
if (_FD == 0) { //srlv
R(rd) = R(rt) >> (R(rs)&0x1F);
break;
} else if (_FD == 1) { // rotrv
// Same for the unused sa field.
if (_FD & 1) { // rotrv
R(rd) = __rotr(R(rt), R(rs));
break;
} else {
goto wrong;
} else { //srlv
R(rd) = R(rt) >> (R(rs) & 0x1F);
}
break;
case 7: R(rd) = (u32)(((s32)R(rt)) >> (R(rs) & 0x1F)); break; //srav
default:
wrong:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
break;
}
@@ -1011,7 +1003,7 @@ namespace MIPSInt {
return;
}
switch ((op >> 6) & 31) {
switch (AllegrexBitOp(op)) {
case 16: // seb
R(rd) = SignExtend8ToU32(R(rt));
break;
@@ -1041,11 +1033,11 @@ namespace MIPSInt {
return;
}
switch (op & 0x3ff) {
case 0xA0: //wsbh
switch (AllegrexBitOp(op)) {
case 2: //wsbh
R(rd) = ((R(rt) & 0xFF00FF00) >> 8) | ((R(rt) & 0x00FF00FF) << 8);
break;
case 0xE0: //wsbw
case 3: //wsbw
R(rd) = swap32(R(rt));
break;
default:
+36 -6
View File
@@ -399,19 +399,49 @@ int ExecInstruction(MIPSState *mips, MIPSOpcode op) {
case 4:
MIPSInt::Int_Special3(mips, op);
return 1;
// nop
case 24:
MIPSInt::Int_Sync(mips, op);
return 1;
case 32:
{
switch ((op.encoding >> 6) & 0x1f) {
// wsbh, wsbw
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
MIPSInt::Int_Allegrex2(mips, op);
return 1;
// seb, bitrev, seh
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
MIPSInt::Int_Allegrex(mips, op);
return 1;
default:
@@ -622,7 +652,7 @@ int ExecInstruction(MIPSState *mips, MIPSOpcode op) {
case 21:
MIPSInt::Int_Vcmov(mips, op);
return 2;
// vwbn, vwbn, vwbn, vwbn, vwbn, vwbn, vwbn, vwbn
// vwbn
case 24:
case 25:
case 26:
@@ -647,7 +677,7 @@ int ExecInstruction(MIPSState *mips, MIPSOpcode op) {
case 55:
{
switch ((op.encoding >> 23) & 0x7) {
// vpfxs, vpfxs, vpfxt, vpfxt, vpfxd, vpfxd
// vpfxs, vpfxt, vpfxd
case 0:
case 1:
case 2:
@@ -668,14 +698,14 @@ int ExecInstruction(MIPSState *mips, MIPSOpcode op) {
case 60:
{
switch ((op.encoding >> 21) & 0x1f) {
// vmmul, vmmul, vmmul, vmmul
// vmmul
case 0:
case 1:
case 2:
case 3:
MIPSInt::Int_Vmmul(mips, op);
return 2;
// v(h)tfm2, v(h)tfm2, v(h)tfm2, v(h)tfm2, v(h)tfm3, v(h)tfm3, v(h)tfm3, v(h)tfm3, v(h)tfm4, v(h)tfm4, v(h)tfm4, v(h)tfm4
// v(h)tfm2, v(h)tfm3, v(h)tfm4
case 4:
case 5:
case 6:
@@ -690,14 +720,14 @@ int ExecInstruction(MIPSState *mips, MIPSOpcode op) {
case 15:
MIPSInt::Int_Vtfm(mips, op);
return 2;
// vmscl, vmscl, vmscl, vmscl
// vmscl
case 16:
case 17:
case 18:
case 19:
MIPSInt::Int_Vmscl(mips, op);
return 2;
// vcrsp.t/vqmul.q, vcrsp.t/vqmul.q, vcrsp.t/vqmul.q, vcrsp.t/vqmul.q
// vcrsp.t/vqmul.q
case 20:
case 21:
case 22:
+14
View File
@@ -73,6 +73,20 @@ inline uint32_t SignExtend16ToU32(MIPSOpcode op) {
return SignExtend16ToU32(op.encoding);
}
// The Allegrex bit ops (special3, function 0x20) only decode some bits of the sa field, so every
// value is one of them (cpu/cpu_alu/allegrex_encodings). Returns the canonical sa: 2 wsbh, 3 wsbw,
// 16 seb, 20 bitrev, 24 seh.
inline int AllegrexBitOp(MIPSOpcode op) {
int sa = (op >> 6) & 31;
if (!(sa & 0x10)) {
return 2 | (sa & 1);
}
if (sa & 4) {
return 20;
}
return (sa & 8) ? 24 : 16;
}
namespace MIPSCodeUtils
{
u32 GetCallTarget(u32 addr);
+3 -3
View File
@@ -254,9 +254,9 @@ namespace MIPSDis
int rd = _RD;
int sa = (op>>6) & 0x1F;
const char *name = MIPSGetName(op);
if (((op & 0x3f) == 2) && rs == 1)
if (((op & 0x3f) == 2) && (rs & 1))
name = "rotr";
if (((op & 0x3f) == 6) && sa == 1)
if (((op & 0x3f) == 6) && (sa & 1))
name = "rotrv";
snprintf(out, outSize, "%s\t%s, %s, 0x%X", name, RN(rd), RN(rt), sa);
}
@@ -267,7 +267,7 @@ namespace MIPSDis
int rd = _RD;
int sa = (op>>6) & 0x1F;
const char *name = MIPSGetName(op);
if (((op & 0x3f) == 6) && sa == 1)
if (((op & 0x3f) == 6) && (sa & 1))
name = "rotrv";
snprintf(out, outSize, "%s\t%s, %s, %s", name, RN(rd), RN(rt), RN(rs));
}
+40 -24
View File
@@ -287,8 +287,8 @@ static const MIPSInstruction tableSpecial3[64] = // 011111 ..... ..... ..... ...
//16
INVALID_X_8,
//24
// TODO: Is this right? Or should it only be 32? Comment above (24) was mistakenly 32 before.
ENCODING(ALLEGREX0),
// Does nothing, whatever the other fields hold (cpu/cpu_alu/allegrex_encodings).
INSTR("nop", JITFUNC(Comp_DoNothing), Dis_Generic, Int_Sync, 0),
INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID,
//32
ENCODING(ALLEGREX0),
@@ -773,35 +773,46 @@ static const MIPSInstruction tableVFPU9[32] = // 110100 00010 xxxxx . ....... .
INVALID, INVALID, INVALID, INVALID,
};
static const MIPSInstruction tableALLEGREX0[32] = // 011111 ..... ..... ..... xxxxx 100000 - or ending with 011000?
// Only some bits of the op are decoded, so every value is one of the five (see AllegrexBitOp).
static const MIPSInstruction tableALLEGREX0[32] = // 011111 ..... ..... ..... xxxxx 100000
{
INVALID,
INVALID,
INSTR("wsbh", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbw", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INVALID, INVALID, INVALID, INVALID,
INSTR("wsbh", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbw", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbh", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbw", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbh", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbw", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
//8
INVALID_X_8,
INSTR("wsbh", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbw", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbh", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbw", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbh", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbw", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbh", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
INSTR("wsbw", JITFUNC(Comp_Allegrex2), Dis_Allegrex2, Int_Allegrex2, IN_RT|OUT_RD),
//16
INSTR("seb", JITFUNC(Comp_Allegrex), Dis_Allegrex,Int_Allegrex, IN_RT|OUT_RD),
INVALID,
INVALID,
INVALID,
INSTR("seb", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("seb", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("seb", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("seb", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
//20
INSTR("bitrev", JITFUNC(Comp_Allegrex), Dis_Allegrex,Int_Allegrex, IN_RT|OUT_RD),
INVALID,
INVALID,
INVALID,
INSTR("bitrev", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("bitrev", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("bitrev", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("bitrev", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
//24
INSTR("seh", JITFUNC(Comp_Allegrex), Dis_Allegrex,Int_Allegrex, IN_RT|OUT_RD),
INVALID,
INVALID,
INVALID,
INSTR("seh", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("seh", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("seh", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("seh", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
//28
INVALID,
INVALID,
INVALID,
INVALID,
INSTR("bitrev", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("bitrev", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("bitrev", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
INSTR("bitrev", JITFUNC(Comp_Allegrex), Dis_Allegrex, Int_Allegrex, IN_RT|OUT_RD),
};
static const MIPSInstruction tableEMU[4] = {
@@ -973,10 +984,15 @@ static void EmitDispatchLevel(std::string &out, MipsEncoding encoding, int inden
if (instr.altEncoding == Instruc) {
std::string names;
std::vector<std::string_view> seen;
for (u32 idx : group) {
std::string_view name = table[idx].name;
if (std::find(seen.begin(), seen.end(), name) != seen.end())
continue;
seen.push_back(name);
if (!names.empty())
names += ", ";
names += table[idx].name;
names += name;
}
out += ind + StringFromFormat("// %s\n", names.c_str());
for (u32 idx : group)
+6 -6
View File
@@ -706,11 +706,11 @@ namespace MIPSComp
switch (op & 0x3f)
{
case 0: CompShiftImm(op, &XEmitter::SHL, &ShiftType_ImmLogicalLeft); break;
case 2: CompShiftImm(op, rs == 1 ? &XEmitter::ROR : &XEmitter::SHR, rs == 1 ? &ShiftType_ImmRotateRight : &ShiftType_ImmLogicalRight); break; // srl, rotr
case 2: CompShiftImm(op, (rs & 1) ? &XEmitter::ROR : &XEmitter::SHR, (rs & 1) ? &ShiftType_ImmRotateRight : &ShiftType_ImmLogicalRight); break; // srl, rotr
case 3: CompShiftImm(op, &XEmitter::SAR, &ShiftType_ImmArithRight); break; // sra
case 4: CompShiftVar(op, &XEmitter::SHL, &ShiftType_ImmLogicalLeft); break; //sllv
case 6: CompShiftVar(op, fd == 1 ? &XEmitter::ROR : &XEmitter::SHR, fd == 1 ? &ShiftType_ImmRotateRight : &ShiftType_ImmLogicalRight); break; //srlv
case 6: CompShiftVar(op, (fd & 1) ? &XEmitter::ROR : &XEmitter::SHR, (fd & 1) ? &ShiftType_ImmRotateRight : &ShiftType_ImmLogicalRight); break; //srlv
case 7: CompShiftVar(op, &XEmitter::SAR, &ShiftType_ImmArithRight); break; //srav
default:
@@ -818,7 +818,7 @@ namespace MIPSComp
if (rd == MIPS_REG_ZERO)
return;
switch ((op >> 6) & 31)
switch (AllegrexBitOp(op))
{
case 16: // seb // R(rd) = SignExtend8ToU32(R(rt));
if (gpr.IsImm(rt))
@@ -926,9 +926,9 @@ namespace MIPSComp
if (rd == MIPS_REG_ZERO)
return;
switch (op & 0x3ff)
switch (AllegrexBitOp(op))
{
case 0xA0: //wsbh
case 2: //wsbh
if (gpr.IsImm(rt)) {
u32 rtImm = gpr.GetImm(rt);
gpr.SetImm(rd, ((rtImm & 0xFF00FF00) >> 8) | ((rtImm & 0x00FF00FF) << 8));
@@ -943,7 +943,7 @@ namespace MIPSComp
ROR(32, gpr.R(rd), Imm8(16));
gpr.UnlockAll();
break;
case 0xE0: //wsbw
case 3: //wsbw
if (gpr.IsImm(rt)) {
gpr.SetImm(rd, swap32(gpr.GetImm(rt)));
break;
-8
View File
@@ -567,14 +567,6 @@ static u32 GenerateFuzzOp(FuzzRng &rng, const char *classes) {
if (!IsFuzzableName(MIPSGetName(MIPSOpcode(op)))) {
continue;
}
// srl and srlv with other bits than rotr's set in the unused field: the interpreter does
// nothing, the rest shift. TODO: Test on hardware.
if (top == 0 && (op & 0x3F) == 2 && ((op >> 21) & 0x1F) > 1) {
continue;
}
if (top == 0 && (op & 0x3F) == 6 && ((op >> 6) & 0x1F) > 1) {
continue;
}
// Not by MEMTYPE, which movz and movn alias with their CONDTYPE.
if (std::find(std::begin(memOps), std::end(memOps), top) != std::end(memOps)) {
op = FixupMemOp(op, rng);