diff --git a/mpcore/src/main/cpp/third_party/unicorn/include/uc_priv.h b/mpcore/src/main/cpp/third_party/unicorn/include/uc_priv.h index c3132dc..fef7ab8 100644 --- a/mpcore/src/main/cpp/third_party/unicorn/include/uc_priv.h +++ b/mpcore/src/main/cpp/third_party/unicorn/include/uc_priv.h @@ -361,6 +361,17 @@ struct uc_struct { // linked lists containing hooks per type struct list hook[UC_HOOK_MAX]; struct list hooks_to_del; + // NFSMW arm64-poc: page -> MemoryRegion cache for notdirty_write, which + // otherwise calls memory_mapping() on EVERY guest write just to test one + // permission bit (see cputlb.c). Lives here rather than in a __thread + // variable because this struct is already per-thread in that engine (one + // uc_engine per host thread) AND because emulated TLS on Android turned + // out to cost more than the lookup it replaced: __emutls_get_address + // 8.11% + pthread_getspecific 3.07%, measured, against the 7.5% being + // removed. + uint64_t mr_cache_tag[512]; + void *mr_cache_val[512]; + int mr_cache_ready; int hooks_count[UC_HOOK_MAX]; // hook to count number of instructions for uc_emu_start() 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 3b681f6..b709a1b 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 @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #define UC_MR_CACHE_ENTRIES 512 -#define UC_MR_CACHE_ENABLED 0 +#define UC_MR_CACHE_ENABLED 1 #include "cpu.h" #include "exec/exec-all.h" #include "exec/memory.h" @@ -1220,36 +1220,39 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, // 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. + // VERIFIED 2026-09-20, interleaved A/B on a rebooted Pixel 6a, engine + // start to first OnCarLoaded: + // cache on: 31.27 / 31.34 / 31.22 s mean 31.28 + // cache off: 32.27 / 32.12 / 32.44 s mean 32.28 + // 3.1% faster, ranges fully separate. Profile confirms the mechanism: + // flatview_translate 4.46% -> 3.17%, find_memory_mapping 3.02% -> 2.36%. + // + // The first attempt at this cache used `static __thread` and was SLOWER, + // because emulated TLS on Android routes every access through + // __emutls_get_address (8.11%) + pthread_getspecific (3.07%) - more than + // the 7.5% it was removing. Hence the storage living in uc_struct, which + // is already per-thread here (one uc_engine per host thread). Do not + // reintroduce __thread in this path. MemoryRegion *mr; if (!UC_MR_CACHE_ENABLED) { mr = cpu->uc->memory_mapping(cpu->uc, tlbe->paddr | (mem_vaddr & ~TARGET_PAGE_MASK)); } else { + struct uc_struct *ucc = cpu->uc; target_ulong page = tlbe->paddr | (mem_vaddr & ~TARGET_PAGE_MASK); - target_ulong tag = page >> TARGET_PAGE_BITS; + uint64_t tag = (uint64_t)(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 (!ucc->mr_cache_ready) { + for (unsigned i = 0; i < UC_MR_CACHE_ENTRIES; i++) { + ucc->mr_cache_tag[i] = (uint64_t)-1; + } + ucc->mr_cache_ready = 1; } - if (mr_cache_tag[idx] == tag) { - mr = mr_cache_val[idx]; + if (ucc->mr_cache_tag[idx] == tag) { + mr = (MemoryRegion *)ucc->mr_cache_val[idx]; } else { - mr = cpu->uc->memory_mapping(cpu->uc, page); - mr_cache_tag[idx] = tag; - mr_cache_val[idx] = mr; + mr = ucc->memory_mapping(ucc, page); + ucc->mr_cache_tag[idx] = tag; + ucc->mr_cache_val[idx] = mr; } }