From 8656835935b71f949857ad223a44fd9a15a8ccbd Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 13 Jul 2020 20:14:14 +0200 Subject: [PATCH] UserspaceEmulator: Add a very simple instruction fetch cache To avoid MMU region lookup on every single instruction fetch, we now cache a raw pointer to the current instruction. This gets automatically invalidated when we jump somewhere, but as long as we're executing sequentially, instruction fetches will hit the cache and bypass all the region lookup stuff. This is about a ~2x speedup. :^) --- DevTools/UserspaceEmulator/SimpleRegion.cpp | 5 ++++ DevTools/UserspaceEmulator/SimpleRegion.h | 2 ++ DevTools/UserspaceEmulator/SoftCPU.cpp | 27 ++++++++++++++++++--- DevTools/UserspaceEmulator/SoftCPU.h | 12 ++++++++- DevTools/UserspaceEmulator/SoftMMU.h | 2 ++ 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/DevTools/UserspaceEmulator/SimpleRegion.cpp b/DevTools/UserspaceEmulator/SimpleRegion.cpp index 324e651798..c77ad7eefa 100644 --- a/DevTools/UserspaceEmulator/SimpleRegion.cpp +++ b/DevTools/UserspaceEmulator/SimpleRegion.cpp @@ -75,4 +75,9 @@ void SimpleRegion::write32(u32 offset, u32 value) *reinterpret_cast(m_data + offset) = value; } +u8* SimpleRegion::cacheable_ptr(u32 offset) +{ + return m_data + offset; +} + } diff --git a/DevTools/UserspaceEmulator/SimpleRegion.h b/DevTools/UserspaceEmulator/SimpleRegion.h index c4df498882..6cbe707c04 100644 --- a/DevTools/UserspaceEmulator/SimpleRegion.h +++ b/DevTools/UserspaceEmulator/SimpleRegion.h @@ -45,6 +45,8 @@ public: u8* data() { return m_data; } + virtual u8* cacheable_ptr(u32 offset) override; + private: u8* m_data { nullptr }; }; diff --git a/DevTools/UserspaceEmulator/SoftCPU.cpp b/DevTools/UserspaceEmulator/SoftCPU.cpp index bd6f06d20e..980ce3bb83 100644 --- a/DevTools/UserspaceEmulator/SoftCPU.cpp +++ b/DevTools/UserspaceEmulator/SoftCPU.cpp @@ -61,23 +61,44 @@ void SoftCPU::dump() const printf("o=%u s=%u z=%u a=%u p=%u c=%u\n", of(), sf(), zf(), af(), pf(), cf()); } +void SoftCPU::update_code_cache() +{ + auto* region = m_emulator.mmu().find_region({ cs(), eip() }); + ASSERT(region); + + m_cached_code_ptr = region->cacheable_ptr(eip() - region->base()); + m_cached_code_end = region->cacheable_ptr(region->size()); +} + u8 SoftCPU::read8() { - auto value = read_memory8({ cs(), eip() }); + if (!m_cached_code_ptr || m_cached_code_ptr >= m_cached_code_end) + update_code_cache(); + + u8 value = *m_cached_code_ptr; + m_cached_code_ptr += 1; m_eip += 1; return value; } u16 SoftCPU::read16() { - auto value = read_memory16({ cs(), eip() }); + if (!m_cached_code_ptr || (m_cached_code_ptr + 2) >= m_cached_code_end) + update_code_cache(); + + u16 value = *reinterpret_cast(m_cached_code_ptr); + m_cached_code_ptr += 2; m_eip += 2; return value; } u32 SoftCPU::read32() { - auto value = read_memory32({ cs(), eip() }); + if (!m_cached_code_ptr || (m_cached_code_ptr + 4) >= m_cached_code_end) + update_code_cache(); + + u32 value = *reinterpret_cast(m_cached_code_ptr); + m_cached_code_ptr += 4; m_eip += 4; return value; } diff --git a/DevTools/UserspaceEmulator/SoftCPU.h b/DevTools/UserspaceEmulator/SoftCPU.h index 6d8670e74f..137506daab 100644 --- a/DevTools/UserspaceEmulator/SoftCPU.h +++ b/DevTools/UserspaceEmulator/SoftCPU.h @@ -56,7 +56,12 @@ public: void dump() const; u32 eip() const { return m_eip; } - void set_eip(u32 eip) { m_eip = eip; } + void set_eip(u32 eip) + { + m_eip = eip; + m_cached_code_ptr = nullptr; + m_cached_code_end = nullptr; + } struct Flags { enum Flag { @@ -792,6 +797,8 @@ private: template void do_once_or_repeat(const X86::Instruction& insn, Callback); + void update_code_cache(); + private: Emulator& m_emulator; @@ -800,6 +807,9 @@ private: u32 m_eflags { 0 }; u32 m_eip { 0 }; + + const u8* m_cached_code_ptr { nullptr }; + const u8* m_cached_code_end { nullptr }; }; } diff --git a/DevTools/UserspaceEmulator/SoftMMU.h b/DevTools/UserspaceEmulator/SoftMMU.h index 53c75598d6..9d34cf2883 100644 --- a/DevTools/UserspaceEmulator/SoftMMU.h +++ b/DevTools/UserspaceEmulator/SoftMMU.h @@ -53,6 +53,8 @@ public: virtual u16 read16(u32 offset) = 0; virtual u32 read32(u32 offset) = 0; + virtual u8* cacheable_ptr([[maybe_unused]] u32 offset) { return nullptr; } + protected: Region(u32 base, u32 size) : m_base(base)