diff --git a/Core/MIPS/ARM/ArmIRAsm.cpp b/Core/MIPS/ARM/ArmIRAsm.cpp index 696a87fbfc..7023e5b6df 100644 --- a/Core/MIPS/ARM/ArmIRAsm.cpp +++ b/Core/MIPS/ARM/ArmIRAsm.cpp @@ -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. diff --git a/Core/MIPS/ARM64/Arm64IRAsm.cpp b/Core/MIPS/ARM64/Arm64IRAsm.cpp index 0e371d9e2b..fb1311af2b 100644 --- a/Core/MIPS/ARM64/Arm64IRAsm.cpp +++ b/Core/MIPS/ARM64/Arm64IRAsm.cpp @@ -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. diff --git a/Core/MIPS/ARM64/Arm64IRCompBranch.cpp b/Core/MIPS/ARM64/Arm64IRCompBranch.cpp index 223bb4e61a..60d0de1a41 100644 --- a/Core/MIPS/ARM64/Arm64IRCompBranch.cpp +++ b/Core/MIPS/ARM64/Arm64IRCompBranch.cpp @@ -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_); diff --git a/Core/MIPS/ARM64/Arm64IRRegCache.cpp b/Core/MIPS/ARM64/Arm64IRRegCache.cpp index b6cfb94c60..c09f7f0ce2 100644 --- a/Core/MIPS/ARM64/Arm64IRRegCache.cpp +++ b/Core/MIPS/ARM64/Arm64IRRegCache.cpp @@ -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); diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index ec38896f3d..29fa94d86d 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -86,9 +86,9 @@ static u32 RunKernelLoad(MIPSState *mips, u32 addr) { u32 value; if (IsValidAlignedAddress(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 static void RunKernelStore(MIPSState *mips, u32 addr, u32 value) { if (IsValidAlignedAddress(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; } diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index cd424d2249..1b9f398189 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -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> &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 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; } diff --git a/Core/MIPS/IR/IRJit.h b/Core/MIPS/IR/IRJit.h index ecfff94791..5fdb92be3d 100644 --- a/Core/MIPS/IR/IRJit.h +++ b/Core/MIPS/IR/IRJit.h @@ -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 FindInvalidatedBlockNumbers(u32 address, u32 length); void FinalizeBlock(int blockNum); @@ -142,6 +151,7 @@ private: u32 AddressToPage(u32 addr) const; std::vector blocks_; std::unordered_map> byPage_; + const Memory::AddressSpace *space_ = nullptr; }; class IRJit; diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index c45137ea72..613e5d6947 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -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; diff --git a/Core/MIPS/Interpreter.cpp b/Core/MIPS/Interpreter.cpp index 657f1095dd..41f7eec741 100644 --- a/Core/MIPS/Interpreter.cpp +++ b/Core/MIPS/Interpreter.cpp @@ -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"); diff --git a/Core/MIPS/InterpreterVFPU.cpp b/Core/MIPS/InterpreterVFPU.cpp index 0d5464396c..ef57c43c3f 100644 --- a/Core/MIPS/InterpreterVFPU.cpp +++ b/Core/MIPS/InterpreterVFPU.cpp @@ -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(Memory::GetPointerUnchecked(addr)); + cf = reinterpret_cast(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(Memory::GetPointerWriteUnchecked(addr)); + f = reinterpret_cast(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"); diff --git a/Core/MIPS/LoongArch64/LoongArch64Asm.cpp b/Core/MIPS/LoongArch64/LoongArch64Asm.cpp index ecddc3e40f..7c06c60186 100644 --- a/Core/MIPS/LoongArch64/LoongArch64Asm.cpp +++ b/Core/MIPS/LoongArch64/LoongArch64Asm.cpp @@ -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); diff --git a/Core/MIPS/LoongArch64/LoongArch64CompBranch.cpp b/Core/MIPS/LoongArch64/LoongArch64CompBranch.cpp index 588f42d6a6..dc2b31d10c 100644 --- a/Core/MIPS/LoongArch64/LoongArch64CompBranch.cpp +++ b/Core/MIPS/LoongArch64/LoongArch64CompBranch.cpp @@ -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_); diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index a862ab0da4..0472cda79a 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -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: diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index 68b60cb724..3497ec82fc 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -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 diff --git a/Core/MIPS/RiscV/RiscVAsm.cpp b/Core/MIPS/RiscV/RiscVAsm.cpp index 3ab7f23154..37715b7bee 100644 --- a/Core/MIPS/RiscV/RiscVAsm.cpp +++ b/Core/MIPS/RiscV/RiscVAsm.cpp @@ -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); diff --git a/Core/MIPS/RiscV/RiscVCompBranch.cpp b/Core/MIPS/RiscV/RiscVCompBranch.cpp index c616104cae..d44db0785c 100644 --- a/Core/MIPS/RiscV/RiscVCompBranch.cpp +++ b/Core/MIPS/RiscV/RiscVCompBranch.cpp @@ -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); diff --git a/Core/MIPS/x86/X64IRAsm.cpp b/Core/MIPS/x86/X64IRAsm.cpp index ddc9d30b50..17912eef8d 100644 --- a/Core/MIPS/x86/X64IRAsm.cpp +++ b/Core/MIPS/x86/X64IRAsm.cpp @@ -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(&ShowPC), R(MEMBASEREG), (u32)jitbase); } #else - ABI_CallFunctionCC(reinterpret_cast(&ShowPC), (u32)Memory::base, (u32)GetBasePtr()); + ABI_CallFunctionCC(reinterpret_cast(&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 diff --git a/Core/MIPS/x86/X64IRCompLoadStore.cpp b/Core/MIPS/x86/X64IRCompLoadStore.cpp index 7f2f5ac0fe..1a4e51909a 100644 --- a/Core/MIPS/x86/X64IRCompLoadStore.cpp +++ b/Core/MIPS/x86/X64IRCompLoadStore.cpp @@ -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 } diff --git a/Core/MIPS/x86/X64IRCompSystem.cpp b/Core/MIPS/x86/X64IRCompSystem.cpp index ac0eeec570..5db8e72338 100644 --- a/Core/MIPS/x86/X64IRCompSystem.cpp +++ b/Core/MIPS/x86/X64IRCompSystem.cpp @@ -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 diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index a04ee29773..476c0aff8c 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -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 diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index d21f25e41b..ff750a74fe 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -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); diff --git a/Core/MemMap.h b/Core/MemMap.h index 3040db0a47..951d897e1a 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -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);