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
= (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
);
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
, MonoInst
*, 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
, MonoJitInfo
*jinfo
)
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
;
127 if (ins
->flags
& MONO_INST_NESTED_CALL
)
128 sp
->flags
|= MONO_SEQ_POINT_FLAG_NESTED_CALL
;
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
);
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 */
148 if (ins
->inst_offset
== SEQ_POINT_NATIVE_OFFSET_DEAD_CODE
)
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
));
155 /* Link with the last bb in the previous bblocks */
156 collect_pred_seq_points (cfg
, bb
, ins
, next
);
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
)) {
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
);
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
);
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
];
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
);
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
;
221 if (mono_seq_point_info_add_seq_point (array
, sp
, last_seq_point
, next_list
, has_debug_data
))
225 g_slist_free (next
[i
]);
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
);
246 mono_seq_point_info_free (cfg
->seq_point_info
);
247 mono_domain_unlock (domain
);
250 jinfo
->seq_points
= cfg
->seq_point_info
;
253 g_ptr_array_free (cfg
->seq_points
, TRUE
);
254 cfg
->seq_points
= NULL
;
257 /* LOCKING: Acquires the domain lock */
259 mono_get_seq_points (MonoDomain
*domain
, MonoMethod
*method
)
262 MonoSeqPointInfo
*seq_points
;
263 MonoMethod
*declaring_generic_method
= NULL
, *shared_method
= NULL
;
265 if (method
->is_inflated
) {
266 declaring_generic_method
= mono_method_get_declaring_generic_method (method
);
267 shared_method
= mini_get_shared_method_full (method
, SHARE_MODE_NONE
, error
);
268 mono_error_assert_ok (error
);
271 mono_domain_lock (domain
);
272 seq_points
= (MonoSeqPointInfo
*)g_hash_table_lookup (domain_jit_info (domain
)->seq_points
, method
);
273 if (!seq_points
&& method
->is_inflated
) {
274 /* generic sharing + aot */
275 seq_points
= (MonoSeqPointInfo
*)g_hash_table_lookup (domain_jit_info (domain
)->seq_points
, declaring_generic_method
);
277 seq_points
= (MonoSeqPointInfo
*)g_hash_table_lookup (domain_jit_info (domain
)->seq_points
, shared_method
);
279 mono_domain_unlock (domain
);
285 * mono_find_next_seq_point_for_native_offset:
287 * Find the first sequence point after NATIVE_OFFSET.
290 mono_find_next_seq_point_for_native_offset (MonoDomain
*domain
, MonoMethod
*method
, gint32 native_offset
, MonoSeqPointInfo
**info
, SeqPoint
* seq_point
)
292 MonoSeqPointInfo
*seq_points
;
294 seq_points
= mono_get_seq_points (domain
, method
);
303 return mono_seq_point_find_next_by_native_offset (seq_points
, native_offset
, seq_point
);
307 * mono_find_prev_seq_point_for_native_offset:
309 * Find the first sequence point before NATIVE_OFFSET.
312 mono_find_prev_seq_point_for_native_offset (MonoDomain
*domain
, MonoMethod
*method
, gint32 native_offset
, MonoSeqPointInfo
**info
, SeqPoint
* seq_point
)
314 MonoSeqPointInfo
*seq_points
;
316 seq_points
= mono_get_seq_points (domain
, method
);
325 return mono_seq_point_find_prev_by_native_offset (seq_points
, native_offset
, seq_point
);
329 * mono_find_seq_point:
331 * Find the sequence point corresponding to the IL offset IL_OFFSET, which
332 * should be the location of a sequence point.
335 mono_find_seq_point (MonoDomain
*domain
, MonoMethod
*method
, gint32 il_offset
, MonoSeqPointInfo
**info
, SeqPoint
*seq_point
)
337 MonoSeqPointInfo
*seq_points
;
339 seq_points
= mono_get_seq_points (domain
, method
);
348 return mono_seq_point_find_by_il_offset (seq_points
, il_offset
, seq_point
);
352 mono_bb_deduplicate_op_il_seq_points (MonoCompile
*cfg
, MonoBasicBlock
*bb
)
354 MonoInst
*ins
, *n
, *prev
;
356 MONO_BB_FOR_EACH_INS_SAFE (bb
, n
, ins
) {
357 if (ins
->opcode
!= OP_IL_SEQ_POINT
)
360 prev
= mono_inst_prev (ins
, FILTER_NOP
);
362 if (!prev
|| ins
== prev
|| prev
->opcode
!= OP_IL_SEQ_POINT
)
365 MONO_REMOVE_INS (bb
, prev
);