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