Mon Oct 8 11:47:44 CEST 2007 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / utils / mono-codeman.c
1 #include "config.h"
2
3 #ifdef HAVE_UNISTD_H
4 #include <unistd.h>
5 #endif
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <glib.h>
10
11 #include "mono-codeman.h"
12 #include "mono-mmap.h"
13 #include <mono/metadata/class-internals.h>
14 #ifdef HAVE_VALGRIND_MEMCHECK_H
15 #include <valgrind/memcheck.h>
16 #endif
17
18 #define MIN_PAGES 16
19
20 #if defined(__ia64__) || defined(__x86_64__)
21 /*
22  * We require 16 byte alignment on amd64 so the fp literals embedded in the code are 
23  * properly aligned for SSE2.
24  */
25 #define MIN_ALIGN 16
26 #else
27 #define MIN_ALIGN 8
28 #endif
29
30 /* if a chunk has less than this amount of free space it's considered full */
31 #define MAX_WASTAGE 32
32 #define MIN_BSIZE 32
33
34 #ifdef __x86_64__
35 #define ARCH_MAP_FLAGS MONO_MMAP_32BIT
36 #else
37 #define ARCH_MAP_FLAGS 0
38 #endif
39
40 #define MONO_PROT_RWX (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC)
41
42 typedef struct _CodeChunck CodeChunk;
43
44 enum {
45         CODE_FLAG_MMAP,
46         CODE_FLAG_MALLOC
47 };
48
49 struct _CodeChunck {
50         char *data;
51         int pos;
52         int size;
53         CodeChunk *next;
54         unsigned int flags: 8;
55         /* this number of bytes is available to resolve addresses far in memory */
56         unsigned int bsize: 24;
57 };
58
59 struct _MonoCodeManager {
60         int dynamic;
61         CodeChunk *current;
62         CodeChunk *full;
63 };
64
65 #define ALIGN_INT(val,alignment) (((val) + (alignment - 1)) & ~(alignment - 1))
66
67 /**
68  * mono_code_manager_new:
69  *
70  * Creates a new code manager. A code manager can be used to allocate memory
71  * suitable for storing native code that can be later executed.
72  * A code manager allocates memory from the operating system in large chunks
73  * (typically 64KB in size) so that many methods can be allocated inside them
74  * close together, improving cache locality.
75  *
76  * Returns: the new code manager
77  */
78 MonoCodeManager* 
79 mono_code_manager_new (void)
80 {
81         MonoCodeManager *cman = malloc (sizeof (MonoCodeManager));
82         if (!cman)
83                 return NULL;
84         cman->current = NULL;
85         cman->full = NULL;
86         cman->dynamic = 0;
87         return cman;
88 }
89
90 /**
91  * mono_code_manager_new_dynamic:
92  *
93  * Creates a new code manager suitable for holding native code that can be
94  * used for single or small methods that need to be deallocated independently
95  * of other native code.
96  *
97  * Returns: the new code manager
98  */
99 MonoCodeManager* 
100 mono_code_manager_new_dynamic (void)
101 {
102         MonoCodeManager *cman = mono_code_manager_new ();
103         cman->dynamic = 1;
104         return cman;
105 }
106
107
108 static void
109 free_chunklist (CodeChunk *chunk)
110 {
111         CodeChunk *dead;
112         
113 #if defined(HAVE_VALGRIND_MEMCHECK_H) && defined (VALGRIND_JIT_UNREGISTER_MAP)
114         int valgrind_unregister = 0;
115         if (RUNNING_ON_VALGRIND)
116                 valgrind_unregister = 1;
117 #define valgrind_unregister(x) do { if (valgrind_unregister) { VALGRIND_JIT_UNREGISTER_MAP(NULL,x); } } while (0) 
118 #else
119 #define valgrind_unregister(x)
120 #endif
121
122         for (; chunk; ) {
123                 dead = chunk;
124                 chunk = chunk->next;
125                 if (dead->flags == CODE_FLAG_MMAP) {
126                         mono_vfree (dead->data, dead->size);
127                         /* valgrind_unregister(dead->data); */
128                 } else if (dead->flags == CODE_FLAG_MALLOC) {
129                         free (dead->data);
130                 }
131                 free (dead);
132         }
133 }
134
135 /**
136  * mono_code_manager_destroy:
137  * @cman: a code manager
138  *
139  * Free all the memory associated with the code manager @cman.
140  */
141 void
142 mono_code_manager_destroy (MonoCodeManager *cman)
143 {
144         free_chunklist (cman->full);
145         free_chunklist (cman->current);
146         free (cman);
147 }
148
149 /**
150  * mono_code_manager_invalidate:
151  * @cman: a code manager
152  *
153  * Fill all the memory with an invalid native code value
154  * so that any attempt to execute code allocated in the code
155  * manager @cman will fail. This is used for debugging purposes.
156  */
157 void             
158 mono_code_manager_invalidate (MonoCodeManager *cman)
159 {
160         CodeChunk *chunk;
161
162 #if defined(__i386__) || defined(__x86_64__)
163         int fill_value = 0xcc; /* x86 break */
164 #else
165         int fill_value = 0x2a;
166 #endif
167
168         for (chunk = cman->current; chunk; chunk = chunk->next)
169                 memset (chunk->data, fill_value, chunk->size);
170         for (chunk = cman->full; chunk; chunk = chunk->next)
171                 memset (chunk->data, fill_value, chunk->size);
172 }
173
174 /**
175  * mono_code_manager_foreach:
176  * @cman: a code manager
177  * @func: a callback function pointer
178  * @user_data: additional data to pass to @func
179  *
180  * Invokes the callback @func for each different chunk of memory allocated
181  * in the code manager @cman.
182  */
183 void
184 mono_code_manager_foreach (MonoCodeManager *cman, MonoCodeManagerFunc func, void *user_data)
185 {
186         CodeChunk *chunk;
187         for (chunk = cman->current; chunk; chunk = chunk->next) {
188                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
189                         return;
190         }
191         for (chunk = cman->full; chunk; chunk = chunk->next) {
192                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
193                         return;
194         }
195 }
196
197 /* BIND_ROOM is the divisor for the chunck of code size dedicated
198  * to binding branches (branches not reachable with the immediate displacement)
199  * bind_size = size/BIND_ROOM;
200  * we should reduce it and make MIN_PAGES bigger for such systems
201  */
202 #if defined(__ppc__) || defined(__powerpc__)
203 #define BIND_ROOM 4
204 #endif
205 #if defined(__arm__)
206 #define BIND_ROOM 8
207 #endif
208
209 static CodeChunk*
210 new_codechunk (int dynamic, int size)
211 {
212         int minsize, flags = CODE_FLAG_MMAP;
213         int chunk_size, bsize = 0;
214         int pagesize;
215         CodeChunk *chunk;
216         void *ptr;
217
218 #ifdef FORCE_MALLOC
219         flags = CODE_FLAG_MALLOC;
220 #endif
221
222         pagesize = mono_pagesize ();
223
224         if (dynamic) {
225                 chunk_size = size;
226                 flags = CODE_FLAG_MALLOC;
227         } else {
228                 minsize = pagesize * MIN_PAGES;
229                 if (size < minsize)
230                         chunk_size = minsize;
231                 else {
232                         chunk_size = size;
233                         chunk_size += pagesize - 1;
234                         chunk_size &= ~ (pagesize - 1);
235                 }
236         }
237 #ifdef BIND_ROOM
238         bsize = chunk_size / BIND_ROOM;
239         if (bsize < MIN_BSIZE)
240                 bsize = MIN_BSIZE;
241         bsize += MIN_ALIGN -1;
242         bsize &= ~ (MIN_ALIGN - 1);
243         if (chunk_size - size < bsize) {
244                 chunk_size = size + bsize;
245                 chunk_size += pagesize - 1;
246                 chunk_size &= ~ (pagesize - 1);
247         }
248 #endif
249
250         /* does it make sense to use the mmap-like API? */
251         if (flags == CODE_FLAG_MALLOC) {
252                 ptr = malloc (chunk_size + MIN_ALIGN - 1);
253                 if (!ptr)
254                         return NULL;
255         } else {
256                 ptr = mono_valloc (NULL, chunk_size, MONO_PROT_RWX | ARCH_MAP_FLAGS);
257                 if (!ptr)
258                         return NULL;
259         }
260
261         if (flags == CODE_FLAG_MALLOC) {
262                 /*
263                  * AMD64 processors maintain icache coherency only for pages which are 
264                  * marked executable.
265                  */
266 #ifndef PLATFORM_WIN32
267                 {
268                         char *page_start = (char *) (((gssize) (ptr)) & ~ (pagesize - 1));
269                         int pages = ((char*)ptr + chunk_size - page_start + pagesize - 1) / pagesize;
270                         int err = mono_mprotect (page_start, pages * pagesize, MONO_PROT_RWX);
271                         assert (!err);
272                 }
273 #endif
274
275 #ifdef BIND_ROOM
276                         /* Make sure the thunks area is zeroed */
277                         memset (ptr, 0, bsize);
278 #endif
279         }
280
281         chunk = malloc (sizeof (CodeChunk));
282         if (!chunk) {
283                 if (flags == CODE_FLAG_MALLOC)
284                         free (ptr);
285                 else
286                         mono_vfree (ptr, chunk_size);
287                 return NULL;
288         }
289         chunk->next = NULL;
290         chunk->size = chunk_size;
291         chunk->data = ptr;
292         chunk->flags = flags;
293         chunk->pos = bsize;
294         chunk->bsize = bsize;
295
296         /*printf ("code chunk at: %p\n", ptr);*/
297         return chunk;
298 }
299
300 /**
301  * mono_code_manager_reserve:
302  * @cman: a code manager
303  * @size: size of memory to allocate
304  * @alignment: power of two alignment value
305  *
306  * Allocates at least @size bytes of memory inside the code manager @cman.
307  *
308  * Returns: the pointer to the allocated memory or #NULL on failure
309  */
310 void*
311 mono_code_manager_reserve_align (MonoCodeManager *cman, int size, int alignment)
312 {
313         CodeChunk *chunk, *prev;
314         void *ptr;
315
316         /* eventually allow bigger alignments, but we need to fix the dynamic alloc code to
317          * handle this before
318          */
319         g_assert (alignment <= MIN_ALIGN);
320
321         if (cman->dynamic) {
322                 ++mono_stats.dynamic_code_alloc_count;
323                 mono_stats.dynamic_code_bytes_count += size;
324         }
325
326         if (!cman->current) {
327                 cman->current = new_codechunk (cman->dynamic, size);
328                 if (!cman->current)
329                         return NULL;
330         }
331
332         for (chunk = cman->current; chunk; chunk = chunk->next) {
333                 if (ALIGN_INT (chunk->pos, alignment) + size <= chunk->size) {
334                         chunk->pos = ALIGN_INT (chunk->pos, alignment);
335                         ptr = chunk->data + chunk->pos;
336                         chunk->pos += size;
337                         return ptr;
338                 }
339         }
340         /* 
341          * no room found, move one filled chunk to cman->full 
342          * to keep cman->current from growing too much
343          */
344         prev = NULL;
345         for (chunk = cman->current; chunk; prev = chunk, chunk = chunk->next) {
346                 if (chunk->pos + MIN_ALIGN * 4 <= chunk->size)
347                         continue;
348                 if (prev) {
349                         prev->next = chunk->next;
350                 } else {
351                         cman->current = chunk->next;
352                 }
353                 chunk->next = cman->full;
354                 cman->full = chunk;
355                 break;
356         }
357         chunk = new_codechunk (cman->dynamic, size);
358         if (!chunk)
359                 return NULL;
360         chunk->next = cman->current;
361         cman->current = chunk;
362         chunk->pos = ALIGN_INT (chunk->pos, alignment);
363         ptr = chunk->data + chunk->pos;
364         chunk->pos += size;
365         return ptr;
366 }
367
368 /**
369  * mono_code_manager_reserve:
370  * @cman: a code manager
371  * @size: size of memory to allocate
372  *
373  * Allocates at least @size bytes of memory inside the code manager @cman.
374  *
375  * Returns: the pointer to the allocated memory or #NULL on failure
376  */
377 void*
378 mono_code_manager_reserve (MonoCodeManager *cman, int size)
379 {
380         return mono_code_manager_reserve_align (cman, size, MIN_ALIGN);
381 }
382
383 /**
384  * mono_code_manager_commit:
385  * @cman: a code manager
386  * @data: the pointer returned by mono_code_manager_reserve ()
387  * @size: the size requested in the call to mono_code_manager_reserve ()
388  * @newsize: the new size to reserve
389  *
390  * If we reserved too much room for a method and we didn't allocate
391  * already from the code manager, we can get back the excess allocation
392  * for later use in the code manager.
393  */
394 void
395 mono_code_manager_commit (MonoCodeManager *cman, void *data, int size, int newsize)
396 {
397         g_assert (newsize <= size);
398
399         if (cman->current && (size != newsize) && (data == cman->current->data + cman->current->pos - size)) {
400                 cman->current->pos -= size - newsize;
401         }
402 }
403
404 /**
405  * mono_code_manager_size:
406  * @cman: a code manager
407  * @used_size: pointer to an integer for the result
408  *
409  * This function can be used to get statistics about a code manager:
410  * the integer pointed to by @used_size will contain how much
411  * memory is actually used inside the code managed @cman.
412  *
413  * Returns: the amount of memory allocated in @cman
414  */
415 int
416 mono_code_manager_size (MonoCodeManager *cman, int *used_size)
417 {
418         CodeChunk *chunk;
419         guint32 size = 0;
420         guint32 used = 0;
421         for (chunk = cman->current; chunk; chunk = chunk->next) {
422                 size += chunk->size;
423                 used += chunk->pos;
424         }
425         for (chunk = cman->full; chunk; chunk = chunk->next) {
426                 size += chunk->size;
427                 used += chunk->pos;
428         }
429         if (used_size)
430                 *used_size = used;
431         return size;
432 }
433