HLE: Add mechanics for sliced replacements.

This commit is contained in:
Unknown W. Brackets
2023-12-16 09:08:58 -08:00
parent 5311997753
commit 053831bf4d
12 changed files with 75 additions and 6 deletions
+1 -1
View File
@@ -1738,7 +1738,7 @@ bool CanReplaceJalTo(u32 dest, const ReplacementTableEntry **entry, u32 *funcSiz
return false;
}
if ((*entry)->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT | REPFLAG_DISABLED)) {
if ((*entry)->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT | REPFLAG_DISABLED | REPFLAG_SLICED)) {
// If it's a hook, we can't replace the jal, we have to go inside the func.
return false;
}
+2
View File
@@ -48,6 +48,8 @@ enum {
REPFLAG_HOOKENTER = 0x04,
// Only hooks jr ra, so only use on funcs that have that.
REPFLAG_HOOKEXIT = 0x08,
// Function may take a lot of time and execute in slices (executed multiple times.)
REPFLAG_SLICED = 0x10,
};
// Kind of similar to HLE functions but with different data.
+11
View File
@@ -617,7 +617,18 @@ void ArmJit::Comp_ReplacementFunc(MIPSOpcode op)
} else {
ApplyRoundingMode();
RestoreDowncount();
CMPI2R(R0, 0, SCRATCHREG2);
FixupBranch positive = B_CC(CC_GE);
RSB(R0, R0, Operand2(0));
MovFromPC(R1);
FixupBranch done = B();
SetJumpTarget(positive);
LDR(R1, CTXREG, MIPS_REG_RA * 4);
SetJumpTarget(done);
WriteDownCountR(R0);
WriteExitDestInR(R1);
js.compiling = false;
+10 -1
View File
@@ -242,7 +242,16 @@ void Arm64JitBackend::CompIR_System(IRInst inst) {
QuickCallFunction(SCRATCH2_64, GetReplacementFunc(inst.constant)->replaceFunc);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
SUB(DOWNCOUNTREG, DOWNCOUNTREG, W0);
// Absolute value the result and subtract.
CMP(W0, 0);
CSNEG(SCRATCH1, W0, W0, CC_PL);
SUB(DOWNCOUNTREG, DOWNCOUNTREG, SCRATCH1);
// W0 might be the mapped reg, but there's only one.
// Set dest reg to the sign of the result.
regs_.Map(inst);
ASR(regs_.R(inst.dest), W0, 31);
break;
case IROp::Break:
+11
View File
@@ -614,7 +614,18 @@ void Arm64Jit::Comp_ReplacementFunc(MIPSOpcode op)
} else {
ApplyRoundingMode();
LoadStaticRegisters();
CMPI2R(W0, 0);
FixupBranch positive = B(CC_GE);
NEG(W0, W0);
MovFromPC(W1);
FixupBranch done = B();
SetJumpTarget(positive);
LDR(INDEX_UNSIGNED, W1, CTXREG, MIPS_REG_RA * 4);
SetJumpTarget(done);
WriteDownCountR(W0);
WriteExitDestInR(W1);
js.compiling = false;
+4 -1
View File
@@ -164,7 +164,7 @@ void IRFrontend::Comp_ReplacementFunc(MIPSOpcode op) {
FlushAll();
RestoreRoundingMode();
ir.Write(IROp::SetPCConst, 0, ir.AddConstant(GetCompilerPC()));
ir.Write(IROp::CallReplacement, 0, ir.AddConstant(index));
ir.Write(IROp::CallReplacement, IRTEMP_0, ir.AddConstant(index));
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
// Compile the original instruction at this address. We ignore cycles for hooks.
@@ -172,7 +172,10 @@ void IRFrontend::Comp_ReplacementFunc(MIPSOpcode op) {
MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this);
} else {
ApplyRoundingMode();
// If IRTEMP_0 was set to 1, it means the replacement needs to run again (sliced.)
// This is necessary for replacements that take a lot of cycles.
ir.Write(IROp::Downcount, 0, ir.AddConstant(js.downcountAmount));
ir.Write(IROp::ExitToConstIfNeq, ir.AddConstant(GetCompilerPC()), IRTEMP_0, MIPS_REG_ZERO);
ir.Write(IROp::ExitToReg, 0, MIPS_REG_RA, 0);
js.compiling = false;
}
+1 -1
View File
@@ -165,7 +165,7 @@ static const IRMeta irMeta[] = {
{ IROp::Break, "Break", "", IRFLAG_EXIT },
{ IROp::SetPC, "SetPC", "_G" },
{ IROp::SetPCConst, "SetPC", "_C" },
{ IROp::CallReplacement, "CallRepl", "_C", IRFLAG_BARRIER },
{ IROp::CallReplacement, "CallRepl", "GC", IRFLAG_BARRIER },
{ IROp::Breakpoint, "Breakpoint", "_C", IRFLAG_BARRIER },
{ IROp::MemoryCheck, "MemoryCheck", "IGC", IRFLAG_BARRIER },
+2 -1
View File
@@ -1089,7 +1089,8 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) {
int funcIndex = inst->constant;
const ReplacementTableEntry *f = GetReplacementFunc(funcIndex);
int cycles = f->replaceFunc();
mips->downcount -= cycles;
mips->r[inst->dest] = cycles < 0 ? -1 : 0;
mips->downcount -= cycles < 0 ? -cycles : cycles;
break;
}
+5 -1
View File
@@ -1038,13 +1038,17 @@ namespace MIPSInt
int index = op.encoding & 0xFFFFFF;
const ReplacementTableEntry *entry = GetReplacementFunc(index);
if (entry && entry->replaceFunc && (entry->flags & REPFLAG_DISABLED) == 0) {
entry->replaceFunc();
int cycles = entry->replaceFunc();
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
// Interpret the original instruction under the hook.
MIPSInterpret(Memory::Read_Instruction(PC, true));
} else if (cycles < 0) {
// Leave PC unchanged, call the replacement again (assumes args are modified.)
currentMIPS->downcount += cycles;
} else {
PC = currentMIPS->r[MIPS_REG_RA];
currentMIPS->downcount -= cycles;
}
} else {
if (!entry || !entry->replaceFunc) {
+7
View File
@@ -220,6 +220,13 @@ void RiscVJitBackend::CompIR_System(IRInst inst) {
QuickCallFunction(GetReplacementFunc(inst.constant)->replaceFunc, SCRATCH2);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
regs_.Map(inst);
SRAIW(regs_.R(inst.dest), X10, 31);
// Absolute value trick: if neg, abs(x) == (x ^ -1) + 1.
XOR(X10, X10, regs_.R(inst.dest));
SUBW(X10, X10, regs_.R(inst.dest));
SUB(DOWNCOUNTREG, DOWNCOUNTREG, X10);
break;
+10
View File
@@ -658,8 +658,18 @@ void Jit::Comp_ReplacementFunc(MIPSOpcode op) {
ApplyRoundingMode();
MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this);
} else {
CMP(32, R(EAX), Imm32(0));
FixupBranch positive = J_CC(CC_GE);
MOV(32, R(ECX), MIPSSTATE_VAR(pc));
ADD(32, MIPSSTATE_VAR(downcount), R(EAX));
FixupBranch done = J();
SetJumpTarget(positive);
MOV(32, R(ECX), MIPSSTATE_VAR(r[MIPS_REG_RA]));
SUB(32, MIPSSTATE_VAR(downcount), R(EAX));
SetJumpTarget(done);
ApplyRoundingMode();
// Need to set flags again, ApplyRoundingMode destroyed them (and EAX.)
SUB(32, MIPSSTATE_VAR(downcount), Imm8(0));
+11
View File
@@ -232,6 +232,17 @@ void X64JitBackend::CompIR_System(IRInst inst) {
ABI_CallFunction(GetReplacementFunc(inst.constant)->replaceFunc);
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
LoadStaticRegisters();
// Since we flushed above, and we're mapping write, EAX should be safe.
regs_.Map(inst);
MOV(32, regs_.R(inst.dest), R(EAX));
NEG(32, R(EAX));
// Set it back if it negate made it negative. That's the absolute value.
CMOVcc(32, EAX, regs_.R(inst.dest), CC_S);
// Now set the dest to the sign bit status.
SAR(32, regs_.R(inst.dest), Imm8(31));
if (jo.downcountInRegister)
SUB(32, R(DOWNCOUNTREG), R(EAX));
else