From 90517ace59f0c092c850c472882944bc572b6774 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 Aug 2022 13:24:10 -0700 Subject: [PATCH] irjit: Validate alignment in slow memory mode. --- Core/Core.cpp | 1 + Core/Core.h | 1 + Core/MIPS/IR/IRCompVFPU.cpp | 4 +++ Core/MIPS/IR/IRFrontend.cpp | 2 +- Core/MIPS/IR/IRInterpreter.cpp | 24 +++++++++++------- Core/MIPS/IR/IRPassSimplify.cpp | 44 ++++++++++++++++++++++++++++++++- 6 files changed, 65 insertions(+), 11 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 5fb5357352..e723107d30 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -419,6 +419,7 @@ const char *MemoryExceptionTypeAsString(MemoryExceptionType type) { case MemoryExceptionType::WRITE_WORD: return "Write Word"; case MemoryExceptionType::READ_BLOCK: return "Read Block"; case MemoryExceptionType::WRITE_BLOCK: return "Read/Write Block"; + case MemoryExceptionType::ALIGNMENT: return "Alignment"; default: return "N/A"; } diff --git a/Core/Core.h b/Core/Core.h index 62018b832d..894accd6ae 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -93,6 +93,7 @@ enum class MemoryExceptionType { WRITE_WORD, READ_BLOCK, WRITE_BLOCK, + ALIGNMENT, }; enum class ExecExceptionType { JUMP, diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 2a5e00a861..4fffc6765d 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -330,6 +330,8 @@ namespace MIPSComp { ir.Write(IROp::LoadVec4, vregs[0], rs, ir.AddConstant(imm)); } else { // Let's not even bother with "vertical" loads for now. + if (!g_Config.bFastMemory) + ir.Write({ IROp::ValidateAddress128, { 0 }, (u8)rs, 0, (u32)imm }); ir.Write(IROp::LoadFloat, vregs[0], rs, ir.AddConstant(imm)); ir.Write(IROp::LoadFloat, vregs[1], rs, ir.AddConstant(imm + 4)); ir.Write(IROp::LoadFloat, vregs[2], rs, ir.AddConstant(imm + 8)); @@ -342,6 +344,8 @@ namespace MIPSComp { ir.Write(IROp::StoreVec4, vregs[0], rs, ir.AddConstant(imm)); } else { // Let's not even bother with "vertical" stores for now. + if (!g_Config.bFastMemory) + ir.Write({ IROp::ValidateAddress128, { 0 }, (u8)rs, 1, (u32)imm }); ir.Write(IROp::StoreFloat, vregs[0], rs, ir.AddConstant(imm)); ir.Write(IROp::StoreFloat, vregs[1], rs, ir.AddConstant(imm + 4)); ir.Write(IROp::StoreFloat, vregs[2], rs, ir.AddConstant(imm + 8)); diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index 71fdea7eb6..66a3efbafe 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -260,11 +260,11 @@ void IRFrontend::DoJit(u32 em_address, std::vector &instructions, u32 &m IRWriter *code = &ir; if (!js.hadBreakpoints) { static const IRPassFunc passes[] = { + &ApplyMemoryValidation, &RemoveLoadStoreLeftRight, &OptimizeFPMoves, &PropagateConstants, &PurgeTemps, - &ApplyMemoryValidation, // &ReorderLoadStore, // &MergeLoadStore, // &ThreeOpToTwoOp, diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 363a52cd31..bc97b52b73 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -80,14 +80,20 @@ u32 RunMemCheck(u32 pc, u32 addr) { } template -u32 RunValidateAddress(u32 pc, u32 addr) { - if (!Memory::IsValidRange(addr, alignment)) { - Core_MemoryException(addr, pc, MemoryExceptionType::UNKNOWN); +u32 RunValidateAddress(u32 pc, u32 addr, u32 isWrite) { + const auto toss = [&](MemoryExceptionType t) { + Core_MemoryException(addr, pc, t); return coreState != CORE_RUNNING ? 1 : 0; + }; + + if (!Memory::IsValidRange(addr, alignment)) { + MemoryExceptionType t = isWrite == 1 ? MemoryExceptionType::WRITE_WORD : MemoryExceptionType::READ_WORD; + if (alignment > 4) + t = isWrite ? MemoryExceptionType::WRITE_BLOCK : MemoryExceptionType::READ_BLOCK; + return toss(t); } if (alignment > 1 && (addr & (alignment - 1)) != 0) { - Core_MemoryException(addr, pc, MemoryExceptionType::UNKNOWN); - return coreState != CORE_RUNNING ? 1 : 0; + return toss(MemoryExceptionType::ALIGNMENT); } return 0; } @@ -156,25 +162,25 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { break; case IROp::ValidateAddress8: - if (RunValidateAddress<1>(mips->pc, mips->r[inst->src1] + inst->constant)) { + if (RunValidateAddress<1>(mips->pc, mips->r[inst->src1] + inst->constant, inst->src2)) { CoreTiming::ForceCheck(); return mips->pc; } break; case IROp::ValidateAddress16: - if (RunValidateAddress<2>(mips->pc, mips->r[inst->src1] + inst->constant)) { + if (RunValidateAddress<2>(mips->pc, mips->r[inst->src1] + inst->constant, inst->src2)) { CoreTiming::ForceCheck(); return mips->pc; } break; case IROp::ValidateAddress32: - if (RunValidateAddress<4>(mips->pc, mips->r[inst->src1] + inst->constant)) { + if (RunValidateAddress<4>(mips->pc, mips->r[inst->src1] + inst->constant, inst->src2)) { CoreTiming::ForceCheck(); return mips->pc; } break; case IROp::ValidateAddress128: - if (RunValidateAddress<16>(mips->pc, mips->r[inst->src1] + inst->constant)) { + if (RunValidateAddress<16>(mips->pc, mips->r[inst->src1] + inst->constant, inst->src2)) { CoreTiming::ForceCheck(); return mips->pc; } diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index 24f2d8cfda..5725454841 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -1447,9 +1447,51 @@ bool ApplyMemoryValidation(const IRWriter &in, IRWriter &out, const IROptions &o if (g_Config.bFastMemory) DISABLE; + const auto addValidate = [&out](IROp validate, const IRInst &inst, bool isStore) { + out.Write({ validate, { 0 }, inst.src1, isStore ? (u8)1 : (u8)0, inst.constant }); + }; + + // TODO: Could be smart about not double-validating an address that has a load / store, etc. bool logBlocks = false; for (IRInst inst : in.GetInstructions()) { - // TODO + switch (inst.op) { + case IROp::Load8: + case IROp::Load8Ext: + case IROp::Store8: + addValidate(IROp::ValidateAddress8, inst, inst.op == IROp::Store8); + break; + + case IROp::Load16: + case IROp::Load16Ext: + case IROp::Store16: + addValidate(IROp::ValidateAddress16, inst, inst.op == IROp::Store16); + break; + + case IROp::Load32: + case IROp::LoadFloat: + case IROp::Store32: + case IROp::StoreFloat: + addValidate(IROp::ValidateAddress32, inst, inst.op == IROp::Store32 || inst.op == IROp::StoreFloat); + break; + + case IROp::LoadVec4: + case IROp::StoreVec4: + addValidate(IROp::ValidateAddress128, inst, inst.op == IROp::StoreVec4); + break; + + case IROp::Load32Left: + case IROp::Load32Right: + case IROp::Store32Left: + case IROp::Store32Right: + // This explicitly does not require alignment, so validate as an 8-bit operation. + addValidate(IROp::ValidateAddress8, inst, inst.op == IROp::Store32Left || inst.op == IROp::Store32Right); + break; + + default: + break; + } + + // Always write out the original. We're only adding. out.Write(inst); } return logBlocks;