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