lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


#define MMAP_REGION_END		((uintptr_t)0x80000000)

Is there any reason this is 2GB not 4GB? LuaJIT on 32 bit Linux is happy
to get pointers from mmap() above 2GB. I have done some tests on FreeBSD
x86-64 with a 4GB ceiling and it seems to work fine. (Sorry, haven't got a
64 bit Mac to hand right now.) This improvement allows me to allocate over
3.33 GB data (vs 2.75GB on 32 bit Linux). It's probably worth increasing
the mmap floor a bit so the malloc heap size and LuaJIT heap size are more
equally balanced.

Also, what is the reason for the hint + retry wrapper? AFAICT it just
duplicates logic that already exists inside the kernel. I guess that the
kernel prefers to return memory addressed above the program's data segment
(as suggested in the x64 ABI) but on Mac OS X the LuaJIT build tells the
linker to move the text and data segments above 4GB, so LuaJIT needs the
hint to force it to allocate below 4GB. Is that right? I've attached a
patch that simplifies the FreeBSD mmap wrapper a bit.

The pre-GPLv3 gcc-4.2.1 docs say that only -mcmodel=small and
-mcmodel=medium are supported, which restricts code to being in the bottom
2GB memory. More recent versions of GCC do support the large model
which allows the program to sprawl all over the address space, though the
larger pointers slow things down a bit.

I tried fiddling with -Ttext=100000000 to move the executable but the
linker complained of lack of space for the executable headers. (I'm not
sure what logic led to that error message.) I tried frobbing the linker
script to move the text segments but (as you might expect) the small model
uses 32 bit relocations and checks that the results fit, which they don't
if you try to place stuff above 4GB :-)

There are also PC-relative models defined by the x64 ABI. However if you
try linking with --pic-executable it fails since crt1.o is not PIC.

Tony.
-- 
f.anthony.n.finch  <dot@dotat.at>  http://dotat.at/
HUMBER THAMES DOVER WIGHT PORTLAND: NORTH BACKING WEST OR NORTHWEST, 5 TO 7,
DECREASING 4 OR 5, OCCASIONALLY 6 LATER IN HUMBER AND THAMES. MODERATE OR
ROUGH. RAIN THEN FAIR. GOOD.
commit 59cd7738eb9ce9c0a610516406528bbb5a042992
Author: Tony Finch <dot@dotat.at>
Date:   2010-10-25 18:04:06 +0100

    lj_alloc: adjust memory layout on Mac OS X and FreeBSD x64
    
    Increase mmap ceiling to 4GB on Mac OS X, to provide more space for
    LuaJIT.  Increase mmap floor to 1GB on FreeBSD to provide more space
    for the malloc heap at the expense of LuaJIT.

diff --git a/src/lj_alloc.c b/src/lj_alloc.c
index b500132..fa4aec1 100644
--- a/src/lj_alloc.c
+++ b/src/lj_alloc.c
@@ -185,7 +185,7 @@ static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size)
 ** But -pagezero_size must be set, otherwise the lower 4GB are blocked.
 */
 #define MMAP_REGION_START	((uintptr_t)0x10000)
-#define MMAP_REGION_END		((uintptr_t)0x80000000)
+#define MMAP_REGION_END		((uintptr_t)0x100000000)
 
 static LJ_AINLINE void *CALL_MMAP(size_t size)
 {
@@ -214,7 +214,7 @@ static LJ_AINLINE void *CALL_MMAP(size_t size)
 ** bottom 4GB by reducing the data segment limit.
 ** See: grep -C15 RLIMIT_DATA /usr/src/sys/vm/vm_mmap.c
 */
-#define MMAP_REGION_START	((uintptr_t)0x10000000)
+#define MMAP_REGION_START	((uintptr_t)0x40000000)
 #define MMAP_REGION_END		((uintptr_t)0x100000000)
 
 #include <sys/resource.h>

commit da470e5a796b0f55fe227908df421a7218b186e0
Author: Tony Finch <dot@dotat.at>
Date:   2010-10-25 17:28:29 +0100

    lj_alloc: A simpler wrapper for mmap() on FreeBSD x86-64.
    
    No hint+retry logic is needed on FreeBSD to mmap regions above the
    data segment, unlike on Mac OS X where we want to mmap regions
    below the text segment.
    
    Use the INIT_MMAP() hook to set RLIMIT_DATA.

diff --git a/src/lj_alloc.c b/src/lj_alloc.c
index 2c68659..b500132 100644
--- a/src/lj_alloc.c
+++ b/src/lj_alloc.c
@@ -163,28 +163,28 @@ static LJ_AINLINE int CALL_MUNMAP(void *ptr, size_t size)
 #endif
 #define MMAP_FLAGS		(MAP_PRIVATE|MAP_ANONYMOUS)
 
+/* Defaults for easy cases, like 32 bit hosts. */
+#define INIT_MMAP()		((void)0)
+#define CALL_MMAP(s)		mmap(NULL, (s), MMAP_PROT, MMAP_FLAGS, -1, 0)
+#define DIRECT_MMAP(s)		CALL_MMAP(s)
+#define CALL_MUNMAP(a, s)	munmap((a), (s))
+
 #if LJ_64
 /* 64 bit mode needs special support for allocating memory in the lower 2GB. */
 
+#undef CALL_MMAP
+
 #if defined(__linux__)
 
 /* Actually this only gives us max. 1GB in current Linux kernels. */
 #define CALL_MMAP(s)	mmap(NULL, (s), MMAP_PROT, MAP_32BIT|MMAP_FLAGS, -1, 0)
 
-#elif (defined(__MACH__) && defined(__APPLE__)) || \
-      defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#elif defined(__MACH__) && defined(__APPLE__)
 
-/* OSX and FreeBSD mmap() use a naive first-fit linear search.
-** That's perfect for us. Except that -pagezero_size must be set for OSX,
-** otherwise the lower 4GB are blocked. And the 32GB RLIMIT_DATA needs
-** to be reduced to 250MB on FreeBSD.
+/* OSX mmap() uses a naive first-fit linear search. That's perfect for us.
+** But -pagezero_size must be set, otherwise the lower 4GB are blocked.
 */
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-#include <sys/resource.h>
-#define MMAP_REGION_START	((uintptr_t)0x10000000)
-#else
 #define MMAP_REGION_START	((uintptr_t)0x10000)
-#endif
 #define MMAP_REGION_END		((uintptr_t)0x80000000)
 
 static LJ_AINLINE void *CALL_MMAP(size_t size)
@@ -192,15 +192,6 @@ static LJ_AINLINE void *CALL_MMAP(size_t size)
   /* Hint for next allocation. Doesn't need to be thread-safe. */
   static uintptr_t alloc_hint = MMAP_REGION_START;
   int retry = 0;
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-  static int rlimit_modified = 0;
-  if (LJ_UNLIKELY(rlimit_modified == 0)) {
-    struct rlimit rlim;
-    rlim.rlim_cur = rlim.rlim_max = MMAP_REGION_START;
-    setrlimit(RLIMIT_DATA, &rlim);  /* Ignore result. May fail below. */
-    rlimit_modified = 1;
-  }
-#endif
   for (;;) {
     void *p = mmap((void *)alloc_hint, size, MMAP_PROT, MMAP_FLAGS, -1, 0);
     if ((uintptr_t)p >= MMAP_REGION_START &&
@@ -216,22 +207,45 @@ static LJ_AINLINE void *CALL_MMAP(size_t size)
   return CMFAIL;
 }
 
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+
+/* FreeBSD by default reserves the lower 32GB of memory for the data segment,
+** and allocates mmap regions above that. We can get mmap to allocate in the
+** bottom 4GB by reducing the data segment limit.
+** See: grep -C15 RLIMIT_DATA /usr/src/sys/vm/vm_mmap.c
+*/
+#define MMAP_REGION_START	((uintptr_t)0x10000000)
+#define MMAP_REGION_END		((uintptr_t)0x100000000)
+
+#include <sys/resource.h>
+
+#undef INIT_MMAP
+
+static void INIT_MMAP(void)
+{
+  struct rlimit rlim;
+  rlim.rlim_cur = rlim.rlim_max = MMAP_REGION_START;
+  setrlimit(RLIMIT_DATA, &rlim);
+}
+
+static LJ_AINLINE void *CALL_MMAP(size_t size)
+{
+  void *p = mmap(NULL, size, MMAP_PROT, MMAP_FLAGS, -1, 0);
+  if (LJ_LIKELY((uintptr_t)p + size < MMAP_REGION_END)) {
+    return p;
+  } else {
+    munmap(p, size);
+    return CMFAIL;
+  }
+}
+
 #else
 
 #error "NYI: need an equivalent of MAP_32BIT for this 64 bit OS"
 
 #endif
 
-#else
-
-/* 32 bit mode is easy. */
-#define CALL_MMAP(s)		mmap(NULL, (s), MMAP_PROT, MMAP_FLAGS, -1, 0)
-
-#endif
-
-#define INIT_MMAP()		((void)0)
-#define DIRECT_MMAP(s)		CALL_MMAP(s)
-#define CALL_MUNMAP(a, s)	munmap((a), (s))
+#endif /* LJ_64 */
 
 #ifdef __linux__
 /* Need to define _GNU_SOURCE to get the mremap prototype. */