[project @ 2000-08-04 23:31:43 by lewie]
[ghc-hetmet.git] / ghc / rts / MBlock.c
1 /* -----------------------------------------------------------------------------
2  * $Id: MBlock.c,v 1.16 2000/08/04 23:31:44 lewie Exp $
3  *
4  * (c) The GHC Team 1998-1999
5  *
6  * MegaBlock Allocator Interface.  This file contains all the dirty
7  * architecture-dependent hackery required to get a chunk of aligned
8  * memory from the operating system.
9  *
10  * ---------------------------------------------------------------------------*/
11
12 #define NON_POSIX_SOURCE
13
14 #include "Rts.h"
15 #include "RtsUtils.h"
16 #include "RtsFlags.h"
17 #include "MBlock.h"
18 #include "BlockAlloc.h"
19
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23
24 #ifdef HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif
27
28 #ifndef mingw32_TARGET_OS
29 # ifdef HAVE_SYS_MMAN_H
30 # include <sys/mman.h>
31 # endif
32 #endif
33
34 #ifdef HAVE_FCNTL_H
35 #include <fcntl.h>
36 #endif
37
38 #if HAVE_WINDOWS_H
39 #include <windows.h>
40 #endif
41
42 #if freebsd2_TARGET_OS || freebsd_TARGET_OS
43 /* Executable is loaded from      0x0
44  * Shared libraries are loaded at 0x2000000
45  * Stack is at the top of the address space.  The kernel probably owns
46  * 0x8000000 onwards, so we'll pick 0x5000000.
47  */
48 #define ASK_FOR_MEM_AT 0x50000000
49
50 #elif netbsd_TARGET_OS
51 /* NetBSD i386 shared libs are at 0x40000000
52  */
53 #define ASK_FOR_MEM_AT 0x50000000
54 #elif openbsd_TARGET_OS
55 #define ASK_FOR_MEM_AT 0x50000000
56 #elif linux_TARGET_OS
57 /* Any ideas?
58  */
59 #define ASK_FOR_MEM_AT 0x50000000
60
61 #elif solaris2_TARGET_OS
62 /* guess */
63 #define ASK_FOR_MEM_AT 0x50000000
64
65 #elif osf3_TARGET_OS
66 /* guess */
67 #define ASK_FOR_MEM_AT 0x50000000
68
69 #elif hpux_TARGET_OS
70 /* guess */
71 #define ASK_FOR_MEM_AT 0x50000000
72
73 #elif _WIN32
74 /* doesn't matter, we use a reserve/commit algorithm */
75
76 #else
77 #error Dont know where to get memory from on this architecture
78 /* ToDo: memory locations on other architectures */
79 #endif
80
81 lnat mblocks_allocated = 0;
82
83 void *
84 getMBlock(void)
85 {
86   return getMBlocks(1);
87 }
88
89 #ifndef _WIN32
90 void *
91 getMBlocks(nat n)
92 {
93   static caddr_t next_request = (caddr_t)ASK_FOR_MEM_AT;
94   caddr_t ret;
95   lnat size = MBLOCK_SIZE * n;
96  
97 #ifdef solaris2_TARGET_OS
98   { 
99       int fd = open("/dev/zero",O_RDONLY);
100       ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
101                  MAP_FIXED | MAP_PRIVATE, fd, 0);
102       close(fd);
103   }
104 #elif hpux_TARGET_OS
105  ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
106              MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
107 #else
108   ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
109              MAP_ANON | MAP_PRIVATE, -1, 0);
110 #endif
111   
112   if (ret == (void *)-1) {
113     if (errno == ENOMEM) {
114       barf("getMBlock: out of memory");
115     } else {
116       barf("GetMBlock: mmap failed");
117     }
118   }
119
120   if (((W_)ret & MBLOCK_MASK) != 0) {
121     barf("GetMBlock: misaligned block returned");
122   }
123
124   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
125
126   next_request += size;
127
128   mblocks_allocated += n;
129   
130   return ret;
131 }
132
133 #else /* _WIN32 */
134
135 /*
136  On Win32 platforms we make use of the two-phased virtual memory API
137  to allocate mega blocks. We proceed as follows:
138
139  Reserve a large chunk of VM (128M at the time), but don't supply a 
140  base address that's aligned on a MB boundary. Instead we round up to the
141  nearest from the chunk of VM we're given back from the OS (at the
142  moment we just leave the 'slop' at the beginning of the reserved
143  chunk unused - ToDo: reuse it .)
144
145  Reserving memory doesn't allocate physical storage (not even in the
146  page file), this is done by committing pages (or mega-blocks in
147  our case).
148
149 */
150
151 char* base_non_committed = (char*)0;
152
153 /* Reserve VM 128M at the time to try to minimise the slop cost. */
154 #define SIZE_RESERVED_POOL  ( 128 * 1024 * 1024 )
155
156 /* This predicate should be inlined, really. */
157 /* TODO: this only works for a single chunk */
158 int
159 is_heap_alloced(const void* x)
160 {
161   return (((char*)(x) >= base_non_committed) && 
162           ((char*)(x) <= (base_non_committed + SIZE_RESERVED_POOL)));
163 }
164
165 void *
166 getMBlocks(nat n)
167 {
168   static char* base_mblocks       = (char*)0;
169   static char* next_request       = (char*)0;
170   void* ret                       = (void*)0;
171
172   lnat size = MBLOCK_SIZE * n;
173
174   if ( (base_non_committed == 0) || 
175        (next_request + size > base_non_committed + SIZE_RESERVED_POOL) ) {
176     base_non_committed = VirtualAlloc ( NULL
177                                       , SIZE_RESERVED_POOL
178                                       , MEM_RESERVE
179                                       , PAGE_READWRITE
180                                       );
181     if ( base_non_committed == 0 ) {
182 # if 1 /*def DEBUG*/
183          fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
184 # endif
185          ret=(void*)-1;
186     } else {
187     /* The returned pointer is not aligned on a mega-block boundary. Make it. */
188        base_mblocks = (char*)((unsigned long)base_non_committed & (unsigned long)0xfff00000) + MBLOCK_SIZE;
189 # if 0
190        fprintf(stderr, "Dropping %d bytes off of 128M chunk\n", 
191                        (unsigned)base_mblocks - (unsigned)base_non_committed);
192 # endif
193
194        if ( ((char*)base_mblocks + size) > ((char*)base_non_committed + SIZE_RESERVED_POOL) ) {
195 # if 1 /*def DEBUG*/
196           fprintf(stderr, "oops, committed too small a region to start with.");
197 # endif
198           ret=(void*)-1;
199        } else {
200           next_request = base_mblocks;
201        }
202     }
203   }
204   /* Commit the mega block(s) to phys mem */
205   if ( ret != (void*)-1 ) {
206      ret = VirtualAlloc(next_request, size, MEM_COMMIT, PAGE_READWRITE);
207      if (ret == NULL) {
208 # if 1 /*def DEBUG*/
209         fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
210 # endif
211         ret=(void*)-1;
212      }
213   }
214
215   if (((W_)ret & MBLOCK_MASK) != 0) {
216     barf("GetMBlock: misaligned block returned");
217   }
218
219   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
220
221   next_request = (char*)next_request + size;
222
223   mblocks_allocated += n;
224   
225   return ret;
226 }
227
228 /* Hand back the physical memory that is allocated to a mega-block. 
229    ToDo: chain the released mega block onto some list so that
230          getMBlocks() can get at it.
231
232    Currently unused.
233 */
234 #if 0
235 void
236 freeMBlock(void* p, nat n)
237 {
238   BOOL rc;
239
240   rc = VirtualFree(p, n * MBLOCK_SIZE , MEM_DECOMMIT );
241   
242   if (rc == FALSE) {
243 # ifdef DEBUG
244      fprintf(stderr, "freeMBlocks: VirtualFree failed with: %d\n", GetLastError());
245 # endif
246   }
247
248 }
249 #endif
250
251 #endif