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