From aa0b4964edaea2fef2a999eb3b8d89ece0ae13d1 Mon Sep 17 00:00:00 2001 From: Toad King Date: Tue, 5 Aug 2014 18:45:19 -0400 Subject: [PATCH] Try to allocate executable memory close to PPSSPP memory if not below 4GB --- Common/MemoryUtil.cpp | 92 ++++++++++++++++++++++++++++++++----------- Common/MemoryUtil.h | 2 +- 2 files changed, 69 insertions(+), 25 deletions(-) diff --git a/Common/MemoryUtil.cpp b/Common/MemoryUtil.cpp index a2f8f17f63..92edcad13c 100644 --- a/Common/MemoryUtil.cpp +++ b/Common/MemoryUtil.cpp @@ -33,14 +33,20 @@ -#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) +#if defined(_M_X64) +#ifndef _WIN32 #include +#endif +int hint_location; #ifdef __APPLE__ #define PAGE_MASK (4096-1) +#elif defined(_WIN32) +static SYSTEM_INFO sys_info; +#define PAGE_MASK (sys_info.dwPageSize - 1) #else #define PAGE_MASK (getpagesize() - 1) #endif -#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK)) +#define round_page(x) ((((uintptr_t)(x)) + PAGE_MASK) & ~(PAGE_MASK)) #endif #ifdef __SYMBIAN32__ @@ -58,13 +64,60 @@ void ResetExecutableMemory(void* ptr) } #endif +#if defined(_WIN32) && defined(_M_X64) +static uintptr_t last_addr; +static void *SearchForFreeMem(size_t size) +{ + if (!last_addr) + last_addr = (uintptr_t) &hint_location - sys_info.dwPageSize; + last_addr -= size; + + MEMORY_BASIC_INFORMATION info; + while (VirtualQuery((void *)last_addr, &info, sizeof(info)) == sizeof(info)) + { + // went too far, unusable for executable memory + if (last_addr + 0x80000000 < (uintptr_t) &hint_loction) + return NULL; + + uintptr_t end = last_addr + size; + if (info.State != MEM_FREE) + { + last_addr = (uintptr_t) info.AllocationBase - size; + continue; + } + + if ((uintptr_t)info.BaseAddress + (uintptr_t)info.RegionSize >= end && + (uintptr_t)info.BaseAddress <= last_addr) + return (void *)last_addr; + + last_addr -= size; + } + + return NULL; +} +#endif + // This is purposely not a full wrapper for virtualalloc/mmap, but it // provides exactly the primitive operations that PPSSPP needs. -void* AllocateExecutableMemory(size_t size, bool low) +void* AllocateExecutableMemory(size_t size, bool exec) { #if defined(_WIN32) - void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + void* ptr; +#if defined(_M_X64) + if (exec && (uintptr_t) &hint_location >= 0x100000000ULL) + { + if (!last_addr) + GetSystemInfo(&sys_info); + + size_t _size = round_page(size); + ptr = SearchForFreeMem(_size); + if (ptr) + ptr = VirtualAlloc(ptr, _size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + } + else +#endif + ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); #elif defined(__SYMBIAN32__) //This function may be called more than once, and we want to create only one big //memory chunk for all the executable code for the JIT @@ -79,23 +132,22 @@ void* AllocateExecutableMemory(size_t size, bool low) void* ptr = (void*)g_next_ptr; g_next_ptr += size; #else - static char *map_hint = 0; -#if defined(__x86_64__) && !defined(MAP_32BIT) - // This OS has no flag to enforce allocation below the 4 GB boundary, - // but if we hint that we want a low address it is very likely we will - // get one. - // An older version of this code used MAP_FIXED, but that has the side - // effect of discarding already mapped pages that happen to be in the - // requested virtual memory range (such as the emulated RAM, sometimes). - if (low && (!map_hint)) - map_hint = (char*)round_page(512*1024*1024); /* 0.5 GB rounded up to the next page */ + char *map_hint = 0; +#if defined(_M_X64) + // Try to request one that is close to our memory location if we're in high memory. + // We use a dummy global variable to give us a good location to start from. + if (exec && (!map_hint) && (uintptr_t) &hint_location >= 0x100000000ULL) + map_hint = (char*)round_page(&hint_location) - 0x20000000; // 0.5gb lower than our approximate location + else if (exec && map_hint) + map_hint -= round_page(size); /* round down to the next page */ #endif void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE -#if defined(__x86_64__) && defined(MAP_32BIT) - | (low ? MAP_32BIT : 0) +#if defined(_M_X64) && defined(MAP_32BIT) + | (exec && (uintptr_t) map_hint == 0 ? MAP_32BIT : 0) #endif , -1, 0); + #endif /* defined(_WIN32) */ // printf("Mapped executable memory at %p (size %ld)\n", ptr, @@ -111,14 +163,6 @@ void* AllocateExecutableMemory(size_t size, bool low) #endif PanicAlert("Failed to allocate executable memory"); } -#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) - else if (low) - { - map_hint += size; - map_hint = (char*)round_page(map_hint); /* round up to the next page */ - // printf("Next map will (hopefully) be at %p\n", map_hint); - } -#endif return ptr; } diff --git a/Common/MemoryUtil.h b/Common/MemoryUtil.h index 8aecc40940..68a40dbf1c 100644 --- a/Common/MemoryUtil.h +++ b/Common/MemoryUtil.h @@ -27,7 +27,7 @@ using std::size_t; #endif -void* AllocateExecutableMemory(size_t size, bool low = true); +void* AllocateExecutableMemory(size_t size, bool exec = true); void* AllocateMemoryPages(size_t size); void FreeMemoryPages(void* ptr, size_t size); void* AllocateAlignedMemory(size_t size,size_t alignment);