Tue Dec 7 17:53:01 CET 2004 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / utils / mono-codeman.c
1 #include "config.h"
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <assert.h>
6
7 #ifdef PLATFORM_WIN32
8 #include <windows.h>
9 #include <io.h>
10 #else
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <sys/mman.h>
14 #include <fcntl.h>
15 #endif
16
17 #include "mono-codeman.h"
18
19 #ifdef PLATFORM_WIN32
20 #define FORCE_MALLOC
21 #endif
22
23 #define MIN_PAGES 16
24 #define MIN_ALIGN 8
25 /* if a chunk has less than this amount of free space it's considered full */
26 #define MAX_WASTAGE 32
27 #define MIN_BSIZE 32
28
29 #ifndef MAP_ANONYMOUS
30 #ifdef MAP_ANON
31 #define MAP_ANONYMOUS MAP_ANON
32 #else
33 #define FORCE_MALLOC
34 #endif
35 #endif
36
37 typedef struct _CodeChunck CodeChunk;
38
39 enum {
40         CODE_FLAG_MMAP,
41         CODE_FLAG_MALLOC
42 };
43
44 struct _CodeChunck {
45         char *data;
46         int pos;
47         int size;
48         CodeChunk *next;
49         unsigned int flags: 8;
50         /* this number of bytes is available to resolve addresses far in memory */
51         unsigned int bsize: 24;
52 };
53
54 struct _MonoCodeManager {
55         int dynamic;
56         CodeChunk *current;
57         CodeChunk *full;
58 };
59
60 MonoCodeManager* 
61 mono_code_manager_new (void)
62 {
63         MonoCodeManager *cman = malloc (sizeof (MonoCodeManager));
64         if (!cman)
65                 return NULL;
66         cman->current = NULL;
67         cman->full = NULL;
68         cman->dynamic = 0;
69         return cman;
70 }
71
72 MonoCodeManager* 
73 mono_code_manager_new_dynamic (void)
74 {
75         MonoCodeManager *cman = mono_code_manager_new ();
76         cman->dynamic = 1;
77         return cman;
78 }
79
80
81 static void
82 free_chunklist (CodeChunk *chunk)
83 {
84         CodeChunk *dead;
85         for (; chunk; ) {
86                 dead = chunk;
87                 chunk = chunk->next;
88                 if (dead->flags == CODE_FLAG_MMAP) {
89 #ifndef FORCE_MALLOC
90                         munmap (dead->data, dead->size);
91 #endif
92                 } else if (dead->flags == CODE_FLAG_MALLOC) {
93                         free (dead->data);
94                 }
95                 free (dead);
96         }
97 }
98
99 void
100 mono_code_manager_destroy (MonoCodeManager *cman)
101 {
102         free_chunklist (cman->full);
103         free_chunklist (cman->current);
104         free (cman);
105 }
106
107 /* fill all the memory with the 0x2a (42) value */
108 void             
109 mono_code_manager_invalidate (MonoCodeManager *cman)
110 {
111         CodeChunk *chunk;
112
113 #if defined(__i386__) || defined(__x86_64__)
114         int fill_value = 0xcc; /* x86 break */
115 #else
116         int fill_value = 0x2a;
117 #endif
118
119         for (chunk = cman->current; chunk; chunk = chunk->next)
120                 memset (chunk->data, fill_value, chunk->size);
121         for (chunk = cman->full; chunk; chunk = chunk->next)
122                 memset (chunk->data, fill_value, chunk->size);
123 }
124
125 void
126 mono_code_manager_foreach (MonoCodeManager *cman, MonoCodeManagerFunc func, void *user_data)
127 {
128         CodeChunk *chunk;
129         for (chunk = cman->current; chunk; chunk = chunk->next) {
130                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
131                         return;
132         }
133         for (chunk = cman->full; chunk; chunk = chunk->next) {
134                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
135                         return;
136         }
137 }
138
139 static int
140 query_pagesize (void)
141 {
142 #ifdef PLATFORM_WIN32
143         SYSTEM_INFO info;
144         GetSystemInfo (&info);
145         return info.dwAllocationGranularity;
146 #else
147         return getpagesize ();
148 #endif
149 }
150
151 /* BIND_ROOM is the divisor for the chunck of code size dedicated
152  * to binding branches (branches not reachable with the immediate displacement)
153  * bind_size = size/BIND_ROOM;
154  * we should reduce it and make MIN_PAGES bigger for such systems
155  */
156 #if defined(__ppc__) || defined(__powerpc__)
157 #define BIND_ROOM 4
158 #endif
159
160 static CodeChunk*
161 new_codechunk (int dynamic, int size)
162 {
163         static int pagesize = 0;
164         int minsize, flags = CODE_FLAG_MMAP;
165         int chunk_size, bsize = 0;
166         CodeChunk *chunk;
167         void *ptr;
168
169 #ifdef FORCE_MALLOC
170         flags = CODE_FLAG_MALLOC;
171 #endif
172
173         if (dynamic) {
174                 chunk_size = size;
175                 flags = CODE_FLAG_MALLOC;
176         }
177         else {
178                 if (!pagesize)
179                         pagesize = query_pagesize ();
180
181                 minsize = pagesize * MIN_PAGES;
182                 if (size < minsize)
183                         chunk_size = minsize;
184                 else {
185                         chunk_size = size;
186                         chunk_size += pagesize - 1;
187                         chunk_size &= ~ (pagesize - 1);
188                 }
189         }
190 #ifdef BIND_ROOM
191         bsize = chunk_size / BIND_ROOM;
192         if (bsize < MIN_BSIZE)
193                 bsize = MIN_BSIZE;
194         bsize += MIN_ALIGN -1;
195         bsize &= ~ (MIN_ALIGN - 1);
196         g_print ("aligned bsize: %d\n", bsize);
197         if (chunk_size - size < bsize) {
198                 if (dynamic)
199                         chunk_size = size + bsize;
200                 else
201                         chunk_size += pagesize;
202         }
203 #endif
204
205         /* does it make sense to use the mmap-like API? */
206         if (flags == CODE_FLAG_MALLOC) {
207                 ptr = malloc (chunk_size);
208                 if (!ptr)
209                         return NULL;
210
211         }
212         else {
213 #ifndef FORCE_MALLOC
214                 ptr = mmap (0, chunk_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
215                 if (ptr == (void*)-1) {
216                         int fd = open ("/dev/zero", O_RDONLY);
217                         if (fd != -1) {
218                                 ptr = mmap (0, chunk_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0);
219                                 close (fd);
220                         }
221                         if (ptr == (void*)-1) {
222                                 ptr = malloc (chunk_size);
223                                 if (!ptr)
224                                         return NULL;
225                                 flags = CODE_FLAG_MALLOC;
226                         }
227                 }
228 #else
229                 return NULL;
230 #endif
231         }
232
233         if (flags == CODE_FLAG_MALLOC) {
234                 /*
235                  * AMD64 processors maintain icache coherency only for pages which are 
236                  * marked executable.
237                  */
238 #ifndef PLATFORM_WIN32
239                 {
240                         char *page_start = (char *) (((unsigned long long) (ptr)) & ~ (pagesize - 1));
241                         int pages = ((char*)ptr + chunk_size - page_start + pagesize - 1) / pagesize;
242                         int err = mprotect (page_start, pages * pagesize, PROT_READ | PROT_WRITE | PROT_EXEC);
243                         assert (!err);
244                 }
245 #else
246                 {
247                         DWORD oldp;
248                         int err = VirtualProtect (ptr, chunk_size, PAGE_EXECUTE_READWRITE, &oldp);
249                         assert (err);
250                 }
251 #endif
252
253                         /* Make sure the thunks area is zeroed */
254                         memset (ptr, 0, bsize);
255         }
256
257         chunk = malloc (sizeof (CodeChunk));
258         if (!chunk) {
259                 if (flags == CODE_FLAG_MALLOC)
260                         free (ptr);
261 #ifndef FORCE_MALLOC
262                 else
263                         munmap (ptr, chunk_size);
264 #endif
265                 return NULL;
266         }
267         chunk->next = NULL;
268         chunk->size = chunk_size;
269         chunk->data = ptr;
270         chunk->flags = flags;
271         chunk->pos = bsize;
272         chunk->bsize = bsize;
273
274         /*printf ("code chunk at: %p\n", ptr);*/
275         return chunk;
276 }
277
278 void*
279 mono_code_manager_reserve (MonoCodeManager *cman, int size)
280 {
281         CodeChunk *chunk, *prev;
282         void *ptr;
283         
284         size += MIN_ALIGN;
285         size &= ~ (MIN_ALIGN - 1);
286
287         if (!cman->current) {
288                 cman->current = new_codechunk (cman->dynamic, size);
289                 if (!cman->current)
290                         return NULL;
291         }
292
293         for (chunk = cman->current; chunk; chunk = chunk->next) {
294                 if (chunk->pos + size <= chunk->size) {
295                         ptr = chunk->data + chunk->pos;
296                         chunk->pos += size;
297                         return ptr;
298                 }
299         }
300         /* 
301          * no room found, move one filled chunk to cman->full 
302          * to keep cman->current from growing too much
303          */
304         prev = NULL;
305         for (chunk = cman->current; chunk; prev = chunk, chunk = chunk->next) {
306                 if (chunk->pos + MIN_ALIGN * 4 <= chunk->size)
307                         continue;
308                 if (prev) {
309                         prev->next = chunk->next;
310                 } else {
311                         cman->current = chunk->next;
312                 }
313                 chunk->next = cman->full;
314                 cman->full = chunk;
315                 break;
316         }
317         chunk = new_codechunk (cman->dynamic, size);
318         if (!chunk)
319                 return NULL;
320         chunk->next = cman->current;
321         cman->current = chunk;
322         chunk->pos += size;
323         return chunk->data;
324 }
325
326 /* 
327  * if we reserved too much room for a method and we didn't allocate
328  * already from the code manager, we can get back the excess allocation.
329  */
330 void
331 mono_code_manager_commit (MonoCodeManager *cman, void *data, int size, int newsize)
332 {
333         newsize += MIN_ALIGN;
334         newsize &= ~ (MIN_ALIGN - 1);
335         size += MIN_ALIGN;
336         size &= ~ (MIN_ALIGN - 1);
337
338         if (cman->current && (size != newsize) && (data == cman->current->data + cman->current->pos - size)) {
339                 cman->current->pos -= size - newsize;
340         }
341 }
342