Merge pull request #1218 from AndreyAkinshin/master
[mono.git] / mono / metadata / jit-info.c
1 /*
2  * jit-info.c: MonoJitInfo functionality
3  *
4  * Author:
5  *      Dietmar Maurer (dietmar@ximian.com)
6  *      Patrik Torstensson
7  *
8  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
9  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
10  * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
11  */
12
13 #include <config.h>
14 #include <glib.h>
15 #include <string.h>
16 #include <sys/stat.h>
17
18 #include <mono/metadata/gc-internal.h>
19
20 #include <mono/utils/atomic.h>
21 #include <mono/utils/mono-compiler.h>
22 #include <mono/utils/mono-logger-internal.h>
23 #include <mono/utils/mono-membar.h>
24 #include <mono/utils/mono-counters.h>
25 #include <mono/utils/hazard-pointer.h>
26 #include <mono/utils/mono-tls.h>
27 #include <mono/utils/mono-mmap.h>
28 #include <mono/utils/mono-threads.h>
29 #include <mono/metadata/object.h>
30 #include <mono/metadata/object-internals.h>
31 #include <mono/metadata/domain-internals.h>
32 #include <mono/metadata/class-internals.h>
33 #include <mono/metadata/assembly.h>
34 #include <mono/metadata/exception.h>
35 #include <mono/metadata/metadata-internals.h>
36 #include <mono/metadata/gc-internal.h>
37 #include <mono/metadata/appdomain.h>
38 #include <mono/metadata/mono-debug-debugger.h>
39 #include <mono/metadata/mono-config.h>
40 #include <mono/metadata/threads-types.h>
41 #include <mono/metadata/runtime.h>
42 #include <metadata/threads.h>
43 #include <metadata/profiler-private.h>
44 #include <mono/metadata/coree.h>
45
46 static MonoJitInfoFindInAot jit_info_find_in_aot_func = NULL;
47
48 #define JIT_INFO_TABLE_FILL_RATIO_NOM           3
49 #define JIT_INFO_TABLE_FILL_RATIO_DENOM         4
50 #define JIT_INFO_TABLE_FILLED_NUM_ELEMENTS      (MONO_JIT_INFO_TABLE_CHUNK_SIZE * JIT_INFO_TABLE_FILL_RATIO_NOM / JIT_INFO_TABLE_FILL_RATIO_DENOM)
51
52 #define JIT_INFO_TABLE_LOW_WATERMARK(n)         ((n) / 2)
53 #define JIT_INFO_TABLE_HIGH_WATERMARK(n)        ((n) * 5 / 6)
54
55 #define JIT_INFO_TOMBSTONE_MARKER       ((MonoMethod*)NULL)
56 #define IS_JIT_INFO_TOMBSTONE(ji)       ((ji)->d.method == JIT_INFO_TOMBSTONE_MARKER)
57
58 #define JIT_INFO_TABLE_HAZARD_INDEX             0
59 #define JIT_INFO_HAZARD_INDEX                   1
60
61 static int
62 jit_info_table_num_elements (MonoJitInfoTable *table)
63 {
64         int i;
65         int num_elements = 0;
66
67         for (i = 0; i < table->num_chunks; ++i) {
68                 MonoJitInfoTableChunk *chunk = table->chunks [i];
69                 int chunk_num_elements = chunk->num_elements;
70                 int j;
71
72                 for (j = 0; j < chunk_num_elements; ++j) {
73                         if (!IS_JIT_INFO_TOMBSTONE (chunk->data [j]))
74                                 ++num_elements;
75                 }
76         }
77
78         return num_elements;
79 }
80
81 static MonoJitInfoTableChunk*
82 jit_info_table_new_chunk (void)
83 {
84         MonoJitInfoTableChunk *chunk = g_new0 (MonoJitInfoTableChunk, 1);
85         chunk->refcount = 1;
86
87         return chunk;
88 }
89
90 MonoJitInfoTable *
91 mono_jit_info_table_new (MonoDomain *domain)
92 {
93         MonoJitInfoTable *table = g_malloc0 (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*));
94
95         table->domain = domain;
96         table->num_chunks = 1;
97         table->chunks [0] = jit_info_table_new_chunk ();
98
99         return table;
100 }
101
102 void
103 mono_jit_info_table_free (MonoJitInfoTable *table)
104 {
105         int i;
106         int num_chunks = table->num_chunks;
107         MonoDomain *domain = table->domain;
108
109         mono_domain_lock (domain);
110
111         table->domain->num_jit_info_tables--;
112         if (table->domain->num_jit_info_tables <= 1) {
113                 GSList *list;
114
115                 for (list = table->domain->jit_info_free_queue; list; list = list->next)
116                         g_free (list->data);
117
118                 g_slist_free (table->domain->jit_info_free_queue);
119                 table->domain->jit_info_free_queue = NULL;
120         }
121
122         /* At this point we assume that there are no other threads
123            still accessing the table, so we don't have to worry about
124            hazardous pointers. */
125
126         for (i = 0; i < num_chunks; ++i) {
127                 MonoJitInfoTableChunk *chunk = table->chunks [i];
128                 int num_elements;
129                 int j;
130
131                 if (--chunk->refcount > 0)
132                         continue;
133
134                 num_elements = chunk->num_elements;
135                 for (j = 0; j < num_elements; ++j) {
136                         MonoJitInfo *ji = chunk->data [j];
137
138                         if (IS_JIT_INFO_TOMBSTONE (ji))
139                                 g_free (ji);
140                 }
141
142                 g_free (chunk);
143         }
144
145         mono_domain_unlock (domain);
146
147         g_free (table);
148 }
149
150 /* The jit_info_table is sorted in ascending order by the end
151  * addresses of the compiled methods.  The reason why we have to do
152  * this is that once we introduce tombstones, it becomes possible for
153  * code ranges to overlap, and if we sort by code start and insert at
154  * the back of the table, we cannot guarantee that we won't overlook
155  * an entry.
156  *
157  * There are actually two possible ways to do the sorting and
158  * inserting which work with our lock-free mechanism:
159  *
160  * 1. Sort by start address and insert at the front.  When looking for
161  * an entry, find the last one with a start address lower than the one
162  * you're looking for, then work your way to the front of the table.
163  *
164  * 2. Sort by end address and insert at the back.  When looking for an
165  * entry, find the first one with an end address higher than the one
166  * you're looking for, then work your way to the end of the table.
167  *
168  * We chose the latter out of convenience.
169  */
170 static int
171 jit_info_table_index (MonoJitInfoTable *table, gint8 *addr)
172 {
173         int left = 0, right = table->num_chunks;
174
175         g_assert (left < right);
176
177         do {
178                 int pos = (left + right) / 2;
179                 MonoJitInfoTableChunk *chunk = table->chunks [pos];
180
181                 if (addr < chunk->last_code_end)
182                         right = pos;
183                 else
184                         left = pos + 1;
185         } while (left < right);
186         g_assert (left == right);
187
188         if (left >= table->num_chunks)
189                 return table->num_chunks - 1;
190         return left;
191 }
192
193 static int
194 jit_info_table_chunk_index (MonoJitInfoTableChunk *chunk, MonoThreadHazardPointers *hp, gint8 *addr)
195 {
196         int left = 0, right = chunk->num_elements;
197
198         while (left < right) {
199                 int pos = (left + right) / 2;
200                 MonoJitInfo *ji = get_hazardous_pointer((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
201                 gint8 *code_end = (gint8*)ji->code_start + ji->code_size;
202
203                 if (addr < code_end)
204                         right = pos;
205                 else
206                         left = pos + 1;
207         }
208         g_assert (left == right);
209
210         return left;
211 }
212
213 static MonoJitInfo*
214 jit_info_table_find (MonoJitInfoTable *table, MonoThreadHazardPointers *hp, gint8 *addr)
215 {
216         MonoJitInfo *ji;
217         int chunk_pos, pos;
218
219         chunk_pos = jit_info_table_index (table, (gint8*)addr);
220         g_assert (chunk_pos < table->num_chunks);
221
222         pos = jit_info_table_chunk_index (table->chunks [chunk_pos], hp, (gint8*)addr);
223
224         /* We now have a position that's very close to that of the
225            first element whose end address is higher than the one
226            we're looking for.  If we don't have the exact position,
227            then we have a position below that one, so we'll just
228            search upward until we find our element. */
229         do {
230                 MonoJitInfoTableChunk *chunk = table->chunks [chunk_pos];
231
232                 while (pos < chunk->num_elements) {
233                         ji = get_hazardous_pointer ((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
234
235                         ++pos;
236
237                         if (IS_JIT_INFO_TOMBSTONE (ji)) {
238                                 mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
239                                 continue;
240                         }
241                         if ((gint8*)addr >= (gint8*)ji->code_start
242                                         && (gint8*)addr < (gint8*)ji->code_start + ji->code_size) {
243                                 mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
244                                 return ji;
245                         }
246
247                         /* If we find a non-tombstone element which is already
248                            beyond what we're looking for, we have to end the
249                            search. */
250                         if ((gint8*)addr < (gint8*)ji->code_start)
251                                 goto not_found;
252                 }
253
254                 ++chunk_pos;
255                 pos = 0;
256         } while (chunk_pos < table->num_chunks);
257
258  not_found:
259         if (hp)
260                 mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
261         return NULL;
262 }
263
264 /*
265  * mono_jit_info_table_find_internal:
266  *
267  * If TRY_AOT is FALSE, avoid loading information for missing methods from AOT images, which is currently not async safe.
268  * In this case, only those AOT methods will be found whose jit info is already loaded.
269  * If ALLOW_TRAMPOLINES is TRUE, this can return a MonoJitInfo which represents a trampoline (ji->is_trampoline is true).
270  * ASYNC SAFETY: When called in an async context (mono_thread_info_is_async_context ()), this is async safe.
271  * In this case, the returned MonoJitInfo might not have metadata information, in particular,
272  * mono_jit_info_get_method () could fail.
273  */
274 MonoJitInfo*
275 mono_jit_info_table_find_internal (MonoDomain *domain, char *addr, gboolean try_aot, gboolean allow_trampolines)
276 {
277         MonoJitInfoTable *table;
278         MonoJitInfo *ji, *module_ji;
279         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
280
281         ++mono_stats.jit_info_table_lookup_count;
282
283         /* First we have to get the domain's jit_info_table.  This is
284            complicated by the fact that a writer might substitute a
285            new table and free the old one.  What the writer guarantees
286            us is that it looks at the hazard pointers after it has
287            changed the jit_info_table pointer.  So, if we guard the
288            table by a hazard pointer and make sure that the pointer is
289            still there after we've made it hazardous, we don't have to
290            worry about the writer freeing the table. */
291         table = get_hazardous_pointer ((gpointer volatile*)&domain->jit_info_table, hp, JIT_INFO_TABLE_HAZARD_INDEX);
292
293         ji = jit_info_table_find (table, hp, (gint8*)addr);
294         if (hp)
295                 mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX);
296         if (ji && ji->is_trampoline && !allow_trampolines)
297                 return NULL;
298         if (ji)
299                 return ji;
300
301         /* Maybe its an AOT module */
302         if (try_aot && mono_get_root_domain () && mono_get_root_domain ()->aot_modules) {
303                 table = get_hazardous_pointer ((gpointer volatile*)&mono_get_root_domain ()->aot_modules, hp, JIT_INFO_TABLE_HAZARD_INDEX);
304                 module_ji = jit_info_table_find (table, hp, (gint8*)addr);
305                 if (module_ji)
306                         ji = jit_info_find_in_aot_func (domain, module_ji->d.image, addr);
307                 if (hp)
308                         mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX);
309         }
310
311         if (ji && ji->is_trampoline && !allow_trampolines)
312                 return NULL;
313         
314         return ji;
315 }
316
317 MonoJitInfo*
318 mono_jit_info_table_find (MonoDomain *domain, char *addr)
319 {
320         return mono_jit_info_table_find_internal (domain, addr, TRUE, FALSE);
321 }
322
323 static G_GNUC_UNUSED void
324 jit_info_table_check (MonoJitInfoTable *table)
325 {
326         int i;
327
328         for (i = 0; i < table->num_chunks; ++i) {
329                 MonoJitInfoTableChunk *chunk = table->chunks [i];
330                 int j;
331
332                 g_assert (chunk->refcount > 0 /* && chunk->refcount <= 8 */);
333                 if (chunk->refcount > 10)
334                         printf("warning: chunk refcount is %d\n", chunk->refcount);
335                 g_assert (chunk->num_elements <= MONO_JIT_INFO_TABLE_CHUNK_SIZE);
336
337                 for (j = 0; j < chunk->num_elements; ++j) {
338                         MonoJitInfo *this = chunk->data [j];
339                         MonoJitInfo *next;
340
341                         g_assert ((gint8*)this->code_start + this->code_size <= chunk->last_code_end);
342
343                         if (j < chunk->num_elements - 1)
344                                 next = chunk->data [j + 1];
345                         else if (i < table->num_chunks - 1) {
346                                 int k;
347
348                                 for (k = i + 1; k < table->num_chunks; ++k)
349                                         if (table->chunks [k]->num_elements > 0)
350                                                 break;
351
352                                 if (k >= table->num_chunks)
353                                         return;
354
355                                 g_assert (table->chunks [k]->num_elements > 0);
356                                 next = table->chunks [k]->data [0];
357                         } else
358                                 return;
359
360                         g_assert ((gint8*)this->code_start + this->code_size <= (gint8*)next->code_start + next->code_size);
361                 }
362         }
363 }
364
365 static MonoJitInfoTable*
366 jit_info_table_realloc (MonoJitInfoTable *old)
367 {
368         int i;
369         int num_elements = jit_info_table_num_elements (old);
370         int required_size;
371         int num_chunks;
372         int new_chunk, new_element;
373         MonoJitInfoTable *new;
374
375         /* number of needed places for elements needed */
376         required_size = (int)((long)num_elements * JIT_INFO_TABLE_FILL_RATIO_DENOM / JIT_INFO_TABLE_FILL_RATIO_NOM);
377         num_chunks = (required_size + MONO_JIT_INFO_TABLE_CHUNK_SIZE - 1) / MONO_JIT_INFO_TABLE_CHUNK_SIZE;
378         if (num_chunks == 0) {
379                 g_assert (num_elements == 0);
380                 return mono_jit_info_table_new (old->domain);
381         }
382         g_assert (num_chunks > 0);
383
384         new = g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*) * num_chunks);
385         new->domain = old->domain;
386         new->num_chunks = num_chunks;
387
388         for (i = 0; i < num_chunks; ++i)
389                 new->chunks [i] = jit_info_table_new_chunk ();
390
391         new_chunk = 0;
392         new_element = 0;
393         for (i = 0; i < old->num_chunks; ++i) {
394                 MonoJitInfoTableChunk *chunk = old->chunks [i];
395                 int chunk_num_elements = chunk->num_elements;
396                 int j;
397
398                 for (j = 0; j < chunk_num_elements; ++j) {
399                         if (!IS_JIT_INFO_TOMBSTONE (chunk->data [j])) {
400                                 g_assert (new_chunk < num_chunks);
401                                 new->chunks [new_chunk]->data [new_element] = chunk->data [j];
402                                 if (++new_element >= JIT_INFO_TABLE_FILLED_NUM_ELEMENTS) {
403                                         new->chunks [new_chunk]->num_elements = new_element;
404                                         ++new_chunk;
405                                         new_element = 0;
406                                 }
407                         }
408                 }
409         }
410
411         if (new_chunk < num_chunks) {
412                 g_assert (new_chunk == num_chunks - 1);
413                 new->chunks [new_chunk]->num_elements = new_element;
414                 g_assert (new->chunks [new_chunk]->num_elements > 0);
415         }
416
417         for (i = 0; i < num_chunks; ++i) {
418                 MonoJitInfoTableChunk *chunk = new->chunks [i];
419                 MonoJitInfo *ji = chunk->data [chunk->num_elements - 1];
420
421                 new->chunks [i]->last_code_end = (gint8*)ji->code_start + ji->code_size;
422         }
423
424         return new;
425 }
426
427 static void
428 jit_info_table_split_chunk (MonoJitInfoTableChunk *chunk, MonoJitInfoTableChunk **new1p, MonoJitInfoTableChunk **new2p)
429 {
430         MonoJitInfoTableChunk *new1 = jit_info_table_new_chunk ();
431         MonoJitInfoTableChunk *new2 = jit_info_table_new_chunk ();
432
433         g_assert (chunk->num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE);
434
435         new1->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE / 2;
436         new2->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE - new1->num_elements;
437
438         memcpy ((void*)new1->data, (void*)chunk->data, sizeof (MonoJitInfo*) * new1->num_elements);
439         memcpy ((void*)new2->data, (void*)(chunk->data + new1->num_elements), sizeof (MonoJitInfo*) * new2->num_elements);
440
441         new1->last_code_end = (gint8*)new1->data [new1->num_elements - 1]->code_start
442                 + new1->data [new1->num_elements - 1]->code_size;
443         new2->last_code_end = (gint8*)new2->data [new2->num_elements - 1]->code_start
444                 + new2->data [new2->num_elements - 1]->code_size;
445
446         *new1p = new1;
447         *new2p = new2;
448 }
449
450 static MonoJitInfoTable*
451 jit_info_table_copy_and_split_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
452 {
453         MonoJitInfoTable *new_table = g_malloc (MONO_SIZEOF_JIT_INFO_TABLE
454                 + sizeof (MonoJitInfoTableChunk*) * (table->num_chunks + 1));
455         int i, j;
456
457         new_table->domain = table->domain;
458         new_table->num_chunks = table->num_chunks + 1;
459
460         j = 0;
461         for (i = 0; i < table->num_chunks; ++i) {
462                 if (table->chunks [i] == chunk) {
463                         jit_info_table_split_chunk (chunk, &new_table->chunks [j], &new_table->chunks [j + 1]);
464                         j += 2;
465                 } else {
466                         new_table->chunks [j] = table->chunks [i];
467                         ++new_table->chunks [j]->refcount;
468                         ++j;
469                 }
470         }
471
472         g_assert (j == new_table->num_chunks);
473
474         return new_table;
475 }
476
477 static MonoJitInfoTableChunk*
478 jit_info_table_purify_chunk (MonoJitInfoTableChunk *old)
479 {
480         MonoJitInfoTableChunk *new = jit_info_table_new_chunk ();
481         int i, j;
482
483         j = 0;
484         for (i = 0; i < old->num_elements; ++i) {
485                 if (!IS_JIT_INFO_TOMBSTONE (old->data [i]))
486                         new->data [j++] = old->data [i];
487         }
488
489         new->num_elements = j;
490         if (new->num_elements > 0)
491                 new->last_code_end = (gint8*)new->data [j - 1]->code_start + new->data [j - 1]->code_size;
492         else
493                 new->last_code_end = old->last_code_end;
494
495         return new;
496 }
497
498 static MonoJitInfoTable*
499 jit_info_table_copy_and_purify_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
500 {
501         MonoJitInfoTable *new_table = g_malloc (MONO_SIZEOF_JIT_INFO_TABLE
502                 + sizeof (MonoJitInfoTableChunk*) * table->num_chunks);
503         int i, j;
504
505         new_table->domain = table->domain;
506         new_table->num_chunks = table->num_chunks;
507
508         j = 0;
509         for (i = 0; i < table->num_chunks; ++i) {
510                 if (table->chunks [i] == chunk)
511                         new_table->chunks [j++] = jit_info_table_purify_chunk (table->chunks [i]);
512                 else {
513                         new_table->chunks [j] = table->chunks [i];
514                         ++new_table->chunks [j]->refcount;
515                         ++j;
516                 }
517         }
518
519         g_assert (j == new_table->num_chunks);
520
521         return new_table;
522 }
523
524 /* As we add an element to the table the case can arise that the chunk
525  * to which we need to add is already full.  In that case we have to
526  * allocate a new table and do something about that chunk.  We have
527  * several strategies:
528  *
529  * If the number of elements in the table is below the low watermark
530  * or above the high watermark, we reallocate the whole table.
531  * Otherwise we only concern ourselves with the overflowing chunk:
532  *
533  * If there are no tombstones in the chunk then we split the chunk in
534  * two, each half full.
535  *
536  * If the chunk does contain tombstones, we just make a new copy of
537  * the chunk without the tombstones, which will have room for at least
538  * the one element we have to add.
539  */
540 static MonoJitInfoTable*
541 jit_info_table_chunk_overflow (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
542 {
543         int num_elements = jit_info_table_num_elements (table);
544         int i;
545
546         if (num_elements < JIT_INFO_TABLE_LOW_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)
547                         || num_elements > JIT_INFO_TABLE_HIGH_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)) {
548                 //printf ("reallocing table\n");
549                 return jit_info_table_realloc (table);
550         }
551
552         /* count the number of non-tombstone elements in the chunk */
553         num_elements = 0;
554         for (i = 0; i < chunk->num_elements; ++i) {
555                 if (!IS_JIT_INFO_TOMBSTONE (chunk->data [i]))
556                         ++num_elements;
557         }
558
559         if (num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE) {
560                 //printf ("splitting chunk\n");
561                 return jit_info_table_copy_and_split_chunk (table, chunk);
562         }
563
564         //printf ("purifying chunk\n");
565         return jit_info_table_copy_and_purify_chunk (table, chunk);
566 }
567
568 /* We add elements to the table by first making space for them by
569  * shifting the elements at the back to the right, one at a time.
570  * This results in duplicate entries during the process, but during
571  * all the time the table is in a sorted state.  Also, when an element
572  * is replaced by another one, the element that replaces it has an end
573  * address that is equal to or lower than that of the replaced
574  * element.  That property is necessary to guarantee that when
575  * searching for an element we end up at a position not higher than
576  * the one we're looking for (i.e. we either find the element directly
577  * or we end up to the left of it).
578  */
579 static void
580 jit_info_table_add (MonoDomain *domain, MonoJitInfoTable *volatile *table_ptr, MonoJitInfo *ji)
581 {
582         MonoJitInfoTable *table;
583         MonoJitInfoTableChunk *chunk;
584         int chunk_pos, pos;
585         int num_elements;
586         int i;
587
588         table = *table_ptr;
589
590  restart:
591         chunk_pos = jit_info_table_index (table, (gint8*)ji->code_start + ji->code_size);
592         g_assert (chunk_pos < table->num_chunks);
593         chunk = table->chunks [chunk_pos];
594
595         if (chunk->num_elements >= MONO_JIT_INFO_TABLE_CHUNK_SIZE) {
596                 MonoJitInfoTable *new_table = jit_info_table_chunk_overflow (table, chunk);
597
598                 /* Debugging code, should be removed. */
599                 //jit_info_table_check (new_table);
600
601                 *table_ptr = new_table;
602                 mono_memory_barrier ();
603                 domain->num_jit_info_tables++;
604                 mono_thread_hazardous_free_or_queue (table, (MonoHazardousFreeFunc)mono_jit_info_table_free, TRUE, FALSE);
605                 table = new_table;
606
607                 goto restart;
608         }
609
610         /* Debugging code, should be removed. */
611         //jit_info_table_check (table);
612
613         num_elements = chunk->num_elements;
614
615         pos = jit_info_table_chunk_index (chunk, NULL, (gint8*)ji->code_start + ji->code_size);
616
617         /* First we need to size up the chunk by one, by copying the
618            last item, or inserting the first one, if the table is
619            empty. */
620         if (num_elements > 0)
621                 chunk->data [num_elements] = chunk->data [num_elements - 1];
622         else
623                 chunk->data [0] = ji;
624         mono_memory_write_barrier ();
625         chunk->num_elements = ++num_elements;
626
627         /* Shift the elements up one by one. */
628         for (i = num_elements - 2; i >= pos; --i) {
629                 mono_memory_write_barrier ();
630                 chunk->data [i + 1] = chunk->data [i];
631         }
632
633         /* Now we have room and can insert the new item. */
634         mono_memory_write_barrier ();
635         chunk->data [pos] = ji;
636
637         /* Set the high code end address chunk entry. */
638         chunk->last_code_end = (gint8*)chunk->data [chunk->num_elements - 1]->code_start
639                 + chunk->data [chunk->num_elements - 1]->code_size;
640
641         /* Debugging code, should be removed. */
642         //jit_info_table_check (table);
643 }
644
645 void
646 mono_jit_info_table_add (MonoDomain *domain, MonoJitInfo *ji)
647 {
648         g_assert (ji->d.method != NULL);
649
650         mono_domain_lock (domain);
651
652         ++mono_stats.jit_info_table_insert_count;
653
654         jit_info_table_add (domain, &domain->jit_info_table, ji);
655
656         mono_domain_unlock (domain);
657 }
658
659 static MonoJitInfo*
660 mono_jit_info_make_tombstone (MonoJitInfo *ji)
661 {
662         MonoJitInfo *tombstone = g_new0 (MonoJitInfo, 1);
663
664         tombstone->code_start = ji->code_start;
665         tombstone->code_size = ji->code_size;
666         tombstone->d.method = JIT_INFO_TOMBSTONE_MARKER;
667
668         return tombstone;
669 }
670
671 /*
672  * LOCKING: domain lock
673  */
674 static void
675 mono_jit_info_free_or_queue (MonoDomain *domain, MonoJitInfo *ji)
676 {
677         if (domain->num_jit_info_tables <= 1) {
678                 /* Can it actually happen that we only have one table
679                    but ji is still hazardous? */
680                 mono_thread_hazardous_free_or_queue (ji, g_free, TRUE, FALSE);
681         } else {
682                 domain->jit_info_free_queue = g_slist_prepend (domain->jit_info_free_queue, ji);
683         }
684 }
685
686 static void
687 jit_info_table_remove (MonoJitInfoTable *table, MonoJitInfo *ji)
688 {
689         MonoJitInfoTableChunk *chunk;
690         gpointer start = ji->code_start;
691         int chunk_pos, pos;
692
693         chunk_pos = jit_info_table_index (table, start);
694         g_assert (chunk_pos < table->num_chunks);
695
696         pos = jit_info_table_chunk_index (table->chunks [chunk_pos], NULL, start);
697
698         do {
699                 chunk = table->chunks [chunk_pos];
700
701                 while (pos < chunk->num_elements) {
702                         if (chunk->data [pos] == ji)
703                                 goto found;
704
705                         g_assert (IS_JIT_INFO_TOMBSTONE (chunk->data [pos]));
706                         g_assert ((guint8*)chunk->data [pos]->code_start + chunk->data [pos]->code_size
707                                 <= (guint8*)ji->code_start + ji->code_size);
708
709                         ++pos;
710                 }
711
712                 ++chunk_pos;
713                 pos = 0;
714         } while (chunk_pos < table->num_chunks);
715
716  found:
717         g_assert (chunk->data [pos] == ji);
718
719         chunk->data [pos] = mono_jit_info_make_tombstone (ji);
720
721         /* Debugging code, should be removed. */
722         //jit_info_table_check (table);
723 }
724
725 void
726 mono_jit_info_table_remove (MonoDomain *domain, MonoJitInfo *ji)
727 {
728         MonoJitInfoTable *table;
729
730         mono_domain_lock (domain);
731         table = domain->jit_info_table;
732
733         ++mono_stats.jit_info_table_remove_count;
734
735         jit_info_table_remove (table, ji);
736
737         mono_jit_info_free_or_queue (domain, ji);
738
739         mono_domain_unlock (domain);
740 }
741
742 void
743 mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end)
744 {
745         MonoJitInfo *ji;
746         MonoDomain *domain = mono_get_root_domain ();
747
748         g_assert (domain);
749         mono_domain_lock (domain);
750
751         /*
752          * We reuse MonoJitInfoTable to store AOT module info,
753          * this gives us async-safe lookup.
754          */
755         if (!domain->aot_modules) {
756                 domain->num_jit_info_tables ++;
757                 domain->aot_modules = mono_jit_info_table_new (domain);
758         }
759
760         ji = g_new0 (MonoJitInfo, 1);
761         ji->d.image = image;
762         ji->code_start = start;
763         ji->code_size = (guint8*)end - (guint8*)start;
764         jit_info_table_add (domain, &domain->aot_modules, ji);
765
766         mono_domain_unlock (domain);
767 }
768
769 void
770 mono_install_jit_info_find_in_aot (MonoJitInfoFindInAot func)
771 {
772         jit_info_find_in_aot_func = func;
773 }
774
775 int
776 mono_jit_info_size (MonoJitInfoFlags flags, int num_clauses, int num_holes)
777 {
778         int size = MONO_SIZEOF_JIT_INFO;
779
780         size += num_clauses * sizeof (MonoJitExceptionInfo);
781         if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO)
782                 size += sizeof (MonoGenericJitInfo);
783         if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES)
784                 size += sizeof (MonoTryBlockHoleTableJitInfo) + num_holes * sizeof (MonoTryBlockHoleJitInfo);
785         if (flags & JIT_INFO_HAS_ARCH_EH_INFO)
786                 size += sizeof (MonoArchEHJitInfo);
787         if (flags & JIT_INFO_HAS_THUNK_INFO)
788                 size += sizeof (MonoThunkJitInfo);
789         return size;
790 }
791
792 void
793 mono_jit_info_init (MonoJitInfo *ji, MonoMethod *method, guint8 *code, int code_size,
794                                         MonoJitInfoFlags flags, int num_clauses, int num_holes)
795 {
796         ji->d.method = method;
797         ji->code_start = code;
798         ji->code_size = code_size;
799         ji->num_clauses = num_clauses;
800         if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO)
801                 ji->has_generic_jit_info = 1;
802         if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES)
803                 ji->has_try_block_holes = 1;
804         if (flags & JIT_INFO_HAS_ARCH_EH_INFO)
805                 ji->has_arch_eh_info = 1;
806         if (flags & JIT_INFO_HAS_THUNK_INFO)
807                 ji->has_thunk_info = 1;
808 }
809
810 gpointer
811 mono_jit_info_get_code_start (MonoJitInfo* ji)
812 {
813         return ji->code_start;
814 }
815
816 int
817 mono_jit_info_get_code_size (MonoJitInfo* ji)
818 {
819         return ji->code_size;
820 }
821
822 MonoMethod*
823 mono_jit_info_get_method (MonoJitInfo* ji)
824 {
825         g_assert (!ji->async);
826         g_assert (!ji->is_trampoline);
827         return ji->d.method;
828 }
829
830 static gpointer
831 jit_info_key_extract (gpointer value)
832 {
833         MonoJitInfo *info = (MonoJitInfo*)value;
834
835         return info->d.method;
836 }
837
838 static gpointer*
839 jit_info_next_value (gpointer value)
840 {
841         MonoJitInfo *info = (MonoJitInfo*)value;
842
843         return (gpointer*)&info->next_jit_code_hash;
844 }
845
846 void
847 mono_jit_code_hash_init (MonoInternalHashTable *jit_code_hash)
848 {
849         mono_internal_hash_table_init (jit_code_hash,
850                                        mono_aligned_addr_hash,
851                                        jit_info_key_extract,
852                                        jit_info_next_value);
853 }
854
855 MonoGenericJitInfo*
856 mono_jit_info_get_generic_jit_info (MonoJitInfo *ji)
857 {
858         if (ji->has_generic_jit_info)
859                 return (MonoGenericJitInfo*)&ji->clauses [ji->num_clauses];
860         else
861                 return NULL;
862 }
863
864 /*
865  * mono_jit_info_get_generic_sharing_context:
866  * @ji: a jit info
867  *
868  * Returns the jit info's generic sharing context, or NULL if it
869  * doesn't have one.
870  */
871 MonoGenericSharingContext*
872 mono_jit_info_get_generic_sharing_context (MonoJitInfo *ji)
873 {
874         MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji);
875
876         if (gi)
877                 return gi->generic_sharing_context;
878         else
879                 return NULL;
880 }
881
882 /*
883  * mono_jit_info_set_generic_sharing_context:
884  * @ji: a jit info
885  * @gsctx: a generic sharing context
886  *
887  * Sets the jit info's generic sharing context.  The jit info must
888  * have memory allocated for the context.
889  */
890 void
891 mono_jit_info_set_generic_sharing_context (MonoJitInfo *ji, MonoGenericSharingContext *gsctx)
892 {
893         MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji);
894
895         g_assert (gi);
896
897         gi->generic_sharing_context = gsctx;
898 }
899
900 MonoTryBlockHoleTableJitInfo*
901 mono_jit_info_get_try_block_hole_table_info (MonoJitInfo *ji)
902 {
903         if (ji->has_try_block_holes) {
904                 char *ptr = (char*)&ji->clauses [ji->num_clauses];
905                 if (ji->has_generic_jit_info)
906                         ptr += sizeof (MonoGenericJitInfo);
907                 return (MonoTryBlockHoleTableJitInfo*)ptr;
908         } else {
909                 return NULL;
910         }
911 }
912
913 static int
914 try_block_hole_table_size (MonoJitInfo *ji)
915 {
916         MonoTryBlockHoleTableJitInfo *table;
917
918         table = mono_jit_info_get_try_block_hole_table_info (ji);
919         g_assert (table);
920         return sizeof (MonoTryBlockHoleTableJitInfo) + table->num_holes * sizeof (MonoTryBlockHoleJitInfo);
921 }
922
923 MonoArchEHJitInfo*
924 mono_jit_info_get_arch_eh_info (MonoJitInfo *ji)
925 {
926         if (ji->has_arch_eh_info) {
927                 char *ptr = (char*)&ji->clauses [ji->num_clauses];
928                 if (ji->has_generic_jit_info)
929                         ptr += sizeof (MonoGenericJitInfo);
930                 if (ji->has_try_block_holes)
931                         ptr += try_block_hole_table_size (ji);
932                 return (MonoArchEHJitInfo*)ptr;
933         } else {
934                 return NULL;
935         }
936 }
937
938 MonoThunkJitInfo*
939 mono_jit_info_get_thunk_info (MonoJitInfo *ji)
940 {
941         if (ji->has_thunk_info) {
942                 char *ptr = (char*)&ji->clauses [ji->num_clauses];
943                 if (ji->has_generic_jit_info)
944                         ptr += sizeof (MonoGenericJitInfo);
945                 if (ji->has_try_block_holes)
946                         ptr += try_block_hole_table_size (ji);
947                 if (ji->has_arch_eh_info)
948                         ptr += sizeof (MonoArchEHJitInfo);
949                 return (MonoThunkJitInfo*)ptr;
950         } else {
951                 return NULL;
952         }
953 }