Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / seq-points.c
1 /**
2  * \file
3  * Sequence Points functions
4  *
5  * Authors:
6  *   Marcos Henrich (marcos.henrich@xamarin.com)
7  *
8  * Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #include "mini.h"
13 #include "seq-points.h"
14
15 static void
16 insert_pred_seq_point (MonoInst *last_seq_ins, MonoInst *ins, GSList **next)
17 {
18         GSList *l;
19         int src_index = last_seq_ins->backend.size;
20         int dst_index = ins->backend.size;
21
22         /* bb->in_bb might contain duplicates */
23         for (l = next [src_index]; l; l = l->next)
24                 if (GPOINTER_TO_UINT (l->data) == dst_index)
25                         break;
26         if (!l)
27                 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
28 }
29
30 static void
31 recursively_make_pred_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
32 {
33         const gpointer MONO_SEQ_SEEN_LOOP = GINT_TO_POINTER(-1);
34
35         GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
36         GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
37
38         // Insert/remove sentinel into the memoize table to detect loops containing bb
39         bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
40
41         for (int i = 0; i < bb->in_count; ++i) {
42                 MonoBasicBlock *in_bb = bb->in_bb [i];
43                 
44                 // This bb has the last seq point, append it and continue
45                 if (in_bb->last_seq_point != NULL) {
46                         predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
47                         continue;
48                 }
49
50                 // We've looped or handled this before, exit early.
51                 // No last sequence points to find.
52                 if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
53                         continue;
54
55                 // Take sequence points from incoming basic blocks
56         
57                 if (in_bb == cfg->bb_entry)
58                         continue;
59
60                 if (in_bb->pred_seq_points == NULL)
61                         recursively_make_pred_seq_points (cfg, in_bb);
62
63                 // Union sequence points with incoming bb's
64                 for (int i=0; i < in_bb->num_pred_seq_points; i++) {
65                         if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
66                                 g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
67                                 g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
68                         }
69                 }
70                 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
71         }
72
73         g_hash_table_destroy (seen);
74
75         if (predecessors->len != 0) {
76                 bb->pred_seq_points = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst *) * predecessors->len);
77                 bb->num_pred_seq_points = predecessors->len;
78
79                 for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
80                         bb->pred_seq_points [newer] = g_array_index(predecessors, gpointer, newer);
81                 }
82         } 
83
84         g_array_free (predecessors, TRUE);
85 }
86
87 static void
88 collect_pred_seq_points (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, GSList **next)
89 {
90         // Doesn't have a last sequence point, must find from incoming basic blocks
91         if (bb->pred_seq_points == NULL && bb != cfg->bb_entry)
92                 recursively_make_pred_seq_points (cfg, bb);
93
94         for (int i = 0; i < bb->num_pred_seq_points; i++)
95                 insert_pred_seq_point (bb->pred_seq_points [i], ins, next);
96
97         return;
98 }
99
100 void
101 mono_save_seq_point_info (MonoCompile *cfg)
102 {
103         MonoBasicBlock *bb;
104         GSList *bb_seq_points, *l;
105         MonoInst *last;
106         MonoDomain *domain = cfg->domain;
107         int i, seq_info_size;
108         GSList **next = NULL;
109         SeqPoint* seq_points;
110         GByteArray* array;
111         gboolean has_debug_data = cfg->gen_sdb_seq_points;
112
113         if (!cfg->seq_points)
114                 return;
115
116         seq_points = g_new0 (SeqPoint, cfg->seq_points->len);
117
118         for (i = 0; i < cfg->seq_points->len; ++i) {
119                 SeqPoint *sp = &seq_points [i];
120                 MonoInst *ins = (MonoInst *)g_ptr_array_index (cfg->seq_points, i);
121
122                 sp->il_offset = ins->inst_imm;
123                 sp->native_offset = ins->inst_offset;
124                 if (ins->flags & MONO_INST_NONEMPTY_STACK)
125                         sp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
126
127                 /* Used below */
128                 ins->backend.size = i;
129         }
130
131         if (has_debug_data) {
132                 /*
133                  * For each sequence point, compute the list of sequence points immediately
134                  * following it, this is needed to implement 'step over' in the debugger agent.
135                  */
136                 next = g_new0 (GSList*, cfg->seq_points->len);
137                 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
138                         bb_seq_points = g_slist_reverse (bb->seq_points);
139                         last = NULL;
140                         for (l = bb_seq_points; l; l = l->next) {
141                                 MonoInst *ins = (MonoInst *)l->data;
142
143                                 if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
144                                 /* Used to implement method entry/exit events */
145                                         continue;
146                                 if (ins->inst_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE)
147                                         continue;
148
149                                 if (last != NULL) {
150                                         /* Link with the previous seq point in the same bb */
151                                         next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size));
152                                 } else {
153                                         /* Link with the last bb in the previous bblocks */
154                                         collect_pred_seq_points (cfg, bb, ins, next);
155                                 }
156
157                                 last = ins;
158                         }
159
160                         /* The second case handles endfinally opcodes which are in a separate bb by themselves */
161                         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)) {
162                                 MonoBasicBlock *bb2;
163                                 MonoInst *endfinally_seq_point = NULL;
164
165                                 /*
166                                  * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
167                                  */
168                                 l = g_slist_last (bb->seq_points);
169                                 if (l) {
170                                         endfinally_seq_point = (MonoInst *)l->data;
171
172                                         for (bb2 = bb->next_bb; bb2; bb2 = bb2->next_bb) {
173                                                 GSList *l = g_slist_last (bb2->seq_points);
174
175                                                 if (l) {
176                                                         MonoInst *ins = (MonoInst *)l->data;
177
178                                                         if (!(ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET) && ins != endfinally_seq_point)
179                                                                 next [endfinally_seq_point->backend.size] = g_slist_append (next [endfinally_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size));
180                                                 }
181                                         }
182                                 }
183                         }
184                 }
185
186                 if (cfg->verbose_level > 2) {
187                         printf ("\nSEQ POINT MAP: \n");
188
189                         for (i = 0; i < cfg->seq_points->len; ++i) {
190                                 SeqPoint *sp = &seq_points [i];
191                                 GSList *l;
192
193                                 if (!next [i])
194                                         continue;
195
196                                 printf ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
197                                 for (l = next [i]; l; l = l->next) {
198                                         int next_index = GPOINTER_TO_UINT (l->data);
199                                         printf (" IL0x%x", seq_points [next_index].il_offset);
200                                 }
201                                 printf ("\n");
202                         }
203                 }
204         }
205
206         array = g_byte_array_new ();
207
208         { /* Add sequence points to seq_point_info */
209                 SeqPoint zero_seq_point = {0};
210                 SeqPoint* last_seq_point = &zero_seq_point;
211
212                 for (i = 0; i < cfg->seq_points->len; ++i) {
213                         SeqPoint *sp = &seq_points [i];
214                         GSList* next_list = NULL;
215
216                         if (has_debug_data)
217                                 next_list = next[i];
218
219                         if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next_list, has_debug_data))
220                                 last_seq_point = sp;
221
222                         if (has_debug_data)
223                                 g_slist_free (next [i]);
224                 }
225         }
226
227         g_free (seq_points);
228
229         if (has_debug_data)
230                 g_free (next);
231
232         cfg->seq_point_info = mono_seq_point_info_new (array->len, TRUE, array->data, has_debug_data, &seq_info_size);
233         InterlockedAdd (&mono_jit_stats.allocated_seq_points_size, seq_info_size);
234
235         g_byte_array_free (array, TRUE);
236
237         // FIXME: dynamic methods
238         if (!cfg->compile_aot) {
239                 mono_domain_lock (domain);
240                 // FIXME: The lookup can fail if the method is JITted recursively though a type cctor
241                 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, cfg->method_to_register))
242                         g_hash_table_insert (domain_jit_info (domain)->seq_points, cfg->method_to_register, cfg->seq_point_info);
243                 else
244                         mono_seq_point_info_free (cfg->seq_point_info);
245                 mono_domain_unlock (domain);
246         }
247
248         g_ptr_array_free (cfg->seq_points, TRUE);
249         cfg->seq_points = NULL;
250 }
251
252 MonoSeqPointInfo*
253 mono_get_seq_points (MonoDomain *domain, MonoMethod *method)
254 {
255         MonoSeqPointInfo *seq_points;
256         MonoMethod *declaring_generic_method = NULL, *shared_method = NULL;
257
258         if (method->is_inflated) {
259                 declaring_generic_method = mono_method_get_declaring_generic_method (method);
260                 shared_method = mini_get_shared_method (method);
261         }
262
263         mono_loader_lock ();
264         seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
265         if (!seq_points && method->is_inflated) {
266                 /* generic sharing + aot */
267                 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, declaring_generic_method);
268                 if (!seq_points)
269                         seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, shared_method);
270         }
271         mono_loader_unlock ();
272
273         return seq_points;
274 }
275
276 /*
277  * mono_find_next_seq_point_for_native_offset:
278  *
279  *   Find the first sequence point after NATIVE_OFFSET.
280  */
281 gboolean
282 mono_find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_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_next_by_native_offset (seq_points, native_offset, seq_point);
296 }
297
298 /*
299  * mono_find_prev_seq_point_for_native_offset:
300  *
301  *   Find the first sequence point before NATIVE_OFFSET.
302  */
303 gboolean
304 mono_find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
305 {
306         MonoSeqPointInfo *seq_points;
307
308         seq_points = mono_get_seq_points (domain, method);
309         if (!seq_points) {
310                 if (info)
311                         *info = NULL;
312                 return FALSE;
313         }
314         if (info)
315                 *info = seq_points;
316
317         return mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
318 }
319
320 /*
321  * mono_find_seq_point:
322  *
323  *   Find the sequence point corresponding to the IL offset IL_OFFSET, which
324  * should be the location of a sequence point.
325  */
326 gboolean
327 mono_find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
328 {
329         MonoSeqPointInfo *seq_points;
330
331         seq_points = mono_get_seq_points (domain, method);
332         if (!seq_points) {
333                 if (info)
334                         *info = NULL;
335                 return FALSE;
336         }
337         if (info)
338                 *info = seq_points;
339
340         return mono_seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
341 }
342
343 void
344 mono_bb_deduplicate_op_il_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
345 {
346         MonoInst *ins, *n, *prev;
347
348         MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
349                 if (ins->opcode != OP_IL_SEQ_POINT)
350                         continue;
351
352                 prev = mono_inst_prev (ins, FILTER_NOP);
353
354                 if (!prev || ins == prev || prev->opcode != OP_IL_SEQ_POINT)
355                         continue;
356
357                 MONO_REMOVE_INS (bb, prev);
358         };
359 }