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