X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Finterp%2Ftransform.c;h=113a086d3b4a0041f0a5e280d185c9b967abbd08;hb=df203f3532c11d3d54d32b37aba49fd7a7abd5e4;hp=462619f1475c1096587ff4654ce56387330bcc76;hpb=4339c540cab459d657c6f6172e964ec95fa5d59d;p=mono.git diff --git a/mono/mini/interp/transform.c b/mono/mini/interp/transform.c index 462619f1475..113a086d3b4 100644 --- a/mono/mini/interp/transform.c +++ b/mono/mini/interp/transform.c @@ -1,4 +1,5 @@ -/* +/** + * \file * transform CIL into different opcodes for more * efficient interpretation * @@ -14,6 +15,7 @@ #include #include #include +#include #include @@ -33,6 +35,31 @@ typedef struct unsigned char flags; } StackInfo; +typedef struct { + guint8 *ip; + GSList *preds; + GSList *seq_points; + SeqPoint *last_seq_point; + + // This will hold a list of last sequence points of incoming basic blocks + SeqPoint **pred_seq_points; + guint num_pred_seq_points; +} InterpBasicBlock; + +typedef enum { + RELOC_SHORT_BRANCH, + RELOC_LONG_BRANCH, + RELOC_SWITCH +} RelocType; + +typedef struct { + RelocType type; + /* In the interpreter IR */ + int offset; + /* In the IL code */ + int target; +} Reloc; + typedef struct { MonoMethod *method; @@ -44,7 +71,6 @@ typedef struct const unsigned char *in_start; int code_size; int *in_offsets; - int *forward_refs; StackInfo **stack_state; int *stack_height; int *vt_stack_size; @@ -63,6 +89,15 @@ typedef struct int max_data_items; void **data_items; GHashTable *data_hash; + int *clause_indexes; + gboolean gen_sdb_seq_points; + GPtrArray *seq_points; + InterpBasicBlock **offset_to_bb; + InterpBasicBlock *entry_bb; + MonoMemPool *mempool; + GList *basic_blocks; + GPtrArray *relocs; + gboolean verbose_level; } TransformData; #define MINT_TYPE_I1 0 @@ -148,7 +183,7 @@ grow_code (TransformData *td) } while (0) static void -handle_branch(TransformData *td, int short_op, int long_op, int offset) +handle_branch (TransformData *td, int short_op, int long_op, int offset) { int shorten_branch = 0; int target = td->ip + offset - td->il_code; @@ -166,12 +201,20 @@ handle_branch(TransformData *td, int short_op, int long_op, int offset) shorten_branch = 1; } } else { - int prev = td->forward_refs [target]; - td->forward_refs [td->ip - td->il_code] = prev; - td->forward_refs [target] = td->ip - td->il_code; - offset = 0; if (td->header->code_size <= 25000) /* FIX to be precise somehow? */ shorten_branch = 1; + + Reloc *reloc = mono_mempool_alloc0 (td->mempool, sizeof (Reloc)); + if (shorten_branch) { + offset = 0xffff; + reloc->type = RELOC_SHORT_BRANCH; + } else { + offset = 0xdeadbeef; + reloc->type = RELOC_LONG_BRANCH; + } + reloc->offset = td->new_ip - td->new_code; + reloc->target = target; + g_ptr_array_add (td->relocs, reloc); } if (shorten_branch) { ADD_CODE(td, short_op); @@ -257,17 +300,6 @@ binary_arith_op(TransformData *td, int mint_op) --td->sp; } -static void -binary_int_op(TransformData *td, int mint_op) -{ - int op = mint_op + td->sp [-1].type - STACK_TYPE_I4; - CHECK_STACK(td, 2); - if (td->sp [-1].type != td->sp [-2].type) - g_warning("%s.%s: int type mismatch", td->method->klass->name, td->method->name); - ADD_CODE(td, op); - --td->sp; -} - static void shift_op(TransformData *td, int mint_op) { @@ -330,6 +362,8 @@ enum_type: goto enum_type; } else return MINT_TYPE_VT; + case MONO_TYPE_TYPEDBYREF: + return MINT_TYPE_VT; case MONO_TYPE_GENERICINST: type = &type->data.generic_class->container_class->byval_arg; goto enum_type; @@ -502,7 +536,7 @@ store_arg(TransformData *td, int n) else size = mono_class_value_size (klass, NULL); ADD_CODE(td, MINT_STARG_VT); - ADD_CODE(td, n); + ADD_CODE(td, td->rtm->arg_offsets [n]); WRITE32(td, &size); if (td->sp [-1].type == STACK_TYPE_VT) POP_VT(td, size); @@ -561,11 +595,13 @@ load_local(TransformData *td, int n) WRITE32(td, &size); } else { g_assert (mt < MINT_TYPE_VT); - if (mt == MINT_TYPE_I4 && !td->is_bb_start [td->in_start - td->il_code] && td->last_new_ip != NULL && + if (!td->gen_sdb_seq_points && + mt == MINT_TYPE_I4 && !td->is_bb_start [td->in_start - td->il_code] && td->last_new_ip != NULL && td->last_new_ip [0] == MINT_STLOC_I4 && td->last_new_ip [1] == offset) { td->last_new_ip [0] = MINT_STLOC_NP_I4; - } else if (mt == MINT_TYPE_O && !td->is_bb_start [td->in_start - td->il_code] && td->last_new_ip != NULL && - td->last_new_ip [0] == MINT_STLOC_O && td->last_new_ip [1] == offset) { + } else if (!td->gen_sdb_seq_points && + mt == MINT_TYPE_O && !td->is_bb_start [td->in_start - td->il_code] && td->last_new_ip != NULL && + td->last_new_ip [0] == MINT_STLOC_O && td->last_new_ip [1] == offset) { td->last_new_ip [0] = MINT_STLOC_NP_O; } else { ADD_CODE(td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1)); @@ -635,6 +671,35 @@ get_data_item_index (TransformData *td, void *ptr) return index; } +static gboolean +jit_call_supported (MonoMethod *method, MonoMethodSignature *sig) +{ + GSList *l; + + if (sig->param_count > 6) + return FALSE; + if (sig->pinvoke) + return FALSE; + if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) + return FALSE; + if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) + return FALSE; + if (method->is_inflated) + return FALSE; + if (method->string_ctor) + return FALSE; + + for (l = jit_classes; l; l = l->next) { + char *class_name = l->data; + // FIXME: Namespaces + if (!strcmp (method->klass->name, class_name)) + return TRUE; + } + + //return TRUE; + return FALSE; +} + static void interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target_method, MonoDomain *domain, MonoGenericContext *generic_context, unsigned char *is_bb_start, int body_start_offset, MonoClass *constrained_class, gboolean readonly) { @@ -674,22 +739,8 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target else target_method = (MonoMethod *)mono_method_get_wrapper_data (method, token); csignature = mono_method_signature (target_method); - if (target_method->klass == mono_defaults.string_class) { - if (target_method->name [0] == 'g') { - if (strcmp (target_method->name, "get_Chars") == 0) - op = MINT_GETCHR; - else if (strcmp (target_method->name, "get_Length") == 0) - op = MINT_STRLEN; - } - } else if (mono_class_is_subclass_of (target_method->klass, mono_defaults.array_class, FALSE)) { - if (!strcmp (target_method->name, "get_Rank")) { - op = MINT_ARRAY_RANK; - } else if (!strcmp (target_method->name, "get_Length")) { - op = MINT_LDLEN; - } else if (!strcmp (target_method->name, "Address")) { - op = readonly ? MINT_LDELEMA : MINT_LDELEMA_TC; - } - } else if (target_method && generic_context) { + + if (generic_context) { csignature = mono_inflate_generic_signature (csignature, generic_context, &error); mono_error_cleanup (&error); /* FIXME: don't swallow the error */ target_method = mono_class_inflate_generic_method_checked (target_method, generic_context, &error); @@ -700,6 +751,41 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target csignature = mono_method_signature (target_method); } + if (target_method && target_method->string_ctor) { + /* Create the real signature */ + MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (td->mempool, csignature); + ctor_sig->ret = &mono_defaults.string_class->byval_arg; + + csignature = ctor_sig; + } + + /* Intrinsics */ + if (target_method) { + if (target_method->klass == mono_defaults.string_class) { + if (target_method->name [0] == 'g') { + if (strcmp (target_method->name, "get_Chars") == 0) + op = MINT_GETCHR; + else if (strcmp (target_method->name, "get_Length") == 0) + op = MINT_STRLEN; + } + } else if (mono_class_is_subclass_of (target_method->klass, mono_defaults.array_class, FALSE)) { + if (!strcmp (target_method->name, "get_Rank")) { + op = MINT_ARRAY_RANK; + } else if (!strcmp (target_method->name, "get_Length")) { + op = MINT_LDLEN; + } else if (!strcmp (target_method->name, "Address")) { + op = readonly ? MINT_LDELEMA : MINT_LDELEMA_TC; + } + } else if (target_method->klass->image == mono_defaults.corlib && + (strcmp (target_method->klass->name_space, "System.Diagnostics") == 0) && + (strcmp (target_method->klass->name, "Debugger") == 0)) { + if (!strcmp (target_method->name, "Break") && csignature->param_count == 0) { + if (mini_should_insert_breakpoint (method)) + op = MINT_BREAK; + } + } + } + if (constrained_class) { if (constrained_class->enumtype && !strcmp (target_method->name, "GetHashCode")) { /* Use the corresponding method from the base type to avoid boxing */ @@ -771,18 +857,20 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target mono_class_init (target_method->klass); CHECK_STACK (td, csignature->param_count + csignature->hasthis); - if (!calli && (!virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) && + if (!calli && op == -1 && (!virtual || (target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) == 0) && (target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 && - (target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0) { + (target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 && + !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) { int called_inited = mono_class_vtable (domain, target_method->klass)->initialized; MonoMethodHeader *mheader = mono_method_get_header (target_method); if (/*mono_metadata_signature_equal (method->signature, target_method->signature) */ method == target_method && *(td->ip + 5) == CEE_RET) { int offset; - if (mono_interp_traceopt) + if (td->verbose_level) g_print ("Optimize tail call of %s.%s\n", target_method->klass->name, target_method->name); - for (i = csignature->param_count - 1; i >= 0; --i) - store_arg (td, i + csignature->hasthis); + + for (i = csignature->param_count - 1 + !!csignature->hasthis; i >= 0; --i) + store_arg (td, i); ADD_CODE(td, MINT_BR_S); offset = body_start_offset - ((td->new_ip - 1) - td->new_code); @@ -794,11 +882,12 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } else { /* mheader might not exist if this is a delegate invoc, etc */ if (mheader && *mheader->code == CEE_RET && called_inited) { - if (mono_interp_traceopt) + if (td->verbose_level) g_print ("Inline (empty) call of %s.%s\n", target_method->klass->name, target_method->name); - for (i = 0; i < csignature->param_count; i++) + for (i = 0; i < csignature->param_count; i++) { ADD_CODE (td, MINT_POP); /*FIX: vt */ ADD_CODE (td, 0); + } if (csignature->hasthis) { if (virtual) ADD_CODE(td, MINT_CKNULL); @@ -857,6 +946,10 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target ADD_CODE (td, get_data_item_index (td, target_method->klass)); ADD_CODE (td, 1 + target_method->klass->rank); } + } else if (!calli && !virtual && jit_call_supported (target_method, csignature)) { + ADD_CODE(td, MINT_JIT_CALL); + ADD_CODE(td, get_data_item_index (td, (void *)mono_interp_get_runtime_method (domain, target_method, &error))); + mono_error_assert_ok (&error); } else { if (calli) ADD_CODE(td, native ? MINT_CALLI_NAT : MINT_CALLI); @@ -864,7 +957,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target ADD_CODE(td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT); else ADD_CODE(td, is_void ? MINT_VCALL : MINT_CALL); - + if (calli) { ADD_CODE(td, get_data_item_index (td, (void *)csignature)); } else { @@ -881,13 +974,368 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } } +static MonoClassField * +interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, MonoGenericContext *generic_context) +{ + MonoClassField *field = NULL; + if (method->wrapper_type != MONO_WRAPPER_NONE) { + field = (MonoClassField *) mono_method_get_wrapper_data (method, token); + *klass = field->parent; + } else { + MonoError error; + error_init (&error); + field = mono_field_from_token_checked (method->klass->image, token, klass, generic_context, &error); + mono_error_cleanup (&error); /* FIXME: don't swallow the error */ + } + return field; +} + +static InterpBasicBlock* +get_bb (TransformData *td, InterpBasicBlock *cbb, unsigned char *ip) +{ + int offset = ip - td->il_code; + InterpBasicBlock *bb = td->offset_to_bb [offset]; + + if (!bb) { + bb = mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock)); + bb->ip = ip; + td->offset_to_bb [offset] = bb; + + td->basic_blocks = g_list_append_mempool (td->mempool, td->basic_blocks, bb); + } + + if (cbb) + bb->preds = g_slist_prepend_mempool (td->mempool, bb->preds, cbb); + return bb; +} + +/* + * get_basic_blocks: + * + * Compute the set of IL level basic blocks. + */ +static void +get_basic_blocks (TransformData *td) +{ + guint8 *start = (guint8*)td->il_code; + guint8 *end = (guint8*)td->il_code + td->code_size; + guint8 *ip = start; + unsigned char *target; + int i; + guint cli_addr; + const MonoOpcode *opcode; + InterpBasicBlock *cbb; + + td->offset_to_bb = mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * (end - start + 1)); + td->entry_bb = cbb = get_bb (td, NULL, start); + + while (ip < end) { + cli_addr = ip - start; + td->offset_to_bb [cli_addr] = cbb; + i = mono_opcode_value ((const guint8 **)&ip, end); + opcode = &mono_opcodes [i]; + switch (opcode->argument) { + case MonoInlineNone: + ip++; + break; + case MonoInlineString: + case MonoInlineType: + case MonoInlineField: + case MonoInlineMethod: + case MonoInlineTok: + case MonoInlineSig: + case MonoShortInlineR: + case MonoInlineI: + ip += 5; + break; + case MonoInlineVar: + ip += 3; + break; + case MonoShortInlineVar: + case MonoShortInlineI: + ip += 2; + break; + case MonoShortInlineBrTarget: + target = start + cli_addr + 2 + (signed char)ip [1]; + get_bb (td, cbb, target); + ip += 2; + cbb = get_bb (td, cbb, ip); + break; + case MonoInlineBrTarget: + target = start + cli_addr + 5 + (gint32)read32 (ip + 1); + get_bb (td, cbb, target); + ip += 5; + cbb = get_bb (td, cbb, ip); + break; + case MonoInlineSwitch: { + guint32 n = read32 (ip + 1); + guint32 j; + ip += 5; + cli_addr += 5 + 4 * n; + target = start + cli_addr; + get_bb (td, cbb, target); + + for (j = 0; j < n; ++j) { + target = start + cli_addr + (gint32)read32 (ip); + get_bb (td, cbb, target); + ip += 4; + } + cbb = get_bb (td, cbb, ip); + break; + } + case MonoInlineR: + case MonoInlineI8: + ip += 9; + break; + default: + g_assert_not_reached (); + } + } +} + +static void +interp_save_debug_info (RuntimeMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers) +{ + MonoDebugMethodJitInfo *dinfo; + int i; + + if (!mono_debug_enabled ()) + return; + + /* + * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code. + */ + + dinfo = g_new0 (MonoDebugMethodJitInfo, 1); + dinfo->num_params = rtm->param_count; + dinfo->params = g_new0 (MonoDebugVarInfo, dinfo->num_params); + dinfo->num_locals = header->num_locals; + dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals); + dinfo->code_start = (guint8*)rtm->code; + dinfo->code_size = td->new_ip - td->new_code; + dinfo->epilogue_begin = 0; + dinfo->has_var_info = TRUE; + dinfo->num_line_numbers = line_numbers->len; + dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers); + + for (i = 0; i < dinfo->num_params; i++) { + MonoDebugVarInfo *var = &dinfo->params [i]; + var->type = rtm->param_types [i]; + } + for (i = 0; i < dinfo->num_locals; i++) { + MonoDebugVarInfo *var = &dinfo->locals [i]; + var->type = header->locals [i]; + } + + for (i = 0; i < dinfo->num_line_numbers; i++) + dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i); + mono_debug_add_method (rtm->method, dinfo, mono_domain_get ()); + + mono_debug_free_method_jit_info (dinfo); +} + +/* Same as the code in seq-points.c */ +static void +insert_pred_seq_point (SeqPoint *last_sp, SeqPoint *sp, GSList **next) +{ + GSList *l; + int src_index = last_sp->next_offset; + int dst_index = sp->next_offset; + + /* 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 +recursively_make_pred_seq_points (TransformData *td, InterpBasicBlock *bb) +{ + const gpointer MONO_SEQ_SEEN_LOOP = GINT_TO_POINTER(-1); + GSList *l; + + GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer)); + GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL); + + // Insert/remove sentinel into the memoize table to detect loops containing bb + bb->pred_seq_points = MONO_SEQ_SEEN_LOOP; + + for (l = bb->preds; l; l = l->next) { + InterpBasicBlock *in_bb = l->data; + + // 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; + } + + // 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; + + // Take sequence points from incoming basic blocks + + if (in_bb == td->entry_bb) + continue; + + if (in_bb->pred_seq_points == NULL) + recursively_make_pred_seq_points (td, in_bb); + + // 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); + } + + g_hash_table_destroy (seen); + + if (predecessors->len != 0) { + bb->pred_seq_points = mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint *) * predecessors->len); + bb->num_pred_seq_points = predecessors->len; + + for (int newer = 0; newer < bb->num_pred_seq_points; newer++) { + bb->pred_seq_points [newer] = g_array_index (predecessors, gpointer, newer); + } + } + + g_array_free (predecessors, TRUE); +} + +static void +collect_pred_seq_points (TransformData *td, InterpBasicBlock *bb, SeqPoint *seqp, GSList **next) +{ + // Doesn't have a last sequence point, must find from incoming basic blocks + if (bb->pred_seq_points == NULL && bb != td->entry_bb) + recursively_make_pred_seq_points (td, bb); + + for (int i = 0; i < bb->num_pred_seq_points; i++) + insert_pred_seq_point (bb->pred_seq_points [i], seqp, next); + + return; +} + +static void +save_seq_points (TransformData *td) +{ + RuntimeMethod *rtm = td->rtm; + GByteArray *array; + int i, seq_info_size; + MonoSeqPointInfo *info; + MonoDomain *domain = mono_domain_get (); + GSList **next = NULL; + GList *bblist; + + if (!td->gen_sdb_seq_points) + return; + + /* + * For each sequence point, compute the list of sequence points immediately + * following it, this is needed to implement 'step over' in the debugger agent. + * Similar to the code in mono_save_seq_point_info (). + */ + for (i = 0; i < td->seq_points->len; ++i) { + SeqPoint *sp = g_ptr_array_index (td->seq_points, i); + + /* Store the seq point index here temporarily */ + sp->next_offset = i; + } + next = mono_mempool_alloc0 (td->mempool, sizeof (GList*) * td->seq_points->len); + for (bblist = td->basic_blocks; bblist; bblist = bblist->next) { + InterpBasicBlock *bb = bblist->data; + + GSList *bb_seq_points = g_slist_reverse (bb->seq_points); + SeqPoint *last = NULL; + for (GSList *l = bb_seq_points; l; l = l->next) { + SeqPoint *sp = l->data; + + if (sp->il_offset == METHOD_ENTRY_IL_OFFSET || sp->il_offset == METHOD_EXIT_IL_OFFSET) + /* Used to implement method entry/exit events */ + continue; + + if (last != NULL) { + /* Link with the previous seq point in the same bb */ + next [last->next_offset] = g_slist_append_mempool (td->mempool, next [last->next_offset], GINT_TO_POINTER (sp->next_offset)); + } else { + /* Link with the last bb in the previous bblocks */ + collect_pred_seq_points (td, bb, sp, next); + } + last = sp; + } + } + + /* Serialize the seq points into a byte array */ + array = g_byte_array_new (); + SeqPoint zero_seq_point = {0}; + SeqPoint* last_seq_point = &zero_seq_point; + for (i = 0; i < td->seq_points->len; ++i) { + SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i); + + sp->next_offset = 0; + if (mono_seq_point_info_add_seq_point (array, sp, last_seq_point, next [i], TRUE)) + last_seq_point = sp; + } + + if (td->verbose_level) { + g_print ("\nSEQ POINT MAP FOR %s: \n", td->method->name); + + for (i = 0; i < td->seq_points->len; ++i) { + SeqPoint *sp = (SeqPoint*)g_ptr_array_index (td->seq_points, i); + GSList *l; + + if (!next [i]) + continue; + + g_print ("\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); + g_print (" IL0x%x", ((SeqPoint*)g_ptr_array_index (td->seq_points, next_index))->il_offset); + } + g_print ("\n"); + } + } + + info = mono_seq_point_info_new (array->len, TRUE, array->data, TRUE, &seq_info_size); + mono_jit_stats.allocated_seq_points_size += seq_info_size; + + g_byte_array_free (array, TRUE); + + mono_domain_lock (domain); + g_hash_table_insert (domain_jit_info (domain)->seq_points, rtm->method, info); + mono_domain_unlock (domain); +} + +static void +emit_seq_point (TransformData *td, int il_offset, InterpBasicBlock *cbb, gboolean nonempty_stack) +{ + SeqPoint *seqp; + + seqp = mono_mempool_alloc0 (td->mempool, sizeof (SeqPoint)); + seqp->il_offset = il_offset; + seqp->native_offset = (guint8*)td->new_ip - (guint8*)td->new_code; + if (nonempty_stack) + seqp->flags |= MONO_SEQ_POINT_FLAG_NONEMPTY_STACK; + + ADD_CODE (td, MINT_SDB_SEQ_POINT); + g_ptr_array_add (td->seq_points, seqp); + + cbb->seq_points = g_slist_prepend_mempool (td->mempool, cbb->seq_points, seqp); + cbb->last_seq_point = seqp; +} + static void generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, MonoGenericContext *generic_context) { MonoMethodHeader *header = mono_method_get_header (method); MonoMethodSignature *signature = mono_method_signature (method); MonoImage *image = method->klass->image; - MonoDomain *domain = mono_domain_get (); + MonoDomain *domain = rtm->domain; MonoClass *constrained_class = NULL; MonoError error; int offset, mt, i, i32; @@ -901,8 +1349,21 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo guint32 token; TransformData td; int generating_code = 1; + GArray *line_numbers; + MonoDebugMethodInfo *minfo; + MonoBitSet *seq_point_locs = NULL; + MonoBitSet *seq_point_set_locs = NULL; + gboolean sym_seq_points = FALSE; + InterpBasicBlock *bb_exit = NULL; + static gboolean verbose_method_inited; + static char* verbose_method_name; + + if (!verbose_method_inited) { + verbose_method_name = getenv ("MONO_VERBOSE_METHOD"); + verbose_method_inited = TRUE; + } - memset(&td, 0, sizeof(td)); + memset (&td, 0, sizeof(td)); td.method = method; td.rtm = rtm; td.is_bb_start = is_bb_start; @@ -912,8 +1373,8 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo td.max_code_size = td.code_size; td.new_code = (unsigned short *)g_malloc(td.max_code_size * sizeof(gushort)); td.new_code_end = td.new_code + td.max_code_size; + td.mempool = mono_mempool_new (); td.in_offsets = g_malloc0(header->code_size * sizeof(int)); - td.forward_refs = g_malloc(header->code_size * sizeof(int)); td.stack_state = g_malloc0(header->code_size * sizeof(StackInfo *)); td.stack_height = g_malloc(header->code_size * sizeof(int)); td.vt_stack_size = g_malloc(header->code_size * sizeof(int)); @@ -921,11 +1382,62 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo td.max_data_items = 0; td.data_items = NULL; td.data_hash = g_hash_table_new (NULL, NULL); + td.clause_indexes = g_malloc (header->code_size * sizeof (int)); + td.gen_sdb_seq_points = debug_options.gen_sdb_seq_points; + td.seq_points = g_ptr_array_new (); + td.relocs = g_ptr_array_new (); + td.verbose_level = mono_interp_traceopt; rtm->data_items = td.data_items; for (i = 0; i < header->code_size; i++) { - td.forward_refs [i] = -1; td.stack_height [i] = -1; + td.clause_indexes [i] = -1; } + + if (verbose_method_name) { + const char *name = verbose_method_name; + + if ((strchr (name, '.') > name) || strchr (name, ':')) { + MonoMethodDesc *desc; + + desc = mono_method_desc_new (name, TRUE); + if (mono_method_desc_full_match (desc, method)) { + td.verbose_level = 4; + } + mono_method_desc_free (desc); + } else { + if (strcmp (method->name, name) == 0) + td.verbose_level = 4; + } + } + + if (td.gen_sdb_seq_points) { + get_basic_blocks (&td); + + minfo = mono_debug_lookup_method (method); + + if (minfo) { + MonoSymSeqPoint *sps; + int i, n_il_offsets; + + mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets); + // FIXME: Free + seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td.mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0); + seq_point_set_locs = mono_bitset_mem_new (mono_mempool_alloc0 (td.mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0); + sym_seq_points = TRUE; + + for (i = 0; i < n_il_offsets; ++i) { + if (sps [i].il_offset < header->code_size) + mono_bitset_set_fast (seq_point_locs, sps [i].il_offset); + } + g_free (sps); + } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) { + /* Methods without line number info like auto-generated property accessors */ + seq_point_locs = mono_bitset_new (header->code_size, 0); + seq_point_set_locs = mono_bitset_new (header->code_size, 0); + sym_seq_points = TRUE; + } + } + td.new_ip = td.new_code; td.last_new_ip = NULL; @@ -933,6 +1445,8 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo td.sp = td.stack; td.max_stack_height = 0; + line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry)); + for (i = 0; i < header->num_clauses; i++) { MonoExceptionClause *c = header->clauses + i; td.stack_height [c->handler_offset] = 0; @@ -943,12 +1457,28 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo td.stack_state [c->handler_offset] = g_malloc0(sizeof(StackInfo)); td.stack_state [c->handler_offset][0].type = STACK_TYPE_O; td.stack_state [c->handler_offset][0].klass = NULL; /*FIX*/ + + if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) { + td.stack_height [c->data.filter_offset] = 0; + td.vt_stack_size [c->data.filter_offset] = 0; + td.is_bb_start [c->data.filter_offset] = 1; + + td.stack_height [c->data.filter_offset] = 1; + td.stack_state [c->data.filter_offset] = g_malloc0(sizeof(StackInfo)); + td.stack_state [c->data.filter_offset][0].type = STACK_TYPE_O; + td.stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/ + } + + for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) { + if (td.clause_indexes [j] == -1) + td.clause_indexes [j] = i; + } } td.ip = header->code; end = td.ip + header->code_size; - if (mono_interp_traceopt) { + if (td.verbose_level) { char *tmp = mono_disasm_code (NULL, method, td.ip, end); char *name = mono_method_full_name (method, TRUE); g_print ("Method %s, original code:\n", name); @@ -972,6 +1502,12 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo } } + if (sym_seq_points) { + InterpBasicBlock *cbb = td.offset_to_bb [0]; + g_assert (cbb); + emit_seq_point (&td, METHOD_ENTRY_IL_OFFSET, cbb, FALSE); + } + while (td.ip < end) { int in_offset; @@ -981,34 +1517,12 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo td.in_offsets [in_offset] = td.new_ip - td.new_code; new_in_start_offset = td.new_ip - td.new_code; td.in_start = td.ip; - while (td.forward_refs [in_offset] >= 0) { - int j = td.forward_refs [in_offset]; - int slot; - td.forward_refs [in_offset] = td.forward_refs [j]; - if (td.in_offsets [j] < 0) { - int old_switch_offset = -td.in_offsets [j]; - int new_switch_offset = td.in_offsets [old_switch_offset]; - int switch_case = (j - old_switch_offset - 5) / 4; - int n_cases = read32 (header->code + old_switch_offset + 1); - offset = (td.new_ip - td.new_code) - (new_switch_offset + 2 * n_cases + 3); - slot = new_switch_offset + 3 + 2 * switch_case; - td.new_code [slot] = * (unsigned short *)(&offset); - td.new_code [slot + 1] = * ((unsigned short *)&offset + 1); - } else { - int op = td.new_code [td.in_offsets [j]]; - if (mono_interp_opargtype [op] == MintOpShortBranch) { - offset = (td.new_ip - td.new_code) - td.in_offsets [j]; - g_assert (offset <= 32767); - slot = td.in_offsets [j] + 1; - td.new_code [slot] = offset; - } else { - offset = (td.new_ip - td.new_code) - td.in_offsets [j]; - slot = td.in_offsets [j] + 1; - td.new_code [slot] = * (unsigned short *)(&offset); - td.new_code [slot + 1] = * ((unsigned short *)&offset + 1); - } - } - } + + MonoDebugLineNumberEntry lne; + lne.native_offset = (guint8*)td.new_ip - (guint8*)td.new_code; + lne.il_offset = in_offset; + g_array_append_val (line_numbers, lne); + if (td.stack_height [in_offset] >= 0) { g_assert (is_bb_start [in_offset]); if (td.stack_height [in_offset] > 0) @@ -1024,7 +1538,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo ++td.ip; continue; } - if (mono_interp_traceopt > 1) { + if (td.verbose_level > 1) { printf("IL_%04lx %s %-10s -> IL_%04lx, sp %ld, %s %-12s vt_sp %u (max %u)\n", td.ip - td.il_code, td.is_bb_start [td.ip - td.il_code] == 3 ? "<>" : @@ -1035,6 +1549,26 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo (td.sp > td.stack && (td.sp [-1].type == STACK_TYPE_O || td.sp [-1].type == STACK_TYPE_VT)) ? (td.sp [-1].klass == NULL ? "?" : td.sp [-1].klass->name) : "", td.vt_sp, td.max_vt_sp); } + + if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td.ip - header->code)) { + InterpBasicBlock *cbb = td.offset_to_bb [td.ip - header->code]; + g_assert (cbb); + + /* + * Make methods interruptable at the beginning, and at the targets of + * backward branches. + */ + if (in_offset == 0 || g_slist_length (cbb->preds) > 1) + ADD_CODE (&td, MINT_SDB_INTR_LOC); + + emit_seq_point (&td, in_offset, cbb, FALSE); + + mono_bitset_set_fast (seq_point_set_locs, td.ip - header->code); + } + + if (sym_seq_points) + bb_exit = td.offset_to_bb [td.ip - header->code]; + switch (*td.ip) { case CEE_NOP: /* lose it */ @@ -1070,15 +1604,9 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo break; case CEE_LDARGA_S: { /* NOTE: n includes this */ - int n = ((guint8 *)td.ip)[1]; - if (n == 0 && signature->hasthis) { - g_error ("LDTHISA: NOPE"); - ADD_CODE(&td, MINT_LDTHISA); - } - else { - ADD_CODE(&td, MINT_LDARGA); - ADD_CODE(&td, td.rtm->arg_offsets [n]); - } + int n = ((guint8 *) td.ip) [1]; + ADD_CODE (&td, MINT_LDARGA); + ADD_CODE (&td, td.rtm->arg_offsets [n]); PUSH_SIMPLE_TYPE(&td, STACK_TYPE_MP); td.ip += 2; break; @@ -1221,7 +1749,20 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_CALLVIRT: /* Fall through */ case CEE_CALLI: /* Fall through */ case CEE_CALL: { + gboolean need_seq_point = FALSE; + + if (sym_seq_points && !mono_bitset_test_fast (seq_point_locs, td.ip + 5 - header->code)) + need_seq_point = TRUE; + interp_transform_call (&td, method, NULL, domain, generic_context, is_bb_start, body_start_offset, constrained_class, readonly); + + if (need_seq_point) { + InterpBasicBlock *cbb = td.offset_to_bb [td.ip - header->code]; + g_assert (cbb); + + emit_seq_point (&td, td.ip - header->code, cbb, TRUE); + } + constrained_class = NULL; readonly = FALSE; break; @@ -1240,6 +1781,13 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo g_warning ("%s.%s: CEE_RET: more values on stack: %d", td.method->klass->name, td.method->name, td.sp - td.stack); if (td.vt_sp != vt_size) g_error ("%s.%s: CEE_RET: value type stack: %d vs. %d", td.method->klass->name, td.method->name, td.vt_sp, vt_size); + + if (sym_seq_points) { + InterpBasicBlock *cbb = td.offset_to_bb [td.ip - header->code]; + g_assert (cbb); + emit_seq_point (&td, METHOD_EXIT_IL_OFFSET, bb_exit, FALSE); + } + if (vt_size == 0) SIMPLE_OP(td, signature->ret->type == MONO_TYPE_VOID ? MINT_RET_VOID : MINT_RET); else { @@ -1359,15 +1907,12 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_SWITCH: { guint32 n; const unsigned char *next_ip; - const unsigned char *base_ip = td.ip; - unsigned short *next_new_ip; ++td.ip; n = read32 (td.ip); ADD_CODE (&td, MINT_SWITCH); WRITE32 (&td, &n); td.ip += 4; next_ip = td.ip + n * 4; - next_new_ip = td.new_ip + n * 2; --td.sp; int stack_height = td.sp - td.stack; for (i = 0; i < n; i++) { @@ -1378,16 +1923,19 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo if (stack_height > 0 && stack_height != td.stack_height [target]) g_warning ("SWITCH with back branch and non-empty stack"); #endif - target = td.in_offsets [target] - (next_new_ip - td.new_code); + target = td.in_offsets [target] - (td.new_ip - td.new_code); } else { td.stack_height [target] = stack_height; td.vt_stack_size [target] = td.vt_sp; if (stack_height > 0) td.stack_state [target] = g_memdup (td.stack, stack_height * sizeof (td.stack [0])); - int prev = td.forward_refs [target]; - td.forward_refs [td.ip - td.il_code] = prev; - td.forward_refs [target] = td.ip - td.il_code; - td.in_offsets [td.ip - td.il_code] = - (base_ip - td.il_code); + + Reloc *reloc = mono_mempool_alloc0 (td.mempool, sizeof (Reloc)); + reloc->type = RELOC_SWITCH; + reloc->offset = td.new_ip - td.new_code; + reloc->target = target; + g_ptr_array_add (td.relocs, reloc); + target = 0xffff; } WRITE32 (&td, &target); td.ip += 4; @@ -1511,23 +2059,23 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo ++td.ip; break; case CEE_REM: - binary_int_op (&td, MINT_REM_I4); + binary_arith_op (&td, MINT_REM_I4); ++td.ip; break; case CEE_REM_UN: - binary_int_op (&td, MINT_REM_UN_I4); + binary_arith_op (&td, MINT_REM_UN_I4); ++td.ip; break; case CEE_AND: - binary_int_op (&td, MINT_AND_I4); + binary_arith_op (&td, MINT_AND_I4); ++td.ip; break; case CEE_OR: - binary_int_op (&td, MINT_OR_I4); + binary_arith_op (&td, MINT_OR_I4); ++td.ip; break; case CEE_XOR: - binary_int_op (&td, MINT_XOR_I4); + binary_arith_op (&td, MINT_XOR_I4); ++td.ip; break; case CEE_SHL: @@ -1845,12 +2393,13 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo MonoString *s; token = mono_metadata_token_index (read32 (td.ip + 1)); td.ip += 5; - if (method->wrapper_type != MONO_WRAPPER_NONE) { - s = mono_string_new_wrapper( - mono_method_get_wrapper_data (method, token)); - } - else + if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) { + s = mono_method_get_wrapper_data (method, token); + } else if (method->wrapper_type != MONO_WRAPPER_NONE) { + s = mono_string_new_wrapper (mono_method_get_wrapper_data (method, token)); + } else { s = mono_ldstr (domain, image, token); + } ADD_CODE(&td, MINT_LDSTR); ADD_CODE(&td, get_data_item_index (&td, s)); PUSH_TYPE(&td, STACK_TYPE_O, mono_defaults.string_class); @@ -1903,7 +2452,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_CASTCLASS: CHECK_STACK (&td, 1); token = read32 (td.ip + 1); - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); ADD_CODE(&td, MINT_CASTCLASS); ADD_CODE(&td, get_data_item_index (&td, klass)); td.sp [-1].klass = klass; @@ -1912,7 +2461,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_ISINST: CHECK_STACK (&td, 1); token = read32 (td.ip + 1); - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); ADD_CODE(&td, MINT_ISINST); ADD_CODE(&td, get_data_item_index (&td, klass)); td.ip += 5; @@ -1955,10 +2504,10 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo CHECK_STACK (&td, 1); token = read32 (td.ip + 1); - g_assert (method->wrapper_type == MONO_WRAPPER_NONE); - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); if (mini_type_is_reference (&klass->byval_arg)) { + int mt = mint_type (&klass->byval_arg); ADD_CODE (&td, MINT_CASTCLASS); ADD_CODE (&td, get_data_item_index (&td, klass)); SET_TYPE (td.sp - 1, stack_type [mt], klass); @@ -1993,7 +2542,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_LDFLDA: CHECK_STACK (&td, 1); token = read32 (td.ip + 1); - field = mono_field_from_token (image, token, &klass, generic_context); + field = interp_field_from_token (method, token, &klass, generic_context); gboolean is_static = !!(field->type->attrs & FIELD_ATTRIBUTE_STATIC); mono_class_init (klass); if (is_static) { @@ -2016,17 +2565,20 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_LDFLD: { CHECK_STACK (&td, 1); token = read32 (td.ip + 1); - field = mono_field_from_token (image, token, &klass, generic_context); + field = interp_field_from_token (method, token, &klass, generic_context); gboolean is_static = !!(field->type->attrs & FIELD_ATTRIBUTE_STATIC); mono_class_init (klass); MonoClass *field_klass = mono_class_from_mono_type (field->type); mt = mint_type (&field_klass->byval_arg); +#ifndef DISABLE_REMOTING if (klass->marshalbyref) { g_assert (!is_static); ADD_CODE(&td, mt == MINT_TYPE_VT ? MINT_LDRMFLD_VT : MINT_LDRMFLD); ADD_CODE(&td, get_data_item_index (&td, field)); - } else { + } else +#endif + { if (is_static) { ADD_CODE (&td, MINT_POP); ADD_CODE (&td, 0); @@ -2057,16 +2609,19 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_STFLD: { CHECK_STACK (&td, 2); token = read32 (td.ip + 1); - field = mono_field_from_token (image, token, &klass, generic_context); + field = interp_field_from_token (method, token, &klass, generic_context); gboolean is_static = !!(field->type->attrs & FIELD_ATTRIBUTE_STATIC); mono_class_init (klass); mt = mint_type(field->type); +#ifndef DISABLE_REMOTING if (klass->marshalbyref) { g_assert (!is_static); ADD_CODE(&td, mt == MINT_TYPE_VT ? MINT_STRMFLD_VT : MINT_STRMFLD); ADD_CODE(&td, get_data_item_index (&td, field)); - } else { + } else +#endif + { if (is_static) { ADD_CODE (&td, MINT_POP); ADD_CODE (&td, 1); @@ -2089,7 +2644,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo } case CEE_LDSFLDA: token = read32 (td.ip + 1); - field = mono_field_from_token (image, token, &klass, generic_context); + field = interp_field_from_token (method, token, &klass, generic_context); ADD_CODE(&td, MINT_LDSFLDA); ADD_CODE(&td, get_data_item_index (&td, field)); td.ip += 5; @@ -2097,7 +2652,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo break; case CEE_LDSFLD: token = read32 (td.ip + 1); - field = mono_field_from_token (image, token, &klass, generic_context); + field = interp_field_from_token (method, token, &klass, generic_context); mt = mint_type(field->type); ADD_CODE(&td, mt == MINT_TYPE_VT ? MINT_LDSFLD_VT : MINT_LDSFLD); ADD_CODE(&td, get_data_item_index (&td, field)); @@ -2118,7 +2673,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_STSFLD: CHECK_STACK (&td, 1); token = read32 (td.ip + 1); - field = mono_field_from_token (image, token, &klass, generic_context); + field = interp_field_from_token (method, token, &klass, generic_context); mt = mint_type(field->type); ADD_CODE(&td, mt == MINT_TYPE_VT ? MINT_STSFLD_VT : MINT_STSFLD); ADD_CODE(&td, get_data_item_index (&td, field)); @@ -2138,7 +2693,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo if (method->wrapper_type != MONO_WRAPPER_NONE) klass = (MonoClass *)mono_method_get_wrapper_data (method, token); else - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); ADD_CODE(&td, td.sp [-1].type == STACK_TYPE_VT ? MINT_STOBJ_VT : MINT_STOBJ); ADD_CODE(&td, get_data_item_index (&td, klass)); @@ -2170,6 +2725,9 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case STACK_TYPE_I4: #if SIZEOF_VOID_P == 8 ADD_CODE(&td, MINT_CONV_I8_U4); +#elif SIZEOF_VOID_P == 4 + if (*td.ip == CEE_CONV_OVF_I_UN) + ADD_CODE(&td, MINT_CONV_OVF_I4_U4); #endif break; default: @@ -2207,7 +2765,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo if (method->wrapper_type != MONO_WRAPPER_NONE) klass = (MonoClass *)mono_method_get_wrapper_data (method, token); else - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); if (mono_class_is_nullable (klass)) { MonoMethod *target_method = mono_class_get_method_from_name (klass, "Box", 1); @@ -2238,7 +2796,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo if (method->wrapper_type != MONO_WRAPPER_NONE) klass = (MonoClass *)mono_method_get_wrapper_data (method, token); else - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); unsigned char lentype = (td.sp - 1)->type; if (lentype == STACK_TYPE_I8) { @@ -2268,7 +2826,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo if (method->wrapper_type != MONO_WRAPPER_NONE) klass = (MonoClass *) mono_method_get_wrapper_data (method, token); else - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) { ADD_CODE (&td, MINT_LDELEMA_TC); @@ -2364,7 +2922,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_LDELEM: CHECK_STACK (&td, 2); token = read32 (td.ip + 1); - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); switch (mint_type (&klass->byval_arg)) { case MINT_TYPE_I1: ENSURE_I4 (&td, 1); @@ -2494,11 +3052,14 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo CHECK_STACK (&td, 3); ENSURE_I4 (&td, 2); token = read32 (td.ip + 1); - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); switch (mint_type (&klass->byval_arg)) { case MINT_TYPE_U1: SIMPLE_OP (td, MINT_STELEM_U1); break; + case MINT_TYPE_U2: + SIMPLE_OP (td, MINT_STELEM_U2); + break; case MINT_TYPE_I4: SIMPLE_OP (td, MINT_STELEM_I4); break; @@ -2536,12 +3097,37 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo #if SIZEOF_VOID_P == 8 case CEE_CONV_OVF_U: #endif - case CEE_REFANYVAL: ves_abort(); break; #endif case CEE_CKFINITE: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_CKFINITE); break; + case CEE_MKREFANY: + CHECK_STACK (&td, 1); + ENSURE_I4 (&td, 2); + token = read32 (td.ip + 1); + klass = mini_get_class (method, token, generic_context); + + ADD_CODE (&td, MINT_MKREFANY); + ADD_CODE (&td, get_data_item_index (&td, klass)); + + td.ip += 5; + PUSH_VT (&td, sizeof (MonoTypedRef)); + SET_TYPE(td.sp - 1, STACK_TYPE_VT, mono_defaults.typed_reference_class); + break; + case CEE_REFANYVAL: { + CHECK_STACK (&td, 1); + ENSURE_I4 (&td, 2); + token = read32 (td.ip + 1); + + ADD_CODE (&td, MINT_REFANYVAL); + + POP_VT (&td, sizeof (MonoTypedRef)); + SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_MP); + + td.ip += 5; + break; + } case CEE_CONV_OVF_I1: case CEE_CONV_OVF_I1_UN: CHECK_STACK (&td, 1); @@ -2717,9 +3303,15 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo klass = (MonoClass *) mono_method_get_wrapper_data (method, token + 1); if (klass == mono_defaults.typehandle_class) handle = &((MonoClass *) handle)->byval_arg; + + if (generic_context) { + handle = mono_class_inflate_generic_type_checked (handle, generic_context, &error); + mono_error_cleanup (&error); /* FIXME: don't swallow the error */ + } } else { handle = mono_ldtoken (image, token, &klass, generic_context); } + mono_class_init (klass); mt = mint_type (&klass->byval_arg); g_assert (mt == MINT_TYPE_VT); size = mono_class_value_size (klass, NULL); @@ -2758,7 +3350,10 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo ++td.ip; break; case CEE_ENDFINALLY: + g_assert (td.clause_indexes [in_offset] != -1); + td.sp = td.stack; SIMPLE_OP (td, MINT_ENDFINALLY); + ADD_CODE (&td, td.clause_indexes [in_offset]); generating_code = 0; break; case CEE_LEAVE: @@ -2919,6 +3514,21 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo g_assert(klass->valuetype); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_MP); break; + case CEE_MONO_TLS: { + gint32 key = read32 (td.ip + 1); + td.ip += 5; + g_assert (key < TLS_KEY_NUM); + ADD_CODE (&td, MINT_MONO_TLS); + WRITE32 (&td, &key); + PUSH_SIMPLE_TYPE (&td, STACK_TYPE_MP); + break; + } + case CEE_MONO_ATOMIC_STORE_I4: + CHECK_STACK (&td, 2); + SIMPLE_OP (td, MINT_MONO_ATOMIC_STORE_I4); + td.sp -= 2; + td.ip++; + break; case CEE_MONO_SAVE_LMF: case CEE_MONO_RESTORE_LMF: case CEE_MONO_NOT_TAKEN: @@ -2930,6 +3540,14 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo PUSH_TYPE (&td, STACK_TYPE_MP, NULL); ++td.ip; break; + case CEE_MONO_JIT_ATTACH: + ADD_CODE (&td, MINT_MONO_JIT_ATTACH); + ++td.ip; + break; + case CEE_MONO_JIT_DETACH: + ADD_CODE (&td, MINT_MONO_JIT_DETACH); + ++td.ip; + break; default: g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td.ip, td.ip-header->code); } @@ -3033,14 +3651,8 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo break; case CEE_LDARGA: { int n = read16 (td.ip + 1); - if (n == 0 && signature->hasthis) { - g_error ("LDTHISA: NOPE"); - ADD_CODE(&td, MINT_LDTHISA); - } - else { - ADD_CODE(&td, MINT_LDARGA); - ADD_CODE(&td, td.rtm->arg_offsets [n]); /* FIX for large offsets */ - } + ADD_CODE (&td, MINT_LDARGA); + ADD_CODE (&td, td.rtm->arg_offsets [n]); /* FIX for large offsets */ PUSH_SIMPLE_TYPE(&td, STACK_TYPE_MP); td.ip += 3; break; @@ -3077,11 +3689,13 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo break; #if 0 case CEE_UNUSED57: ves_abort(); break; - case CEE_ENDFILTER: ves_abort(); break; #endif - case CEE_UNALIGNED_: + case CEE_ENDFILTER: + ADD_CODE (&td, MINT_ENDFILTER); ++td.ip; - /* FIX: should do something? */; + break; + case CEE_UNALIGNED_: + td.ip += 2; break; case CEE_VOLATILE_: ++td.ip; @@ -3094,7 +3708,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_INITOBJ: CHECK_STACK(&td, 1); token = read32 (td.ip + 1); - klass = mono_class_get_full (image, token, generic_context); + klass = mini_get_class (method, token, generic_context); if (klass->valuetype) { ADD_CODE (&td, MINT_INITOBJ); i32 = mono_class_value_size (klass, NULL); @@ -3119,7 +3733,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo break; case CEE_CONSTRAINED_: token = read32 (td.ip + 1); - constrained_class = mono_class_get_full (image, token, generic_context); + constrained_class = mini_get_class (method, token, generic_context); mono_class_init (constrained_class); td.ip += 5; break; @@ -3127,6 +3741,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo CHECK_STACK(&td, 3); ADD_CODE(&td, MINT_INITBLK); td.sp -= 3; + td.ip += 1; break; #if 0 case CEE_NO_: @@ -3134,10 +3749,14 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo ip += 2; break; #endif - case CEE_RETHROW: + case CEE_RETHROW: { + int clause_index = td.clause_indexes [in_offset]; + g_assert (clause_index != -1); SIMPLE_OP (td, MINT_RETHROW); + ADD_CODE (&td, rtm->exvar_offsets [clause_index]); generating_code = 0; break; + } case CEE_SIZEOF: { gint32 size; token = read32 (td.ip + 1); @@ -3148,7 +3767,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo size = mono_type_size (type, &align); } else { int align; - MonoClass *szclass = mono_class_get_full (image, token, generic_context); + MonoClass *szclass = mini_get_class (method, token, generic_context); mono_class_init (szclass); #if 0 if (!szclass->valuetype) @@ -3161,9 +3780,13 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo PUSH_SIMPLE_TYPE(&td, STACK_TYPE_I4); break; } -#if 0 - case CEE_REFANYTYPE: ves_abort(); break; -#endif + case CEE_REFANYTYPE: + ADD_CODE (&td, MINT_REFANYTYPE); + td.ip += 1; + POP_VT (&td, sizeof (MonoTypedRef)); + PUSH_VT (&td, sizeof (gpointer)); + SET_TYPE(td.sp - 1, STACK_TYPE_VT, NULL); + break; default: g_error ("transform.c: Unimplemented opcode: 0xFE %02x (%s) at 0x%x\n", *td.ip, mono_opcode_name (256 + *td.ip), td.ip-header->code); } @@ -3180,9 +3803,40 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo td.last_ip = td.in_start; } - if (mono_interp_traceopt) { + /* Handle relocations */ + for (int i = 0; i < td.relocs->len; ++i) { + Reloc *reloc = g_ptr_array_index (td.relocs, i); + + int offset = td.in_offsets [reloc->target] - reloc->offset; + + switch (reloc->type) { + case RELOC_SHORT_BRANCH: + g_assert (td.new_code [reloc->offset + 1] == 0xffff); + td.new_code [reloc->offset + 1] = offset; + break; + case RELOC_LONG_BRANCH: { + guint16 *v = (guint16 *) &offset; + g_assert (td.new_code [reloc->offset + 1] == 0xbeef); + g_assert (td.new_code [reloc->offset + 2] == 0xdead); + td.new_code [reloc->offset + 1] = *(guint16 *) v; + td.new_code [reloc->offset + 2] = *(guint16 *) (v + 1); + break; + } + case RELOC_SWITCH: { + guint16 *v = (guint16*)&offset; + td.new_code [reloc->offset] = *(guint16*)v; + td.new_code [reloc->offset + 1] = *(guint16*)(v + 1); + break; + } + default: + g_assert_not_reached (); + break; + } + } + + if (td.verbose_level) { const guint16 *p = td.new_code; - printf("Runtime method: %p, VT stack size: %d\n", rtm, td.max_vt_sp); + printf("Runtime method: %s %p, VT stack size: %d\n", mono_method_full_name (method, TRUE), rtm, td.max_vt_sp); printf("Calculated stack size: %d, stated size: %d\n", td.max_stack_height, header->max_stack); while (p < td.new_ip) { p = mono_interp_dis_mintop(td.new_code, p); @@ -3191,9 +3845,11 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo } g_assert (td.max_stack_height <= (header->max_stack + 1)); - rtm->clauses = mono_mempool_alloc (domain->mp, header->num_clauses * sizeof(MonoExceptionClause)); + int code_len = td.new_ip - td.new_code; + + rtm->clauses = mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause)); memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause)); - rtm->code = mono_mempool_alloc (domain->mp, (td.new_ip - td.new_code) * sizeof(gushort)); + rtm->code = mono_domain_alloc0 (domain, (td.new_ip - td.new_code) * sizeof (gushort)); memcpy (rtm->code, td.new_code, (td.new_ip - td.new_code) * sizeof(gushort)); g_free (td.new_code); rtm->new_body_start = rtm->code + body_start_offset; @@ -3206,13 +3862,43 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo end_off = c->handler_offset + c->handler_len; c->handler_offset = td.in_offsets [c->handler_offset]; c->handler_len = td.in_offsets [end_off] - c->handler_offset; + if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) + c->data.filter_offset = td.in_offsets [c->data.filter_offset]; } rtm->vt_stack_size = td.max_vt_sp; rtm->alloca_size = rtm->locals_size + rtm->args_size + rtm->vt_stack_size + rtm->stack_size; - rtm->data_items = mono_mempool_alloc (domain->mp, td.n_data_items * sizeof (td.data_items [0])); + rtm->data_items = mono_domain_alloc0 (domain, td.n_data_items * sizeof (td.data_items [0])); memcpy (rtm->data_items, td.data_items, td.n_data_items * sizeof (td.data_items [0])); + + /* Save debug info */ + interp_save_debug_info (rtm, header, &td, line_numbers); + + /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */ + int jinfo_len = mono_jit_info_size (0, header->num_clauses, 0); + MonoJitInfo *jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len); + jinfo->is_interp = 1; + rtm->jinfo = jinfo; + mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len, 0, header->num_clauses, 0); + for (i = 0; i < jinfo->num_clauses; ++i) { + MonoJitExceptionInfo *ei = &jinfo->clauses [i]; + MonoExceptionClause *c = rtm->clauses + i; + + ei->flags = c->flags; + ei->try_start = (guint8*)(rtm->code + c->try_offset); + ei->try_end = (guint8*)(rtm->code + c->try_offset + c->try_len); + ei->handler_start = (guint8*)(rtm->code + c->handler_offset); + ei->exvar_offset = rtm->exvar_offsets [i]; + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + ei->data.filter = (guint8*)(rtm->code + c->data.filter_offset); + } else if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) { + } else { + ei->data.catch_class = c->data.catch_class; + } + } + + save_seq_points (&td); + g_free (td.in_offsets); - g_free (td.forward_refs); for (i = 0; i < header->code_size; ++i) g_free (td.stack_state [i]); g_free (td.stack_state); @@ -3221,6 +3907,11 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo g_free (td.data_items); g_free (td.stack); g_hash_table_destroy (td.data_hash); + g_free (td.clause_indexes); + g_ptr_array_free (td.seq_points, TRUE); + g_array_free (line_numbers, TRUE); + g_ptr_array_free (td.relocs, TRUE); + mono_mempool_destroy (td.mempool); } static mono_mutex_t calc_section; @@ -3243,12 +3934,12 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont const MonoOpcode *opcode; MonoMethod *m; MonoClass *class; - MonoDomain *domain = mono_domain_get (); unsigned char *is_bb_start; int in; MonoVTable *method_class_vt; int backwards; MonoGenericContext *generic_context = NULL; + MonoDomain *domain = runtime_method->domain; // g_printerr ("TRANSFORM(0x%016lx): begin %s::%s\n", mono_thread_current (), method->klass->name, method->name); method_class_vt = mono_class_vtable (domain, runtime_method->method->klass); @@ -3291,8 +3982,7 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont mono_os_mutex_lock(&calc_section); if (runtime_method->transformed) { mono_os_mutex_unlock(&calc_section); - g_error ("FIXME: no jit info?"); - mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_OK); + mono_profiler_method_end_jit (method, runtime_method->jinfo, MONO_PROFILE_OK); return NULL; } @@ -3315,7 +4005,7 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont } else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) { nm = mono_marshal_get_delegate_end_invoke (method); } - } + } if (nm == NULL) { runtime_method->code = g_malloc(sizeof(short)); runtime_method->code[0] = MINT_CALLRUN; @@ -3332,6 +4022,22 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont method = nm; header = mono_method_get_header (nm); mono_os_mutex_unlock(&calc_section); + } else if (method->klass == mono_defaults.array_class) { + if (!strcmp (method->name, "UnsafeMov") || !strcmp (method->name, "UnsafeLoad")) { + mono_os_mutex_lock (&calc_section); + if (!runtime_method->transformed) { + runtime_method->code = g_malloc (sizeof (short)); + runtime_method->code[0] = MINT_CALLRUN; + runtime_method->stack_size = sizeof (stackval); /* for tracing */ + runtime_method->alloca_size = runtime_method->stack_size; + runtime_method->transformed = TRUE; + } + mono_os_mutex_unlock(&calc_section); + mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_OK); + return NULL; + } else if (!strcmp (method->name, "UnsafeStore")) { + g_error ("TODO"); + } } g_assert ((signature->param_count + signature->hasthis) < 1000); g_assert (header->max_stack < 10000); @@ -3363,7 +4069,7 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont break; case MonoInlineType: if (method->wrapper_type == MONO_WRAPPER_NONE) { - class = mono_class_get_full (image, read32 (ip + 1), generic_context); + class = mini_get_class (method, read32 (ip + 1), generic_context); mono_class_init (class); /* quick fix to not do this for the fake ptr classes - probably should not be getting the vtable at all here */ #if 0 @@ -3451,12 +4157,12 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont if (runtime_method->transformed) { mono_os_mutex_unlock(&calc_section); g_free (is_bb_start); - mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_OK); + mono_profiler_method_end_jit (method, runtime_method->jinfo, MONO_PROFILE_OK); return NULL; } runtime_method->local_offsets = g_malloc (header->num_locals * sizeof(guint32)); - runtime_method->stack_size = (sizeof (stackval) + 2) * header->max_stack; /* + 1 for returns of called functions + 1 for 0-ing in trace*/ + runtime_method->stack_size = (sizeof (stackval)) * (header->max_stack + 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/ runtime_method->stack_size = (runtime_method->stack_size + 7) & ~7; offset = 0; for (i = 0; i < header->num_locals; ++i) { @@ -3467,6 +4173,14 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont offset += size; } offset = (offset + 7) & ~7; + + runtime_method->exvar_offsets = g_malloc (header->num_clauses * sizeof (guint32)); + for (i = 0; i < header->num_clauses; i++) { + offset += sizeof (MonoObject*); + runtime_method->exvar_offsets [i] = offset; + } + offset = (offset + 7) & ~7; + runtime_method->locals_size = offset; g_assert (runtime_method->locals_size < 65536); offset = 0; @@ -3502,7 +4216,8 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont g_free (is_bb_start); - mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_OK); + // FIXME: Add a different callback ? + mono_profiler_method_end_jit (method, runtime_method->jinfo, MONO_PROFILE_OK); runtime_method->transformed = TRUE; mono_os_mutex_unlock(&calc_section);