[wasm] Add a --runtime-arg= argument to runtime-tests.js to allow setting runtime...
[mono-project.git] / mono / mini / seq-points.c
blob849297cc38205871a2a652ac730154460f59a0bb
1 /**
2 * \file
3 * Sequence Points functions
5 * Authors:
6 * Marcos Henrich (marcos.henrich@xamarin.com)
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.
12 #include "mini.h"
13 #include "mini-runtime.h"
14 #include "seq-points.h"
16 static void
17 insert_pred_seq_point (MonoInst *last_seq_ins, MonoInst *ins, GSList **next)
19 GSList *l;
20 int src_index = last_seq_ins->backend.size;
21 int dst_index = ins->backend.size;
23 /* bb->in_bb might contain duplicates */
24 for (l = next [src_index]; l; l = l->next)
25 if (GPOINTER_TO_UINT (l->data) == dst_index)
26 break;
27 if (!l)
28 next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
31 static void
32 recursively_make_pred_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
34 const gpointer MONO_SEQ_SEEN_LOOP = GINT_TO_POINTER(-1);
36 GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
37 GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
39 // Insert/remove sentinel into the memoize table to detect loops containing bb
40 bb->pred_seq_points = (MonoInst**)MONO_SEQ_SEEN_LOOP;
42 for (int i = 0; i < bb->in_count; ++i) {
43 MonoBasicBlock *in_bb = bb->in_bb [i];
45 // This bb has the last seq point, append it and continue
46 if (in_bb->last_seq_point != NULL) {
47 predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
48 continue;
51 // We've looped or handled this before, exit early.
52 // No last sequence points to find.
53 if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
54 continue;
56 // Take sequence points from incoming basic blocks
58 if (in_bb == cfg->bb_entry)
59 continue;
61 if (in_bb->pred_seq_points == NULL)
62 recursively_make_pred_seq_points (cfg, in_bb);
64 // Union sequence points with incoming bb's
65 for (int i=0; i < in_bb->num_pred_seq_points; i++) {
66 if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
67 g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
68 g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
71 // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
74 g_hash_table_destroy (seen);
76 if (predecessors->len != 0) {
77 bb->pred_seq_points = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst *) * predecessors->len);
78 bb->num_pred_seq_points = predecessors->len;
80 for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
81 bb->pred_seq_points [newer] = g_array_index(predecessors, MonoInst*, newer);
85 g_array_free (predecessors, TRUE);
88 static void
89 collect_pred_seq_points (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, GSList **next)
91 // Doesn't have a last sequence point, must find from incoming basic blocks
92 if (bb->pred_seq_points == NULL && bb != cfg->bb_entry)
93 recursively_make_pred_seq_points (cfg, bb);
95 for (int i = 0; i < bb->num_pred_seq_points; i++)
96 insert_pred_seq_point (bb->pred_seq_points [i], ins, next);
98 return;
101 void
102 mono_save_seq_point_info (MonoCompile *cfg, MonoJitInfo *jinfo)
104 MonoBasicBlock *bb;
105 GSList *bb_seq_points, *l;
106 MonoInst *last;
107 MonoDomain *domain = cfg->domain;
108 int i, seq_info_size;
109 GSList **next = NULL;
110 SeqPoint* seq_points;
111 GByteArray* array;
112 gboolean has_debug_data = cfg->gen_sdb_seq_points;
114 if (!cfg->seq_points)
115 return;
117 seq_points = g_new0 (SeqPoint, cfg->seq_points->len);
119 for (i = 0; i < cfg->seq_points->len; ++i) {
120 SeqPoint *sp = &seq_points [i];
121 MonoInst *ins = (MonoInst *)g_ptr_array_index (cfg->seq_points, i);
123 sp->il_offset = ins->inst_imm;
124 sp->native_offset = ins->inst_offset;
125 if (ins->flags & MONO_INST_NONEMPTY_STACK)
126 sp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK;
127 if (ins->flags & MONO_INST_NESTED_CALL)
128 sp->flags |= MONO_SEQ_POINT_FLAG_NESTED_CALL;
129 /* Used below */
130 ins->backend.size = i;
133 if (has_debug_data) {
135 * For each sequence point, compute the list of sequence points immediately
136 * following it, this is needed to implement 'step over' in the debugger agent.
138 next = g_new0 (GSList*, cfg->seq_points->len);
139 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
140 bb_seq_points = g_slist_reverse (bb->seq_points);
141 last = NULL;
142 for (l = bb_seq_points; l; l = l->next) {
143 MonoInst *ins = (MonoInst *)l->data;
145 if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
146 /* Used to implement method entry/exit events */
147 continue;
148 if (ins->inst_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE)
149 continue;
151 if (last != NULL) {
152 /* Link with the previous seq point in the same bb */
153 next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size));
154 } else {
155 /* Link with the last bb in the previous bblocks */
156 collect_pred_seq_points (cfg, bb, ins, next);
159 last = ins;
162 /* The second case handles endfinally opcodes which are in a separate bb by themselves */
163 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)) {
164 MonoBasicBlock *bb2;
165 MonoInst *endfinally_seq_point = NULL;
168 * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
170 l = g_slist_last (bb->seq_points);
171 if (l) {
172 endfinally_seq_point = (MonoInst *)l->data;
174 for (bb2 = bb->next_bb; bb2; bb2 = bb2->next_bb) {
175 GSList *l = g_slist_last (bb2->seq_points);
177 if (l) {
178 MonoInst *ins = (MonoInst *)l->data;
180 if (!(ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET) && ins != endfinally_seq_point)
181 next [endfinally_seq_point->backend.size] = g_slist_append (next [endfinally_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size));
188 if (cfg->verbose_level > 2) {
189 printf ("\nSEQ POINT MAP: \n");
191 for (i = 0; i < cfg->seq_points->len; ++i) {
192 SeqPoint *sp = &seq_points [i];
193 GSList *l;
195 if (!next [i])
196 continue;
198 printf ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
199 for (l = next [i]; l; l = l->next) {
200 int next_index = GPOINTER_TO_UINT (l->data);
201 printf (" IL0x%x", seq_points [next_index].il_offset);
203 printf ("\n");
208 array = g_byte_array_new ();
210 { /* Add sequence points to seq_point_info */
211 SeqPoint zero_seq_point = {0};
212 SeqPoint* last_seq_point = &zero_seq_point;
214 for (i = 0; i < cfg->seq_points->len; ++i) {
215 SeqPoint *sp = &seq_points [i];
216 GSList* next_list = NULL;
218 if (has_debug_data)
219 next_list = next[i];
221 if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next_list, has_debug_data))
222 last_seq_point = sp;
224 if (has_debug_data)
225 g_slist_free (next [i]);
229 g_free (seq_points);
231 if (has_debug_data)
232 g_free (next);
234 cfg->seq_point_info = mono_seq_point_info_new (array->len, TRUE, array->data, has_debug_data, &seq_info_size);
235 mono_atomic_fetch_add_i32 (&mono_jit_stats.allocated_seq_points_size, seq_info_size);
237 g_byte_array_free (array, TRUE);
239 // FIXME: dynamic methods
240 if (!cfg->compile_aot) {
241 mono_domain_lock (domain);
242 // FIXME: The lookup can fail if the method is JITted recursively though a type cctor
243 if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, cfg->method_to_register))
244 g_hash_table_insert (domain_jit_info (domain)->seq_points, cfg->method_to_register, cfg->seq_point_info);
245 else
246 mono_seq_point_info_free (cfg->seq_point_info);
247 mono_domain_unlock (domain);
249 g_assert (jinfo);
250 jinfo->seq_points = cfg->seq_point_info;
253 g_ptr_array_free (cfg->seq_points, TRUE);
254 cfg->seq_points = NULL;
257 MonoSeqPointInfo*
258 mono_get_seq_points (MonoDomain *domain, MonoMethod *method)
260 ERROR_DECL (error);
261 MonoSeqPointInfo *seq_points;
262 MonoMethod *declaring_generic_method = NULL, *shared_method = NULL;
264 if (method->is_inflated) {
265 declaring_generic_method = mono_method_get_declaring_generic_method (method);
266 shared_method = mini_get_shared_method_full (method, SHARE_MODE_NONE, error);
267 mono_error_assert_ok (error);
270 mono_loader_lock ();
271 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
272 if (!seq_points && method->is_inflated) {
273 /* generic sharing + aot */
274 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, declaring_generic_method);
275 if (!seq_points)
276 seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, shared_method);
278 mono_loader_unlock ();
280 return seq_points;
284 * mono_find_next_seq_point_for_native_offset:
286 * Find the first sequence point after NATIVE_OFFSET.
288 gboolean
289 mono_find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
291 MonoSeqPointInfo *seq_points;
293 seq_points = mono_get_seq_points (domain, method);
294 if (!seq_points) {
295 if (info)
296 *info = NULL;
297 return FALSE;
299 if (info)
300 *info = seq_points;
302 return mono_seq_point_find_next_by_native_offset (seq_points, native_offset, seq_point);
306 * mono_find_prev_seq_point_for_native_offset:
308 * Find the first sequence point before NATIVE_OFFSET.
310 gboolean
311 mono_find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
313 MonoSeqPointInfo *seq_points;
315 seq_points = mono_get_seq_points (domain, method);
316 if (!seq_points) {
317 if (info)
318 *info = NULL;
319 return FALSE;
321 if (info)
322 *info = seq_points;
324 return mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
328 * mono_find_seq_point:
330 * Find the sequence point corresponding to the IL offset IL_OFFSET, which
331 * should be the location of a sequence point.
333 gboolean
334 mono_find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
336 MonoSeqPointInfo *seq_points;
338 seq_points = mono_get_seq_points (domain, method);
339 if (!seq_points) {
340 if (info)
341 *info = NULL;
342 return FALSE;
344 if (info)
345 *info = seq_points;
347 return mono_seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
350 void
351 mono_bb_deduplicate_op_il_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
353 MonoInst *ins, *n, *prev;
355 MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
356 if (ins->opcode != OP_IL_SEQ_POINT)
357 continue;
359 prev = mono_inst_prev (ins, FILTER_NOP);
361 if (!prev || ins == prev || prev->opcode != OP_IL_SEQ_POINT)
362 continue;
364 MONO_REMOVE_INS (bb, prev);