Merge pull request #2429 from alexanderkyte/nunit_lite_integration
[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 void
14 insert_pred_seq_point (MonoBasicBlock *in_bb, MonoInst *ins, GSList **next)
15 {
16         GSList *l;
17         int src_index = in_bb->last_seq_point->backend.size;
18         int dst_index = ins->backend.size;
19
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)
23                         break;
24         if (!l)
25                 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
26 }
27
28 static void
29 collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, GHashTable *memoize)
30 {
31         const gpointer MONO_SEQ_SEEN_LOOP = GINT_TO_POINTER(-1);
32
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);
36
37                 if (result == MONO_SEQ_SEEN_LOOP) {
38                         // We've looped or handled this before, exit early.
39                         // No last sequence points to find.
40                         continue;
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);
44                 } else {
45                         // Compute predecessors of in_bb
46
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
49                         // at any given time
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);
53                 }
54         }
55 }
56
57 void
58 mono_save_seq_point_info (MonoCompile *cfg)
59 {
60         MonoBasicBlock *bb;
61         GSList *bb_seq_points, *l;
62         MonoInst *last;
63         MonoDomain *domain = cfg->domain;
64         int i, seq_info_size;
65         GSList **next = NULL;
66         SeqPoint* seq_points;
67         GByteArray* array;
68         gboolean has_debug_data = cfg->gen_sdb_seq_points;
69
70         if (!cfg->seq_points)
71                 return;
72
73         seq_points = g_new0 (SeqPoint, cfg->seq_points->len);
74
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);
78
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;
83
84                 /* Used below */
85                 ins->backend.size = i;
86         }
87
88         if (has_debug_data) {
89                 /*
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.
92                  */
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);
97                         last = NULL;
98                         for (l = bb_seq_points; l; l = l->next) {
99                                 MonoInst *ins = (MonoInst *)l->data;
100
101                                 if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
102                                 /* Used to implement method entry/exit events */
103                                         continue;
104                                 if (ins->inst_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE)
105                                         continue;
106
107                                 if (last != NULL) {
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));
110                                 } else {
111                                         /* Link with the last bb in the previous bblocks */
112                                         collect_pred_seq_points (bb, ins, next, memoize);
113                                 }
114
115                                 last = ins;
116                         }
117
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)) {
120                                 MonoBasicBlock *bb2;
121                                 MonoInst *endfinally_seq_point = NULL;
122
123                                 /*
124                                  * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
125                                  */
126                                 l = g_slist_last (bb->seq_points);
127                                 if (l) {
128                                         endfinally_seq_point = (MonoInst *)l->data;
129
130                                         for (bb2 = cfg->bb_entry; bb2; bb2 = bb2->next_bb) {
131                                                 GSList *l = g_slist_last (bb2->seq_points);
132
133                                                 if (l) {
134                                                         MonoInst *ins = (MonoInst *)l->data;
135
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));
138                                                 }
139                                         }
140                                 }
141                         }
142                 }
143                 g_hash_table_destroy (memoize);
144
145                 if (cfg->verbose_level > 2) {
146                         printf ("\nSEQ POINT MAP: \n");
147
148                         for (i = 0; i < cfg->seq_points->len; ++i) {
149                                 SeqPoint *sp = &seq_points [i];
150                                 GSList *l;
151
152                                 if (!next [i])
153                                         continue;
154
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);
159                                 }
160                                 printf ("\n");
161                         }
162                 }
163         }
164
165         array = g_byte_array_new ();
166
167         { /* Add sequence points to seq_point_info */
168                 SeqPoint zero_seq_point = {0};
169                 SeqPoint* last_seq_point = &zero_seq_point;
170
171                 for (i = 0; i < cfg->seq_points->len; ++i) {
172                         SeqPoint *sp = &seq_points [i];
173                         GSList* next_list = NULL;
174
175                         if (has_debug_data)
176                                 next_list = next[i];
177
178                         if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next_list, has_debug_data))
179                                 last_seq_point = sp;
180
181                         if (has_debug_data)
182                                 g_slist_free (next [i]);
183                 }
184         }
185
186         if (has_debug_data)
187                 g_free (next);
188
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;
191
192         g_byte_array_free (array, TRUE);
193
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);
201         }
202
203         g_ptr_array_free (cfg->seq_points, TRUE);
204         cfg->seq_points = NULL;
205 }
206
207 MonoSeqPointInfo*
208 mono_get_seq_points (MonoDomain *domain, MonoMethod *method)
209 {
210         MonoSeqPointInfo *seq_points;
211         MonoMethod *declaring_generic_method = NULL, *shared_method = NULL;
212
213         if (method->is_inflated) {
214                 declaring_generic_method = mono_method_get_declaring_generic_method (method);
215                 shared_method = mini_get_shared_method (method);
216         }
217
218         mono_loader_lock ();
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);
223                 if (!seq_points)
224                         seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, shared_method);
225         }
226         mono_loader_unlock ();
227
228         return seq_points;
229 }
230
231 /*
232  * mono_find_next_seq_point_for_native_offset:
233  *
234  *   Find the first sequence point after NATIVE_OFFSET.
235  */
236 gboolean
237 mono_find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
238 {
239         MonoSeqPointInfo *seq_points;
240
241         seq_points = mono_get_seq_points (domain, method);
242         if (!seq_points) {
243                 if (info)
244                         *info = NULL;
245                 return FALSE;
246         }
247         if (info)
248                 *info = seq_points;
249
250         return mono_seq_point_find_next_by_native_offset (seq_points, native_offset, seq_point);
251 }
252
253 /*
254  * mono_find_prev_seq_point_for_native_offset:
255  *
256  *   Find the first sequence point before NATIVE_OFFSET.
257  */
258 gboolean
259 mono_find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
260 {
261         MonoSeqPointInfo *seq_points;
262
263         seq_points = mono_get_seq_points (domain, method);
264         if (!seq_points) {
265                 if (info)
266                         *info = NULL;
267                 return FALSE;
268         }
269         if (info)
270                 *info = seq_points;
271
272         return mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
273 }
274
275 /*
276  * mono_find_seq_point:
277  *
278  *   Find the sequence point corresponding to the IL offset IL_OFFSET, which
279  * should be the location of a sequence point.
280  */
281 gboolean
282 mono_find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
283 {
284         MonoSeqPointInfo *seq_points;
285
286         seq_points = mono_get_seq_points (domain, method);
287         if (!seq_points) {
288                 if (info)
289                         *info = NULL;
290                 return FALSE;
291         }
292         if (info)
293                 *info = seq_points;
294
295         return mono_seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
296 }
297
298 void
299 mono_bb_deduplicate_op_il_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
300 {
301         MonoInst *ins, *n, *prev;
302
303         MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
304                 if (ins->opcode != OP_IL_SEQ_POINT)
305                         continue;
306
307                 prev = mono_inst_prev (ins, FILTER_NOP);
308
309                 if (!prev || ins == prev || prev->opcode != OP_IL_SEQ_POINT)
310                         continue;
311
312                 MONO_REMOVE_INS (bb, prev);
313         };
314 }
315
316 void
317 mono_image_get_aot_seq_point_path (MonoImage *image, char **str)
318 {
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);
322 }