* Marcos Henrich (marcos.henrich@xamarin.com)
*
* Copyright 2014 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include "mini.h"
#include "seq-points.h"
-static guint8
-encode_var_int (guint8* buf, int val)
-{
- guint8 size = 0;
-
- do {
- guint8 byte = val & 0x7f;
- g_assert (size < 4 && "value has more than 28 bits");
- val >>= 7;
- if(val) byte |= 0x80;
- *(buf++) = byte;
- size++;
- } while (val);
-
- return size;
-}
-
-static guint8
-decode_var_int (guint8* buf, int* val)
-{
- guint8* p = buf;
-
- int low;
- int b;
- b = *(p++); low = (b & 0x7f) ; if(!(b & 0x80)) goto done;
- b = *(p++); low |= (b & 0x7f) << 7; if(!(b & 0x80)) goto done;
- b = *(p++); low |= (b & 0x7f) << 14; if(!(b & 0x80)) goto done;
- b = *(p++); low |= (b & 0x7f) << 21; if(!(b & 0x80)) goto done;
-
- g_assert (FALSE && "value has more than 28 bits");
-
-done:
-
- *val = low;
- return p-buf;
-}
-
-static guint32
-encode_zig_zag (int val)
-{
- return (val << 1) ^ (val >> 31);
-}
-
-static int
-decode_zig_zag (guint32 val)
+static void
+insert_pred_seq_point (MonoInst *last_seq_ins, MonoInst *ins, GSList **next)
{
- int n = val;
- return (n >> 1) ^ (-(n & 1));
+ GSList *l;
+ int src_index = last_seq_ins->backend.size;
+ int dst_index = ins->backend.size;
+
+ /* bb->in_bb might contain duplicates */
+ for (l = next [src_index]; l; l = l->next)
+ if (GPOINTER_TO_UINT (l->data) == dst_index)
+ break;
+ if (!l)
+ next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
}
static void
-allocated_seq_point_size_add (MonoSeqPointInfo *info)
+recursively_make_pred_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
{
- int size = sizeof (MonoSeqPointInfo) + info->len;
+ const gpointer MONO_SEQ_SEEN_LOOP = GINT_TO_POINTER(-1);
- mono_jit_stats.allocated_seq_points_size += size;
-}
+ GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
+ GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);
-static int
-seq_point_read (SeqPoint* seq_point, guint8* ptr, guint8* buffer_ptr, gboolean has_debug_data)
-{
- int value;
- guint8* ptr0 = ptr;
+ // Insert/remove sentinel into the memoize table to detect loops containing bb
+ bb->pred_seq_points = MONO_SEQ_SEEN_LOOP;
- ptr += decode_var_int (ptr, &value);
- seq_point->il_offset += decode_zig_zag (value);
+ for (int i = 0; i < bb->in_count; ++i) {
+ MonoBasicBlock *in_bb = bb->in_bb [i];
+
+ // This bb has the last seq point, append it and continue
+ if (in_bb->last_seq_point != NULL) {
+ predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
+ continue;
+ }
- ptr += decode_var_int (ptr, &value);
- seq_point->native_offset += decode_zig_zag (value);
+ // We've looped or handled this before, exit early.
+ // No last sequence points to find.
+ if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
+ continue;
- if (has_debug_data) {
- ptr += decode_var_int (ptr, &value);
- seq_point->flags = value;
+ // Take sequence points from incoming basic blocks
+
+ if (in_bb == cfg->bb_entry)
+ continue;
- ptr += decode_var_int (ptr, &value);
- seq_point->next_len = value;
+ if (in_bb->pred_seq_points == NULL)
+ recursively_make_pred_seq_points (cfg, in_bb);
- if (seq_point->next_len) {
- // store next offset and skip it
- seq_point->next_offset = ptr - buffer_ptr;
- ptr += seq_point->next_len;
+ // Union sequence points with incoming bb's
+ for (int i=0; i < in_bb->num_pred_seq_points; i++) {
+ if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
+ g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
+ g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
+ }
}
+ // predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
}
- return ptr - ptr0;
-}
-
-static MonoSeqPointInfo*
-seq_point_info_new (int len, gboolean alloc_data, gboolean has_debug_data)
-{
- MonoSeqPointInfo* info = g_new0 (MonoSeqPointInfo, 1);
- info->has_debug_data = has_debug_data;
- info->alloc_data = alloc_data;
- info->len = len;
+ g_hash_table_destroy (seen);
- if (info->alloc_data)
- info->data = g_new0 (guint8, len);
+ if (predecessors->len != 0) {
+ bb->pred_seq_points = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst *) * predecessors->len);
+ bb->num_pred_seq_points = predecessors->len;
- return info;
-}
-
-void
-seq_point_info_free (gpointer ptr)
-{
- MonoSeqPointInfo* info = (MonoSeqPointInfo*) ptr;
- if (info->alloc_data)
- g_free (info->data);
- g_free (info);
-}
-
-static gboolean
-seq_point_info_add_seq_point (GByteArray* array, SeqPoint *sp, SeqPoint *last_seq_point, GSList *next, gboolean has_debug_data)
-{
- int il_delta, native_delta;
- GSList *l;
- guint8 buffer[4];
- guint8 len;
-
- if (!has_debug_data &&
- (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET))
- return FALSE;
-
- il_delta = sp->il_offset - last_seq_point->il_offset;
- native_delta = sp->native_offset - last_seq_point->native_offset;
-
- len = encode_var_int (buffer, encode_zig_zag (il_delta));
- g_byte_array_append (array, buffer, len);
-
- len = encode_var_int (buffer, encode_zig_zag (native_delta));
- g_byte_array_append (array, buffer, len);
-
- if (has_debug_data) {
- sp->next_offset = array->len;
- sp->next_len = g_slist_length (next);
-
- len = encode_var_int (buffer, sp->flags);
- g_byte_array_append (array, buffer, len);
-
- len = encode_var_int (buffer, sp->next_len);
- g_byte_array_append (array, buffer, len);
-
- for (l = next; l; l = l->next) {
- int next_index = GPOINTER_TO_UINT (l->data);
- guint8 buffer[4];
- int len = encode_var_int (buffer, next_index);
- g_byte_array_append (array, buffer, len);
+ for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
+ bb->pred_seq_points [newer] = g_array_index(predecessors, gpointer, newer);
}
- }
+ }
- return TRUE;
+ g_free (predecessors);
}
static void
-collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, int depth)
+collect_pred_seq_points (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, GSList **next)
{
- int i;
- MonoBasicBlock *in_bb;
- GSList *l;
+ // Doesn't have a last sequence point, must find from incoming basic blocks
+ if (bb->pred_seq_points == NULL && bb != cfg->bb_entry)
+ recursively_make_pred_seq_points (cfg, bb);
- for (i = 0; i < bb->in_count; ++i) {
- in_bb = bb->in_bb [i];
-
- if (in_bb->last_seq_point) {
- int src_index = in_bb->last_seq_point->backend.size;
- int dst_index = ins->backend.size;
-
- /* bb->in_bb might contain duplicates */
- for (l = next [src_index]; l; l = l->next)
- if (GPOINTER_TO_UINT (l->data) == dst_index)
- break;
- if (!l)
- next [src_index] = g_slist_append (next [src_index], GUINT_TO_POINTER (dst_index));
- } else {
- /* Have to look at its predecessors */
- if (depth < 5)
- collect_pred_seq_points (in_bb, ins, next, depth + 1);
- }
- }
+ for (int i = 0; i < bb->num_pred_seq_points; i++)
+ insert_pred_seq_point (bb->pred_seq_points [i], ins, next);
+
+ return;
}
void
GSList *bb_seq_points, *l;
MonoInst *last;
MonoDomain *domain = cfg->domain;
- int i;
- GSList **next;
+ int i, seq_info_size;
+ GSList **next = NULL;
SeqPoint* seq_points;
GByteArray* array;
- gboolean has_debug_data = cfg->gen_seq_points_debug_data;
+ gboolean has_debug_data = cfg->gen_sdb_seq_points;
if (!cfg->seq_points)
return;
for (i = 0; i < cfg->seq_points->len; ++i) {
SeqPoint *sp = &seq_points [i];
- MonoInst *ins = g_ptr_array_index (cfg->seq_points, i);
+ MonoInst *ins = (MonoInst *)g_ptr_array_index (cfg->seq_points, i);
sp->il_offset = ins->inst_imm;
sp->native_offset = ins->inst_offset;
bb_seq_points = g_slist_reverse (bb->seq_points);
last = NULL;
for (l = bb_seq_points; l; l = l->next) {
- MonoInst *ins = l->data;
+ MonoInst *ins = (MonoInst *)l->data;
if (ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET)
/* Used to implement method entry/exit events */
next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size));
} else {
/* Link with the last bb in the previous bblocks */
- collect_pred_seq_points (bb, ins, next, 0);
+ collect_pred_seq_points (cfg, bb, ins, next);
}
last = ins;
}
- if (bb->last_ins && bb->last_ins->opcode == OP_ENDFINALLY && bb->seq_points) {
+ /* The second case handles endfinally opcodes which are in a separate bb by themselves */
+ 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)) {
MonoBasicBlock *bb2;
MonoInst *endfinally_seq_point = NULL;
*/
l = g_slist_last (bb->seq_points);
if (l) {
- endfinally_seq_point = l->data;
+ endfinally_seq_point = (MonoInst *)l->data;
for (bb2 = cfg->bb_entry; bb2; bb2 = bb2->next_bb) {
GSList *l = g_slist_last (bb2->seq_points);
if (l) {
- MonoInst *ins = l->data;
+ MonoInst *ins = (MonoInst *)l->data;
if (!(ins->inst_imm == METHOD_ENTRY_IL_OFFSET || ins->inst_imm == METHOD_EXIT_IL_OFFSET) && ins != endfinally_seq_point)
next [endfinally_seq_point->backend.size] = g_slist_append (next [endfinally_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size));
if (!next [i])
continue;
- printf ("\tIL0x%x ->", sp->il_offset);
+ printf ("\tIL0x%x[0x%0x] ->", sp->il_offset, sp->native_offset);
for (l = next [i]; l; l = l->next) {
int next_index = GPOINTER_TO_UINT (l->data);
printf (" IL0x%x", seq_points [next_index].il_offset);
if (has_debug_data)
next_list = next[i];
- if (seq_point_info_add_seq_point (array, sp, last_seq_point, next_list, has_debug_data))
+ if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next_list, has_debug_data))
last_seq_point = sp;
if (has_debug_data)
if (has_debug_data)
g_free (next);
- cfg->seq_point_info = seq_point_info_new (array->len, TRUE, has_debug_data);
- memcpy (cfg->seq_point_info->data, array->data, array->len);
- g_byte_array_free (array, TRUE);
+ cfg->seq_point_info = mono_seq_point_info_new (array->len, TRUE, array->data, has_debug_data, &seq_info_size);
+ mono_jit_stats.allocated_seq_points_size += seq_info_size;
- allocated_seq_point_size_add (cfg->seq_point_info);
+ g_byte_array_free (array, TRUE);
// FIXME: dynamic methods
if (!cfg->compile_aot) {
}
MonoSeqPointInfo*
-get_seq_points (MonoDomain *domain, MonoMethod *method)
+mono_get_seq_points (MonoDomain *domain, MonoMethod *method)
{
MonoSeqPointInfo *seq_points;
+ MonoMethod *declaring_generic_method = NULL, *shared_method = NULL;
+
+ if (method->is_inflated) {
+ declaring_generic_method = mono_method_get_declaring_generic_method (method);
+ shared_method = mini_get_shared_method (method);
+ }
- mono_domain_lock (domain);
- seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
+ mono_loader_lock ();
+ seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
if (!seq_points && method->is_inflated) {
/* generic sharing + aot */
- seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (method));
+ seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, declaring_generic_method);
if (!seq_points)
- seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mini_get_shared_method (method));
+ seq_points = (MonoSeqPointInfo *)g_hash_table_lookup (domain_jit_info (domain)->seq_points, shared_method);
}
- mono_domain_unlock (domain);
+ mono_loader_unlock ();
return seq_points;
}
-static gboolean
-seq_point_find_next_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
-{
- SeqPointIterator it;
- seq_point_iterator_init (&it, info);
- while (seq_point_iterator_next (&it)) {
- if (it.seq_point.native_offset >= native_offset) {
- memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-static gboolean
-seq_point_find_prev_by_native_offset (MonoSeqPointInfo* info, int native_offset, SeqPoint* seq_point)
-{
- SeqPoint prev_seq_point;
- gboolean is_first = TRUE;
- SeqPointIterator it;
- seq_point_iterator_init (&it, info);
- while (seq_point_iterator_next (&it) && it.seq_point.native_offset <= native_offset) {
- memcpy (&prev_seq_point, &it.seq_point, sizeof (SeqPoint));
- is_first = FALSE;
- }
-
- if (!is_first && prev_seq_point.native_offset <= native_offset) {
- memcpy (seq_point, &prev_seq_point, sizeof (SeqPoint));
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-seq_point_find_by_il_offset (MonoSeqPointInfo* info, int il_offset, SeqPoint* seq_point)
-{
- SeqPointIterator it;
- seq_point_iterator_init (&it, info);
- while (seq_point_iterator_next (&it)) {
- if (it.seq_point.il_offset == il_offset) {
- memcpy (seq_point, &it.seq_point, sizeof (SeqPoint));
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
/*
- * find_next_seq_point_for_native_offset:
+ * mono_find_next_seq_point_for_native_offset:
*
* Find the first sequence point after NATIVE_OFFSET.
*/
gboolean
-find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
+mono_find_next_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
{
MonoSeqPointInfo *seq_points;
- seq_points = get_seq_points (domain, method);
+ seq_points = mono_get_seq_points (domain, method);
if (!seq_points) {
if (info)
*info = NULL;
if (info)
*info = seq_points;
- return seq_point_find_next_by_native_offset (seq_points, native_offset, seq_point);
+ return mono_seq_point_find_next_by_native_offset (seq_points, native_offset, seq_point);
}
/*
- * find_prev_seq_point_for_native_offset:
+ * mono_find_prev_seq_point_for_native_offset:
*
* Find the first sequence point before NATIVE_OFFSET.
*/
gboolean
-find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
+mono_find_prev_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset, MonoSeqPointInfo **info, SeqPoint* seq_point)
{
MonoSeqPointInfo *seq_points;
- seq_points = get_seq_points (domain, method);
+ seq_points = mono_get_seq_points (domain, method);
if (!seq_points) {
if (info)
*info = NULL;
if (info)
*info = seq_points;
- return seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
+ return mono_seq_point_find_prev_by_native_offset (seq_points, native_offset, seq_point);
}
/*
- * find_seq_point:
+ * mono_find_seq_point:
*
* Find the sequence point corresponding to the IL offset IL_OFFSET, which
* should be the location of a sequence point.
*/
gboolean
-find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
+mono_find_seq_point (MonoDomain *domain, MonoMethod *method, gint32 il_offset, MonoSeqPointInfo **info, SeqPoint *seq_point)
{
MonoSeqPointInfo *seq_points;
- seq_points = get_seq_points (domain, method);
+ seq_points = mono_get_seq_points (domain, method);
if (!seq_points) {
if (info)
*info = NULL;
if (info)
*info = seq_points;
- return seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
-}
-
-void
-seq_point_init_next (MonoSeqPointInfo* info, SeqPoint sp, SeqPoint* next)
-{
- int i;
- guint8* ptr;
- SeqPointIterator it;
- GArray* seq_points = g_array_new (FALSE, TRUE, sizeof (SeqPoint));
-
- g_assert (info->has_debug_data);
-
- seq_point_iterator_init (&it, info);
- while (seq_point_iterator_next (&it))
- g_array_append_vals (seq_points, &it.seq_point, 1);
-
- ptr = info->data + sp.next_offset;
- for (i = 0; i < sp.next_len; i++) {
- int next_index;
- ptr += decode_var_int (ptr, &next_index);
- g_assert (next_index < seq_points->len);
- memcpy (&next[i], seq_points->data + next_index * sizeof (SeqPoint), sizeof (SeqPoint));
- }
-
- g_array_free (seq_points, TRUE);
-}
-
-gboolean
-seq_point_iterator_next (SeqPointIterator* it)
-{
- if (it->ptr >= it->info->data + it->info->len)
- return FALSE;
-
- it->ptr += seq_point_read (&it->seq_point, it->ptr, it->info->data, it->info->has_debug_data);
-
- return TRUE;
+ return mono_seq_point_find_by_il_offset (seq_points, il_offset, seq_point);
}
void
-seq_point_iterator_init (SeqPointIterator* it, MonoSeqPointInfo* info)
+mono_bb_deduplicate_op_il_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
{
- it->info = info;
- it->ptr = it->info->data;
- memset(&it->seq_point, 0, sizeof(SeqPoint));
-}
-
-int
-seq_point_info_write (MonoSeqPointInfo* info, guint8* buffer)
-{
- guint8* buffer0 = buffer;
+ MonoInst *ins, *n, *prev;
- memcpy (buffer, &info->has_debug_data, 1);
- buffer++;
+ MONO_BB_FOR_EACH_INS_SAFE (bb, n, ins) {
+ if (ins->opcode != OP_IL_SEQ_POINT)
+ continue;
- //Write sequence points
- buffer += encode_var_int (buffer, info->len);
- memcpy (buffer, info->data, info->len);
- buffer += info->len;
+ prev = mono_inst_prev (ins, FILTER_NOP);
- return buffer - buffer0;
-}
-
-int
-seq_point_info_read (MonoSeqPointInfo** info, guint8* buffer, gboolean copy)
-{
- guint8* buffer0 = buffer;
- int size;
- gboolean has_debug_data;
-
- memcpy (&has_debug_data, buffer, 1);
- buffer++;
-
- buffer += decode_var_int (buffer, &size);
- (*info) = seq_point_info_new (size, copy, has_debug_data);
- if (copy)
- memcpy ((*info)->data, buffer, size);
- else
- (*info)->data = buffer;
- buffer += size;
-
- return buffer - buffer0;
-}
-
-/*
- * Returns the maximum size of mono_seq_point_info_write.
- */
-int
-seq_point_info_write_size (MonoSeqPointInfo* info)
-{
- //4 is the maximum size required to store the size of the data.
- //1 is the byte used to store has_debug_data.
- int size = 4 + 1 + info->len;
+ if (!prev || ins == prev || prev->opcode != OP_IL_SEQ_POINT)
+ continue;
- return size;
+ MONO_REMOVE_INS (bb, prev);
+ };
}