First set of licensing changes
[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 collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, int depth)
16 {
17         int i;
18         MonoBasicBlock *in_bb;
19         GSList *l;
20
21         for (i = 0; i < bb->in_count; ++i) {
22                 in_bb = bb->in_bb [i];
23
24                 if (in_bb->last_seq_point) {
25                         int src_index = in_bb->last_seq_point->backend.size;
26                         int dst_index = ins->backend.size;
27
28                         /* bb->in_bb might contain duplicates */
29                         for (l = next [src_index]; l; l = l->next)
30                                 if (GPOINTER_TO_UINT (l->data) == dst_index)
31                                         break;
32                         if (!l)
33                                 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
34                 } else {
35                         /* Have to look at its predecessors */
36                         if (depth < 5)
37                                 collect_pred_seq_points (in_bb, ins, next, depth + 1);
38                 }
39         }
40 }
41
42 void
43 mono_save_seq_point_info (MonoCompile *cfg)
44 {
45         MonoBasicBlock *bb;
46         GSList *bb_seq_points, *l;
47         MonoInst *last;
48         MonoDomain *domain = cfg->domain;
49         int i, seq_info_size;
50         GSList **next = NULL;
51         SeqPoint* seq_points;
52         GByteArray* array;
53         gboolean has_debug_data = cfg->gen_sdb_seq_points;
54
55         if (!cfg->seq_points)
56                 return;
57
58         seq_points = g_new0 (SeqPoint, cfg->seq_points->len);
59
60         for (i = 0; i < cfg->seq_points->len; ++i) {
61                 SeqPoint *sp = &seq_points [i];
62                 MonoInst *ins = (MonoInst *)g_ptr_array_index (cfg->seq_points, i);
63
64                 sp->il_offset = ins->inst_imm;
65                 sp->native_offset = ins->inst_offset;
66                 if (ins->flags & MONO_INST_NONEMPTY_STACK)
67                         sp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
68
69                 /* Used below */
70                 ins->backend.size = i;
71         }
72
73         if (has_debug_data) {
74                 /*
75                  * For each sequence point, compute the list of sequence points immediately
76                  * following it, this is needed to implement 'step over' in the debugger agent.
77                  */
78                 next = g_new0 (GSList*, cfg->seq_points->len);
79                 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
80                         bb_seq_points = g_slist_reverse (bb->seq_points);
81                         last = NULL;
82                         for (l = bb_seq_points; l; l = l->next) {
83                                 MonoInst *ins = (MonoInst *)l->data;
84
85                                 if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
86                                 /* Used to implement method entry/exit events */
87                                         continue;
88                                 if (ins->inst_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE)
89                                         continue;
90
91                                 if (last != NULL) {
92                                         /* Link with the previous seq point in the same bb */
93                                         next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size));
94                                 } else {
95                                         /* Link with the last bb in the previous bblocks */
96                                         collect_pred_seq_points (bb, ins, next, 0);
97                                 }
98
99                                 last = ins;
100                         }
101
102                         /* The second case handles endfinally opcodes which are in a separate bb by themselves */
103                         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)) {
104                                 MonoBasicBlock *bb2;
105                                 MonoInst *endfinally_seq_point = NULL;
106
107                                 /*
108                                  * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
109                                  */
110                                 l = g_slist_last (bb->seq_points);
111                                 if (l) {
112                                         endfinally_seq_point = (MonoInst *)l->data;
113
114                                         for (bb2 = cfg->bb_entry; bb2; bb2 = bb2->next_bb) {
115                                                 GSList *l = g_slist_last (bb2->seq_points);
116
117                                                 if (l) {
118                                                         MonoInst *ins = (MonoInst *)l->data;
119
120                                                         if (!(ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET) && ins != endfinally_seq_point)
121                                                                 next [endfinally_seq_point->backend.size] = g_slist_append (next [endfinally_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size));
122                                                 }
123                                         }
124                                 }
125                         }
126                 }
127
128                 if (cfg->verbose_level > 2) {
129                         printf ("\nSEQ POINT MAP: \n");
130
131                         for (i = 0; i < cfg->seq_points->len; ++i) {
132                                 SeqPoint *sp = &seq_points [i];
133                                 GSList *l;
134
135                                 if (!next [i])
136                                         continue;
137
138                                 printf ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
139                                 for (l = next [i]; l; l = l->next) {
140                                         int next_index = GPOINTER_TO_UINT (l->data);
141                                         printf (" IL0x%x", seq_points [next_index].il_offset);
142                                 }
143                                 printf ("\n");
144                         }
145                 }
146         }
147
148         array = g_byte_array_new ();
149
150         { /* Add sequence points to seq_point_info */
151                 SeqPoint zero_seq_point = {0};
152                 SeqPoint* last_seq_point = &zero_seq_point;
153
154                 for (i = 0; i < cfg->seq_points->len; ++i) {
155                         SeqPoint *sp = &seq_points [i];
156                         GSList* next_list = NULL;
157
158                         if (has_debug_data)
159                                 next_list = next[i];
160
161                         if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next_list, has_debug_data))
162                                 last_seq_point = sp;
163
164                         if (has_debug_data)
165                                 g_slist_free (next [i]);
166                 }
167         }
168
169         if (has_debug_data)
170                 g_free (next);
171
172         cfg->seq_point_info = mono_seq_point_info_new (array->len, TRUE, array->data, has_debug_data, &seq_info_size);
173         mono_jit_stats.allocated_seq_points_size += seq_info_size;
174
175         g_byte_array_free (array, TRUE);
176
177         // FIXME: dynamic methods
178         if (!cfg->compile_aot) {
179                 mono_domain_lock (domain);
180                 // FIXME: How can the lookup succeed ?
181                 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, cfg->method_to_register))
182                         g_hash_table_insert (domain_jit_info (domain)->seq_points, cfg->method_to_register, cfg->seq_point_info);
183                 mono_domain_unlock (domain);
184         }
185
186         g_ptr_array_free (cfg->seq_points, TRUE);
187         cfg->seq_points = NULL;
188 }
189
190 MonoSeqPointInfo*
191 mono_get_seq_points (MonoDomain *domain, MonoMethod *method)
192 {
193         MonoSeqPointInfo *seq_points;
194         MonoMethod *declaring_generic_method = NULL, *shared_method = NULL;
195
196         if (method->is_inflated) {
197                 declaring_generic_method = mono_method_get_declaring_generic_method (method);
198                 shared_method = mini_get_shared_method (method);
199         }
200
201         mono_loader_lock ();
202         seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
203         if (!seq_points && method->is_inflated) {
204                 /* generic sharing + aot */
205                 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, declaring_generic_method);
206                 if (!seq_points)
207                         seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, shared_method);
208         }
209         mono_loader_unlock ();
210
211         return seq_points;
212 }
213
214 /*
215  * mono_find_next_seq_point_for_native_offset:
216  *
217  *   Find the first sequence point after NATIVE_OFFSET.
218  */
219 gboolean
220 mono_find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
221 {
222         MonoSeqPointInfo *seq_points;
223
224         seq_points = mono_get_seq_points (domain, method);
225         if (!seq_points) {
226                 if (info)
227                         *info = NULL;
228                 return FALSE;
229         }
230         if (info)
231                 *info = seq_points;
232
233         return mono_seq_point_find_next_by_native_offset (seq_points, native_offset, seq_point);
234 }
235
236 /*
237  * mono_find_prev_seq_point_for_native_offset:
238  *
239  *   Find the first sequence point before NATIVE_OFFSET.
240  */
241 gboolean
242 mono_find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
243 {
244         MonoSeqPointInfo *seq_points;
245
246         seq_points = mono_get_seq_points (domain, method);
247         if (!seq_points) {
248                 if (info)
249                         *info = NULL;
250                 return FALSE;
251         }
252         if (info)
253                 *info = seq_points;
254
255         return mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
256 }
257
258 /*
259  * mono_find_seq_point:
260  *
261  *   Find the sequence point corresponding to the IL offset IL_OFFSET, which
262  * should be the location of a sequence point.
263  */
264 gboolean
265 mono_find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
266 {
267         MonoSeqPointInfo *seq_points;
268
269         seq_points = mono_get_seq_points (domain, method);
270         if (!seq_points) {
271                 if (info)
272                         *info = NULL;
273                 return FALSE;
274         }
275         if (info)
276                 *info = seq_points;
277
278         return mono_seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
279 }
280
281 void
282 mono_bb_deduplicate_op_il_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
283 {
284         MonoInst *ins, *n, *prev;
285
286         MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
287                 if (ins->opcode != OP_IL_SEQ_POINT)
288                         continue;
289
290                 prev = mono_inst_prev (ins, FILTER_NOP);
291
292                 if (!prev || ins == prev || prev->opcode != OP_IL_SEQ_POINT)
293                         continue;
294
295                 MONO_REMOVE_INS (bb, prev);
296         };
297 }