mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-14 10:09:32 +01:00
Merge pull request #15879 from unknownbrackets/ir-slowmem
irjit: Validate alignment in slow memory mode
This commit is contained in:
9 files changed
+132
No files matched your search
@@ -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";
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ enum class MemoryExceptionType {
|
||||
WRITE_WORD,
|
||||
READ_BLOCK,
|
||||
WRITE_BLOCK,
|
||||
ALIGNMENT,
|
||||
};
|
||||
enum class ExecExceptionType {
|
||||
JUMP,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -260,6 +260,7 @@ void IRFrontend::DoJit(u32 em_address, std::vector<IRInst> &instructions, u32 &m
|
||||
IRWriter *code = &ir;
|
||||
if (!js.hadBreakpoints) {
|
||||
static const IRPassFunc passes[] = {
|
||||
&ApplyMemoryValidation,
|
||||
&RemoveLoadStoreLeftRight,
|
||||
&OptimizeFPMoves,
|
||||
&PropagateConstants,
|
||||
|
||||
@@ -163,6 +163,11 @@ static const IRMeta irMeta[] = {
|
||||
{ IROp::Breakpoint, "Breakpoint", "", IRFLAG_EXIT },
|
||||
{ IROp::MemoryCheck, "MemoryCheck", "_GC", IRFLAG_EXIT },
|
||||
|
||||
{ IROp::ValidateAddress8, "ValidAddr8", "_GC", IRFLAG_EXIT },
|
||||
{ IROp::ValidateAddress16, "ValidAddr16", "_GC", IRFLAG_EXIT },
|
||||
{ IROp::ValidateAddress32, "ValidAddr32", "_GC", IRFLAG_EXIT },
|
||||
{ IROp::ValidateAddress128, "ValidAddr128", "_GC", IRFLAG_EXIT },
|
||||
|
||||
{ IROp::RestoreRoundingMode, "RestoreRoundingMode", "" },
|
||||
{ IROp::ApplyRoundingMode, "ApplyRoundingMode", "" },
|
||||
{ IROp::UpdateRoundingMode, "UpdateRoundingMode", "" },
|
||||
|
||||
@@ -213,8 +213,15 @@ enum class IROp : u8 {
|
||||
SetPCConst, // hack to make replacement know PC
|
||||
CallReplacement,
|
||||
Break,
|
||||
|
||||
// Debugging breakpoints.
|
||||
Breakpoint,
|
||||
MemoryCheck,
|
||||
|
||||
ValidateAddress8,
|
||||
ValidateAddress16,
|
||||
ValidateAddress32,
|
||||
ValidateAddress128,
|
||||
};
|
||||
|
||||
enum IRComparison {
|
||||
|
||||
@@ -79,6 +79,25 @@ u32 RunMemCheck(u32 pc, u32 addr) {
|
||||
return coreState != CORE_RUNNING ? 1 : 0;
|
||||
}
|
||||
|
||||
template <uint32_t alignment>
|
||||
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) {
|
||||
return toss(MemoryExceptionType::ALIGNMENT);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We cannot use NEON on ARM32 here until we make it a hard dependency. We can, however, on ARM64.
|
||||
u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) {
|
||||
const IRInst *end = inst + count;
|
||||
@@ -142,6 +161,31 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) {
|
||||
mips->r[inst->dest] = ReverseBits32(mips->r[inst->src1]);
|
||||
break;
|
||||
|
||||
case IROp::ValidateAddress8:
|
||||
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, inst->src2)) {
|
||||
CoreTiming::ForceCheck();
|
||||
return mips->pc;
|
||||
}
|
||||
break;
|
||||
case IROp::ValidateAddress32:
|
||||
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, inst->src2)) {
|
||||
CoreTiming::ForceCheck();
|
||||
return mips->pc;
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Load8:
|
||||
mips->r[inst->dest] = Memory::ReadUnchecked_U8(mips->r[inst->src1] + inst->constant);
|
||||
break;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Common/BitSet.h"
|
||||
#include "Common/Data/Convert/SmallDataConvert.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/MIPS/IR/IRInterpreter.h"
|
||||
#include "Core/MIPS/IR/IRPassSimplify.h"
|
||||
#include "Core/MIPS/IR/IRRegCache.h"
|
||||
@@ -622,6 +623,18 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::ValidateAddress8:
|
||||
case IROp::ValidateAddress16:
|
||||
case IROp::ValidateAddress32:
|
||||
case IROp::ValidateAddress128:
|
||||
if (gpr.IsImm(inst.src1)) {
|
||||
out.Write(inst.op, inst.dest, 0, out.AddConstant(gpr.GetImm(inst.src1) + inst.constant));
|
||||
} else {
|
||||
gpr.MapIn(inst.src1);
|
||||
goto doDefault;
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Downcount:
|
||||
case IROp::SetPCConst:
|
||||
goto doDefault;
|
||||
@@ -1428,3 +1441,58 @@ bool MergeLoadStore(const IRWriter &in, IRWriter &out, const IROptions &opts) {
|
||||
}
|
||||
return logBlocks;
|
||||
}
|
||||
|
||||
bool ApplyMemoryValidation(const IRWriter &in, IRWriter &out, const IROptions &opts) {
|
||||
CONDITIONAL_DISABLE;
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
@@ -14,3 +14,4 @@ bool ThreeOpToTwoOp(const IRWriter &in, IRWriter &out, const IROptions &opts);
|
||||
bool OptimizeFPMoves(const IRWriter &in, IRWriter &out, const IROptions &opts);
|
||||
bool ReorderLoadStore(const IRWriter &in, IRWriter &out, const IROptions &opts);
|
||||
bool MergeLoadStore(const IRWriter &in, IRWriter &out, const IROptions &opts);
|
||||
bool ApplyMemoryValidation(const IRWriter &in, IRWriter &out, const IROptions &opts);
|
||||
Reference in new issue
Block a user