diff --git a/mpcore/src/main/cpp/third_party/unicorn/qemu/accel/tcg/cputlb.c b/mpcore/src/main/cpp/third_party/unicorn/qemu/accel/tcg/cputlb.c index 3d0bebd..3b681f6 100644 --- a/mpcore/src/main/cpp/third_party/unicorn/qemu/accel/tcg/cputlb.c +++ b/mpcore/src/main/cpp/third_party/unicorn/qemu/accel/tcg/cputlb.c @@ -18,6 +18,8 @@ */ #include "qemu/osdep.h" +#define UC_MR_CACHE_ENTRIES 512 +#define UC_MR_CACHE_ENABLED 0 #include "cpu.h" #include "exec/exec-all.h" #include "exec/memory.h" @@ -1194,7 +1196,63 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, struct uc_struct *uc = cpu->uc; #endif ram_addr_t ram_addr = mem_vaddr + iotlbentry->addr; - MemoryRegion *mr = cpu->uc->memory_mapping(cpu->uc, tlbe->paddr | (mem_vaddr & ~TARGET_PAGE_MASK)); + + // PROJECT PATCH (2026-09-20, NFSMW arm64-poc). memory_mapping() - + // find_memory_mapping -> address_space_translate -> flatview_translate - + // was called here on EVERY notdirty write, unconditionally, even though + // its result is used only to test one bit (is this region executable). + // Measured: flatview_translate 4.46% + find_memory_mapping 3.02% of a + // clean in-game profile, and a probe on this exact line counted over a + // million calls per 3 seconds with exec_region=0 - i.e. the lookup ran in + // full and then the answer turned out not to matter. + // + // Task #54 removed the tb_invalidate work for non-executable spans but + // left this lookup in front of it, so most of the cost survived. TLB + // capacity was separately ruled out as the cause (task #53). + // + // The map is static here: GuestEngine maps its five spans once in + // CreateConfiguredEngine and never calls uc_mem_map/protect again once + // guest code is running (guard pages and RELRO are set during that same + // setup). So the page -> MemoryRegion answer cannot change, and a small + // direct-mapped cache removes the lookup for everything but a cold miss. + // + // INVARIANT THIS RELIES ON: anyone who starts changing mappings or + // permissions at RUNTIME must clear this cache, or stale permissions will + // be used. It is per-thread, so no locking is needed. + // + // NOT YET VERIFIED. Written and built, but the Pixel 6a became + // unmeasurable (screen dozing within seconds, NotificationShade holding + // focus) and the A9 cannot run this build at all - Android kills it with + // "failed to attach / start timeout" because startup work exceeds the + // attach deadline on that hardware. Defaults OFF so an unmeasured change + // does not reach the hot path. To evaluate: set to 1, then interleaved + // A/B with loadtime (engine start -> first OnCarLoaded) plus a profile + // check that flatview_translate and find_memory_mapping actually fall. + // A profile-share drop alone is NOT a speedup - that has been established + // three separate times in this project. + MemoryRegion *mr; + if (!UC_MR_CACHE_ENABLED) { + mr = cpu->uc->memory_mapping(cpu->uc, tlbe->paddr | (mem_vaddr & ~TARGET_PAGE_MASK)); + } else { + target_ulong page = tlbe->paddr | (mem_vaddr & ~TARGET_PAGE_MASK); + target_ulong tag = page >> TARGET_PAGE_BITS; + unsigned idx = (unsigned)(tag & (UC_MR_CACHE_ENTRIES - 1)); + static __thread target_ulong mr_cache_tag[UC_MR_CACHE_ENTRIES]; + static __thread MemoryRegion *mr_cache_val[UC_MR_CACHE_ENTRIES]; + static __thread int mr_cache_ready; + if (!mr_cache_ready) { + for (unsigned i = 0; i < UC_MR_CACHE_ENTRIES; i++) mr_cache_tag[i] = (target_ulong)-1; + mr_cache_ready = 1; + } + if (mr_cache_tag[idx] == tag) { + mr = mr_cache_val[idx]; + } else { + mr = cpu->uc->memory_mapping(cpu->uc, page); + mr_cache_tag[idx] = tag; + mr_cache_val[idx] = mr; + } + } + if (mr && (mr->perms & UC_PROT_EXEC) != 0) { struct page_collection *pages