1 /* -----------------------------------------------------------------------------
3 * (c) The GHC Team 1998-1999
5 * MegaBlock Allocator Interface. This file contains all the dirty
6 * architecture-dependent hackery required to get a chunk of aligned
7 * memory from the operating system.
9 * ---------------------------------------------------------------------------*/
11 /* This is non-posix compliant. */
12 /* #include "PosixSource.h" */
18 #include "BlockAlloc.h"
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
32 #ifndef mingw32_TARGET_OS
33 # ifdef HAVE_SYS_MMAN_H
34 # include <sys/mman.h>
44 #include <mach/vm_map.h>
49 lnat mblocks_allocated = 0;
51 /* -----------------------------------------------------------------------------
52 The MBlock Map: provides our implementation of HEAP_ALLOCED()
53 -------------------------------------------------------------------------- */
55 #if SIZEOF_VOID_P == 4
56 StgWord8 mblock_map[MBLOCK_MAP_SIZE]; // initially all zeros
57 #elif SIZEOF_VOID_P == 8
58 static MBlockMap dummy_mblock_map;
59 MBlockMap *mblock_cache = &dummy_mblock_map;
60 int mblock_map_count = 0;
61 MBlockMap **mblock_maps = NULL;
64 findMBlockMap(void *p)
67 StgWord32 hi = (StgWord32) (((StgWord)p) >> 32);
68 for( i = 0; i < mblock_map_count; i++ )
70 if(mblock_maps[i]->addrHigh32 == hi)
72 return mblock_maps[i];
79 slowIsHeapAlloced(void *p)
81 MBlockMap *map = findMBlockMap(p);
85 return map->mblocks[MBLOCK_MAP_ENTRY(p)];
93 markHeapAlloced(void *p)
95 #if SIZEOF_VOID_P == 4
96 mblock_map[MBLOCK_MAP_ENTRY(p)] = 1;
97 #elif SIZEOF_VOID_P == 8
98 MBlockMap *map = findMBlockMap(p);
102 mblock_maps = realloc(mblock_maps,
103 sizeof(MBlockMap*) * mblock_map_count);
104 map = mblock_maps[mblock_map_count-1] = calloc(1,sizeof(MBlockMap));
105 map->addrHigh32 = (StgWord32) (((StgWord)p) >> 32);
107 map->mblocks[MBLOCK_MAP_ENTRY(p)] = 1;
112 /* -----------------------------------------------------------------------------
113 Allocate new mblock(s)
114 -------------------------------------------------------------------------- */
119 return getMBlocks(1);
122 /* -----------------------------------------------------------------------------
125 On Unix-like systems, we use mmap() to allocate our memory. We
126 want memory in chunks of MBLOCK_SIZE, and aligned on an MBLOCK_SIZE
127 boundary. The mmap() interface doesn't give us this level of
128 control, so we have to use some heuristics.
130 In the general case, if we want a block of n megablocks, then we
131 allocate n+1 and trim off the slop from either side (using
132 munmap()) to get an aligned chunk of size n. However, the next
133 time we'll try to allocate directly after the previously allocated
134 chunk, on the grounds that this is aligned and likely to be free.
135 If it turns out that we were wrong, we have to munmap() and try
136 again using the general method.
137 -------------------------------------------------------------------------- */
139 #if !defined(mingw32_TARGET_OS) && !defined(cygwin32_TARGET_OS)
141 // A wrapper around mmap(), to abstract away from OS differences in
142 // the mmap() interface.
145 my_mmap (void *addr, lnat size)
149 #if defined(solaris2_TARGET_OS) || defined(irix_TARGET_OS)
151 int fd = open("/dev/zero",O_RDONLY);
152 ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
156 ret = mmap(addr, size, PROT_READ | PROT_WRITE,
157 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
158 #elif darwin_TARGET_OS
159 // Without MAP_FIXED, Apple's mmap ignores addr.
160 // With MAP_FIXED, it overwrites already mapped regions, whic
161 // mmap(0, ... MAP_FIXED ...) is worst of all: It unmaps the program text
162 // and replaces it with zeroes, causing instant death.
163 // This behaviour seems to be conformant with IEEE Std 1003.1-2001.
164 // Let's just use the underlying Mach Microkernel calls directly,
165 // they're much nicer.
169 if(addr) // try to allocate at adress
170 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, FALSE);
171 if(!addr || err) // try to allocate anywhere
172 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, TRUE);
174 if(err) // don't know what the error codes mean exactly
175 barf("memory allocation failed (requested %lu bytes)", size);
177 vm_protect(mach_task_self(),ret,size,FALSE,VM_PROT_READ|VM_PROT_WRITE);
179 ret = mmap(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC,
180 MAP_ANON | MAP_PRIVATE, -1, 0);
183 if (ret == (void *)-1) {
184 if (errno == ENOMEM ||
185 (errno == EINVAL && sizeof(void*)==4 && size >= 0xc0000000)) {
186 // If we request more than 3Gig, then we get EINVAL
187 // instead of ENOMEM (at least on Linux).
188 errorBelch("out of memory (requested %lu bytes)", size);
189 stg_exit(EXIT_FAILURE);
191 barf("getMBlock: mmap: %s", strerror(errno));
198 // Implements the general case: allocate a chunk of memory of 'size'
202 gen_map_mblocks (lnat size)
207 // Try to map a larger block, and take the aligned portion from
208 // it (unmap the rest).
210 ret = my_mmap(0, size);
212 // unmap the slop bits around the chunk we allocated
213 slop = (W_)ret & MBLOCK_MASK;
215 if (munmap(ret, MBLOCK_SIZE - slop) == -1) {
216 barf("gen_map_mblocks: munmap failed");
218 if (slop > 0 && munmap(ret+size-slop, slop) == -1) {
219 barf("gen_map_mblocks: munmap failed");
222 // ToDo: if we happened to get an aligned block, then don't
223 // unmap the excess, just use it. For this to work, you
224 // need to keep in mind the following:
225 // * Calling my_mmap() with an 'addr' arg pointing to
226 // already my_mmap()ed space is OK and won't fail.
227 // * If my_mmap() can't satisfy the request at the
228 // given 'next_request' address in getMBlocks(), that
229 // you unmap the extra mblock mmap()ed here (or simply
230 // satisfy yourself that the slop introduced isn't worth
234 // next time, try after the block we just got.
235 ret += MBLOCK_SIZE - slop;
240 // The external interface: allocate 'n' mblocks, and return the
246 static caddr_t next_request = (caddr_t)HEAP_BASE;
248 lnat size = MBLOCK_SIZE * n;
251 if (next_request == 0) {
252 // use gen_map_mblocks the first time.
253 ret = gen_map_mblocks(size);
255 ret = my_mmap(next_request, size);
257 if (((W_)ret & MBLOCK_MASK) != 0) {
259 #if 0 // defined(DEBUG)
260 errorBelch("warning: getMBlock: misaligned block %p returned when allocating %d megablock(s) at %p", ret, n, next_request);
263 // unmap this block...
264 if (munmap(ret, size) == -1) {
265 barf("getMBlock: munmap failed");
267 // and do it the hard way
268 ret = gen_map_mblocks(size);
272 // Next time, we'll try to allocate right after the block we just got.
273 // ToDo: check that we haven't already grabbed the memory at next_request
274 next_request = ret + size;
276 IF_DEBUG(gc,debugBelch("Allocated %d megablock(s) at %p\n",n,ret));
279 for (i = 0; i < n; i++) {
280 markHeapAlloced( ret + i * MBLOCK_SIZE );
283 mblocks_allocated += n;
288 #else /* defined(mingw32_TARGET_OS) || defined(cygwin32_TARGET_OS) */
291 On Win32 platforms we make use of the two-phased virtual memory API
292 to allocate mega blocks. We proceed as follows:
294 Reserve a large chunk of VM (256M at the time, or what the user asked
295 for via the -M option), but don't supply a base address that's aligned on
296 a MB boundary. Instead we round up to the nearest mblock from the chunk of
297 VM we're handed back from the OS (at the moment we just leave the 'slop' at
298 the beginning of the reserved chunk unused - ToDo: reuse it .)
300 Reserving memory doesn't allocate physical storage (not even in the
301 page file), this is done later on by committing pages (or mega-blocks in
305 char* base_non_committed = (char*)0;
306 char* end_non_committed = (char*)0;
308 /* Default is to reserve 256M of VM to minimise the slop cost. */
309 #define SIZE_RESERVED_POOL ( 256 * 1024 * 1024 )
311 /* Number of bytes reserved */
312 static unsigned long size_reserved_pool = SIZE_RESERVED_POOL;
317 static char* base_mblocks = (char*)0;
318 static char* next_request = (char*)0;
319 void* ret = (void*)0;
322 lnat size = MBLOCK_SIZE * n;
324 if ( (base_non_committed == 0) || (next_request + size > end_non_committed) ) {
325 if (base_non_committed) {
326 /* Tacky, but if no user-provided -M option is in effect,
327 * set it to the default (==256M) in time for the heap overflow PSA.
329 if (RtsFlags.GcFlags.maxHeapSize == 0) {
330 RtsFlags.GcFlags.maxHeapSize = size_reserved_pool / BLOCK_SIZE;
334 if (RtsFlags.GcFlags.maxHeapSize != 0) {
335 size_reserved_pool = BLOCK_SIZE * RtsFlags.GcFlags.maxHeapSize;
336 if (size_reserved_pool < MBLOCK_SIZE) {
337 size_reserved_pool = 2*MBLOCK_SIZE;
340 base_non_committed = VirtualAlloc ( NULL
345 if ( base_non_committed == 0 ) {
346 errorBelch("getMBlocks: VirtualAlloc failed with: %ld\n", GetLastError());
349 end_non_committed = (char*)base_non_committed + (unsigned long)size_reserved_pool;
350 /* The returned pointer is not aligned on a mega-block boundary. Make it. */
351 base_mblocks = (char*)((unsigned long)base_non_committed & (unsigned long)~MBLOCK_MASK) + MBLOCK_SIZE;
353 debugBelch("getMBlocks: Dropping %d bytes off of 256M chunk\n",
354 (unsigned)base_mblocks - (unsigned)base_non_committed);
357 if ( ((char*)base_mblocks + size) > end_non_committed ) {
358 debugBelch("getMBlocks: oops, committed too small a region to start with.");
361 next_request = base_mblocks;
365 /* Commit the mega block(s) to phys mem */
366 if ( ret != (void*)-1 ) {
367 ret = VirtualAlloc(next_request, size, MEM_COMMIT, PAGE_READWRITE);
369 debugBelch("getMBlocks: VirtualAlloc failed with: %ld\n", GetLastError());
374 if (((W_)ret & MBLOCK_MASK) != 0) {
375 barf("getMBlocks: misaligned block returned");
378 if (ret == (void*)-1) {
379 barf("getMBlocks: unknown memory allocation failure on Win32.");
382 IF_DEBUG(gc,debugBelch("Allocated %d megablock(s) at 0x%x\n",n,(nat)ret));
383 next_request = (char*)next_request + size;
385 mblocks_allocated += n;
388 for (i = 0; i < n; i++) {
389 markHeapAlloced( ret + i * MBLOCK_SIZE );
395 /* Hand back the physical memory that is allocated to a mega-block.
396 ToDo: chain the released mega block onto some list so that
397 getMBlocks() can get at it.
403 freeMBlock(void* p, nat n)
407 rc = VirtualFree(p, n * MBLOCK_SIZE , MEM_DECOMMIT );
411 debugBelch("freeMBlocks: VirtualFree failed with: %d\n", GetLastError());