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"
14 insert_pred_seq_point (MonoBasicBlock *in_bb, MonoInst *ins, GSList **next)
17 int src_index = in_bb->last_seq_point->backend.size;
18 int dst_index = ins->backend.size;
20 /* bb->in_bb might contain duplicates */
21 for (l = next [src_index]; l; l = l->next)
22 if (GPOINTER_TO_UINT (l->data) == dst_index)
25 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
29 collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, GHashTable *memoize)
31 const gpointer MONO_SEQ_SEEN_LOOP = GINT_TO_POINTER(-1);
33 for (int i = 0; i < bb->in_count; ++i) {
34 MonoBasicBlock *in_bb = bb->in_bb [i];
35 gpointer result = g_hash_table_lookup (memoize, in_bb);
37 if (result == MONO_SEQ_SEEN_LOOP) {
38 // We've looped or handled this before, exit early.
39 // No last sequence points to find.
41 } else if (in_bb->last_seq_point) {
42 // if last seq point, insert into next
43 insert_pred_seq_point (in_bb, ins, next);
45 // Compute predecessors of in_bb
47 // Insert/remove sentinel into the memoize table to detect loops containing in_bb
48 // This works to ensure that we only have a basic block on the stack once
50 g_hash_table_insert (memoize, in_bb, MONO_SEQ_SEEN_LOOP);
51 collect_pred_seq_points (in_bb, ins, next, memoize);
52 g_hash_table_remove (memoize, in_bb);
58 mono_save_seq_point_info (MonoCompile *cfg)
61 GSList *bb_seq_points, *l;
63 MonoDomain *domain = cfg->domain;
68 gboolean has_debug_data = cfg->gen_sdb_seq_points;
73 seq_points = g_new0 (SeqPoint, cfg->seq_points->len);
75 for (i = 0; i < cfg->seq_points->len; ++i) {
76 SeqPoint *sp = &seq_points [i];
77 MonoInst *ins = (MonoInst *)g_ptr_array_index (cfg->seq_points, i);
79 sp->il_offset = ins->inst_imm;
80 sp->native_offset = ins->inst_offset;
81 if (ins->flags & MONO_INST_NONEMPTY_STACK)
82 sp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
85 ins->backend.size = i;
90 * For each sequence point, compute the list of sequence points immediately
91 * following it, this is needed to implement 'step over' in the debugger agent.
93 next = g_new0 (GSList*, cfg->seq_points->len);
94 GHashTable *memoize = g_hash_table_new (NULL, NULL);
95 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
96 bb_seq_points = g_slist_reverse (bb->seq_points);
98 for (l = bb_seq_points; l; l = l->next) {
99 MonoInst *ins = (MonoInst *)l->data;
101 if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
102 /* Used to implement method entry/exit events */
104 if (ins->inst_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE)
108 /* Link with the previous seq point in the same bb */
109 next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size));
111 /* Link with the last bb in the previous bblocks */
112 collect_pred_seq_points (bb, ins, next, memoize);
118 /* The second case handles endfinally opcodes which are in a separate bb by themselves */
119 if ((bb->last_ins && bb->last_ins->opcode == OP_ENDFINALLY && bb->seq_points) || (bb->out_count == 1 && bb->out_bb [0]->code && bb->out_bb [0]->code->opcode == OP_ENDFINALLY)) {
121 MonoInst *endfinally_seq_point = NULL;
124 * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
126 l = g_slist_last (bb->seq_points);
128 endfinally_seq_point = (MonoInst *)l->data;
130 for (bb2 = cfg->bb_entry; bb2; bb2 = bb2->next_bb) {
131 GSList *l = g_slist_last (bb2->seq_points);
134 MonoInst *ins = (MonoInst *)l->data;
136 if (!(ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET) && ins != endfinally_seq_point)
137 next [endfinally_seq_point->backend.size] = g_slist_append (next [endfinally_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size));
143 g_hash_table_destroy (memoize);
145 if (cfg->verbose_level > 2) {
146 printf ("\nSEQ POINT MAP: \n");
148 for (i = 0; i < cfg->seq_points->len; ++i) {
149 SeqPoint *sp = &seq_points [i];
155 printf ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
156 for (l = next [i]; l; l = l->next) {
157 int next_index = GPOINTER_TO_UINT (l->data);
158 printf (" IL0x%x", seq_points [next_index].il_offset);
165 array = g_byte_array_new ();
167 { /* Add sequence points to seq_point_info */
168 SeqPoint zero_seq_point = {0};
169 SeqPoint* last_seq_point = &zero_seq_point;
171 for (i = 0; i < cfg->seq_points->len; ++i) {
172 SeqPoint *sp = &seq_points [i];
173 GSList* next_list = NULL;
178 if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next_list, has_debug_data))
182 g_slist_free (next [i]);
189 cfg->seq_point_info = mono_seq_point_info_new (array->len, TRUE, array->data, has_debug_data, &seq_info_size);
190 mono_jit_stats.allocated_seq_points_size += seq_info_size;
192 g_byte_array_free (array, TRUE);
194 // FIXME: dynamic methods
195 if (!cfg->compile_aot) {
196 mono_domain_lock (domain);
197 // FIXME: How can the lookup succeed ?
198 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, cfg->method_to_register))
199 g_hash_table_insert (domain_jit_info (domain)->seq_points, cfg->method_to_register, cfg->seq_point_info);
200 mono_domain_unlock (domain);
203 g_ptr_array_free (cfg->seq_points, TRUE);
204 cfg->seq_points = NULL;
208 mono_get_seq_points (MonoDomain *domain, MonoMethod *method)
210 MonoSeqPointInfo *seq_points;
211 MonoMethod *declaring_generic_method = NULL, *shared_method = NULL;
213 if (method->is_inflated) {
214 declaring_generic_method = mono_method_get_declaring_generic_method (method);
215 shared_method = mini_get_shared_method (method);
219 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
220 if (!seq_points && method->is_inflated) {
221 /* generic sharing + aot */
222 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, declaring_generic_method);
224 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, shared_method);
226 mono_loader_unlock ();
232 * mono_find_next_seq_point_for_native_offset:
234 * Find the first sequence point after NATIVE_OFFSET.
237 mono_find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
239 MonoSeqPointInfo *seq_points;
241 seq_points = mono_get_seq_points (domain, method);
250 return mono_seq_point_find_next_by_native_offset (seq_points, native_offset, seq_point);
254 * mono_find_prev_seq_point_for_native_offset:
256 * Find the first sequence point before NATIVE_OFFSET.
259 mono_find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
261 MonoSeqPointInfo *seq_points;
263 seq_points = mono_get_seq_points (domain, method);
272 return mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
276 * mono_find_seq_point:
278 * Find the sequence point corresponding to the IL offset IL_OFFSET, which
279 * should be the location of a sequence point.
282 mono_find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
284 MonoSeqPointInfo *seq_points;
286 seq_points = mono_get_seq_points (domain, method);
295 return mono_seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
299 mono_bb_deduplicate_op_il_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
301 MonoInst *ins, *n, *prev;
303 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
304 if (ins->opcode != OP_IL_SEQ_POINT)
307 prev = mono_inst_prev (ins, FILTER_NOP);
309 if (!prev || ins == prev || prev->opcode != OP_IL_SEQ_POINT)
312 MONO_REMOVE_INS (bb, prev);
317 mono_image_get_aot_seq_point_path (MonoImage *image, char **str)
319 int size = strlen (image->name) + strlen (SEQ_POINT_AOT_EXT) + 1;
320 *str = (char *)g_malloc (size);
321 g_sprintf (*str, "%s%s", image->name, SEQ_POINT_AOT_EXT);