3 * Sequence Points functions
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.
13 #include "mini-runtime.h"
14 #include "seq-points.h"
17 insert_pred_seq_point (MonoInst
*last_seq_ins
, MonoInst
*ins
, GSList
**next
)
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
)
28 next
[src_index
] = g_slist_append (next
[src_index
], GUINT_TO_POINTER (dst_index
));
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
= 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
);
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
)
56 // Take sequence points from incoming basic blocks
58 if (in_bb
== cfg
->bb_entry
)
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
, gpointer
, newer
);
85 g_array_free (predecessors
, TRUE
);
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
);
102 mono_save_seq_point_info (MonoCompile
*cfg
)
105 GSList
*bb_seq_points
, *l
;
107 MonoDomain
*domain
= cfg
->domain
;
108 int i
, seq_info_size
;
109 GSList
**next
= NULL
;
110 SeqPoint
* seq_points
;
112 gboolean has_debug_data
= cfg
->gen_sdb_seq_points
;
114 if (!cfg
->seq_points
)
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
;
129 ins
->backend
.size
= i
;
132 if (has_debug_data
) {
134 * For each sequence point, compute the list of sequence points immediately
135 * following it, this is needed to implement 'step over' in the debugger agent.
137 next
= g_new0 (GSList
*, cfg
->seq_points
->len
);
138 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
139 bb_seq_points
= g_slist_reverse (bb
->seq_points
);
141 for (l
= bb_seq_points
; l
; l
= l
->next
) {
142 MonoInst
*ins
= (MonoInst
*)l
->data
;
144 if (ins
->inst_imm
== METHOD_ENTRY_IL_OFFSET
|| ins
->inst_imm
== METHOD_EXIT_IL_OFFSET
)
145 /* Used to implement method entry/exit events */
147 if (ins
->inst_offset
== SEQ_POINT_NATIVE_OFFSET_DEAD_CODE
)
151 /* Link with the previous seq point in the same bb */
152 next
[last
->backend
.size
] = g_slist_append (next
[last
->backend
.size
], GUINT_TO_POINTER (ins
->backend
.size
));
154 /* Link with the last bb in the previous bblocks */
155 collect_pred_seq_points (cfg
, bb
, ins
, next
);
161 /* The second case handles endfinally opcodes which are in a separate bb by themselves */
162 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 MonoInst
*endfinally_seq_point
= NULL
;
167 * The ENDFINALLY branches are not represented in the cfg, so link it with all seq points starting bbs.
169 l
= g_slist_last (bb
->seq_points
);
171 endfinally_seq_point
= (MonoInst
*)l
->data
;
173 for (bb2
= bb
->next_bb
; bb2
; bb2
= bb2
->next_bb
) {
174 GSList
*l
= g_slist_last (bb2
->seq_points
);
177 MonoInst
*ins
= (MonoInst
*)l
->data
;
179 if (!(ins
->inst_imm
== METHOD_ENTRY_IL_OFFSET
|| ins
->inst_imm
== METHOD_EXIT_IL_OFFSET
) && ins
!= endfinally_seq_point
)
180 next
[endfinally_seq_point
->backend
.size
] = g_slist_append (next
[endfinally_seq_point
->backend
.size
], GUINT_TO_POINTER (ins
->backend
.size
));
187 if (cfg
->verbose_level
> 2) {
188 printf ("\nSEQ POINT MAP: \n");
190 for (i
= 0; i
< cfg
->seq_points
->len
; ++i
) {
191 SeqPoint
*sp
= &seq_points
[i
];
197 printf ("\tIL0x%x[0x%0x] ->", sp
->il_offset
, sp
->native_offset
);
198 for (l
= next
[i
]; l
; l
= l
->next
) {
199 int next_index
= GPOINTER_TO_UINT (l
->data
);
200 printf (" IL0x%x", seq_points
[next_index
].il_offset
);
207 array
= g_byte_array_new ();
209 { /* Add sequence points to seq_point_info */
210 SeqPoint zero_seq_point
= {0};
211 SeqPoint
* last_seq_point
= &zero_seq_point
;
213 for (i
= 0; i
< cfg
->seq_points
->len
; ++i
) {
214 SeqPoint
*sp
= &seq_points
[i
];
215 GSList
* next_list
= NULL
;
220 if (mono_seq_point_info_add_seq_point (array
, sp
, last_seq_point
, next_list
, has_debug_data
))
224 g_slist_free (next
[i
]);
233 cfg
->seq_point_info
= mono_seq_point_info_new (array
->len
, TRUE
, array
->data
, has_debug_data
, &seq_info_size
);
234 mono_atomic_fetch_add_i32 (&mono_jit_stats
.allocated_seq_points_size
, seq_info_size
);
236 g_byte_array_free (array
, TRUE
);
238 // FIXME: dynamic methods
239 if (!cfg
->compile_aot
) {
240 mono_domain_lock (domain
);
241 // FIXME: The lookup can fail if the method is JITted recursively though a type cctor
242 if (!g_hash_table_lookup (domain_jit_info (domain
)->seq_points
, cfg
->method_to_register
))
243 g_hash_table_insert (domain_jit_info (domain
)->seq_points
, cfg
->method_to_register
, cfg
->seq_point_info
);
245 mono_seq_point_info_free (cfg
->seq_point_info
);
246 mono_domain_unlock (domain
);
249 g_ptr_array_free (cfg
->seq_points
, TRUE
);
250 cfg
->seq_points
= NULL
;
254 mono_get_seq_points (MonoDomain
*domain
, MonoMethod
*method
)
257 MonoSeqPointInfo
*seq_points
;
258 MonoMethod
*declaring_generic_method
= NULL
, *shared_method
= NULL
;
260 if (method
->is_inflated
) {
261 declaring_generic_method
= mono_method_get_declaring_generic_method (method
);
262 shared_method
= mini_get_shared_method_full (method
, SHARE_MODE_NONE
, error
);
263 mono_error_assert_ok (error
);
267 seq_points
= (MonoSeqPointInfo
*)g_hash_table_lookup (domain_jit_info (domain
)->seq_points
, method
);
268 if (!seq_points
&& method
->is_inflated
) {
269 /* generic sharing + aot */
270 seq_points
= (MonoSeqPointInfo
*)g_hash_table_lookup (domain_jit_info (domain
)->seq_points
, declaring_generic_method
);
272 seq_points
= (MonoSeqPointInfo
*)g_hash_table_lookup (domain_jit_info (domain
)->seq_points
, shared_method
);
274 mono_loader_unlock ();
280 * mono_find_next_seq_point_for_native_offset:
282 * Find the first sequence point after NATIVE_OFFSET.
285 mono_find_next_seq_point_for_native_offset (MonoDomain
*domain
, MonoMethod
*method
, gint32 native_offset
, MonoSeqPointInfo
**info
, SeqPoint
* seq_point
)
287 MonoSeqPointInfo
*seq_points
;
289 seq_points
= mono_get_seq_points (domain
, method
);
298 return mono_seq_point_find_next_by_native_offset (seq_points
, native_offset
, seq_point
);
302 * mono_find_prev_seq_point_for_native_offset:
304 * Find the first sequence point before NATIVE_OFFSET.
307 mono_find_prev_seq_point_for_native_offset (MonoDomain
*domain
, MonoMethod
*method
, gint32 native_offset
, MonoSeqPointInfo
**info
, SeqPoint
* seq_point
)
309 MonoSeqPointInfo
*seq_points
;
311 seq_points
= mono_get_seq_points (domain
, method
);
320 return mono_seq_point_find_prev_by_native_offset (seq_points
, native_offset
, seq_point
);
324 * mono_find_seq_point:
326 * Find the sequence point corresponding to the IL offset IL_OFFSET, which
327 * should be the location of a sequence point.
330 mono_find_seq_point (MonoDomain
*domain
, MonoMethod
*method
, gint32 il_offset
, MonoSeqPointInfo
**info
, SeqPoint
*seq_point
)
332 MonoSeqPointInfo
*seq_points
;
334 seq_points
= mono_get_seq_points (domain
, method
);
343 return mono_seq_point_find_by_il_offset (seq_points
, il_offset
, seq_point
);
347 mono_bb_deduplicate_op_il_seq_points (MonoCompile
*cfg
, MonoBasicBlock
*bb
)
349 MonoInst
*ins
, *n
, *prev
;
351 MONO_BB_FOR_EACH_INS_SAFE (bb
, n
, ins
) {
352 if (ins
->opcode
!= OP_IL_SEQ_POINT
)
355 prev
= mono_inst_prev (ins
, FILTER_NOP
);
357 if (!prev
|| ins
== prev
|| prev
->opcode
!= OP_IL_SEQ_POINT
)
360 MONO_REMOVE_INS (bb
, prev
);