]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
JIT: Use UAE VM module for memory reservation
authorFrode Solheim <frode@fs-uae.net>
Thu, 17 Sep 2015 17:15:19 +0000 (19:15 +0200)
committerFrode Solheim <frode@fs-uae.net>
Thu, 17 Sep 2015 17:18:09 +0000 (19:18 +0200)
include/uae/vm.h
od-win32/mman.cpp
vm.cpp

index 5e5b0a273b62e6c77852d38384a29a44f082c6cd..047b709c2d028ae95d828f194e6ed46f9c0f8026 100644 (file)
@@ -15,6 +15,7 @@
 #define UAE_VM_EXECUTE 4
 
 #define UAE_VM_32BIT (1 << 8)
+#define UAE_VM_WRITE_WATCH (1 << 9)
 #define UAE_VM_ALLOC_FAILED NULL
 
 /* Even though it looks like you can OR together vm protection values,
@@ -33,15 +34,13 @@ void *uae_vm_alloc(uae_u32 size);
 void *uae_vm_alloc(uae_u32 size, int flags);
 #endif
 void *uae_vm_alloc(uae_u32 size, int flags, int protect);
-void uae_vm_protect(void *address, int size, int protect);
-void uae_vm_free(void *address, int size);
+bool uae_vm_protect(void *address, int size, int protect);
+bool uae_vm_free(void *address, int size);
 
-#if 0
-/* Replacement functions for mman - implement later */
 void *uae_vm_reserve(uae_u32 size, int flags);
+void *uae_vm_reserve_fixed(void *address, uae_u32 size, int flags);
 void *uae_vm_commit(void *address, uae_u32 size, int protect);
-void *uae_vm_decommit(uae_u32 size, int flags);
-#endif
+bool uae_vm_decommit(void *address, uae_u32 size);
 
 int uae_vm_page_size(void);
 
index e4a2e949e69a03978d19d545b05c42de9b9bd46e..ca5dfe85d3380e17f17b0d6c2a0443e0064c3d0b 100644 (file)
@@ -8,6 +8,7 @@
 #include "sysdeps.h"
 #include "memory.h"
 #include "uae/mman.h"
+#include "uae/vm.h"
 #include "options.h"
 #include "autoconf.h"
 #include "gfxboard.h"
@@ -149,11 +150,11 @@ bool preinit_shm (void)
        max_allowed_mman = 512 + 256;
 #if 1
        if (os_64bit) {
-#ifdef WIN64
-               max_allowed_mman = 3072;
-#else
+//#ifdef WIN64
+//             max_allowed_mman = 3072;
+//#else
                max_allowed_mman = 2048;
-#endif
+//#endif
        }
 #endif
        if (maxmem > max_allowed_mman)
@@ -204,14 +205,21 @@ bool preinit_shm (void)
 
        //natmem_size = 257 * 1024 * 1024;
 
-       write_log (_T("Total physical RAM %lluM, all RAM %lluM. Attempting to reserve: %uM.\n"), totalphys64 >> 20, total64 >> 20, natmem_size >> 20);
-       natmem_offset_allocated = 0;
-
-#ifdef _WIN64
-       natmem_offset_allocated = (uae_u8*) VirtualAlloc((void*)(uintptr_t)0x80000000, 0x80000000, MEM_RESERVE | MEM_WRITE_WATCH, PAGE_READWRITE);
-       if (natmem_offset_allocated) {
+       if (natmem_size > 0x80000000) {
                natmem_size = 0x80000000;
        }
+
+       write_log (_T("NATMEM: Total physical RAM %llu MB, all RAM %llu MB\n"),
+                                 totalphys64 >> 20, total64 >> 20);
+       write_log(_T("NATMEM: Attempting to reserve: %u MB\n"), natmem_size >> 20);
+
+#if 1
+       natmem_offset_allocated = (uae_u8 *) uae_vm_reserve(
+               natmem_size, UAE_VM_32BIT | UAE_VM_WRITE_WATCH);
+#else
+       natmem_size = 0x20000000;
+       natmem_offset_allocated = (uae_u8 *) uae_vm_reserve_fixed(
+               (void *) 0x90000000, natmem_size, UAE_VM_32BIT | UAE_VM_WRITE_WATCH);
 #endif
 
        if (!natmem_offset_allocated) {
diff --git a/vm.cpp b/vm.cpp
index 7d170034de7ea9668b28b785ea5f4341df68bae9..c35d3dfbe57ba067d578f80c7b0079427bc25518 100644 (file)
--- a/vm.cpp
+++ b/vm.cpp
@@ -10,6 +10,7 @@
 #include "sysdeps.h"
 #include "uae/vm.h"
 #include "uae/log.h"
+#include "memory.h"
 #ifdef _WIN32
 
 #else
 #include <sys/sysctl.h>
 #endif
 
+#ifdef LINUX
+#define HAVE_MAP_32BIT 1
+#endif
+
+/* Commiting memory on Windows zeroes the region. We do the same on other
+ * platforms so the functions exhibits the same behavior. I.e. a decommit
+ * followed by a commit results in zeroed memory. */
+#define CLEAR_MEMORY_ON_COMMIT
+
 // #define TRACK_ALLOCATIONS
 
 #ifdef TRACK_ALLOCATIONS
@@ -39,7 +49,7 @@ static struct alloc_size alloc_sizes[MAX_ALLOCATIONS];
 
 static void add_allocation(void *address, uae_u32 size)
 {
-       uae_log("add_allocation %p (%d)\n", address, size);
+       uae_log("VM: add_allocation %p (%d)\n", address, size);
        for (int i = 0; i < MAX_ALLOCATIONS; i++) {
                if (alloc_sizes[i].address == NULL) {
                        alloc_sizes[i].address = address;
@@ -83,7 +93,7 @@ static int protect_to_native(int protect)
        if (protect == UAE_VM_READ_WRITE) return PAGE_READWRITE;
        if (protect == UAE_VM_READ_EXECUTE) return PAGE_EXECUTE_READ;
        if (protect == UAE_VM_READ_WRITE_EXECUTE) return PAGE_EXECUTE_READWRITE;
-       uae_log("ERROR: invalid protect value %d\n", protect);
+       uae_log("VM: Invalid protect value %d\n", protect);
        return PAGE_NOACCESS;
 #else
        if (protect == UAE_VM_NO_ACCESS) return PROT_NONE;
@@ -93,11 +103,21 @@ static int protect_to_native(int protect)
        if (protect == UAE_VM_READ_WRITE_EXECUTE) {
                return PROT_READ | PROT_WRITE | PROT_EXEC;
        }
-       uae_log("ERROR: invalid protect value %d\n", protect);
+       uae_log("VM: Invalid protect value %d\n", protect);
        return PROT_NONE;
 #endif
 }
 
+static const char *protect_description(int protect)
+{
+       if (protect == UAE_VM_NO_ACCESS) return "NO_ACCESS";
+       if (protect == UAE_VM_READ) return "READ";
+       if (protect == UAE_VM_READ_WRITE) return "READ_WRITE";
+       if (protect == UAE_VM_READ_EXECUTE) return "READ_EXECUTE";
+       if (protect == UAE_VM_READ_WRITE_EXECUTE) return "READ_WRITE_EXECUTE";
+       return "UNKNOWN";
+}
+
 int uae_vm_page_size(void)
 {
        static int page_size = 0;
@@ -116,9 +136,13 @@ int uae_vm_page_size(void)
 static void *uae_vm_alloc_with_flags(uae_u32 size, int flags, int protect)
 {
        void *address = NULL;
-       uae_log("uae_vm_alloc(%u, %d, %d)\n", size, flags, protect);
+       uae_log("VM: Allocate 0x%-8x bytes [%d] (%s)\n",
+                       size, flags, protect_description(protect));
 #ifdef _WIN32
        int va_type = MEM_COMMIT | MEM_RESERVE;
+       if (flags & UAE_VM_WRITE_WATCH) {
+               va_type |= MEM_WRITE_WATCH;
+       }
        int va_protect = protect_to_native(protect);
 #ifdef CPU_64_BIT
        if (flags & UAE_VM_32BIT) {
@@ -126,11 +150,11 @@ static void *uae_vm_alloc_with_flags(uae_u32 size, int flags, int protect)
                 * work well enough when there is not a lot of allocations. */
                uae_u8 *p = (uae_u8 *) 0x50000000;
                while (address == NULL) {
-                       address = VirtualAlloc(p, size, va_type, va_protect);
-                       p += uae_vm_page_size();
-                       if (p > (void*) 0x60000000) {
+                       if (p >= (void*) 0x60000000) {
                                break;
                        }
+                       address = VirtualAlloc(p, size, va_type, va_protect);
+                       p += uae_vm_page_size();
                }
        }
 #endif
@@ -143,48 +167,54 @@ static void *uae_vm_alloc_with_flags(uae_u32 size, int flags, int protect)
        int mmap_prot = protect_to_native(protect);
 #ifdef CPU_64_BIT
        if (flags & UAE_VM_32BIT) {
+#ifdef HAVE_MAP_32BIT
                mmap_flags |= MAP_32BIT;
+#else
+               /* Stupid algorithm to find available space, but should
+                * work well enough when there is not a lot of allocations. */
+               uae_u8 *p = natmem_offset - 0x10000000;
+               uae_u8 *p_end = p + 0x10000000;
+               while (address == NULL) {
+                       if (p >= p_end) {
+                               break;
+                       }
+                       printf("%p\n", p);
+                       address = mmap(p, size, mmap_prot, mmap_flags, -1, 0);
+                       /* FIXME: check 32-bit result */
+                       if (address == MAP_FAILED) {
+                               address = NULL;
+                       }
+                       p += uae_vm_page_size();
+               }
+#endif
        }
 #endif
-       address = mmap(0, size, mmap_prot, mmap_flags, -1, 0);
-       if (address == MAP_FAILED) {
-               address = NULL;
-               return NULL;
+       if (address == NULL) {
+               address = mmap(0, size, mmap_prot, mmap_flags, -1, 0);
+               if (address == MAP_FAILED) {
+                       address = NULL;
+               }
        }
 #endif
        if (address == NULL) {
-           uae_log("uae_vm_alloc(%u, %d, %d) mmap failed (%d)\n",
+               uae_log("VM: uae_vm_alloc(%u, %d, %d) mmap failed (%d)\n",
                    size, flags, protect, errno);
            return NULL;
        }
 #ifdef TRACK_ALLOCATIONS
        add_allocation(address, size);
 #endif
+       uae_log("VM: %p\n", address);
        return address;
 }
 
-#if 0
-
-void *uae_vm_alloc(uae_u32 size)
-{
-       return uae_vm_alloc_with_flags(size, UAE_VM_32BIT, UAE_VM_READ_WRITE);
-}
-
-void *uae_vm_alloc(uae_u32 size, int flags)
-{
-       return uae_vm_alloc_with_flags(size, flags, UAE_VM_READ_WRITE);
-}
-
-#endif
-
 void *uae_vm_alloc(uae_u32 size, int flags, int protect)
 {
        return uae_vm_alloc_with_flags(size, flags, protect);
 }
 
-void uae_vm_protect(void *address, int size, int protect)
+static bool do_protect(void *address, int size, int protect)
 {
-       uae_log("uae_vm_protect(%p, %d, %d)\n", address, size, protect);
 #ifdef TRACK_ALLOCATIONS
        uae_u32 allocated_size = find_allocation(address);
        assert(allocated_size == size);
@@ -192,30 +222,189 @@ void uae_vm_protect(void *address, int size, int protect)
 #ifdef _WIN32
        DWORD old;
        if (VirtualProtect(address, size, protect_to_native(protect), &old) == 0) {
-               uae_log("uae_vm_protect(%p, %d, %d) VirtualProtect failed (%d)\n",
+               uae_log("VM: uae_vm_protect(%p, %d, %d) VirtualProtect failed (%d)\n",
                                address, size, protect, GetLastError());
+               return false;
        }
 #else
        if (mprotect(address, size, protect_to_native(protect)) != 0) {
-               uae_log("uae_vm_protect(%p, %d, %d) mprotect failed (%d)\n",
+               uae_log("VM: uae_vm_protect(%p, %d, %d) mprotect failed (%d)\n",
                                address, size, protect, errno);
+               return false;
        }
 #endif
+       return true;
+}
+
+bool uae_vm_protect(void *address, int size, int protect)
+{
+       return do_protect(address, size, protect);
 }
 
-void uae_vm_free(void *address, int size)
+static bool do_free(void *address, int size)
 {
-       uae_log("uae_vm_free(%p, %d)\n", address, size);
 #ifdef TRACK_ALLOCATIONS
        uae_u32 allocated_size = remove_allocation(address);
        assert(allocated_size == size);
 #endif
 #ifdef _WIN32
-       VirtualFree(address, 0, MEM_RELEASE);
+       return VirtualFree(address, 0, MEM_RELEASE) != 0;
 #else
        if (munmap(address, size) != 0) {
-               uae_log("uae_vm_free(%p, %d) munmap failed (%d)\n",
+               uae_log("VM: uae_vm_free(%p, %d) munmap failed (%d)\n",
                                address, size, errno);
+               return false;
+       }
+#endif
+       return true;
+}
+
+bool uae_vm_free(void *address, int size)
+{
+       uae_log("VM: Free     0x%-8x bytes at %p\n", size, address);
+       return do_free(address, size);
+}
+
+static void *try_reserve(uintptr_t try_addr, uae_u32 size, int flags)
+{
+       void *address = NULL;
+       if (try_addr) {
+               uae_log("VM: Reserve  0x%-8x bytes, try address 0x%llx\n",
+                               size, (uae_u64) try_addr);
+       } else {
+               uae_log("VM: Reserve  0x%-8x bytes\n", size);
+       }
+#ifdef _WIN32
+       int va_type = MEM_RESERVE;
+       if (flags & UAE_VM_WRITE_WATCH) {
+               va_type |= MEM_WRITE_WATCH;
+       }
+       int va_protect = protect_to_native(UAE_VM_NO_ACCESS);
+       address = VirtualAlloc((void *) try_addr, size, va_type, va_protect);
+       if (address == NULL) {
+               return NULL;
+       }
+#else
+       int mmap_flags = MAP_PRIVATE | MAP_ANON;
+       address = mmap((void *) try_addr, size, PROT_NONE, mmap_flags, -1, 0);
+       if (address == MAP_FAILED) {
+               return NULL;
        }
 #endif
+#ifdef CPU_64_BIT
+       if (flags & UAE_VM_32BIT) {
+               uintptr_t end = (uintptr_t) address + size;
+               if (address && end > (uintptr_t) 0x100000000ULL) {
+                       uae_log("VM: Reserve  0x%-8x bytes, got address 0x%llx (> 32-bit)\n",
+                                       size, (uae_u64) (uintptr_t) address);
+#ifdef _WIN32
+                       VirtualFree(address, 0, MEM_RELEASE);
+#else
+                       munmap(address, size);
+#endif
+                       return NULL;
+               }
+       }
+#endif
+       return address;
+}
+
+void *uae_vm_reserve(uae_u32 size, int flags)
+{
+       void *address = NULL;
+#ifdef _WIN32
+       address = try_reserve(0x80000000, size, flags);
+       if (address == NULL && (flags & UAE_VM_32BIT)) {
+               if (size <= 768 * 1024 * 1024) {
+                       address = try_reserve(0x78000000 - size, size, flags);
+               }
+       }
+       if (address == NULL && (flags & UAE_VM_32BIT) == 0) {
+               address = try_reserve(0, size, flags);
+       }
+#else
+#ifdef CPU_64_BIT
+       if (flags & UAE_VM_32BIT) {
+#else
+       if (true) {
+#endif
+               uintptr_t try_addr = 0x50000000;
+               while (address == NULL) {
+                       address = try_reserve(try_addr, size, flags);
+                       if (address == NULL) {
+                               try_addr -= 0x4000000;
+                               if (try_addr < 0x20000000) {
+                                       break;
+                               }
+                               continue;
+                       }
+               }
+       }
+       if (address == NULL && (flags & UAE_VM_32BIT) == 0) {
+               address = try_reserve(0, size, flags);
+       }
+#endif
+       if (address) {
+               uae_log("VM: Reserve  0x%-8x bytes, got address 0x%llx\n",
+                               size, (uae_u64) (uintptr_t) address);
+       } else {
+               uae_log("VM: Reserve  0x%-8x bytes failed!\n", size);
+       }
+       return address;
+}
+
+void *uae_vm_reserve_fixed(void *want_addr, uae_u32 size, int flags)
+{
+       void *address = NULL;
+       uae_log("VM: Reserve  0x%-8x bytes at %p (fixed)\n", size, want_addr);
+       address = try_reserve((uintptr_t) want_addr, size, flags);
+       if (address == NULL) {
+               uae_log("VM: Reserve  0x%-8x bytes at %p failed!\n", size, want_addr);
+               return NULL;
+       }
+       if (address != want_addr) {
+               do_free(address, size);
+               return NULL;
+       }
+       uae_log("VM: Reserve  0x%-8x bytes, got address 0x%llx\n",
+                       size, (uae_u64) (uintptr_t) address);
+       return address;
+}
+
+void *uae_vm_commit(void *address, uae_u32 size, int protect)
+{
+       uae_log("VM: Commit   0x%-8x bytes at %p (%s)\n",
+                       size, address, protect_description(protect));
+#ifdef _WIN32
+       int va_type = MEM_COMMIT ;
+       int va_protect = protect_to_native(protect);
+       address = VirtualAlloc(address, size, va_type, va_protect);
+#else
+#ifdef CLEAR_MEMORY_ON_COMMIT
+       do_protect(address, size, UAE_VM_READ_WRITE);
+       memset(address, 0, size);
+#endif
+       do_protect(address, size, protect);
+#endif
+       return address;
+}
+
+bool uae_vm_decommit(void *address, uae_u32 size)
+{
+       uae_log("VM: Decommit 0x%-8x bytes at %p\n", size, address);
+#ifdef _WIN32
+       return VirtualFree (address, size, MEM_DECOMMIT) != 0;
+#else
+#if 0
+       /* FIXME: perhaps we can unmmap and mmap the memory region again to
+        * allow the operating system to throw away the (unused) pages. Of
+        * course, we have problem if re-mmaping it fails. If we do this,
+        * we may be able to remove the memory clear operation in
+        * uae_vm_commit. We might also be able to use mmap with MAP_FIXED
+        * to more "safely" overwrite the old mapping. */
+       munmap(address, size);
+       mmap(...);
+#endif
+       return do_protect(address, size, UAE_VM_NO_ACCESS);
+#endif
 }