2 * seq-points.c: Sequence Points functions
5 * Marcos Henrich (marcos.henrich@xamarin.com)
7 * Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
11 #include "seq-points.h"
16 /* When has_debug_data is set to false only il and native deltas are saved */
17 gboolean has_debug_data;
18 /* When alloc_data is set to true data allocation/deallocation is managed by this structure */
20 } SeqPointInfoInflated;
23 encode_var_int (guint8 *buf, guint8 **out_buf, int val)
28 guint8 byte = val & 0x7f;
29 g_assert (size < 4 && "value has more than 28 bits");
43 decode_var_int (guint8* buf, guint8 **out_buf)
49 b = *(p++); low = (b & 0x7f) ; if(!(b & 0x80)) goto done;
50 b = *(p++); low |= (b & 0x7f) << 7; if(!(b & 0x80)) goto done;
51 b = *(p++); low |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
52 b = *(p++); low |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
54 g_assert (FALSE && "value has more than 28 bits");
65 encode_zig_zag (int val)
67 return (val << 1) ^ (val >> 31);
71 decode_zig_zag (guint32 val)
74 return (n >> 1) ^ (-(n & 1));
77 static SeqPointInfoInflated
78 seq_point_info_inflate (MonoSeqPointInfo *info)
80 SeqPointInfoInflated info_inflated;
81 guint8 *ptr = (guint8*) info;
84 value = decode_var_int (ptr, &ptr);
86 info_inflated.len = value >> 2;
87 info_inflated.has_debug_data = (value & 1) != 0;
88 info_inflated.alloc_data = (value & 2) != 0;
90 if (info_inflated.alloc_data)
91 info_inflated.data = ptr;
93 memcpy (&info_inflated.data, ptr, sizeof (guint8*));
98 static MonoSeqPointInfo*
99 seq_point_info_new (int len, gboolean alloc_data, guint8 *data, gboolean has_debug_data)
101 MonoSeqPointInfo *info;
114 buffer_len = encode_var_int (buffer, NULL, value);
116 data_size = buffer_len + (alloc_data? len : sizeof (guint8*));
117 info_ptr = g_new0 (guint8, data_size);
118 info = (MonoSeqPointInfo*) info_ptr;
120 memcpy (info_ptr, buffer, buffer_len);
121 info_ptr += buffer_len;
124 memcpy (info_ptr, data, len);
126 memcpy (info_ptr, &data, sizeof (guint8*));
128 mono_jit_stats.allocated_seq_points_size += data_size;
134 seq_point_info_free (gpointer ptr)
136 MonoSeqPointInfo* info = (MonoSeqPointInfo*) ptr;
141 seq_point_read (SeqPoint* seq_point, guint8* ptr, guint8* buffer_ptr, gboolean has_debug_data)
146 value = decode_var_int (ptr, &ptr);
147 seq_point->il_offset += decode_zig_zag (value);
149 value = decode_var_int (ptr, &ptr);
150 seq_point->native_offset += decode_zig_zag (value);
152 if (has_debug_data) {
153 value = decode_var_int (ptr, &ptr);
154 seq_point->flags = value;
156 if (seq_point->flags & MONO_SEQ_POINT_FLAG_EXIT_IL)
157 seq_point->il_offset = METHOD_EXIT_IL_OFFSET;
159 value = decode_var_int (ptr, &ptr);
160 seq_point->next_len = value;
162 if (seq_point->next_len) {
163 // store next offset and skip it
164 seq_point->next_offset = ptr - buffer_ptr;
165 for (i = 0; i < seq_point->next_len; ++i)
166 decode_var_int (ptr, &ptr);
174 seq_point_info_add_seq_point (GByteArray* array, SeqPoint *sp, SeqPoint *last_seq_point, GSList *next, gboolean has_debug_data)
176 int il_delta, native_delta;
182 if (!has_debug_data &&
183 (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET))
186 il_delta = sp->il_offset - last_seq_point->il_offset;
187 native_delta = sp->native_offset - last_seq_point->native_offset;
191 if (has_debug_data && sp->il_offset == METHOD_EXIT_IL_OFFSET) {
193 flags |= MONO_SEQ_POINT_FLAG_EXIT_IL;
196 len = encode_var_int (buffer, NULL, encode_zig_zag (il_delta));
197 g_byte_array_append (array, buffer, len);
199 len = encode_var_int (buffer, NULL, encode_zig_zag (native_delta));
200 g_byte_array_append (array, buffer, len);
202 if (has_debug_data) {
203 sp->next_offset = array->len;
204 sp->next_len = g_slist_length (next);
206 len = encode_var_int (buffer, NULL, flags);
207 g_byte_array_append (array, buffer, len);
209 len = encode_var_int (buffer, NULL, sp->next_len);
210 g_byte_array_append (array, buffer, len);
212 for (l = next; l; l = l->next) {
213 int next_index = GPOINTER_TO_UINT (l->data);
215 int len = encode_var_int (buffer, NULL, next_index);
216 g_byte_array_append (array, buffer, len);
224 collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, int depth)
227 MonoBasicBlock *in_bb;
230 for (i = 0; i < bb->in_count; ++i) {
231 in_bb = bb->in_bb [i];
233 if (in_bb->last_seq_point) {
234 int src_index = in_bb->last_seq_point->backend.size;
235 int dst_index = ins->backend.size;
237 /* bb->in_bb might contain duplicates */
238 for (l = next [src_index]; l; l = l->next)
239 if (GPOINTER_TO_UINT (l->data) == dst_index)
242 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
244 /* Have to look at its predecessors */
246 collect_pred_seq_points (in_bb, ins, next, depth + 1);
252 mono_save_seq_point_info (MonoCompile *cfg)
255 GSList *bb_seq_points, *l;
257 MonoDomain *domain = cfg->domain;
259 GSList **next = NULL;
260 SeqPoint* seq_points;
262 gboolean has_debug_data = cfg->gen_seq_points_debug_data;
264 if (!cfg->seq_points)
267 seq_points = g_new0 (SeqPoint, cfg->seq_points->len);
269 for (i = 0; i < cfg->seq_points->len; ++i) {
270 SeqPoint *sp = &seq_points [i];
271 MonoInst *ins = g_ptr_array_index (cfg->seq_points, i);
273 sp->il_offset = ins->inst_imm;
274 sp->native_offset = ins->inst_offset;
275 if (ins->flags & MONO_INST_NONEMPTY_STACK)
276 sp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
279 ins->backend.size = i;
282 if (has_debug_data) {
284 * For each sequence point, compute the list of sequence points immediately
285 * following it, this is needed to implement 'step over' in the debugger agent.
287 next = g_new0 (GSList*, cfg->seq_points->len);
288 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
289 bb_seq_points = g_slist_reverse (bb->seq_points);
291 for (l = bb_seq_points; l; l = l->next) {
292 MonoInst *ins = l->data;
294 if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
295 /* Used to implement method entry/exit events */
297 if (ins->inst_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE)
301 /* Link with the previous seq point in the same bb */
302 next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size));
304 /* Link with the last bb in the previous bblocks */
305 collect_pred_seq_points (bb, ins, next, 0);
311 if (bb->last_ins && bb->last_ins->opcode == OP_ENDFINALLY && bb->seq_points) {
313 MonoInst *endfinally_seq_point = NULL;
316 * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
318 l = g_slist_last (bb->seq_points);
320 endfinally_seq_point = l->data;
322 for (bb2 = cfg->bb_entry; bb2; bb2 = bb2->next_bb) {
323 GSList *l = g_slist_last (bb2->seq_points);
326 MonoInst *ins = l->data;
328 if (!(ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET) && ins != endfinally_seq_point)
329 next [endfinally_seq_point->backend.size] = g_slist_append (next [endfinally_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size));
336 if (cfg->verbose_level > 2) {
337 printf ("\nSEQ POINT MAP: \n");
339 for (i = 0; i < cfg->seq_points->len; ++i) {
340 SeqPoint *sp = &seq_points [i];
346 printf ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
347 for (l = next [i]; l; l = l->next) {
348 int next_index = GPOINTER_TO_UINT (l->data);
349 printf (" IL0x%x", seq_points [next_index].il_offset);
356 array = g_byte_array_new ();
358 { /* Add sequence points to seq_point_info */
359 SeqPoint zero_seq_point = {0};
360 SeqPoint* last_seq_point = &zero_seq_point;
362 for (i = 0; i < cfg->seq_points->len; ++i) {
363 SeqPoint *sp = &seq_points [i];
364 GSList* next_list = NULL;
369 if (seq_point_info_add_seq_point (array, sp, last_seq_point, next_list, has_debug_data))
373 g_slist_free (next [i]);
380 cfg->seq_point_info = seq_point_info_new (array->len, TRUE, array->data, has_debug_data);
382 g_byte_array_free (array, TRUE);
384 // FIXME: dynamic methods
385 if (!cfg->compile_aot) {
386 mono_domain_lock (domain);
387 // FIXME: How can the lookup succeed ?
388 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, cfg->method_to_register))
389 g_hash_table_insert (domain_jit_info (domain)->seq_points, cfg->method_to_register, cfg->seq_point_info);
390 mono_domain_unlock (domain);
393 g_ptr_array_free (cfg->seq_points, TRUE);
394 cfg->seq_points = NULL;
398 get_seq_points (MonoDomain *domain, MonoMethod *method)
400 MonoSeqPointInfo *seq_points;
402 mono_domain_lock (domain);
403 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
404 if (!seq_points && method->is_inflated) {
405 /* generic sharing + aot */
406 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (method));
408 seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mini_get_shared_method (method));
410 mono_domain_unlock (domain);
416 seq_point_find_next_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
419 seq_point_iterator_init (&it, info);
420 while (seq_point_iterator_next (&it)) {
421 if (it.seq_point.native_offset >= native_offset) {
422 memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
431 seq_point_find_prev_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
433 SeqPoint prev_seq_point;
434 gboolean is_first = TRUE;
436 seq_point_iterator_init (&it, info);
437 while (seq_point_iterator_next (&it) && it.seq_point.native_offset <= native_offset) {
438 memcpy (&prev_seq_point, &it.seq_point, sizeof (SeqPoint));
442 if (!is_first && prev_seq_point.native_offset <= native_offset) {
443 memcpy (seq_point, &prev_seq_point, sizeof (SeqPoint));
451 seq_point_find_by_il_offset (MonoSeqPointInfo* info, int il_offset, SeqPoint* seq_point)
454 seq_point_iterator_init (&it, info);
455 while (seq_point_iterator_next (&it)) {
456 if (it.seq_point.il_offset == il_offset) {
457 memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
466 * find_next_seq_point_for_native_offset:
468 * Find the first sequence point after NATIVE_OFFSET.
471 find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
473 MonoSeqPointInfo *seq_points;
475 seq_points = get_seq_points (domain, method);
484 return seq_point_find_next_by_native_offset (seq_points, native_offset, seq_point);
488 * find_prev_seq_point_for_native_offset:
490 * Find the first sequence point before NATIVE_OFFSET.
493 find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
495 MonoSeqPointInfo *seq_points;
497 seq_points = get_seq_points (domain, method);
506 return seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
512 * Find the sequence point corresponding to the IL offset IL_OFFSET, which
513 * should be the location of a sequence point.
516 find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
518 MonoSeqPointInfo *seq_points;
520 seq_points = get_seq_points (domain, method);
529 return seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
533 seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next)
538 GArray* seq_points = g_array_new (FALSE, TRUE, sizeof (SeqPoint));
539 SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
541 g_assert (info_inflated.has_debug_data);
543 seq_point_iterator_init (&it, info);
544 while (seq_point_iterator_next (&it))
545 g_array_append_vals (seq_points, &it.seq_point, 1);
547 ptr = info_inflated.data + sp.next_offset;
548 for (i = 0; i < sp.next_len; i++) {
550 next_index = decode_var_int (ptr, &ptr);
551 g_assert (next_index < seq_points->len);
552 memcpy (&next[i], seq_points->data + next_index * sizeof (SeqPoint), sizeof (SeqPoint));
555 g_array_free (seq_points, TRUE);
559 seq_point_iterator_next (SeqPointIterator* it)
561 if (it->ptr >= it->end)
564 it->ptr += seq_point_read (&it->seq_point, it->ptr, it->begin, it->has_debug_data);
570 seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info)
572 SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
573 it->ptr = info_inflated.data;
574 it->begin = info_inflated.data;
575 it->end = it->begin + info_inflated.len;
576 it->has_debug_data = info_inflated.has_debug_data;
577 memset(&it->seq_point, 0, sizeof(SeqPoint));
581 seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer)
583 guint8* buffer0 = buffer;
584 SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
586 memcpy (buffer, &info_inflated.has_debug_data, 1);
589 //Write sequence points
590 encode_var_int (buffer, &buffer, info_inflated.len);
591 memcpy (buffer, info_inflated.data, info_inflated.len);
592 buffer += info_inflated.len;
594 return buffer - buffer0;
598 seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy)
600 guint8* buffer0 = buffer;
602 gboolean has_debug_data;
604 memcpy (&has_debug_data, buffer, 1);
607 size = decode_var_int (buffer, &buffer);
608 (*info) = seq_point_info_new (size, copy, buffer, has_debug_data);
611 return buffer - buffer0;
615 * Returns the maximum size of mono_seq_point_info_write.
618 seq_point_info_get_write_size (MonoSeqPointInfo* info)
620 SeqPointInfoInflated info_inflated = seq_point_info_inflate (info);
622 //4 is the maximum size required to store the size of the data.
623 //1 is the byte used to store has_debug_data.
624 int size = 4 + 1 + info_inflated.len;