Merge pull request #22683 from hrydgard/address-space

CPU: Give each MIPSState an address space for its memory accesses
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-10-20 21:12:37 +02:00
commit 47c97b92aa
22 files changed
+177 -96

No files matched your search

+2 -2
View File
@@ -111,7 +111,7 @@ void ArmJitBackend::GenerateFixedCode(MIPSState *mipsState) {
VPUSH(D8, 8);
// Fixed registers, these are always kept when in Jit context.
MOVP2R(MEMBASEREG, Memory::base);
MOVP2R(MEMBASEREG, mipsState->space->base);
MOVP2R(CTXREG, mipsState);
LoadStaticRegisters();
@@ -157,7 +157,7 @@ void ArmJitBackend::GenerateFixedCode(MIPSState *mipsState) {
static_assert(Memory::BLOCK_SHADOW_ENTRY_MASK == 0x0FFFFFFC, "BICs below assume the 32-bit shadow mask");
BIC(SCRATCH1, SCRATCH1, Operand2(0xF0, 4)); // &= 0x0FFFFFFF. Rotation is to the right, in 2-bit increments.
BIC(SCRATCH1, SCRATCH1, Operand2(3));
MOVP2R(SCRATCH2, Memory::blockShadow);
MOVP2R(SCRATCH2, mipsState->space->blockShadow);
hooks_.dispatchFetch = GetCodePtr();
LDR(SCRATCH1, SCRATCH2, SCRATCH1);
// The entry is the block's offset into the code space, or 0 for none.
+3 -2
View File
@@ -51,6 +51,7 @@ static void ShowPC(void *membase, void *jitbase) {
}
void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
regs_.SetMemoryBase(mipsState->space->base);
// This will be used as a writable scratch area, always 32-bit accessible.
const u8 *start = AlignCodePage();
if (DebugProfilerEnabled()) {
@@ -155,7 +156,7 @@ void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
fp_.ABI_PushRegisters(regs_to_save, regs_to_save_fp);
// Fixed registers, these are always kept when in Jit context.
MOVP2R(MEMBASEREG, Memory::base);
MOVP2R(MEMBASEREG, mipsState->space->base);
MOVP2R(CTXREG, mipsState);
MOVP2R(JITBASEREG, GetBasePtr());
@@ -205,7 +206,7 @@ void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
MovFromPC(SCRATCH1);
WriteDebugPC(SCRATCH1);
ANDI2R(SCRATCH1, SCRATCH1, Memory::BLOCK_SHADOW_ENTRY_MASK);
MOVP2R(SCRATCH2_64, Memory::blockShadow);
MOVP2R(SCRATCH2_64, mipsState->space->blockShadow);
hooks_.dispatchFetch = GetCodePtr();
LDR(SCRATCH1, SCRATCH2_64, SCRATCH1_64);
// The entry is the block's offset into the code space, or 0 for none.
+1 -1
View File
@@ -60,7 +60,7 @@ void Arm64JitBackend::CompIR_Exit(IRInst inst) {
B(hooks_.dispatcher);
SetJumpTarget(inTime);
ANDI2R(SCRATCH1, SCRATCH1, Memory::BLOCK_SHADOW_ENTRY_MASK);
MOVP2R(SCRATCH2_64, Memory::blockShadow);
MOVP2R(SCRATCH2_64, mips_->space->blockShadow);
LDR(SCRATCH1, SCRATCH2_64, SCRATCH1_64);
FixupBranch found = CBNZ(SCRATCH1);
B(dispatcherNoCheck_);
+2 -2
View File
@@ -123,7 +123,7 @@ void Arm64IRRegCache::EmitLoadStaticRegisters() {
emit_->LDR(INDEX_UNSIGNED, FromNativeReg(allocs[i].nr), CTXREG, offset);
if (allocs[i].pointerified && jo_->enablePointerify) {
ARM64Reg r64 = FromNativeReg64(allocs[i].nr);
uint32_t membaseHigh = (uint32_t)((uint64_t)Memory::base >> 32);
uint32_t membaseHigh = (uint32_t)((uint64_t)membase_ >> 32);
emit_->MOVK(r64, membaseHigh & 0xFFFF, SHIFT_32);
if (membaseHigh & 0xFFFF0000)
emit_->MOVK(r64, membaseHigh >> 16, SHIFT_48);
@@ -347,7 +347,7 @@ void Arm64IRRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) {
#endif
emit_->ADD(r, r, MEMBASEREG);
} else {
uint32_t membaseHigh = (uint32_t)((uint64_t)Memory::base >> 32);
uint32_t membaseHigh = (uint32_t)((uint64_t)membase_ >> 32);
emit_->MOVK(r, membaseHigh & 0xFFFF, SHIFT_32);
if (membaseHigh & 0xFFFF0000)
emit_->MOVK(r, membaseHigh >> 16, SHIFT_48);
+27 -26
View File
@@ -86,9 +86,9 @@ static u32 RunKernelLoad(MIPSState *mips, u32 addr) {
u32 value;
if (IsValidAlignedAddress<size>(addr)) {
switch (size) {
case 1: value = Memory::ReadUnchecked_U8(addr); break;
case 2: value = Memory::ReadUnchecked_U16(addr); break;
default: value = Memory::ReadUnchecked_U32(addr); break;
case 1: value = mips->space->ReadUnchecked_U8(addr); break;
case 2: value = mips->space->ReadUnchecked_U16(addr); break;
default: value = mips->space->ReadUnchecked_U32(addr); break;
}
} else if (Memory::IsMMIOAccess(addr)) {
value = MMIO::Read(mips, addr, size);
@@ -105,9 +105,9 @@ template <int size>
static void RunKernelStore(MIPSState *mips, u32 addr, u32 value) {
if (IsValidAlignedAddress<size>(addr)) {
switch (size) {
case 1: Memory::WriteUnchecked_U8((u8)value, addr); break;
case 2: Memory::WriteUnchecked_U16((u16)value, addr); break;
default: Memory::WriteUnchecked_U32(value, addr); break;
case 1: mips->space->WriteUnchecked_U8((u8)value, addr); break;
case 2: mips->space->WriteUnchecked_U16((u16)value, addr); break;
default: mips->space->WriteUnchecked_U32(value, addr); break;
}
} else if (Memory::IsMMIOAccess(addr)) {
MMIO::Write(mips, addr, size, value);
@@ -159,6 +159,7 @@ static void RunKernelStore(MIPSState *mips, u32 addr, u32 value) {
} while (false)
u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) {
const Memory::AddressSpace &space = *mips->space;
#if IR_THREADED_DISPATCH
static const void *dispatch[256];
static bool dispatchReady = false;
@@ -423,25 +424,25 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) {
IR_NEXT;
IR_CASE(Load8)
mips->r[inst->dest] = Memory::ReadUnchecked_U8(mips->r[inst->src1] + inst->constant);
mips->r[inst->dest] = space.ReadUnchecked_U8(mips->r[inst->src1] + inst->constant);
IR_NEXT;
IR_CASE(Load8Ext)
mips->r[inst->dest] = SignExtend8ToU32(Memory::ReadUnchecked_U8(mips->r[inst->src1] + inst->constant));
mips->r[inst->dest] = SignExtend8ToU32(space.ReadUnchecked_U8(mips->r[inst->src1] + inst->constant));
IR_NEXT;
IR_CASE(Load16)
mips->r[inst->dest] = Memory::ReadUnchecked_U16(mips->r[inst->src1] + inst->constant);
mips->r[inst->dest] = space.ReadUnchecked_U16(mips->r[inst->src1] + inst->constant);
IR_NEXT;
IR_CASE(Load16Ext)
mips->r[inst->dest] = SignExtend16ToU32(Memory::ReadUnchecked_U16(mips->r[inst->src1] + inst->constant));
mips->r[inst->dest] = SignExtend16ToU32(space.ReadUnchecked_U16(mips->r[inst->src1] + inst->constant));
IR_NEXT;
IR_CASE(Load32)
mips->r[inst->dest] = Memory::ReadUnchecked_U32(mips->r[inst->src1] + inst->constant);
mips->r[inst->dest] = space.ReadUnchecked_U32(mips->r[inst->src1] + inst->constant);
IR_NEXT;
IR_CASE(Load32Left)
{
u32 addr = mips->r[inst->src1] + inst->constant;
u32 shift = (addr & 3) * 8;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
u32 mem = space.ReadUnchecked_U32(addr & 0xfffffffc);
u32 destMask = 0x00ffffff >> shift;
mips->r[inst->dest] = (mips->r[inst->dest] & destMask) | (mem << (24 - shift));
IR_NEXT;
@@ -450,52 +451,52 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) {
{
u32 addr = mips->r[inst->src1] + inst->constant;
u32 shift = (addr & 3) * 8;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
u32 mem = space.ReadUnchecked_U32(addr & 0xfffffffc);
u32 destMask = 0xffffff00 << (24 - shift);
mips->r[inst->dest] = (mips->r[inst->dest] & destMask) | (mem >> shift);
IR_NEXT;
}
IR_CASE(Load32Linked)
if (inst->dest != MIPS_REG_ZERO)
mips->r[inst->dest] = Memory::ReadUnchecked_U32(mips->r[inst->src1] + inst->constant);
mips->r[inst->dest] = space.ReadUnchecked_U32(mips->r[inst->src1] + inst->constant);
mips->llBit = 1;
IR_NEXT;
IR_CASE(LoadFloat)
mips->f[inst->dest] = Memory::ReadUnchecked_Float(mips->r[inst->src1] + inst->constant);
mips->f[inst->dest] = space.ReadUnchecked_Float(mips->r[inst->src1] + inst->constant);
IR_NEXT;
IR_CASE(Store8)
Memory::WriteUnchecked_U8(mips->r[inst->src3], mips->r[inst->src1] + inst->constant);
space.WriteUnchecked_U8(mips->r[inst->src3], mips->r[inst->src1] + inst->constant);
IR_NEXT;
IR_CASE(Store16)
Memory::WriteUnchecked_U16(mips->r[inst->src3], mips->r[inst->src1] + inst->constant);
space.WriteUnchecked_U16(mips->r[inst->src3], mips->r[inst->src1] + inst->constant);
IR_NEXT;
IR_CASE(Store32)
Memory::WriteUnchecked_U32(mips->r[inst->src3], mips->r[inst->src1] + inst->constant);
space.WriteUnchecked_U32(mips->r[inst->src3], mips->r[inst->src1] + inst->constant);
IR_NEXT;
IR_CASE(Store32Left)
{
u32 addr = mips->r[inst->src1] + inst->constant;
u32 shift = (addr & 3) * 8;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
u32 mem = space.ReadUnchecked_U32(addr & 0xfffffffc);
u32 memMask = 0xffffff00 << shift;
u32 result = (mips->r[inst->src3] >> (24 - shift)) | (mem & memMask);
Memory::WriteUnchecked_U32(result, addr & 0xfffffffc);
space.WriteUnchecked_U32(result, addr & 0xfffffffc);
IR_NEXT;
}
IR_CASE(Store32Right)
{
u32 addr = mips->r[inst->src1] + inst->constant;
u32 shift = (addr & 3) * 8;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
u32 mem = space.ReadUnchecked_U32(addr & 0xfffffffc);
u32 memMask = 0x00ffffff >> (24 - shift);
u32 result = (mips->r[inst->src3] << shift) | (mem & memMask);
Memory::WriteUnchecked_U32(result, addr & 0xfffffffc);
space.WriteUnchecked_U32(result, addr & 0xfffffffc);
IR_NEXT;
}
IR_CASE(Store32Conditional)
if (mips->llBit) {
Memory::WriteUnchecked_U32(mips->r[inst->src3], mips->r[inst->src1] + inst->constant);
space.WriteUnchecked_U32(mips->r[inst->src3], mips->r[inst->src1] + inst->constant);
if (inst->dest != MIPS_REG_ZERO) {
mips->r[inst->dest] = 1;
}
@@ -504,20 +505,20 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) {
}
IR_NEXT;
IR_CASE(StoreFloat)
Memory::WriteUnchecked_Float(mips->f[inst->src3], mips->r[inst->src1] + inst->constant);
space.WriteUnchecked_Float(mips->f[inst->src3], mips->r[inst->src1] + inst->constant);
IR_NEXT;
IR_CASE(LoadVec4)
{
u32 base = mips->r[inst->src1] + inst->constant;
// This compiles to a nice SSE load/store on x86, and hopefully similar on ARM.
memcpy(&mips->f[inst->dest], Memory::GetPointerUnchecked(base), 4 * 4);
memcpy(&mips->f[inst->dest], space.GetPointerUnchecked(base), 4 * 4);
IR_NEXT;
}
IR_CASE(StoreVec4)
{
u32 base = mips->r[inst->src1] + inst->constant;
memcpy((float *)Memory::GetPointerUnchecked(base), &mips->f[inst->dest], 4 * 4);
memcpy((float *)space.GetPointerUnchecked(base), &mips->f[inst->dest], 4 * 4);
IR_NEXT;
}
+9 -8
View File
@@ -90,6 +90,7 @@ IRJit::IRJit(MIPSState *mipsState, bool compileToNative) : frontend_(mipsState->
frontend_.SetOptions(opts);
profiling_ = !g_Config.sJitProfileFile.empty();
blocks_.SetAddressSpace(mipsState->space);
}
IRJit::~IRJit() {
@@ -190,7 +191,7 @@ void IRJit::InvalidateBlock(int block_num) {
// Before Destroy, which forgets the block's address. Blocks linked to it jump straight in.
InvalidateTargetBlock(block_num);
blocks_.RemoveBlockFromPageLookup(block_num);
block->Destroy();
block->Destroy(blocks_.Space());
}
void IRJit::InvalidateCacheExcept(const std::vector<std::pair<u32, u32>> &keep) {
@@ -358,7 +359,7 @@ void IRInterpreterJit::RunBlocks() {
while (mips->downcount >= 0) {
// The block's offset into the IR arena, or 0 for none, or a replacement hook (handled by
// compiling a block).
const u32 offset = *Memory::GetBlockShadowEntry(mips->pc);
const u32 offset = *mips->space->GetBlockShadowEntry(mips->pc);
static_assert(Memory::BLOCK_SHADOW_HOOK_NO_JIT == 1, "IR arena reserves offsets 0 and 1");
if (offset > Memory::BLOCK_SHADOW_HOOK_NO_JIT) {
const IRInst *instPtr = arena_.data() + offset;
@@ -407,7 +408,7 @@ void IRInterpreterJit::RunBlocks() {
void JitBlockTable::Clear() {
for (JitBlock &block : blocks_) {
block.Destroy();
block.Destroy(*space_);
}
blocks_.clear();
byPage_.clear();
@@ -473,7 +474,7 @@ std::vector<int> JitBlockTable::FindInvalidatedBlockNumbers(u32 address, u32 len
void JitBlockTable::FinalizeBlock(int blockIndex) {
// TODO: What's different about preload blocks?
JitBlock &block = blocks_[blockIndex];
block.Finalize();
block.Finalize(*space_);
u32 startAddr, size;
block.GetRange(&startAddr, &size);
@@ -551,24 +552,24 @@ int JitBlockTable::GetBlockNumberFromStartAddress(u32 em_address) const {
return best;
}
void JitBlock::Finalize() {
void JitBlock::Finalize(const Memory::AddressSpace &space) {
// Check it wasn't invalidated, in case this is after preload.
// TODO: Allow reusing blocks when the code matches hash_ again, instead.
if (origAddr_ && Memory::IsValid4AlignedAddress(origAddr_)) {
origFirstOpcode_ = Memory::ReadUnchecked_Instruction(origAddr_);
_dbg_assert_(entry_ != 0);
Memory::WriteBlockShadow(origAddr_, entry_);
space.WriteBlockShadow(origAddr_, entry_);
} else {
WARN_LOG(Log::JIT, "Finalizing invalid block (entry: %d)", entry_);
}
}
void JitBlock::Destroy() {
void JitBlock::Destroy(const Memory::AddressSpace &space) {
if (origAddr_ && Memory::IsValid4AlignedAddress(origAddr_)) {
// A block at a mirror may have taken over the entry, leave that alone. One that never got an
// entry (the arena or code space filled while compiling it) has nothing in the shadow.
if (entry_ != 0)
Memory::ClearBlockShadow(origAddr_, entry_);
space.ClearBlockShadow(origAddr_, entry_);
// Let's mark this invalid so we don't try to clear it again.
origAddr_ = 0;
}
+12 -2
View File
@@ -80,8 +80,9 @@ public:
return hash_;
}
void Finalize();
void Destroy();
// Enter the block in the space's block shadow, and take it out.
void Finalize(const Memory::AddressSpace &space);
void Destroy(const Memory::AddressSpace &space);
// Only filled in by the IR interpreter, when profiling.
JitBlockProfileStats profileStats_{};
@@ -105,6 +106,14 @@ public:
Clear();
}
// Whose block shadow the blocks go in.
void SetAddressSpace(const Memory::AddressSpace *space) {
space_ = space;
}
const Memory::AddressSpace &Space() const {
return *space_;
}
void Clear();
std::vector<int> FindInvalidatedBlockNumbers(u32 address, u32 length);
void FinalizeBlock(int blockNum);
@@ -142,6 +151,7 @@ private:
u32 AddressToPage(u32 addr) const;
std::vector<JitBlock> blocks_;
std::unordered_map<u32, std::vector<int>> byPage_;
const Memory::AddressSpace *space_ = nullptr;
};
class IRJit;
+5
View File
@@ -163,6 +163,10 @@ public:
virtual ~IRNativeRegCacheBase() {}
virtual void Start(u32 startPC, const IRInst *instructions, int count);
// The guest memory base of the CPU the code is for, where pointerified registers point.
void SetMemoryBase(const u8 *base) {
membase_ = base;
}
void SetIRIndex(int index) {
irIndex_ = index;
}
@@ -259,6 +263,7 @@ protected:
bool IsValidFPR(IRReg r) const;
MIPSComp::JitOptions *jo_;
const u8 *membase_ = nullptr;
// The block being compiled, and its IR.
u32 irStartPC_ = 0;
const IRInst *irInsts_ = nullptr;
+18 -18
View File
@@ -379,7 +379,7 @@ namespace MIPSInt {
R(rt) = 0;
}
} else {
R(rt) = Memory::ReadUnchecked_U32(addr);
R(rt) = mips->space->ReadUnchecked_U32(addr);
}
}
mips->llBit = 1;
@@ -392,7 +392,7 @@ namespace MIPSInt {
else
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sc");
} else {
Memory::WriteUnchecked_U32(R(rt), addr);
mips->space->WriteUnchecked_U32(R(rt), addr);
}
// Report success even if the store got dropped - reporting failure just makes
// the usual retry loop spin on the same bad address forever.
@@ -472,7 +472,7 @@ namespace MIPSInt {
R(rt) = 0;
break;
}
R(rt) = SignExtend8ToU32(Memory::ReadUnchecked_U8(addr));
R(rt) = SignExtend8ToU32(mips->space->ReadUnchecked_U8(addr));
break; //lb
case 33:
if (!Memory::IsValid2AlignedAddress(addr)) {
@@ -485,7 +485,7 @@ namespace MIPSInt {
R(rt) = 0;
break;
}
R(rt) = SignExtend16ToU32(Memory::ReadUnchecked_U16(addr));
R(rt) = SignExtend16ToU32(mips->space->ReadUnchecked_U16(addr));
break; //lh
case 35:
if (!Memory::IsValid4AlignedAddress(addr)) {
@@ -498,7 +498,7 @@ namespace MIPSInt {
R(rt) = 0;
break;
}
R(rt) = Memory::ReadUnchecked_U32(addr);
R(rt) = mips->space->ReadUnchecked_U32(addr);
break; //lw
case 36:
if (!Memory::IsValidAddress(addr)) {
@@ -511,7 +511,7 @@ namespace MIPSInt {
R(rt) = 0;
break;
}
R(rt) = Memory::ReadUnchecked_U8(addr);
R(rt) = mips->space->ReadUnchecked_U8(addr);
break; //lbu
case 37:
if (!Memory::IsValid2AlignedAddress(addr)) {
@@ -524,7 +524,7 @@ namespace MIPSInt {
R(rt) = 0;
break;
}
R(rt) = Memory::ReadUnchecked_U16(addr);
R(rt) = mips->space->ReadUnchecked_U16(addr);
break; //lhu
case 40:
if (!Memory::IsValidAddress(addr)) {
@@ -536,7 +536,7 @@ namespace MIPSInt {
Core_MemoryException(addr, 1, PC, MemoryExceptionType::WRITE_WORD, "sb");
break;
}
Memory::WriteUnchecked_U8(R(rt), addr);
mips->space->WriteUnchecked_U8(R(rt), addr);
break; //sb
case 41:
if (!Memory::IsValid2AlignedAddress(addr)) {
@@ -548,7 +548,7 @@ namespace MIPSInt {
Core_MemoryException(addr, 2, PC, MemoryExceptionType::WRITE_WORD, "sh");
break;
}
Memory::WriteUnchecked_U16(R(rt), addr);
mips->space->WriteUnchecked_U16(R(rt), addr);
break; //sh
case 43:
if (!Memory::IsValid4AlignedAddress(addr)) {
@@ -560,7 +560,7 @@ namespace MIPSInt {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sw");
break;
}
Memory::WriteUnchecked_U32(R(rt), addr);
mips->space->WriteUnchecked_U32(R(rt), addr);
break; //sw
// When there's an LWL and an LWR together, we should be able to peephole optimize that
@@ -570,7 +570,7 @@ namespace MIPSInt {
// Not checking for alignment here - the actual read will be aligned.
u32 mem = 0;
if (Memory::IsValidAddress(addr)) {
mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
mem = mips->space->ReadUnchecked_U32(addr & 0xfffffffc);
} else if (Memory::IsMMIOAccess(addr)) {
mem = ReadMMIO(mips, addr & 0xfffffffc, 4);
} else {
@@ -587,7 +587,7 @@ namespace MIPSInt {
// Not checking for alignment here - the actual read will be aligned.
u32 mem = 0;
if (Memory::IsValidAddress(addr)) {
mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
mem = mips->space->ReadUnchecked_U32(addr & 0xfffffffc);
} else if (Memory::IsMMIOAccess(addr)) {
mem = ReadMMIO(mips, addr & 0xfffffffc, 4);
} else {
@@ -608,9 +608,9 @@ namespace MIPSInt {
break;
}
u32 shift = (addr & 3) * 8;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
u32 mem = mips->space->ReadUnchecked_U32(addr & 0xfffffffc);
u32 result = ( ( u32(R(rt)) >> (24 - shift) ) ) | ( mem & (0xffffff00 << shift) );
Memory::WriteUnchecked_U32(result, (addr & 0xfffffffc));
mips->space->WriteUnchecked_U32(result, (addr & 0xfffffffc));
}
break;
@@ -622,9 +622,9 @@ namespace MIPSInt {
break;
}
u32 shift = (addr & 3) << 3;
u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc);
u32 mem = mips->space->ReadUnchecked_U32(addr & 0xfffffffc);
u32 result = ( ( u32(R(rt)) << shift ) | (mem & (0x00ffffff >> (24 - shift)) ) );
Memory::WriteUnchecked_U32(result, (addr & 0xfffffffc));
mips->space->WriteUnchecked_U32(result, (addr & 0xfffffffc));
}
break;
@@ -652,7 +652,7 @@ namespace MIPSInt {
FI(ft) = 0;
break;
}
FI(ft) = Memory::ReadUnchecked_U32(addr);
FI(ft) = mips->space->ReadUnchecked_U32(addr);
break; //lwc1
case 57:
if (!Memory::IsValid4AlignedAddress(addr)) {
@@ -663,7 +663,7 @@ namespace MIPSInt {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "swc1");
break;
}
Memory::WriteUnchecked_U32(FI(ft), addr);
mips->space->WriteUnchecked_U32(FI(ft), addr);
break; //swc1
default:
_dbg_assert_msg_(false,"Trying to interpret FPULS instruction that can't be interpreted");
+16 -16
View File
@@ -262,7 +262,7 @@ namespace MIPSInt
}
// It's an LVL
for (int i = 0; i < offset + 1; i++) {
d[3 - i] = valid ? Memory::ReadUnchecked_Float(addr - 4 * i) : 0.0f;
d[3 - i] = valid ? mips->space->ReadUnchecked_Float(addr - 4 * i) : 0.0f;
}
} else {
if (!valid) {
@@ -270,7 +270,7 @@ namespace MIPSInt
}
// It's an LVR
for (int i = 0; i < (3 - offset) + 1; i++) {
d[i] = valid ? Memory::ReadUnchecked_Float(addr + 4 * i) : 0.0f;
d[i] = valid ? mips->space->ReadUnchecked_Float(addr + 4 * i) : 0.0f;
}
}
WriteLoadedVector(mips, d, vt);
@@ -288,15 +288,15 @@ namespace MIPSInt
}
#ifndef COMMON_BIG_ENDIAN
cf = reinterpret_cast<const float *>(Memory::GetPointerUnchecked(addr));
cf = reinterpret_cast<const float *>(mips->space->GetPointerUnchecked(addr));
WriteLoadedVector(mips, cf, vt);
#else
float lvqd[4];
lvqd[0] = Memory::ReadUnchecked_Float(addr);
lvqd[1] = Memory::ReadUnchecked_Float(addr + 4);
lvqd[2] = Memory::ReadUnchecked_Float(addr + 8);
lvqd[3] = Memory::ReadUnchecked_Float(addr + 12);
lvqd[0] = mips->space->ReadUnchecked_Float(addr);
lvqd[1] = mips->space->ReadUnchecked_Float(addr + 4);
lvqd[2] = mips->space->ReadUnchecked_Float(addr + 8);
lvqd[3] = mips->space->ReadUnchecked_Float(addr + 12);
WriteLoadedVector(mips, lvqd, vt);
#endif
@@ -315,7 +315,7 @@ namespace MIPSInt
// It's an SVL
for (int i = 0; i < offset + 1; i++)
{
Memory::WriteUnchecked_Float(d[3 - i], addr - i * 4);
mips->space->WriteUnchecked_Float(d[3 - i], addr - i * 4);
}
} else {
if (!Memory::IsValid4AlignedAddress(addr)) {
@@ -324,7 +324,7 @@ namespace MIPSInt
}
// It's an SVR
for (int i = 0; i < (3 - offset) + 1; i++) {
Memory::WriteUnchecked_Float(d[i], addr + 4 * i);
mips->space->WriteUnchecked_Float(d[i], addr + 4 * i);
}
}
break;
@@ -337,7 +337,7 @@ namespace MIPSInt
break;
}
#ifndef COMMON_BIG_ENDIAN
f = reinterpret_cast<float *>(Memory::GetPointerWriteUnchecked(addr));
f = reinterpret_cast<float *>(mips->space->GetPointerUnchecked(addr));
if (f) {
ReadVector(mips, f, V_Quad, vt);
}
@@ -345,10 +345,10 @@ namespace MIPSInt
float svqd[4];
ReadVector(mips, svqd, V_Quad, vt);
Memory::WriteUnchecked_Float(svqd[0], addr);
Memory::WriteUnchecked_Float(svqd[1], addr + 4);
Memory::WriteUnchecked_Float(svqd[2], addr + 8);
Memory::WriteUnchecked_Float(svqd[3], addr + 12);
mips->space->WriteUnchecked_Float(svqd[0], addr);
mips->space->WriteUnchecked_Float(svqd[1], addr + 4);
mips->space->WriteUnchecked_Float(svqd[2], addr + 8);
mips->space->WriteUnchecked_Float(svqd[3], addr + 12);
#endif
break;
@@ -1750,14 +1750,14 @@ namespace MIPSInt
VI(vt) = 0;
break;
}
VI(vt) = Memory::ReadUnchecked_U32(addr);
VI(vt) = mips->space->ReadUnchecked_U32(addr);
break;
case 58: //sv.s
if (!Memory::IsValid4AlignedAddress(addr)) {
Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sv.s");
break;
}
Memory::WriteUnchecked_U32(VI(vt), addr);
mips->space->WriteUnchecked_U32(VI(vt), addr);
break;
default:
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
+2 -2
View File
@@ -120,7 +120,7 @@ void LoongArch64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
_assert_(saveOffset <= saveSize);
// Fixed registers, these are always kept when in Jit context.
LI(MEMBASEREG, Memory::base);
LI(MEMBASEREG, mipsState->space->base);
LI(CTXREG, mipsState);
LI(JITBASEREG, GetBasePtr());
@@ -170,7 +170,7 @@ void LoongArch64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
WriteDebugPC(SCRATCH1);
LI(SCRATCH2, (u32)Memory::BLOCK_SHADOW_ENTRY_MASK);
AND(SCRATCH1, SCRATCH1, SCRATCH2);
LI(SCRATCH2, Memory::blockShadow);
LI(SCRATCH2, mipsState->space->blockShadow);
ADD_D(SCRATCH1, SCRATCH1, SCRATCH2);
hooks_.dispatchFetch = GetCodePtr();
LD_WU(SCRATCH1, SCRATCH1, 0);
@@ -57,7 +57,7 @@ void LoongArch64JitBackend::CompIR_Exit(IRInst inst) {
SetJumpTarget(inTime);
LI(SCRATCH2, (u32)Memory::BLOCK_SHADOW_ENTRY_MASK);
AND(SCRATCH1, SCRATCH1, SCRATCH2);
LI(SCRATCH2, Memory::blockShadow);
LI(SCRATCH2, mips_->space->blockShadow);
LDX_WU(SCRATCH1, SCRATCH2, SCRATCH1);
FixupBranch found = BNEZ(SCRATCH1);
QuickJ(R_RA, dispatcherNoCheck_);
+3
View File
@@ -33,6 +33,7 @@
#include "Common/Serialize/SerializeFuncs.h"
#include "Core/ConfigValues.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MemMap.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/MIPSDebugInterface.h"
@@ -232,6 +233,8 @@ const float cst_constants[32] = {
};
MIPSState::MIPSState() {
space = &Memory::g_mainSpace;
// Initialize vorder
// This reordering of the VFPU registers in RAM means that instead of being like this:
+6
View File
@@ -34,6 +34,10 @@ namespace MIPSComp {
class JitInterface;
}
namespace Memory {
struct AddressSpace;
}
typedef Memory::Opcode MIPSOpcode;
// A fake opcode the CPU cores fetch in place of an instruction with a replacement function (see
@@ -317,6 +321,8 @@ public:
// Owned. Null on the interpreter.
MIPSComp::JitInterface *jit = nullptr;
// What this CPU sees of memory. The main CPU's is Memory::g_mainSpace.
const Memory::AddressSpace *space = nullptr;
};
// Access control ('private') break standard layout. Let's not allow it, to avoid excessive
+2 -2
View File
@@ -132,7 +132,7 @@ void RiscVJitBackend::GenerateFixedCode(MIPSState *mipsState) {
_assert_(saveOffset <= saveSize);
// Fixed registers, these are always kept when in Jit context.
LI(MEMBASEREG, Memory::base, SCRATCH1);
LI(MEMBASEREG, mipsState->space->base, SCRATCH1);
LI(CTXREG, mipsState, SCRATCH1);
LI(JITBASEREG, GetBasePtr(), SCRATCH1);
@@ -182,7 +182,7 @@ void RiscVJitBackend::GenerateFixedCode(MIPSState *mipsState) {
WriteDebugPC(SCRATCH1);
LI(SCRATCH2, (u32)Memory::BLOCK_SHADOW_ENTRY_MASK);
AND(SCRATCH1, SCRATCH1, SCRATCH2);
LI(SCRATCH2, Memory::blockShadow, X7);
LI(SCRATCH2, mipsState->space->blockShadow, X7);
ADD(SCRATCH1, SCRATCH1, SCRATCH2);
hooks_.dispatchFetch = GetCodePtr();
LWU(SCRATCH1, SCRATCH1, 0);
+1 -1
View File
@@ -57,7 +57,7 @@ void RiscVJitBackend::CompIR_Exit(IRInst inst) {
SetJumpTarget(inTime);
LI(SCRATCH2, (u32)Memory::BLOCK_SHADOW_ENTRY_MASK);
AND(SCRATCH1, SCRATCH1, SCRATCH2);
LI(SCRATCH2, Memory::blockShadow, X7);
LI(SCRATCH2, mips_->space->blockShadow, X7);
ADD(SCRATCH1, SCRATCH1, SCRATCH2);
LWU(SCRATCH1, SCRATCH1, 0);
FixupBranch found = BNE(SCRATCH1, R_ZERO);
+6 -5
View File
@@ -50,6 +50,7 @@ static void ShowPC(void *membase, void *jitbase) {
}
void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
regs_.SetMemoryBase(mipsState->space->base);
// This will be used as a writable scratch area, always 32-bit accessible.
const u8 *start = AlignCodePage();
if (DebugProfilerEnabled()) {
@@ -150,7 +151,7 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
ABI_PushAllCalleeSavedRegsAndAdjustStack();
#if PPSSPP_ARCH(AMD64)
// Two x64-specific statically allocated registers.
MOV(64, R(MEMBASEREG), ImmPtr(Memory::base));
MOV(64, R(MEMBASEREG), ImmPtr(mipsState->space->base));
if (jitbaseInR15)
MOV(64, R(JITBASEREG), ImmPtr((const void *)jitbase));
#endif
@@ -221,7 +222,7 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
ABI_CallFunctionAC(reinterpret_cast<void *>(&ShowPC), R(MEMBASEREG), (u32)jitbase);
}
#else
ABI_CallFunctionCC(reinterpret_cast<void *>(&ShowPC), (u32)Memory::base, (u32)GetBasePtr());
ABI_CallFunctionCC(reinterpret_cast<void *>(&ShowPC), (u32)mipsState->space->base, (u32)GetBasePtr());
#endif
}
@@ -230,11 +231,11 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
// Look the block up in the shadow. Its entry is the offset into the code space, or 0 for none.
AND(32, R(SCRATCH1), Imm32(Memory::BLOCK_SHADOW_ENTRY_MASK));
#if PPSSPP_ARCH(X86)
_assert_msg_(Memory::blockShadow != 0, "Block shadow bogus");
_assert_msg_(mipsState->space->blockShadow != 0, "Block shadow bogus");
hooks_.dispatchFetch = GetCodePtr();
MOV(32, R(SCRATCH1), MDisp(SCRATCH1, (u32)Memory::blockShadow));
MOV(32, R(SCRATCH1), MDisp(SCRATCH1, (u32)mipsState->space->blockShadow));
#elif PPSSPP_ARCH(AMD64)
MOV(64, R(RDX), ImmPtr(Memory::blockShadow));
MOV(64, R(RDX), ImmPtr(mipsState->space->blockShadow));
hooks_.dispatchFetch = GetCodePtr();
MOV(32, R(SCRATCH1), MComplex(RDX, SCRATCH1, SCALE_1, 0));
#endif
+3 -3
View File
@@ -64,7 +64,7 @@ Gen::OpArg X64JitBackend::PrepareSrc1Address(IRInst inst) {
#if PPSSPP_ARCH(AMD64)
addrArg = MDisp(MEMBASEREG, disp & 0x7FFFFFFF);
#else
addrArg = M(Memory::base + disp);
addrArg = M(mips_->space->base + disp);
#endif
} else if ((jo.cachePointers || src1IsPointer) && !readsFromSrc1 && (!clobbersSrc1 || src1IsPointer)) {
X64Reg src1 = regs_.MapGPRAsPointer(inst.src1);
@@ -74,12 +74,12 @@ Gen::OpArg X64JitBackend::PrepareSrc1Address(IRInst inst) {
#ifdef MASKED_PSP_MEMORY
LEA(PTRBITS, SCRATCH1, MDisp(regs_.RX(inst.src1), disp));
AND(PTRBITS, R(SCRATCH1), Imm32(Memory::MEMVIEW32_MASK));
addrArg = MDisp(SCRATCH1, (intptr_t)Memory::base);
addrArg = MDisp(SCRATCH1, (intptr_t)mips_->space->base);
#else
#if PPSSPP_ARCH(AMD64)
addrArg = MComplex(MEMBASEREG, regs_.RX(inst.src1), SCALE_1, disp);
#else
addrArg = MDisp(regs_.RX(inst.src1), Memory::base + disp);
addrArg = MDisp(regs_.RX(inst.src1), mips_->space->base + disp);
#endif
#endif
}
+1 -1
View File
@@ -400,7 +400,7 @@ void X64JitBackend::CompIR_ValidateAddress(IRInst inst) {
if (regs_.IsGPRMappedAsPointer(inst.src1)) {
LEA(PTRBITS, SCRATCH1, MDisp(regs_.RXPtr(inst.src1), inst.constant));
#if defined(MASKED_PSP_MEMORY)
SUB(PTRBITS, R(SCRATCH1), ImmPtr(Memory::base));
SUB(PTRBITS, R(SCRATCH1), ImmPtr(mips_->space->base));
#else
SUB(PTRBITS, R(SCRATCH1), R(MEMBASEREG));
#endif
+2 -2
View File
@@ -392,14 +392,14 @@ void X64IRRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) {
// This destroys the value...
_dbg_assert_(!nr[nreg].isDirty);
emit_->AND(PTRBITS, ::R(r), Imm32(Memory::MEMVIEW32_MASK));
emit_->ADD(PTRBITS, ::R(r), ImmPtr(Memory::base));
emit_->ADD(PTRBITS, ::R(r), ImmPtr(membase_));
#else
emit_->ADD(PTRBITS, ::R(r), ::R(MEMBASEREG));
#endif
} else {
#if defined(MASKED_PSP_MEMORY)
_dbg_assert_(!nr[nreg].isDirty);
emit_->SUB(PTRBITS, ::R(r), ImmPtr(Memory::base));
emit_->SUB(PTRBITS, ::R(r), ImmPtr(membase_));
#else
emit_->SUB(PTRBITS, ::R(r), ::R(MEMBASEREG));
#endif
+18 -2
View File
@@ -48,6 +48,7 @@ namespace Memory {
u8* base = nullptr;
u8 *blockShadow = nullptr;
AddressSpace g_mainSpace;
static const size_t BLOCK_SHADOW_SIZE = (size_t)BLOCK_SHADOW_MASK + 1;
// The MemArena class
@@ -430,6 +431,8 @@ bool Init(MemMapSetupFlags flags) {
INFO_LOG(Log::MemMap, "Memory system initialized. Base at %p (RAM at @ %p, uncached @ %p)",
base, m_pPhysicalRAM, m_pUncachedRAM);
g_mainSpace.base = base;
g_mainSpace.blockShadow = blockShadow;
MemFault_Init();
return true;
}
@@ -609,6 +612,7 @@ void Shutdown() {
base = nullptr;
FreeMemoryPages(blockShadow, BLOCK_SHADOW_SIZE);
blockShadow = nullptr;
g_mainSpace = AddressSpace();
DEBUG_LOG(Log::MemMap, "Memory system shut down.");
}
@@ -631,18 +635,30 @@ Opcode ReadUnchecked_Instruction(u32 address) {
}
u32 ReadBlockShadow(u32 address) {
return g_mainSpace.ReadBlockShadow(address);
}
void WriteBlockShadow(u32 address, u32 value) {
g_mainSpace.WriteBlockShadow(address, value);
}
bool ClearBlockShadow(u32 address, u32 value) {
return g_mainSpace.ClearBlockShadow(address, value);
}
u32 AddressSpace::ReadBlockShadow(u32 address) const {
if (!blockShadow || !IsValid4AlignedAddress(address))
return 0;
return *GetBlockShadowEntry(address);
}
void WriteBlockShadow(u32 address, u32 value) {
void AddressSpace::WriteBlockShadow(u32 address, u32 value) const {
if (!blockShadow || !IsValid4AlignedAddress(address))
return;
*GetBlockShadowEntry(address) = value;
}
bool ClearBlockShadow(u32 address, u32 value) {
bool AddressSpace::ClearBlockShadow(u32 address, u32 value) const {
if (!blockShadow || !IsValid4AlignedAddress(address))
return false;
u32 *entry = GetBlockShadowEntry(address);
+37
View File
@@ -311,6 +311,43 @@ inline void WriteUnchecked_U8(u8 data, u32 address) {
#endif
}
// What one CPU sees of memory: where its addresses point (base) and its block shadow. The main CPU's
// is g_mainSpace, which follows base and blockShadow above. The CPU cores go through their
// MIPSState's, so another CPU (the Media Engine) can have its own.
struct AddressSpace {
u8 *base = nullptr;
u8 *blockShadow = nullptr;
u8 *GetPointerUnchecked(u32 address) const {
#ifdef MASKED_PSP_MEMORY
return base + (address & MEMVIEW32_MASK);
#else
return base + address;
#endif
}
u8 ReadUnchecked_U8(u32 address) const { return *GetPointerUnchecked(address); }
u16 ReadUnchecked_U16(u32 address) const { return *(u16_le *)GetPointerUnchecked(DepthMirrored16(address)); }
u32 ReadUnchecked_U32(u32 address) const { return *(u32_le *)GetPointerUnchecked(address); }
u64 ReadUnchecked_U64(u32 address) const { return *(u64_le *)GetPointerUnchecked(address); }
float ReadUnchecked_Float(u32 address) const { return *(float_le *)GetPointerUnchecked(address); }
void WriteUnchecked_U8(u8 data, u32 address) const { *GetPointerUnchecked(address) = data; }
void WriteUnchecked_U16(u16 data, u32 address) const { *(u16_le *)GetPointerUnchecked(DepthMirrored16(address)) = data; }
void WriteUnchecked_U32(u32 data, u32 address) const { *(u32_le *)GetPointerUnchecked(address) = data; }
void WriteUnchecked_U64(u64 data, u32 address) const { *(u64_le *)GetPointerUnchecked(address) = data; }
void WriteUnchecked_Float(float data, u32 address) const { *(float_le *)GetPointerUnchecked(address) = data; }
u32 *GetBlockShadowEntry(u32 address) const {
return (u32 *)(blockShadow + (address & BLOCK_SHADOW_ENTRY_MASK));
}
// As the functions of the same name above, for this space's shadow.
u32 ReadBlockShadow(u32 address) const;
void WriteBlockShadow(u32 address, u32 value) const;
bool ClearBlockShadow(u32 address, u32 value) const;
};
extern AddressSpace g_mainSpace;
void WriteOrException_U8(const u8 data, const u32 address);
void WriteOrException_U16(const u16 data, const u32 address);
void WriteOrException_U32(const u32 data, const u32 address);