Merged SeqPoint.next_array with SeqPoint.array.
[mono.git] / mono / mini / seq-points.c
1 /*
2  * seq-points.c: Sequence Points functions
3  *
4  * Authors:
5  *   Marcos Henrich (marcos.henrich@xamarin.com)
6  *
7  * Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
8  */
9
10 #include "mini.h"
11 #include "seq-points.h"
12
13 static guint8
14 encode_var_int (guint8* buf, int val)
15 {
16         guint8 size = 0;
17
18         do {
19                 guint8 byte = val & 0x7f;
20                 g_assert (size < 4 && "value has more than 28 bits");
21                 val >>= 7;
22                 if(val) byte |= 0x80;
23                 *(buf++) = byte;
24                 size++;
25         } while (val);
26
27         return size;
28 }
29
30 static guint8
31 decode_var_int (guint8* buf, int* val)
32 {
33         guint8* p = buf;
34
35         int low;
36         int b;
37         b = *(p++); low   = (b & 0x7f)      ; if(!(b & 0x80)) goto done;
38         b = *(p++); low  |= (b & 0x7f) <<  7; if(!(b & 0x80)) goto done;
39         b = *(p++); low  |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
40         b = *(p++); low  |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
41
42         g_assert (FALSE && "value has more than 28 bits");
43
44 done:
45
46         *val = low;
47         return p-buf;
48 }
49
50 static guint32
51 encode_zig_zag (int val)
52 {
53         return (val << 1) ^ (val >> 31);
54 }
55
56 static int
57 decode_zig_zag (guint32 val)
58 {
59         int n = val;
60         return (n >> 1) ^ (-(n & 1));
61 }
62
63 static void
64 allocated_seq_point_size_add (MonoSeqPointInfo *info)
65 {
66         int size = sizeof (MonoSeqPointInfo) + sizeof (GByteArray) + info->array->len;
67
68         mono_jit_stats.allocated_seq_points_size += size;
69 }
70
71 static int
72 seq_point_read (SeqPoint* seq_point, guint8* ptr, guint8* buffer_ptr, gboolean has_debug_data)
73 {
74         int value;
75         guint8* ptr0 = ptr;
76
77         ptr += decode_var_int (ptr, &value);
78         seq_point->il_offset += decode_zig_zag (value);
79
80         ptr += decode_var_int (ptr, &value);
81         seq_point->native_offset += decode_zig_zag (value);
82
83         if (has_debug_data) {
84                 ptr += decode_var_int (ptr, &value);
85                 seq_point->flags = value;
86
87                 ptr += decode_var_int (ptr, &value);
88                 seq_point->next_len = value;
89
90                 if (seq_point->next_len) {
91                         // store next offset and skip it
92                         seq_point->next_offset = ptr - buffer_ptr;
93                         ptr += seq_point->next_len;
94                 }
95         }
96
97         return ptr - ptr0;
98 }
99
100 static MonoSeqPointInfo*
101 seq_point_info_new (gboolean alloc_arrays, gboolean has_debug_data)
102 {
103         MonoSeqPointInfo* info = g_new0 (MonoSeqPointInfo, 1);
104         info->alloc_arrays = alloc_arrays;
105         info->has_debug_data = has_debug_data;
106         if (alloc_arrays) {
107                 info->array = g_byte_array_new ();
108         } else {
109                 info->array = g_new0 (GByteArray, 1);
110         }
111         return info;
112 }
113
114 void
115 seq_point_info_free (gpointer ptr)
116 {
117         MonoSeqPointInfo* info = (MonoSeqPointInfo*) ptr;
118
119         if (info->alloc_arrays) {
120                 g_byte_array_free (info->array, TRUE);
121         } else {
122                 g_free (info->array);
123         }
124         g_free (info);
125 }
126
127 static gboolean
128 seq_point_info_add_seq_point (MonoSeqPointInfo *info, SeqPoint *sp, SeqPoint *last_seq_point, GSList *next)
129 {
130         int il_delta, native_delta;
131         GSList *l;
132         guint8 buffer[4];
133         guint8 len;
134
135         if (!info->has_debug_data &&
136                 (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET))
137                 return FALSE;
138
139         /* check that data can be added to the arrays */
140         g_assert (info->alloc_arrays);
141
142         il_delta = sp->il_offset - last_seq_point->il_offset;
143         native_delta = sp->native_offset - last_seq_point->native_offset;
144
145         len = encode_var_int (buffer, encode_zig_zag (il_delta));
146         g_byte_array_append (info->array, buffer, len);
147
148         len = encode_var_int (buffer, encode_zig_zag (native_delta));
149         g_byte_array_append (info->array, buffer, len);
150
151         if (info->has_debug_data) {
152                 sp->next_offset = info->array->len;
153                 sp->next_len = g_slist_length (next);
154
155                 len = encode_var_int (buffer, sp->flags);
156                 g_byte_array_append (info->array, buffer, len);
157
158                 len = encode_var_int (buffer, sp->next_len);
159                 g_byte_array_append (info->array, buffer, len);
160
161                 for (l = next; l; l = l->next) {
162                         int next_index = GPOINTER_TO_UINT (l->data);
163                         guint8 buffer[4];
164                         int len = encode_var_int (buffer, next_index);
165                         g_byte_array_append (info->array, buffer, len);
166                 }
167         }
168
169         return TRUE;
170 }
171
172 static void
173 collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, int depth)
174 {
175         int i;
176         MonoBasicBlock *in_bb;
177         GSList *l;
178
179         for (i = 0; i < bb->in_count; ++i) {
180                 in_bb = bb->in_bb [i];
181
182                 if (in_bb->last_seq_point) {
183                         int src_index = in_bb->last_seq_point->backend.size;
184                         int dst_index = ins->backend.size;
185
186                         /* bb->in_bb might contain duplicates */
187                         for (l = next [src_index]; l; l = l->next)
188                                 if (GPOINTER_TO_UINT (l->data) == dst_index)
189                                         break;
190                         if (!l)
191                                 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
192                 } else {
193                         /* Have to look at its predecessors */
194                         if (depth < 5)
195                                 collect_pred_seq_points (in_bb, ins, next, depth + 1);
196                 }
197         }
198 }
199
200 void
201 mono_save_seq_point_info (MonoCompile *cfg)
202 {
203         MonoBasicBlock *bb;
204         GSList *bb_seq_points, *l;
205         MonoInst *last;
206         MonoDomain *domain = cfg->domain;
207         int i;
208         GSList **next;
209         SeqPoint* seq_points;
210         gboolean has_debug_data = cfg->gen_seq_points_debug_data;
211
212         if (!cfg->seq_points)
213                 return;
214
215         seq_points = g_new0 (SeqPoint, cfg->seq_points->len);
216
217         for (i = 0; i < cfg->seq_points->len; ++i) {
218                 SeqPoint *sp = &seq_points [i];
219                 MonoInst *ins = g_ptr_array_index (cfg->seq_points, i);
220
221                 sp->il_offset = ins->inst_imm;
222                 sp->native_offset = ins->inst_offset;
223                 if (ins->flags & MONO_INST_NONEMPTY_STACK)
224                         sp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
225
226                 /* Used below */
227                 ins->backend.size = i;
228         }
229
230         if (has_debug_data) {
231                 /*
232                  * For each sequence point, compute the list of sequence points immediately
233                  * following it, this is needed to implement 'step over' in the debugger agent.
234                  */
235                 next = g_new0 (GSList*, cfg->seq_points->len);
236                 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
237                         bb_seq_points = g_slist_reverse (bb->seq_points);
238                         last = NULL;
239                         for (l = bb_seq_points; l; l = l->next) {
240                                 MonoInst *ins = l->data;
241
242                                 if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
243                                 /* Used to implement method entry/exit events */
244                                         continue;
245                                 if (ins->inst_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE)
246                                         continue;
247
248                                 if (last != NULL) {
249                                         /* Link with the previous seq point in the same bb */
250                                         next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size));
251                                 } else {
252                                         /* Link with the last bb in the previous bblocks */
253                                         collect_pred_seq_points (bb, ins, next, 0);
254                                 }
255
256                                 last = ins;
257                         }
258
259                         if (bb->last_ins && bb->last_ins->opcode == OP_ENDFINALLY && bb->seq_points) {
260                                 MonoBasicBlock *bb2;
261                                 MonoInst *endfinally_seq_point = NULL;
262
263                                 /*
264                                  * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
265                                  */
266                                 l = g_slist_last (bb->seq_points);
267                                 if (l) {
268                                         endfinally_seq_point = l->data;
269
270                                         for (bb2 = cfg->bb_entry; bb2; bb2 = bb2->next_bb) {
271                                                 GSList *l = g_slist_last (bb2->seq_points);
272
273                                                 if (l) {
274                                                         MonoInst *ins = l->data;
275
276                                                         if (!(ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET) && ins != endfinally_seq_point)
277                                                                 next [endfinally_seq_point->backend.size] = g_slist_append (next [endfinally_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size));
278                                                 }
279                                         }
280                                 }
281                         }
282                 }
283
284                 if (cfg->verbose_level > 2) {
285                         printf ("\nSEQ POINT MAP: \n");
286
287                         for (i = 0; i < cfg->seq_points->len; ++i) {
288                                 SeqPoint *sp = &seq_points [i];
289                                 GSList *l;
290
291                                 if (!next [i])
292                                         continue;
293
294                                 printf ("\tIL0x%x ->", sp->il_offset);
295                                 for (l = next [i]; l; l = l->next) {
296                                         int next_index = GPOINTER_TO_UINT (l->data);
297                                         printf (" IL0x%x", seq_points [next_index].il_offset);
298                                 }
299                                 printf ("\n");
300                         }
301                 }
302         }
303
304         cfg->seq_point_info = seq_point_info_new (TRUE, has_debug_data);
305
306         { /* Add sequence points to seq_point_info */
307                 SeqPoint zero_seq_point = {0};
308                 SeqPoint* last_seq_point = &zero_seq_point;
309
310                 for (i = 0; i < cfg->seq_points->len; ++i) {
311                         SeqPoint *sp = &seq_points [i];
312                         GSList* next_list = NULL;
313
314                         if (has_debug_data)
315                                 next_list = next[i];
316
317                         if (seq_point_info_add_seq_point (cfg->seq_point_info, sp, last_seq_point, next_list))
318                                 last_seq_point = sp;
319
320                         if (has_debug_data)
321                                 g_slist_free (next [i]);
322                 }
323         }
324
325         if (has_debug_data)
326                 g_free (next);
327
328         allocated_seq_point_size_add (cfg->seq_point_info);
329
330         // FIXME: dynamic methods
331         if (!cfg->compile_aot) {
332                 mono_domain_lock (domain);
333                 // FIXME: How can the lookup succeed ?
334                 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, cfg->method_to_register))
335                         g_hash_table_insert (domain_jit_info (domain)->seq_points, cfg->method_to_register, cfg->seq_point_info);
336                 mono_domain_unlock (domain);
337         }
338
339         g_ptr_array_free (cfg->seq_points, TRUE);
340         cfg->seq_points = NULL;
341 }
342
343 MonoSeqPointInfo*
344 get_seq_points (MonoDomain *domain, MonoMethod *method)
345 {
346         MonoSeqPointInfo *seq_points;
347
348         mono_domain_lock (domain);
349         seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
350         if (!seq_points && method->is_inflated) {
351                 /* generic sharing + aot */
352                 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (method));
353                 if (!seq_points)
354                         seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mini_get_shared_method (method));
355         }
356         mono_domain_unlock (domain);
357
358         return seq_points;
359 }
360
361 static gboolean
362 seq_point_find_next_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
363 {
364         SeqPointIterator it;
365         seq_point_iterator_init (&it, info);
366         while (seq_point_iterator_next (&it)) {
367                 if (it.seq_point.native_offset >= native_offset) {
368                         memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
369                         return TRUE;
370                 }
371         }
372
373         return FALSE;
374 }
375
376 static gboolean
377 seq_point_find_prev_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
378 {
379         SeqPoint prev_seq_point;
380         gboolean  is_first = TRUE;
381         SeqPointIterator it;
382         seq_point_iterator_init (&it, info);
383         while (seq_point_iterator_next (&it) && it.seq_point.native_offset <= native_offset) {
384                 memcpy (&prev_seq_point, &it.seq_point, sizeof (SeqPoint));
385                 is_first = FALSE;
386         }
387
388         if (!is_first && prev_seq_point.native_offset <= native_offset) {
389                 memcpy (seq_point, &prev_seq_point, sizeof (SeqPoint));
390                 return TRUE;
391         }
392
393         return FALSE;
394 }
395
396 static gboolean
397 seq_point_find_by_il_offset (MonoSeqPointInfo* info, int il_offset, SeqPoint* seq_point)
398 {
399         SeqPointIterator it;
400         seq_point_iterator_init (&it, info);
401         while (seq_point_iterator_next (&it)) {
402                 if (it.seq_point.il_offset == il_offset) {
403                         memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
404                         return TRUE;
405                 }
406         }
407
408         return FALSE;
409 }
410
411 /*
412  * find_next_seq_point_for_native_offset:
413  *
414  *   Find the first sequence point after NATIVE_OFFSET.
415  */
416 gboolean
417 find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
418 {
419         MonoSeqPointInfo *seq_points;
420
421         seq_points = get_seq_points (domain, method);
422         if (!seq_points) {
423                 if (info)
424                         *info = NULL;
425                 return FALSE;
426         }
427         if (info)
428                 *info = seq_points;
429
430         return seq_point_find_next_by_native_offset (seq_points, native_offset, seq_point);
431 }
432
433 /*
434  * find_prev_seq_point_for_native_offset:
435  *
436  *   Find the first sequence point before NATIVE_OFFSET.
437  */
438 gboolean
439 find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
440 {
441         MonoSeqPointInfo *seq_points;
442
443         seq_points = get_seq_points (domain, method);
444         if (!seq_points) {
445                 if (info)
446                         *info = NULL;
447                 return FALSE;
448         }
449         if (info)
450                 *info = seq_points;
451
452         return seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
453 }
454
455 /*
456  * find_seq_point:
457  *
458  *   Find the sequence point corresponding to the IL offset IL_OFFSET, which
459  * should be the location of a sequence point.
460  */
461 gboolean
462 find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
463 {
464         MonoSeqPointInfo *seq_points;
465
466         seq_points = get_seq_points (domain, method);
467         if (!seq_points) {
468                 if (info)
469                         *info = NULL;
470                 return FALSE;
471         }
472         if (info)
473                 *info = seq_points;
474
475         return seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
476 }
477
478 void
479 seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next)
480 {
481         int i;
482         guint8* ptr;
483         SeqPointIterator it;
484         GArray* seq_points = g_array_new (FALSE, TRUE, sizeof (SeqPoint));
485
486         g_assert (info->has_debug_data);
487
488         seq_point_iterator_init (&it, info);
489         while (seq_point_iterator_next (&it))
490                 g_array_append_vals (seq_points, &it.seq_point, 1);
491
492         ptr = info->array->data + sp.next_offset;
493         for (i = 0; i < sp.next_len; i++) {
494                 int next_index;
495                 ptr += decode_var_int (ptr, &next_index);
496                 g_assert (next_index < seq_points->len);
497                 memcpy (&next[i], seq_points->data + next_index * sizeof (SeqPoint), sizeof (SeqPoint));
498         }
499
500         g_array_free (seq_points, TRUE);
501 }
502
503 gboolean
504 seq_point_iterator_next (SeqPointIterator* it)
505 {
506         if (it->ptr >= it->info->array->data + it->info->array->len)
507                 return FALSE;
508
509         it->ptr += seq_point_read (&it->seq_point, it->ptr, it->info->array->data, it->info->has_debug_data);
510
511         return TRUE;
512 }
513
514 void
515 seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info)
516 {
517         it->info = info;
518         it->ptr = it->info->array->data;
519         memset(&it->seq_point, 0, sizeof(SeqPoint));
520 }
521
522 int
523 seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer)
524 {
525         guint8* buffer0 = buffer;
526
527         memcpy (buffer, &info->has_debug_data, 1);
528         buffer++;
529
530         //Write sequence points
531         buffer += encode_var_int (buffer, info->array->len);
532         memcpy (buffer, info->array->data, info->array->len);
533         buffer += info->array->len;
534
535         return buffer - buffer0;
536 }
537
538 int
539 seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy)
540 {
541         guint8* buffer0 = buffer;
542         int size;
543         gboolean has_debug_data;
544
545         memcpy (&has_debug_data, buffer, 1);
546         buffer++;
547
548         (*info) = seq_point_info_new (copy, has_debug_data);
549
550         buffer += decode_var_int (buffer, &size);
551         if (copy)
552                 g_byte_array_append ((*info)->array, buffer, size);
553         else {
554                 (*info)->array->data = buffer;
555                 (*info)->array->len = size;
556         }
557         buffer += size;
558
559         return buffer - buffer0;
560 }
561
562 /*
563  * Returns the maximum size of mono_seq_point_info_write.
564  */
565 int
566 seq_point_info_write_size (MonoSeqPointInfo* info)
567 {
568         //8 is the maximum size required to store the size of the arrays.
569         //1 is the byte used to store has_debug_data.
570         int size = 8 + 1 + info->array->len;
571
572         return size;
573 }