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