Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

irjit: Validate alignment in slow memory mode #15879

Merged
merged 2 commits into from
Aug 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Core/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand Down
1 change: 1 addition & 0 deletions Core/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ enum class MemoryExceptionType {
WRITE_WORD,
READ_BLOCK,
WRITE_BLOCK,
ALIGNMENT,
};
enum class ExecExceptionType {
JUMP,
Expand Down
4 changes: 4 additions & 0 deletions Core/MIPS/IR/IRCompVFPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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));
Expand Down
1 change: 1 addition & 0 deletions Core/MIPS/IR/IRFrontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions Core/MIPS/IR/IRInst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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", "" },
Expand Down
7 changes: 7 additions & 0 deletions Core/MIPS/IR/IRInst.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
44 changes: 44 additions & 0 deletions Core/MIPS/IR/IRInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
68 changes: 68 additions & 0 deletions Core/MIPS/IR/IRPassSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions Core/MIPS/IR/IRPassSimplify.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);