Core: Update page table mappings incrementally

Removing and readding every page table mapping every time something
changes in the page table is very slow. Instead, let's generate a diff
and ask Memmap to update only the diff.
This commit is contained in:
JosJuice
2026-02-04 21:35:20 +01:00
parent 7b885b857e
commit 9462e9d890
7 changed files with 474 additions and 113 deletions
+31 -15
View File
@@ -13,6 +13,7 @@
#include <cstring>
#include <map>
#include <memory>
#include <set>
#include <span>
#include <tuple>
@@ -243,11 +244,7 @@ void MemoryManager::UpdateDBATMappings(const PowerPC::BatTable& dbat_table)
}
m_dbat_mapped_entries.clear();
for (auto& entry : m_page_table_mapped_entries)
{
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
}
m_page_table_mapped_entries.clear();
RemoveAllPageTableMappings();
m_logical_page_mappings.fill(nullptr);
@@ -301,7 +298,7 @@ void MemoryManager::UpdateDBATMappings(const PowerPC::BatTable& dbat_table)
intersection_start, mapped_size, logical_address);
continue;
}
m_dbat_mapped_entries.push_back({mapped_pointer, mapped_size});
m_dbat_mapped_entries.push_back({mapped_pointer, mapped_size, logical_address});
}
m_logical_page_mappings[i] =
@@ -312,18 +309,12 @@ void MemoryManager::UpdateDBATMappings(const PowerPC::BatTable& dbat_table)
}
}
void MemoryManager::UpdatePageTableMappings(const std::map<u32, u32>& page_mappings)
void MemoryManager::AddPageTableMappings(const std::map<u32, u32>& mappings)
{
if (!m_is_fastmem_arena_initialized || m_page_size > PowerPC::HW_PAGE_SIZE)
return;
for (auto& entry : m_page_table_mapped_entries)
{
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
}
m_page_table_mapped_entries.clear();
for (const auto [logical_address, translated_address] : page_mappings)
for (const auto [logical_address, translated_address] : mappings)
{
constexpr u32 logical_size = PowerPC::HW_PAGE_SIZE;
for (const auto& physical_region : m_physical_regions)
@@ -351,11 +342,36 @@ void MemoryManager::UpdatePageTableMappings(const std::map<u32, u32>& page_mappi
intersection_start, mapped_size, logical_address);
continue;
}
m_page_table_mapped_entries.push_back({mapped_pointer, mapped_size});
m_page_table_mapped_entries.push_back({mapped_pointer, mapped_size, logical_address});
}
}
}
void MemoryManager::RemovePageTableMappings(const std::set<u32>& mappings)
{
if (m_page_size > PowerPC::HW_PAGE_SIZE)
return;
if (mappings.empty())
return;
std::erase_if(m_page_table_mapped_entries, [this, &mappings](const LogicalMemoryView& entry) {
const bool remove = mappings.contains(entry.logical_address);
if (remove)
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
return remove;
});
}
void MemoryManager::RemoveAllPageTableMappings()
{
for (auto& entry : m_page_table_mapped_entries)
{
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
}
m_page_table_mapped_entries.clear();
}
void MemoryManager::DoState(PointerWrap& p)
{
const u32 current_ram_size = GetRamSize();
+5 -1
View File
@@ -6,6 +6,7 @@
#include <array>
#include <map>
#include <memory>
#include <set>
#include <span>
#include <string>
#include <vector>
@@ -55,6 +56,7 @@ struct LogicalMemoryView
{
void* mapped_pointer;
u32 mapped_size;
u32 logical_address;
};
class MemoryManager
@@ -101,7 +103,9 @@ public:
void DoState(PointerWrap& p);
void UpdateDBATMappings(const PowerPC::BatTable& dbat_table);
void UpdatePageTableMappings(const std::map<u32, u32>& page_mappings);
void AddPageTableMappings(const std::map<u32, u32>& mappings);
void RemovePageTableMappings(const std::set<u32>& mappings);
void RemoveAllPageTableMappings();
void Clear();
@@ -33,19 +33,19 @@ constexpr Arm64Gen::ARM64Reg DISPATCHER_PC = Arm64Gen::ARM64Reg::W26;
PowerPC::PowerPCState, elem); \
_Pragma("GCC diagnostic pop") \
}())
#else
#define PPCSTATE_OFF(elem) (offsetof(PowerPC::PowerPCState, elem))
#endif
#define PPCSTATE_OFF_ARRAY(elem, i) \
(PPCSTATE_OFF(elem[0]) + sizeof(PowerPC::PowerPCState::elem[0]) * (i))
#else
#define PPCSTATE_OFF(elem) (offsetof(PowerPC::PowerPCState, elem))
#define PPCSTATE_OFF_ARRAY(elem, i) \
(offsetof(PowerPC::PowerPCState, elem[0]) + sizeof(PowerPC::PowerPCState::elem[0]) * (i))
#endif
#define PPCSTATE_OFF_STD_ARRAY(elem, i) \
(PPCSTATE_OFF(elem) + sizeof(PowerPC::PowerPCState::elem[0]) * (i))
#define PPCSTATE_OFF_GPR(i) PPCSTATE_OFF_ARRAY(gpr, i)
#define PPCSTATE_OFF_CR(i) PPCSTATE_OFF_ARRAY(cr.fields, i)
#define PPCSTATE_OFF_SR(i) PPCSTATE_OFF_ARRAY(sr, i)
#define PPCSTATE_OFF_SR(i) PPCSTATE_OFF_STD_ARRAY(sr, i)
#define PPCSTATE_OFF_SPR(i) PPCSTATE_OFF_ARRAY(spr, i)
static_assert(std::is_same_v<decltype(PowerPC::PowerPCState::ps[0]), PowerPC::PairedSingle&>);
File diff suppressed because it is too large Load Diff
+38 -2
View File
@@ -7,7 +7,10 @@
#include <cstddef>
#include <map>
#include <optional>
#include <set>
#include <span>
#include <string>
#include <vector>
#include "Common/BitField.h"
#include "Common/CommonTypes.h"
@@ -123,7 +126,7 @@ public:
~MMU();
void Reset();
void DoState(PointerWrap& p);
void DoState(PointerWrap& p, bool sr_changed);
// Routines for debugger UI, cheats, etc. to access emulated memory from the
// perspective of the CPU. Not for use by core emulation routines.
@@ -301,6 +304,26 @@ private:
explicit EffectiveAddress(u32 address) : Hex{address} {}
};
union PageMapping
{
// A small priority number wins over a larger priority number.
BitField<0, 11, u32> priority;
// Whether we're allowed to create a host mapping for this mapping.
BitField<11, 1, u32> host_mapping;
// The physical address of the page.
BitField<12, 20, u32> RPN;
u32 Hex = 0;
PageMapping() = default;
PageMapping(u32 RPN_, bool host_mapping_, u32 priority_)
{
RPN = RPN_;
host_mapping = host_mapping_;
priority = priority_;
}
};
template <const XCheckTLBFlag flag>
TranslateAddressResult TranslateAddress(u32 address);
@@ -312,6 +335,10 @@ private:
void Memcheck(u32 address, u64 var, bool write, size_t size);
void ClearPageTable();
void ReloadPageTable();
void PageTableUpdated(std::span<u8> page_table);
void UpdateBATs(BatTable& bat_table, u32 base_spr);
void UpdateFakeMMUBat(BatTable& bat_table, u32 start_addr);
@@ -329,9 +356,18 @@ private:
PowerPC::PowerPCState& m_ppc_state;
// STATE_TO_SAVE
std::map<u32, u32> m_page_mappings;
std::vector<u8> m_page_table;
// END STATE_TO_SAVE
// This keeps track of all valid page table mappings in m_page_table.
// The key is the logical address.
std::map<u32, PageMapping> m_page_mappings;
// These are kept around just for their memory allocations. They are always cleared before use.
std::vector<u8> m_temp_page_table;
std::set<u32> m_removed_mappings;
std::map<u32, u32> m_added_mappings;
BatTable m_ibat_table;
BatTable m_dbat_table;
};
+9 -2
View File
@@ -4,6 +4,7 @@
#include "Core/PowerPC/PowerPC.h"
#include <algorithm>
#include <array>
#include <bit>
#include <cstring>
@@ -78,6 +79,8 @@ void PowerPCManager::DoState(PointerWrap& p)
// *((u64 *)&TL(m_ppc_state)) = SystemTimers::GetFakeTimeBase(); //works since we are little
// endian and TL comes first :)
const std::array<u32, 16> old_sr = m_ppc_state.sr;
p.DoArray(m_ppc_state.gpr);
p.Do(m_ppc_state.pc);
p.Do(m_ppc_state.npc);
@@ -105,10 +108,10 @@ void PowerPCManager::DoState(PointerWrap& p)
m_ppc_state.dCache.DoState(memory, p);
auto& mmu = m_system.GetMMU();
mmu.DoState(p);
if (p.IsReadMode())
{
mmu.DoState(p, old_sr != m_ppc_state.sr);
if (!m_ppc_state.m_enable_dcache)
{
INFO_LOG_FMT(POWERPC, "Flushing data cache");
@@ -121,6 +124,10 @@ void PowerPCManager::DoState(PointerWrap& p)
mmu.IBATUpdated();
mmu.DBATUpdated();
}
else
{
mmu.DoState(p, false);
}
// SystemTimers::DecrementerSet();
// SystemTimers::TimeBaseSet();
+1 -1
View File
@@ -175,7 +175,7 @@ struct PowerPCState
alignas(16) PairedSingle ps[32];
#endif
u32 sr[16]{}; // Segment registers.
std::array<u32, 16> sr{}; // Segment registers.
// special purpose registers - controls quantizers, DMA, and lots of other misc extensions.
// also for power management, but we don't care about that.