// SPDX-License-Identifier: MIT /* $info$ tags: glue|block-database desc: Stores information about blocks, and provides C++ implementations to lookup the blocks $end_info$ */ #include #include #include #include "Interface/Context/Context.h" #include "Interface/Core/LookupCache.h" namespace FEXCore { GuestToHostMap::GuestToHostMap() : BlockLinks_mbr {"FEXMem_BlockLinks"} { BlockLinks_pma = fextl::make_unique>(&BlockLinks_mbr); // Setup our PMR map. BlockLinks = BlockLinks_pma->new_object(); } LookupCache::LookupCache(FEXCore::Context::ContextImpl* CTX) : ctx {CTX} { TotalCacheSize = ctx->Config.VirtualMemSize / FEXCore::Utils::FEX_PAGE_SIZE * 8 + CODE_SIZE + MAX_L1_SIZE; // Block cache ends up looking like this // PageMemoryMap[VirtualMemoryRegion >> 12] // | // v // PageMemory[Memory & (VIRTUAL_PAGE_SIZE - 1)] // | // v // Pointer to Code // // Allocate a region of memory that we can use to back our block pointers // We need one pointer per page of virtual memory // At 64GB of virtual memory this will allocate 128MB of virtual memory space PagePointer = reinterpret_cast(FEXCore::Allocator::VirtualAlloc(TotalCacheSize, false, false)); LOGMAN_THROW_A_FMT(PagePointer != -1ULL, "Failed to allocate PagePointer"); FEXCore::Allocator::VirtualName("FEXMem_Lookup", reinterpret_cast(PagePointer), ctx->Config.VirtualMemSize / FEXCore::Utils::FEX_PAGE_SIZE * 8 + CODE_SIZE); CTX->SyscallHandler->MarkOvercommitRange(PagePointer, TotalCacheSize); // Allocate our memory backing our pages // We need 32KB per guest page (One pointer per byte) // XXX: We can drop down to 16KB if we store 4byte offsets from the code base // We currently limit to 128MB of real memory for caching for the total cache size. // Can end up being inefficient if we compile a small number of blocks per page PageMemory = PagePointer + ctx->Config.VirtualMemSize / FEXCore::Utils::FEX_PAGE_SIZE * 8; // L1 Cache L1Pointer = PageMemory + CODE_SIZE; FEXCore::Allocator::VirtualName("FEXMem_Lookup_L1", reinterpret_cast(L1Pointer), MAX_L1_SIZE); VirtualMemSize = ctx->Config.VirtualMemSize; if (DynamicL1Cache()) { // Start at minimum size when dynamic. L1PointerMask = MIN_L1_ENTRIES - 1; } else { // Start at maximum instead. L1PointerMask = MAX_L1_ENTRIES - 1; } } LookupCache::~LookupCache() { FEXCore::Allocator::VirtualFree(reinterpret_cast(PagePointer), TotalCacheSize); ctx->SyscallHandler->UnmarkOvercommitRange(PagePointer, TotalCacheSize); // No need to free BlockLinks map. // These will get freed when their memory allocators are deallocated. } void LookupCache::ClearL2Cache(const FEXCore::LookupCacheBaseLockToken& lk) { // Clear out the page memory // PagePointer and PageMemory are sequential with each other. Clear both at once. FEXCore::Allocator::VirtualDontNeed(reinterpret_cast(PagePointer), ctx->Config.VirtualMemSize / FEXCore::Utils::FEX_PAGE_SIZE * 8 + CODE_SIZE, false); AllocateOffset = 0; } void LookupCache::ClearThreadLocalCaches(const LookupCacheWriteLockToken&) { // Clear L1 and L2 by clearing the full cache. FEXCore::Allocator::VirtualDontNeed(reinterpret_cast(PagePointer), TotalCacheSize, false); CachedCodePages.clear(); } void LookupCache::ClearCache(const LookupCacheWriteLockToken& lk) { // Clear L1 and L2 by clearing the full cache. ClearThreadLocalCaches(lk); Shared->ClearCache(lk); } void GuestToHostMap::ClearCache(const LookupCacheWriteLockToken&) { // Allocate a new pointer from the BlockLinks pma again. BlockLinks = BlockLinks_pma->new_object(); // All code is gone, clear the block list BlockList.clear(); } } // namespace FEXCore