#include <sys/sysctl.h>
#endif
-#ifdef LINUX
+#if defined(LINUX) && defined(CPU_x86_64)
#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 CLEAR_MEMORY_ON_COMMIT
+// #define LOG_ALLOCATIONS
// #define TRACK_ALLOCATIONS
#ifdef TRACK_ALLOCATIONS
static void *uae_vm_alloc_with_flags(uae_u32 size, int flags, int protect)
{
void *address = NULL;
+ static bool first_allocation = true;
+ if (first_allocation) {
+ /* FIXME: log contents of /proc/self/maps on Linux */
+ /* FIXME: use VirtualQuery function on Windows? */
+ first_allocation = false;
+ }
+#ifdef LOG_ALLOCATIONS
uae_log("VM: Allocate 0x%-8x bytes [%d] (%s)\n",
size, flags, protect_description(protect));
+#endif
+
#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) {
- /* Stupid algorithm to find available space, but should
- * work well enough when there is not a lot of allocations. */
- uae_u8 *p = (uae_u8 *) 0x50000000;
- while (address == NULL) {
- if (p >= (void*) 0x60000000) {
- break;
- }
- address = VirtualAlloc(p, size, va_type, va_protect);
- p += uae_vm_page_size();
- }
- }
-#endif
- if (!address) {
- address = VirtualAlloc(NULL, size, va_type, va_protect);
- }
#else
- //size = size < uae_vm_page_size() ? uae_vm_page_size() : size;
int mmap_flags = MAP_PRIVATE | MAP_ANON;
int mmap_prot = protect_to_native(protect);
-#ifdef CPU_64_BIT
+#endif
+
+#ifndef CPU_64_BIT
+ flags &= ~UAE_VM_32BIT;
+#endif
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;
+ int step = uae_vm_page_size();
+ uae_u8 *p = (uae_u8 *) 0x40000000;
+ uae_u8 *p_end = natmem_reserved - size;
+ if (size > 1024 * 1024) {
+ /* Reserve some space for smaller allocations */
+ p += 32 * 1024 * 1024;
+ step = 1024 * 1024;
+ }
+#ifdef HAVE_MAP_32BIT
+ address = mmap(0, size, mmap_prot, mmap_flags | MAP_32BIT, -1, 0);
+ if (address == MAP_FAILED) {
+ address = NULL;
+ }
+#endif
while (address == NULL) {
- if (p >= p_end) {
+ if (p > p_end) {
break;
}
- printf("%p\n", p);
+#ifdef _WIN32
+ address = VirtualAlloc(p, size, va_type, va_protect);
+#else
address = mmap(p, size, mmap_prot, mmap_flags, -1, 0);
- /* FIXME: check 32-bit result */
+ // write_log("VM: trying %p step is 0x%x = %p\n", p, step, address);
if (address == MAP_FAILED) {
address = NULL;
+ } else if (((uintptr_t) address) + size > (uintptr_t) 0xffffffff) {
+ munmap(address, size);
+ address = NULL;
}
- p += uae_vm_page_size();
- }
-#endif
- }
#endif
- if (address == NULL) {
+ p += step;
+ }
+ } else {
+#ifdef _WIN32
+ address = VirtualAlloc(NULL, size, va_type, va_protect);
+#else
address = mmap(0, size, mmap_prot, mmap_flags, -1, 0);
if (address == MAP_FAILED) {
address = NULL;
}
- }
#endif
+ }
+
if (address == NULL) {
uae_log("VM: uae_vm_alloc(%u, %d, %d) mmap failed (%d)\n",
size, flags, protect, errno);
#ifdef TRACK_ALLOCATIONS
add_allocation(address, size);
#endif
+#ifdef LOG_ALLOCATIONS
uae_log("VM: %p\n", address);
+#endif
return address;
}
#else
if (true) {
#endif
- uintptr_t try_addr = 0x50000000;
+ uintptr_t try_addr = 0x80000000;
while (address == NULL) {
address = try_reserve(try_addr, size, flags);
if (address == NULL) {
#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);
+ /* Re-map the memory so we get fresh unused pages (and the old ones can be
+ * released and physical memory reclaimed). We also assume that the new
+ * pages will be zero-initialized (tested on Linux and OS X). */
+ void *result = mmap(address, size, PROT_NONE,
+ MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
+ if (result == MAP_FAILED) {
+ uae_log("VM: Warning - could not re-map with MAP_FIXED at %p\n",
+ address);
+ do_protect(address, size, UAE_VM_NO_ACCESS);
+ }
+ return result != MAP_FAILED;
#endif
}