X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Finterp%2Ftransform.c;h=02a92f271c83a99ee29e82aac9cd2be02629a25f;hb=ea4e4a9ef6fc42570a23026adbe826cf7248290e;hp=1b1776867fcc1bfe1b585db11a1e008a96c4ea3f;hpb=a3304910e9e08ec965fb06ef6fa4a19257dddf8c;p=mono.git diff --git a/mono/mini/interp/transform.c b/mono/mini/interp/transform.c index 1b1776867fc..02a92f271c8 100644 --- a/mono/mini/interp/transform.c +++ b/mono/mini/interp/transform.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -34,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; @@ -45,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; @@ -64,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 @@ -149,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; @@ -167,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); @@ -258,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) { @@ -331,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; @@ -562,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)); @@ -636,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) { @@ -675,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); @@ -701,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 */ @@ -772,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); @@ -795,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); @@ -858,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); @@ -865,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 { @@ -882,17 +974,381 @@ 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; +} + +#define BARRIER_IF_VOLATILE(td) \ + do { \ + if (volatile_) { \ + ADD_CODE (&td, MINT_MONO_MEMORY_BARRIER); \ + volatile_ = FALSE; \ + } \ + } while (0) + 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; gboolean readonly = FALSE; + gboolean volatile_ = FALSE; MonoClass *klass; MonoClassField *field; const unsigned char *end; @@ -902,8 +1358,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; @@ -913,8 +1382,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)); @@ -922,11 +1391,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; @@ -934,6 +1454,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; @@ -944,12 +1466,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); @@ -973,6 +1511,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; @@ -982,34 +1526,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) @@ -1025,7 +1547,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 ? "<>" : @@ -1036,6 +1558,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 */ @@ -1216,7 +1758,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; @@ -1235,6 +1790,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 { @@ -1354,15 +1916,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++) { @@ -1373,16 +1932,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; @@ -1393,95 +1955,114 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_I1); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_I4); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_U1: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_U1); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_I4); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_I2: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_I2); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_I4); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_U2: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_U2); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_I4); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_I4: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_I4); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_I4); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_U4: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_U4); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_I4); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_I8: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_I8); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_I8); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_I: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_I); ADD_CODE (&td, 0); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_I); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_R4: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_R4); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_R8); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_R8: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_R8); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_R8); + BARRIER_IF_VOLATILE (td); break; case CEE_LDIND_REF: CHECK_STACK (&td, 1); SIMPLE_OP (td, MINT_LDIND_REF); + BARRIER_IF_VOLATILE (td); SET_SIMPLE_TYPE(td.sp - 1, STACK_TYPE_O); break; case CEE_STIND_REF: CHECK_STACK (&td, 2); + BARRIER_IF_VOLATILE (td); SIMPLE_OP (td, MINT_STIND_REF); td.sp -= 2; break; case CEE_STIND_I1: CHECK_STACK (&td, 2); + BARRIER_IF_VOLATILE (td); SIMPLE_OP (td, MINT_STIND_I1); td.sp -= 2; break; case CEE_STIND_I2: CHECK_STACK (&td, 2); + BARRIER_IF_VOLATILE (td); SIMPLE_OP (td, MINT_STIND_I2); td.sp -= 2; break; case CEE_STIND_I4: CHECK_STACK (&td, 2); + BARRIER_IF_VOLATILE (td); SIMPLE_OP (td, MINT_STIND_I4); td.sp -= 2; break; case CEE_STIND_I: CHECK_STACK (&td, 2); + BARRIER_IF_VOLATILE (td); SIMPLE_OP (td, MINT_STIND_I); td.sp -= 2; break; case CEE_STIND_I8: CHECK_STACK (&td, 2); + BARRIER_IF_VOLATILE (td); SIMPLE_OP (td, MINT_STIND_I8); td.sp -= 2; break; case CEE_STIND_R4: CHECK_STACK (&td, 2); + BARRIER_IF_VOLATILE (td); SIMPLE_OP (td, MINT_STIND_R4); td.sp -= 2; break; case CEE_STIND_R8: CHECK_STACK (&td, 2); + BARRIER_IF_VOLATILE (td); SIMPLE_OP (td, MINT_STIND_R8); td.sp -= 2; break; @@ -1506,23 +2087,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: @@ -1834,18 +2415,20 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo } td.ip += 5; SET_TYPE(td.sp - 1, stack_type[mint_type(&klass->byval_arg)], klass); + BARRIER_IF_VOLATILE (td); break; } case CEE_LDSTR: { 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); @@ -1898,7 +2481,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; @@ -1907,7 +2490,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; @@ -1950,10 +2533,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); @@ -1988,7 +2571,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) { @@ -2011,17 +2594,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); @@ -2047,21 +2633,27 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo } td.ip += 5; SET_TYPE(td.sp - 1, stack_type [mt], field_klass); + BARRIER_IF_VOLATILE (td); break; } 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); + BARRIER_IF_VOLATILE (td); + +#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); @@ -2084,7 +2676,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; @@ -2092,7 +2684,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)); @@ -2113,7 +2705,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)); @@ -2133,8 +2725,9 @@ 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); + BARRIER_IF_VOLATILE (td); ADD_CODE(&td, td.sp [-1].type == STACK_TYPE_VT ? MINT_STOBJ_VT : MINT_STOBJ); ADD_CODE(&td, get_data_item_index (&td, klass)); if (td.sp [-1].type == STACK_TYPE_VT) { @@ -2165,6 +2758,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: @@ -2202,7 +2798,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); @@ -2233,7 +2829,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) { @@ -2263,7 +2859,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); @@ -2359,7 +2955,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); @@ -2489,11 +3085,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; @@ -2531,12 +3130,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); @@ -2759,7 +3383,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: @@ -2920,6 +3547,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: @@ -2931,6 +3573,24 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo PUSH_TYPE (&td, STACK_TYPE_MP, NULL); ++td.ip; break; + case CEE_MONO_MEMORY_BARRIER: + ADD_CODE (&td, MINT_MONO_MEMORY_BARRIER); + ++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; + case CEE_MONO_LDDOMAIN: + ADD_CODE (&td, MINT_MONO_LDDOMAIN); + td.sp [0].type = STACK_TYPE_I; + ++td.sp; + ++td.ip; + break; default: g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td.ip, td.ip-header->code); } @@ -3072,15 +3732,17 @@ 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; - /* FIX: should do something? */; + volatile_ = TRUE; break; case CEE_TAIL_: ++td.ip; @@ -3089,7 +3751,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); @@ -3104,7 +3766,10 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo case CEE_CPBLK: CHECK_STACK(&td, 3); /* FIX? convert length to I8? */ + if (volatile_) + ADD_CODE (&td, MINT_MONO_MEMORY_BARRIER); ADD_CODE(&td, MINT_CPBLK); + BARRIER_IF_VOLATILE (td); td.sp -= 3; ++td.ip; break; @@ -3114,25 +3779,29 @@ 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; case CEE_INITBLK: CHECK_STACK(&td, 3); + BARRIER_IF_VOLATILE (td); ADD_CODE(&td, MINT_INITBLK); td.sp -= 3; + td.ip += 1; break; -#if 0 case CEE_NO_: /* FIXME: implement */ - ip += 2; + td.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); @@ -3143,7 +3812,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) @@ -3156,9 +3825,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); } @@ -3175,9 +3848,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); @@ -3186,6 +3890,8 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo } g_assert (td.max_stack_height <= (header->max_stack + 1)); + 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_domain_alloc0 (domain, (td.new_ip - td.new_code) * sizeof (gushort)); @@ -3201,13 +3907,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_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); @@ -3216,6 +3952,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; @@ -3238,12 +3979,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); @@ -3271,7 +4012,7 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont context->current_env = old_env; } - mono_profiler_method_jit (method); /* sort of... */ + MONO_PROFILER_RAISE (jit_begin, (method)); if (mono_method_signature (method)->is_inflated) generic_context = mono_method_get_context (method); @@ -3286,7 +4027,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); - mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_OK); + MONO_PROFILER_RAISE (jit_done, (method, runtime_method->jinfo)); return NULL; } @@ -3320,14 +4061,14 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont 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); + MONO_PROFILER_RAISE (jit_done, (method, NULL)); return NULL; } 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")) { + 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)); @@ -3337,11 +4078,9 @@ mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *cont runtime_method->transformed = TRUE; } mono_os_mutex_unlock(&calc_section); - mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_OK); + MONO_PROFILER_RAISE (jit_done, (method, NULL)); return NULL; - } else if (!strcmp (method->name, "UnsafeStore)")) { - g_error ("TODO"); - } else if (!strcmp (method->name, "UnsafeLoad)")) { + } else if (!strcmp (method->name, "UnsafeStore")) { g_error ("TODO"); } } @@ -3375,7 +4114,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 @@ -3463,12 +4202,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_RAISE (jit_done, (method, runtime_method->jinfo)); 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) { @@ -3479,6 +4218,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; @@ -3514,7 +4261,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_RAISE (jit_done, (method, runtime_method->jinfo)); runtime_method->transformed = TRUE; mono_os_mutex_unlock(&calc_section);