From d3e00b478e6bfb3a591f250a5fb766ce8ce4e580 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 2 Mar 2021 12:10:41 +0200 Subject: [PATCH 01/17] [interp] Cleanup get_interp_local_offset Use it just for allocating an offset for a var, at the top of the locals space. --- src/mono/mono/mini/interp/transform.c | 33 +++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 848ad9c6aba76..749aa6dd5748e 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -1253,15 +1253,12 @@ create_interp_local (TransformData *td, MonoType *type) } static int -get_interp_local_offset (TransformData *td, int local, gboolean resolve_stack_locals) +get_interp_local_offset (TransformData *td, int local) { // FIXME MINT_PROF_EXIT when void if (local == -1) return -1; - if ((td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && !resolve_stack_locals) - return -1; - if (td->locals [local].offset != -1) return td->locals [local].offset; @@ -7301,7 +7298,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in *ip++ = opcode; if (opcode == MINT_SWITCH) { int labels = READ32 (&ins->data [0]); - *ip++ = get_interp_local_offset (td, ins->sregs [0], TRUE); + *ip++ = get_interp_local_offset (td, ins->sregs [0]); // Write number of switch labels *ip++ = ins->data [0]; *ip++ = ins->data [1]; @@ -7320,7 +7317,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK || opcode == MINT_CALL_HANDLER_S) { const int br_offset = start_ip - td->new_code; for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) - *ip++ = get_interp_local_offset (td, ins->sregs [i], TRUE); + *ip++ = get_interp_local_offset (td, ins->sregs [i]); if (ins->info.target_bb->native_offset >= 0) { // Backwards branch. We can already patch it. *ip++ = ins->info.target_bb->native_offset - br_offset; @@ -7341,7 +7338,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK || opcode == MINT_CALL_HANDLER) { const int br_offset = start_ip - td->new_code; for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) - *ip++ = get_interp_local_offset (td, ins->sregs [i], TRUE); + *ip++ = get_interp_local_offset (td, ins->sregs [i]); if (ins->info.target_bb->native_offset >= 0) { // Backwards branch. We can already patch it int target_offset = ins->info.target_bb->native_offset - br_offset; @@ -7407,14 +7404,14 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in #endif } else { if (mono_interp_op_dregs [opcode]) - *ip++ = get_interp_local_offset (td, ins->dreg, TRUE); + *ip++ = get_interp_local_offset (td, ins->dreg); if (mono_interp_op_sregs [opcode]) { for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) - *ip++ = get_interp_local_offset (td, ins->sregs [i], TRUE); + *ip++ = get_interp_local_offset (td, ins->sregs [i]); } else if (opcode == MINT_LDLOCA_S) { // This opcode receives a local but it is not viewed as a sreg since we don't load the value - *ip++ = get_interp_local_offset (td, ins->sregs [0], TRUE); + *ip++ = get_interp_local_offset (td, ins->sregs [0]); } int left = get_inst_length (ins) - (ip - start_ip); @@ -7431,15 +7428,21 @@ alloc_ins_locals (TransformData *td, InterpInst *ins) { int opcode = ins->opcode; if (mono_interp_op_sregs [opcode]) { - for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) - get_interp_local_offset (td, ins->sregs [i], FALSE); + for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { + int local = ins->sregs [i]; + if (!(td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK)) + get_interp_local_offset (td, local); + } } else if (opcode == MINT_LDLOCA_S) { // This opcode receives a local but it is not viewed as a sreg since we don't load the value - get_interp_local_offset (td, ins->sregs [0], FALSE); + get_interp_local_offset (td, ins->sregs [0]); } - if (mono_interp_op_dregs [opcode]) - get_interp_local_offset (td, ins->dreg, FALSE); + if (mono_interp_op_dregs [opcode]) { + int local = ins->dreg; + if (!(td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK)) + get_interp_local_offset (td, local); + } } // Generates the final code, after we are done with all the passes From 96378a93c78d977f3bcf32034c54d46cbc141573 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 11:42:09 +0200 Subject: [PATCH 02/17] [interp] Save the args inside call instructions This will aid later optimizations, in order to easily detect the call args for an opcode. This is stored as a -1 terminated array of var indexes. Also change the structure of newobj_reg_map array, so it can reuse this format (newobj_reg_map should be killed at some point anyway). --- src/mono/mono/mini/interp/transform.c | 105 ++++++++++++++++++++------ src/mono/mono/mini/interp/transform.h | 4 + 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 749aa6dd5748e..f27b0abf4ec57 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -1168,19 +1168,27 @@ interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *ta push_simple_type (td, STACK_TYPE_I); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); td->last_ins->data [0] = get_data_item_index (td, method); - td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; interp_add_ins (td, MINT_MONO_LDPTR); push_simple_type (td, STACK_TYPE_I); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); td->last_ins->data [0] = get_data_item_index (td, target_method); - td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; td->sp -= 2; + int *call_args = (int*)mono_mempool_alloc (td->mempool, 3 * sizeof (int)); + call_args [0] = td->sp [0].local; + td->locals [td->sp [0].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [1] = td->sp [1].local; + td->locals [td->sp [1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [2] = -1; + interp_add_ins (td, MINT_ICALL_PP_V); + // Allocate a dummy local to serve as dreg for this instruction + push_simple_type (td, STACK_TYPE_I4); + td->sp--; interp_ins_set_dreg (td->last_ins, td->sp [0].local); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); - + td->last_ins->info.call_args = call_args; } static void @@ -1194,6 +1202,7 @@ interp_generate_bie_throw (TransformData *td) td->sp--; interp_ins_set_dreg (td->last_ins, td->sp [0].local); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); + td->last_ins->info.call_args = NULL; } static void @@ -1207,6 +1216,7 @@ interp_generate_not_supported_throw (TransformData *td) td->sp--; interp_ins_set_dreg (td->last_ins, td->sp [0].local); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); + td->last_ins->info.call_args = NULL; } static void @@ -1232,13 +1242,21 @@ interp_generate_ipe_throw_with_msg (TransformData *td, MonoError *error_msg) interp_add_ins (td, MINT_MONO_LDPTR); push_simple_type (td, STACK_TYPE_I); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; td->last_ins->data [0] = get_data_item_index (td, msg); td->sp -= 1; + int *call_args = (int*)mono_mempool_alloc (td->mempool, 2 * sizeof (int)); + call_args [0] = td->sp [0].local; + td->locals [td->sp [0].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [1] = -1; + interp_add_ins (td, MINT_ICALL_P_V); + // Allocate a dummy local to serve as dreg for this instruction + push_simple_type (td, STACK_TYPE_I4); + td->sp--; interp_ins_set_dreg (td->last_ins, td->sp [0].local); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); + td->last_ins->info.call_args = call_args; } static int @@ -1710,7 +1728,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check MonoClass *element_class = m_class_get_element_class (array_class); int rank = m_class_get_rank (array_class); int size = mono_class_array_element_size (element_class); - gboolean call_args = FALSE; + gboolean is_call_args = FALSE; gboolean bounded = m_class_get_byval_arg (array_class) ? m_class_get_byval_arg (array_class)->type == MONO_TYPE_ARRAY : FALSE; @@ -1724,24 +1742,34 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check td->last_ins->data [0] = size; } else { interp_add_ins (td, MINT_LDELEMA); - for (int i = 0; i < rank + 1; i++) + int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); + for (int i = 0; i < rank + 1; i++) { td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [i] = td->sp [i].local; + } + call_args [rank + 1] = -1; td->last_ins->data [0] = rank; g_assert (size < G_MAXUINT16); td->last_ins->data [1] = size; - call_args = TRUE; + td->last_ins->info.call_args = call_args; + is_call_args = TRUE; } } else { interp_add_ins (td, MINT_LDELEMA_TC); - for (int i = 0; i < rank + 1; i++) + int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); + for (int i = 0; i < rank + 1; i++) { td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [i] = td->sp [i].local; + } + call_args [rank + 1] = -1; td->last_ins->data [0] = get_data_item_index (td, check_class); - call_args = TRUE; + td->last_ins->info.call_args = call_args; + is_call_args = TRUE; } push_simple_type (td, STACK_TYPE_MP); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - if (call_args) + if (is_call_args) td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; } @@ -3014,11 +3042,18 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target td->sp -= csignature->param_count + !!csignature->hasthis; guint32 params_stack_size = tos_offset - get_tos_offset (td); + int *call_args = NULL; + if (op == -1 || mono_interp_op_dregs [op] == MINT_CALL_ARGS) { // We must not optimize out these locals, storing to them is part of the interp call convention // unless we already intrinsified this call - for (int i = 0; i < (csignature->param_count + !!csignature->hasthis); i++) + int num_args = csignature->param_count + !!csignature->hasthis; + call_args = (int*) mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); + for (int i = 0; i < num_args; i++) { td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [i] = td->sp [i].local; + } + call_args [num_args] = -1; } // We overwrite it with the return local, save it for future use @@ -3181,6 +3216,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } } td->ip += 5; + td->last_ins->info.call_args = call_args; return TRUE; } @@ -5346,9 +5382,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_conv (td, td->sp - 1, NULL, stack_type [ret_mt], MINT_CONV_OVF_I4_I8); #endif } else if (m_class_get_parent (klass) == mono_defaults.array_class) { + int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); td->sp -= csignature->param_count; - for (int i = 0; i < csignature->param_count; i++) + for (int i = 0; i < csignature->param_count; i++) { td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [i] = td->sp [i].local; + } + call_args [csignature->param_count] = -1; interp_add_ins (td, MINT_NEWOBJ_ARRAY); td->last_ins->data [0] = get_data_item_index (td, m->klass); @@ -5356,13 +5396,18 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type (td, stack_type [ret_mt], klass); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + td->last_ins->info.call_args = call_args; } else if (klass == mono_defaults.string_class) { + int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); guint32 tos_offset = get_tos_offset (td); td->sp -= csignature->param_count; guint32 params_stack_size = tos_offset - get_tos_offset (td); - for (int i = 0; i < csignature->param_count; i++) + for (int i = 0; i < csignature->param_count; i++) { td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [i] = td->sp [i].local; + } + call_args [csignature->param_count] = -1; interp_add_ins (td, MINT_NEWOBJ_STRING); td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (m, error)); @@ -5370,6 +5415,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type (td, stack_type [ret_mt], klass); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + td->last_ins->info.call_args = call_args; } else if (m_class_get_image (klass) == mono_defaults.corlib && !strcmp (m_class_get_name (m->klass), "ByReference`1") && !strcmp (m->name, ".ctor")) { @@ -5399,14 +5445,17 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, guint32 params_stack_size = tos_offset - get_tos_offset (td); // Move params types in temporary buffer - // FIXME stop leaking sp_params - StackInfo *sp_params = (StackInfo*) g_malloc (sizeof (StackInfo) * csignature->param_count); + StackInfo *sp_params = (StackInfo*) mono_mempool_alloc (td->mempool, sizeof (StackInfo) * csignature->param_count); memcpy (sp_params, td->sp, sizeof (StackInfo) * csignature->param_count); // We must not optimize out these locals, storing to them is part of the interp call convention // FIXME this affects inlining efficiency. We need to first remove the param moving by NEWOBJ - for (int i = 0; i < csignature->param_count; i++) + int *call_args = (int*) mono_mempool_alloc (td->mempool, csignature->param_count * sizeof (int)); + for (int i = 0; i < csignature->param_count; i++) { td->locals [sp_params [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [i] = sp_params [i].local; + } + call_args [csignature->param_count] = -1; // Push the return value and `this` argument to the ctor gboolean is_vt = m_class_is_valuetype (klass); @@ -5453,10 +5502,11 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // Add local mapping information for cprop to use, in case we inline int param_count = csignature->param_count; - int *newobj_reg_map = (int*)mono_mempool_alloc (td->mempool, sizeof (int) * param_count * 2); + int *newobj_reg_map = (int*)mono_mempool_alloc (td->mempool, sizeof (int) * (param_count * 2 + 1)); + newobj_reg_map [param_count] = -1; for (int i = 0; i < param_count; i++) { - newobj_reg_map [2 * i] = sp_params [i].local; - newobj_reg_map [2 * i + 1] = td->sp [-param_count + i].local; + newobj_reg_map [i] = sp_params [i].local; + newobj_reg_map [i + 1 + param_count] = td->sp [-param_count + i].local; } if (interp_inline_method (td, m, mheader, error)) { @@ -5477,6 +5527,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (m, error)); td->last_ins->data [1] = params_stack_size; } + td->last_ins->info.call_args = call_args; goto_if_nok (error, exit); // Parameters and this pointer are popped of the stack. The return value remains td->sp -= csignature->param_count + 1; @@ -6003,12 +6054,17 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, CHECK_TYPELOAD (klass); interp_add_ins (td, MINT_LDELEMA_TC); td->sp -= 2; + int *call_args = (int*)mono_mempool_alloc (td->mempool, 3 * sizeof (int)); + call_args [0] = td->sp [0].local; td->locals [td->sp [0].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [1] = td->sp [1].local; td->locals [td->sp [1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [2] = -1; push_simple_type (td, STACK_TYPE_MP); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; td->last_ins->data [0] = get_data_item_index (td, klass); + td->last_ins->info.call_args = call_args; } else { interp_add_ins (td, MINT_LDELEMA1); td->sp -= 2; @@ -6620,8 +6676,12 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, CHECK_STACK (td, info->sig->param_count); td->sp -= info->sig->param_count; - for (int i = 0; i < info->sig->param_count; i++) + int *call_args = (int*)mono_mempool_alloc (td->mempool, (info->sig->param_count + 1) * sizeof (int)); + for (int i = 0; i < info->sig->param_count; i++) { td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + call_args [i] = td->sp [i].local; + } + call_args [info->sig->param_count] = -1; if (!MONO_TYPE_IS_VOID (info->sig->ret)) { int mt = mint_type (info->sig->ret); push_simple_type (td, stack_type [mt]); @@ -6646,6 +6706,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // hash here is overkill interp_ins_set_dreg (td->last_ins, dreg); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); + td->last_ins->info.call_args = call_args; } break; } @@ -8112,8 +8173,8 @@ interp_cprop (TransformData *td) int param_count = ins->data [3]; int *newobj_reg_map = ins->info.newobj_reg_map; for (int i = 0; i < param_count; i++) { - int src = newobj_reg_map [2 * i]; - int dst = newobj_reg_map [2 * i + 1]; + int src = newobj_reg_map [i]; + int dst = newobj_reg_map [i + 1 + param_count]; local_defs [dst] = local_defs [src]; local_defs [dst].ins = NULL; } diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index bc31c6f2daf9d..2ebb0401d02b2 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -70,6 +70,10 @@ struct _InterpInst { union { InterpBasicBlock *target_bb; InterpBasicBlock **target_bb_table; + // For CallArgs instruction, this represents an array of all call arg vars + // in the order they are pushed to the stack. This makes it easy to find + // all source vars for these types of opcodes. This is terminated with -1. + int *call_args; // We handle newobj poorly due to not having our own local offset allocator. // We temporarily use this array to let cprop know the values of the newobj args. int *newobj_reg_map; From 7f717e93d030f1d49fa4261156571790f4eabe6f Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 11:45:29 +0200 Subject: [PATCH 03/17] [interp] Pass target_ip in a normal var to MINT_CALLI_NAT_FAST Making it consistent with other calli opcodes and simplifies a little bit the code generation path. --- src/mono/mono/mini/interp/interp.c | 12 ++++++------ src/mono/mono/mini/interp/mintops.def | 2 +- src/mono/mono/mini/interp/transform.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 89bd0bed3c92b..bec4445c538ec 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -3398,19 +3398,19 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs goto call; } MINT_IN_CASE(MINT_CALLI_NAT_FAST) { - MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [2]]; - int opcode = ip [3]; - gboolean save_last_error = ip [4]; + MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [3]]; + int opcode = ip [4]; + gboolean save_last_error = ip [5]; stackval *args = (stackval*)(locals + ip [1]); - gpointer target_ip = args [csignature->param_count].data.p; + gpointer target_ip = LOCAL_VAR (ip [2], gpointer); /* for calls, have ip pointing at the start of next instruction */ - frame->state.ip = ip + 5; + frame->state.ip = ip + 6; do_icall_wrapper (frame, csignature, opcode, args, target_ip, save_last_error); EXCEPTION_CHECKPOINT_GC_UNSAFE; CHECK_RESUME_STATE (context); - ip += 5; + ip += 6; MINT_IN_BREAK; } MINT_IN_CASE(MINT_CALLI_NAT_DYNAMIC) { diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 04dfb90a2a56e..f4bb2d7bbd6ee 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -612,7 +612,7 @@ OPDEF(MINT_CALL_DELEGATE, "call.delegate", 4, CallArgs, 0, MintOpTwoShorts) OPDEF(MINT_CALLI, "calli", 4, CallArgs, 1, MintOpMethodToken) OPDEF(MINT_CALLI_NAT, "calli.nat", 7, CallArgs, 1, MintOpMethodToken) OPDEF(MINT_CALLI_NAT_DYNAMIC, "calli.nat.dynamic", 4, CallArgs, 1, MintOpMethodToken) -OPDEF(MINT_CALLI_NAT_FAST, "calli.nat.fast", 5, CallArgs, 0, MintOpMethodToken) +OPDEF(MINT_CALLI_NAT_FAST, "calli.nat.fast", 6, CallArgs, 1, MintOpMethodToken) OPDEF(MINT_CALL_VARARG, "call.vararg", 5, CallArgs, 0, MintOpMethodToken) OPDEF(MINT_CALLRUN, "callrun", 4, CallArgs, 0, MintOpNoArgs) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index f27b0abf4ec57..21caccf441da1 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -3035,7 +3035,6 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (calli) { --td->sp; fp_sreg = td->sp [0].local; - td->locals [fp_sreg].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; } guint32 tos_offset = get_tos_offset (td); @@ -3141,6 +3140,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (op != -1) { interp_add_ins (td, MINT_CALLI_NAT_FAST); interp_ins_set_dreg (td->last_ins, dreg); + interp_ins_set_sreg (td->last_ins, fp_sreg); td->last_ins->data [0] = get_data_item_index (td, (void *)csignature); td->last_ins->data [1] = op; td->last_ins->data [2] = save_last_error; From cb50602c532075f443912904b0240298e02578e6 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:07:40 +0200 Subject: [PATCH 04/17] [interp] Add explicit return for CallArgs opcodes Before this change, calls used to receive a single special dreg argument. This was resolved to an offset. At this offset, the call could find all the parameters and the return value was also written at the same offset. With this change we move towards having an explicit dreg return. For calls, the last sreg must be of the special type MINT_CALL_ARGS_SREG. The var offset allocator should ensure all call args are allocated one after the other and that this special reg type is resolved to the offset where these args reside. --- src/mono/mono/mini/interp/interp.c | 288 ++++++++++++++------------ src/mono/mono/mini/interp/mintops.def | 68 +++--- src/mono/mono/mini/interp/mintops.h | 1 + src/mono/mono/mini/interp/transform.c | 55 ++--- 4 files changed, 212 insertions(+), 200 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index bec4445c538ec..1953ec10bc52b 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -222,11 +222,12 @@ frame_data_allocator_pop (FrameDataAllocator *stack, InterpFrame *frame) * Reinitialize a frame. */ static void -reinit_frame (InterpFrame *frame, InterpFrame *parent, InterpMethod *imethod, gpointer stack) +reinit_frame (InterpFrame *frame, InterpFrame *parent, InterpMethod *imethod, gpointer retval, gpointer stack) { frame->parent = parent; frame->imethod = imethod; frame->stack = (stackval*)stack; + frame->retval = (stackval*)retval; frame->state.ip = NULL; } @@ -1092,7 +1093,7 @@ ves_array_get (InterpFrame *frame, stackval *sp, stackval *retval, MonoMethodSig } static MonoException* -ves_array_element_address (InterpFrame *frame, MonoClass *required_type, MonoArray *ao, stackval *sp, gboolean needs_typecheck) +ves_array_element_address (InterpFrame *frame, MonoClass *required_type, MonoArray *ao, gpointer *ret, stackval *sp, gboolean needs_typecheck) { MonoClass *ac = ((MonoObject *) ao)->vtable->klass; @@ -1105,7 +1106,7 @@ ves_array_element_address (InterpFrame *frame, MonoClass *required_type, MonoArr if (needs_typecheck && !mono_class_is_assignable_from_internal (m_class_get_element_class (mono_object_class ((MonoObject *) ao)), required_type)) return mono_get_exception_array_type_mismatch (); gint32 esize = mono_array_element_size (ac); - sp [-1].data.p = mono_array_addr_with_size_fast (ao, esize, pos); + *ret = mono_array_addr_with_size_fast (ao, esize, pos); return NULL; } @@ -1379,12 +1380,12 @@ static InterpMethodArguments* build_args_from_sig (MonoMethodSignature *sig, Int case MONO_TYPE_U8: case MONO_TYPE_VALUETYPE: case MONO_TYPE_GENERICINST: - margs->retval = &frame->stack->data.p; + margs->retval = (gpointer*)frame->retval; margs->is_float_ret = 0; break; case MONO_TYPE_R4: case MONO_TYPE_R8: - margs->retval = &frame->stack->data.p; + margs->retval = (gpointer*)frame->retval; margs->is_float_ret = 1; break; case MONO_TYPE_VOID: @@ -1404,9 +1405,9 @@ interp_frame_arg_to_data (MonoInterpFrameHandle frame, MonoMethodSignature *sig, InterpFrame *iframe = (InterpFrame*)frame; InterpMethod *imethod = iframe->imethod; - // If index == -1, we finished executing an InterpFrame and the result is at the bottom of the stack. + // If index == -1, we finished executing an InterpFrame and the result is at retval. if (index == -1) - stackval_to_data (sig->ret, iframe->stack, data, TRUE); + stackval_to_data (sig->ret, iframe->retval, data, TRUE); else if (sig->hasthis && index == 0) *(gpointer*)data = iframe->stack->data.p; else @@ -1421,7 +1422,7 @@ interp_data_to_frame_arg (MonoInterpFrameHandle frame, MonoMethodSignature *sig, // Get result from pinvoke call, put it directly on top of execution stack in the caller frame if (index == -1) - stackval_from_data (sig->ret, iframe->stack, data, TRUE); + stackval_from_data (sig->ret, iframe->retval, data, TRUE); else if (sig->hasthis && index == 0) iframe->stack->data.p = *(gpointer*)data; else @@ -1435,7 +1436,7 @@ interp_frame_arg_to_storage (MonoInterpFrameHandle frame, MonoMethodSignature *s InterpMethod *imethod = iframe->imethod; if (index == -1) - return iframe->stack; + return iframe->retval; else return STACK_ADD_BYTES (iframe->stack, get_arg_offset (imethod, sig, index)); } @@ -1475,6 +1476,7 @@ ves_pinvoke_method ( MonoFuncV addr, ThreadContext *context, InterpFrame *parent_frame, + stackval *ret_sp, stackval *sp, gboolean save_last_error, gpointer *cache) @@ -1483,6 +1485,7 @@ ves_pinvoke_method ( frame.parent = parent_frame; frame.imethod = imethod; frame.stack = sp; + frame.retval = ret_sp; MonoLMFExt ext; gpointer args; @@ -1556,7 +1559,7 @@ ves_pinvoke_method ( #else // Only the vt address has been returned, we need to copy the entire content on interp stack if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (sig->ret)) - stackval_from_data (sig->ret, frame.stack, (char*)frame.stack->data.p, sig->pinvoke); + stackval_from_data (sig->ret, frame.retval, (char*)frame.retval->data.p, sig->pinvoke); g_free (margs->iargs); g_free (margs->fargs); @@ -1840,6 +1843,7 @@ interp_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject InterpFrame frame = {0}; frame.imethod = imethod; frame.stack = sp; + frame.retval = sp; // The method to execute might not be transformed yet, so we don't know how much stack // it uses. We bump the stack_pointer here so any code triggered by method compilation @@ -1926,6 +1930,7 @@ interp_entry (InterpEntryData *data) InterpFrame frame = {0}; frame.imethod = data->rmethod; frame.stack = sp; + frame.retval = sp; context->stack_pointer = (guchar*)sp_args; @@ -1953,7 +1958,7 @@ interp_entry (InterpEntryData *data) } static void -do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean save_last_error) +do_icall (MonoMethodSignature *sig, int op, stackval *ret_sp, stackval *sp, gpointer ptr, gboolean save_last_error) { if (save_last_error) mono_marshal_clear_last_error (); @@ -1968,7 +1973,7 @@ do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean case MINT_ICALL_V_P: { typedef gpointer (*T)(void); T func = (T)ptr; - sp [0].data.p = func (); + ret_sp->data.p = func (); break; } case MINT_ICALL_P_V: { @@ -1980,7 +1985,7 @@ do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean case MINT_ICALL_P_P: { typedef gpointer (*T)(gpointer); T func = (T)ptr; - sp [0].data.p = func (sp [0].data.p); + ret_sp->data.p = func (sp [0].data.p); break; } case MINT_ICALL_PP_V: { @@ -1992,7 +1997,7 @@ do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean case MINT_ICALL_PP_P: { typedef gpointer (*T)(gpointer,gpointer); T func = (T)ptr; - sp [0].data.p = func (sp [0].data.p, sp [1].data.p); + ret_sp->data.p = func (sp [0].data.p, sp [1].data.p); break; } case MINT_ICALL_PPP_V: { @@ -2004,7 +2009,7 @@ do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean case MINT_ICALL_PPP_P: { typedef gpointer (*T)(gpointer,gpointer,gpointer); T func = (T)ptr; - sp [0].data.p = func (sp [0].data.p, sp [1].data.p, sp [2].data.p); + ret_sp->data.p = func (sp [0].data.p, sp [1].data.p, sp [2].data.p); break; } case MINT_ICALL_PPPP_V: { @@ -2016,7 +2021,7 @@ do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean case MINT_ICALL_PPPP_P: { typedef gpointer (*T)(gpointer,gpointer,gpointer,gpointer); T func = (T)ptr; - sp [0].data.p = func (sp [0].data.p, sp [1].data.p, sp [2].data.p, sp [3].data.p); + ret_sp->data.p = func (sp [0].data.p, sp [1].data.p, sp [2].data.p, sp [3].data.p); break; } case MINT_ICALL_PPPPP_V: { @@ -2028,7 +2033,7 @@ do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean case MINT_ICALL_PPPPP_P: { typedef gpointer (*T)(gpointer,gpointer,gpointer,gpointer,gpointer); T func = (T)ptr; - sp [0].data.p = func (sp [0].data.p, sp [1].data.p, sp [2].data.p, sp [3].data.p, sp [4].data.p); + ret_sp->data.p = func (sp [0].data.p, sp [1].data.p, sp [2].data.p, sp [3].data.p, sp [4].data.p); break; } case MINT_ICALL_PPPPPP_V: { @@ -2040,7 +2045,7 @@ do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean case MINT_ICALL_PPPPPP_P: { typedef gpointer (*T)(gpointer,gpointer,gpointer,gpointer,gpointer,gpointer); T func = (T)ptr; - sp [0].data.p = func (sp [0].data.p, sp [1].data.p, sp [2].data.p, sp [3].data.p, sp [4].data.p, sp [5].data.p); + ret_sp->data.p = func (sp [0].data.p, sp [1].data.p, sp [2].data.p, sp [3].data.p, sp [4].data.p, sp [5].data.p); break; } default: @@ -2052,7 +2057,7 @@ do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean /* convert the native representation to the stackval representation */ if (sig) - stackval_from_data (sig->ret, &sp [0], (char*) &sp [0].data.p, sig->pinvoke); + stackval_from_data (sig->ret, ret_sp, (char*) &ret_sp->data.p, sig->pinvoke); } /* MONO_NO_OPTIMIZATION is needed due to usage of INTERP_PUSH_LMF_WITH_CTX. */ @@ -2061,12 +2066,12 @@ do_icall (MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean #endif // Do not inline in case order of frame addresses matters, and maybe other reasons. static MONO_NO_OPTIMIZATION MONO_NEVER_INLINE gpointer -do_icall_wrapper (InterpFrame *frame, MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean save_last_error) +do_icall_wrapper (InterpFrame *frame, MonoMethodSignature *sig, int op, stackval *ret_sp, stackval *sp, gpointer ptr, gboolean save_last_error) { MonoLMFExt ext; INTERP_PUSH_LMF_WITH_CTX (frame, ext, exit_icall); - do_icall (sig, op, sp, ptr, save_last_error); + do_icall (sig, op, ret_sp, sp, ptr, save_last_error); interp_pop_lmf (&ext); @@ -2264,7 +2269,7 @@ init_jit_call_info (InterpMethod *rmethod, MonoError *error) } static MONO_NEVER_INLINE void -do_jit_call (stackval *sp, InterpFrame *frame, InterpMethod *rmethod, MonoError *error) +do_jit_call (stackval *ret_sp, stackval *sp, InterpFrame *frame, InterpMethod *rmethod, MonoError *error) { MonoLMFExt ext; JitCallInfo *cinfo; @@ -2293,7 +2298,7 @@ do_jit_call (stackval *sp, InterpFrame *frame, InterpMethod *rmethod, MonoError } /* return address */ if (cinfo->ret_mt != -1) - args [pindex ++] = sp; + args [pindex ++] = ret_sp; for (int i = 0; i < rmethod->param_count; ++i) { stackval *sval = STACK_ADD_BYTES (sp, get_arg_offset_fast (rmethod, stack_index + i)); if (cinfo->arginfo [i] == JIT_ARG_BYVAL) @@ -2330,16 +2335,16 @@ do_jit_call (stackval *sp, InterpFrame *frame, InterpMethod *rmethod, MonoError // Sign/zero extend if necessary switch (cinfo->ret_mt) { case MINT_TYPE_I1: - sp->data.i = *(gint8*)sp; + ret_sp->data.i = *(gint8*)sp; break; case MINT_TYPE_U1: - sp->data.i = *(guint8*)sp; + ret_sp->data.i = *(guint8*)sp; break; case MINT_TYPE_I2: - sp->data.i = *(gint16*)sp; + ret_sp->data.i = *(gint16*)sp; break; case MINT_TYPE_U2: - sp->data.i = *(guint16*)sp; + ret_sp->data.i = *(guint16*)sp; break; case MINT_TYPE_I4: case MINT_TYPE_I8: @@ -2347,7 +2352,7 @@ do_jit_call (stackval *sp, InterpFrame *frame, InterpMethod *rmethod, MonoError case MINT_TYPE_R8: case MINT_TYPE_VT: case MINT_TYPE_O: - /* The result was written to sp */ + /* The result was written to ret_sp */ break; default: g_assert_not_reached (); @@ -2593,6 +2598,7 @@ interp_entry_from_trampoline (gpointer ccontext_untyped, gpointer rmethod_untype InterpFrame frame = {0}; frame.imethod = rmethod; frame.stack = sp; + frame.retval = sp; /* Copy the args saved in the trampoline to the frame stack */ gpointer retp = mono_arch_get_native_call_context_args (ccontext, &frame, sig); @@ -3007,7 +3013,7 @@ mono_interp_leave (InterpFrame* parent_frame) * to check the abort threshold. For this to work we use frame as a * dummy frame that is stored in the lmf and serves as the transition frame */ - do_icall_wrapper (&frame, NULL, MINT_ICALL_V_P, &tmp_sp, (gpointer)mono_thread_get_undeniable_exception, FALSE); + do_icall_wrapper (&frame, NULL, MINT_ICALL_V_P, &tmp_sp, &tmp_sp, (gpointer)mono_thread_get_undeniable_exception, FALSE); return (MonoException*)tmp_sp.data.p; } @@ -3114,6 +3120,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs const guint16 *ip = NULL; unsigned char *locals = NULL; int call_args_offset; + int return_offset; #if DEBUG_INTERP int tracing = global_tracing; @@ -3208,10 +3215,10 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs ip += 2; MINT_IN_BREAK; MINT_IN_CASE(MINT_INIT_ARGLIST) { - const guint16 *call_ip = frame->parent->state.ip - 5; + const guint16 *call_ip = frame->parent->state.ip - 6; g_assert_checked (*call_ip == MINT_CALL_VARARG); - int params_stack_size = call_ip [4]; - MonoMethodSignature *sig = (MonoMethodSignature*)frame->parent->imethod->data_items [call_ip [3]]; + int params_stack_size = call_ip [5]; + MonoMethodSignature *sig = (MonoMethodSignature*)frame->parent->imethod->data_items [call_ip [4]]; // we are being overly conservative with the size here, for simplicity gpointer arglist = frame_data_allocator_alloc (&context->data_stack, frame, params_stack_size + MINT_STACK_SLOT_SIZE); @@ -3307,9 +3314,10 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs } MINT_IN_CASE(MINT_CALL_DELEGATE) { // FIXME We don't need to encode the whole signature, just param_count - MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [3]]; + MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [4]]; int param_count = csignature->param_count; - call_args_offset = ip [1]; + return_offset = ip [1]; + call_args_offset = ip [2]; MonoDelegate *del = LOCAL_VAR (call_args_offset, MonoDelegate*); gboolean is_multicast = del->method == NULL; InterpMethod *del_imethod = (InterpMethod*)del->interp_invoke_impl; @@ -3351,31 +3359,31 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs // Target method is static but the delegate has a target object. We handle // this separately from the case below, because, for these calls, the instance // is allowed to be null. - LOCAL_VAR (ip [1], MonoObject*) = del->target; + LOCAL_VAR (call_args_offset, MonoObject*) = del->target; } else if (del->target) { MonoObject *this_arg = del->target; // replace the MonoDelegate* on the stack with 'this' pointer if (m_class_is_valuetype (this_arg->vtable->klass)) { gpointer unboxed = mono_object_unbox_internal (this_arg); - LOCAL_VAR (ip [1], gpointer) = unboxed; + LOCAL_VAR (call_args_offset, gpointer) = unboxed; } else { - LOCAL_VAR (ip [1], MonoObject*) = this_arg; + LOCAL_VAR (call_args_offset, MonoObject*) = this_arg; } } else { // skip the delegate pointer for static calls // FIXME we could avoid memmove - memmove (locals + call_args_offset, locals + call_args_offset + MINT_STACK_SLOT_SIZE, ip [2]); + memmove (locals + call_args_offset, locals + call_args_offset + MINT_STACK_SLOT_SIZE, ip [3]); } } - ip += 4; + ip += 5; goto call; } MINT_IN_CASE(MINT_CALLI) { MonoMethodSignature *csignature; - csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [3]]; + csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [4]]; cmethod = LOCAL_VAR (ip [2], InterpMethod*); if (cmethod->method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { @@ -3383,7 +3391,8 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs mono_interp_error_cleanup (error); /* FIXME: don't swallow the error */ } - call_args_offset = ip [1]; + return_offset = ip [1]; + call_args_offset = ip [3]; if (csignature->hasthis) { MonoObject *this_arg = LOCAL_VAR (call_args_offset, MonoObject*); @@ -3393,66 +3402,69 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs LOCAL_VAR (call_args_offset, gpointer) = unboxed; } } - ip += 4; + ip += 5; goto call; } MINT_IN_CASE(MINT_CALLI_NAT_FAST) { - MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [3]]; - int opcode = ip [4]; - gboolean save_last_error = ip [5]; + MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [4]]; + int opcode = ip [5]; + gboolean save_last_error = ip [6]; - stackval *args = (stackval*)(locals + ip [1]); + stackval *ret = (stackval*)(locals + ip [1]); gpointer target_ip = LOCAL_VAR (ip [2], gpointer); + stackval *args = (stackval*)(locals + ip [3]); /* for calls, have ip pointing at the start of next instruction */ - frame->state.ip = ip + 6; + frame->state.ip = ip + 7; - do_icall_wrapper (frame, csignature, opcode, args, target_ip, save_last_error); + do_icall_wrapper (frame, csignature, opcode, ret, args, target_ip, save_last_error); EXCEPTION_CHECKPOINT_GC_UNSAFE; CHECK_RESUME_STATE (context); - ip += 6; + ip += 7; MINT_IN_BREAK; } MINT_IN_CASE(MINT_CALLI_NAT_DYNAMIC) { - MonoMethodSignature* csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [3]]; + MonoMethodSignature* csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [4]]; - call_args_offset = ip [1]; + return_offset = ip [1]; guchar* code = LOCAL_VAR (ip [2], guchar*); + call_args_offset = ip [3]; cmethod = mono_interp_get_native_func_wrapper (frame->imethod, csignature, code); - ip += 4; + ip += 5; goto call; } MINT_IN_CASE(MINT_CALLI_NAT) { - MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [3]]; - InterpMethod *imethod = (InterpMethod*)frame->imethod->data_items [ip [4]]; + MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [4]]; + InterpMethod *imethod = (InterpMethod*)frame->imethod->data_items [ip [5]]; guchar *code = LOCAL_VAR (ip [2], guchar*); - gboolean save_last_error = ip [5]; - gpointer *cache = (gpointer*)&frame->imethod->data_items [ip [6]]; + gboolean save_last_error = ip [6]; + gpointer *cache = (gpointer*)&frame->imethod->data_items [ip [7]]; /* for calls, have ip pointing at the start of next instruction */ - frame->state.ip = ip + 7; - ves_pinvoke_method (imethod, csignature, (MonoFuncV)code, context, frame, (stackval*)(locals + ip [1]), save_last_error, cache); + frame->state.ip = ip + 8; + ves_pinvoke_method (imethod, csignature, (MonoFuncV)code, context, frame, (stackval*)(locals + ip [1]), (stackval*)(locals + ip [3]), save_last_error, cache); EXCEPTION_CHECKPOINT_GC_UNSAFE; CHECK_RESUME_STATE (context); - ip += 7; + ip += 8; MINT_IN_BREAK; } MINT_IN_CASE(MINT_CALLVIRT_FAST) { MonoObject *this_arg; int slot; - cmethod = (InterpMethod*)frame->imethod->data_items [ip [2]]; - call_args_offset = ip [1]; + cmethod = (InterpMethod*)frame->imethod->data_items [ip [3]]; + return_offset = ip [1]; + call_args_offset = ip [2]; this_arg = LOCAL_VAR (call_args_offset, MonoObject*); - slot = (gint16)ip [3]; - ip += 4; + slot = (gint16)ip [4]; + ip += 5; cmethod = get_virtual_method_fast (cmethod, this_arg->vtable, slot); if (m_class_is_valuetype (this_arg->vtable->klass) && m_class_is_valuetype (cmethod->method->klass)) { /* unbox */ @@ -3482,7 +3494,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs } else if (code_type == IMETHOD_CODE_COMPILED) { frame->state.ip = ip; error_init_reuse (error); - do_jit_call ((stackval*)(locals + call_args_offset), frame, cmethod, error); + do_jit_call ((stackval*)(locals + return_offset), (stackval*)(locals + call_args_offset), frame, cmethod, error); if (!is_ok (error)) { MonoException *ex = mono_error_convert_to_exception (error); THROW_EX (ex, ip); @@ -3494,18 +3506,20 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } MINT_IN_CASE(MINT_CALL_VARARG) { - // Same as MINT_CALL, except at ip [3] we have the index for the csignature, + // Same as MINT_CALL, except at ip [4] we have the index for the csignature, // which is required by the called method to set up the arglist. - cmethod = (InterpMethod*)frame->imethod->data_items [ip [2]]; - call_args_offset = ip [1]; - ip += 5; + cmethod = (InterpMethod*)frame->imethod->data_items [ip [3]]; + return_offset = ip [1]; + call_args_offset = ip [2]; + ip += 6; goto call; } MINT_IN_CASE(MINT_CALLVIRT) { // FIXME CALLVIRT opcodes are not used on netcore. We should kill them. - cmethod = (InterpMethod*)frame->imethod->data_items [ip [2]]; - call_args_offset = ip [1]; + cmethod = (InterpMethod*)frame->imethod->data_items [ip [3]]; + return_offset = ip [1]; + call_args_offset = ip [2]; MonoObject *this_arg = LOCAL_VAR (call_args_offset, MonoObject*); @@ -3519,18 +3533,19 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs #ifdef ENABLE_EXPERIMENT_TIERED ip += 5; #else - ip += 3; + ip += 4; #endif goto call; } MINT_IN_CASE(MINT_CALL) { - cmethod = (InterpMethod*)frame->imethod->data_items [ip [2]]; - call_args_offset = ip [1]; + cmethod = (InterpMethod*)frame->imethod->data_items [ip [3]]; + return_offset = ip [1]; + call_args_offset = ip [2]; #ifdef ENABLE_EXPERIMENT_TIERED ip += 5; #else - ip += 3; + ip += 4; #endif call: /* @@ -3548,7 +3563,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs // Not free currently, but will be when allocation attempted. frame->next_free = child_frame; } - reinit_frame (child_frame, frame, cmethod, locals + call_args_offset); + reinit_frame (child_frame, frame, cmethod, locals + return_offset, locals + call_args_offset); frame = child_frame; } if (method_entry (context, frame, @@ -3570,18 +3585,18 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } MINT_IN_CASE(MINT_JIT_CALL) { - InterpMethod *rmethod = (InterpMethod*)frame->imethod->data_items [ip [2]]; + InterpMethod *rmethod = (InterpMethod*)frame->imethod->data_items [ip [3]]; error_init_reuse (error); /* for calls, have ip pointing at the start of next instruction */ - frame->state.ip = ip + 3; - do_jit_call ((stackval*)(locals + ip [1]), frame, rmethod, error); + frame->state.ip = ip + 4; + do_jit_call ((stackval*)(locals + ip [1]), (stackval*)(locals + ip [2]), frame, rmethod, error); if (!is_ok (error)) { MonoException *ex = mono_error_convert_to_exception (error); THROW_EX (ex, ip); } CHECK_RESUME_STATE (context); - ip += 3; + ip += 4; MINT_IN_BREAK; } @@ -3612,23 +3627,23 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } MINT_IN_CASE(MINT_RET) - frame->stack [0] = LOCAL_VAR (ip [1], stackval); + frame->retval [0] = LOCAL_VAR (ip [1], stackval); goto exit_frame; MINT_IN_CASE(MINT_RET_VOID) goto exit_frame; MINT_IN_CASE(MINT_RET_VT) { - memmove (frame->stack, locals + ip [1], ip [2]); + memmove (frame->retval, locals + ip [1], ip [2]); goto exit_frame; } MINT_IN_CASE(MINT_RET_LOCALLOC) - frame->stack [0] = LOCAL_VAR (ip [1], stackval); + frame->retval [0] = LOCAL_VAR (ip [1], stackval); frame_data_allocator_pop (&context->data_stack, frame); goto exit_frame; MINT_IN_CASE(MINT_RET_VOID_LOCALLOC) frame_data_allocator_pop (&context->data_stack, frame); goto exit_frame; MINT_IN_CASE(MINT_RET_VT_LOCALLOC) { - memmove (frame->stack, locals + ip [1], ip [2]); + memmove (frame->retval, locals + ip [1], ip [2]); frame_data_allocator_pop (&context->data_stack, frame); goto exit_frame; } @@ -4705,42 +4720,44 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs } MINT_IN_CASE(MINT_NEWOBJ_ARRAY) { MonoClass *newobj_class; - guint32 token = ip [2]; - guint16 param_count = ip [3]; + guint32 token = ip [3]; + guint16 param_count = ip [4]; newobj_class = (MonoClass*) frame->imethod->data_items [token]; - LOCAL_VAR (ip [1], MonoObject*) = ves_array_create (newobj_class, param_count, (stackval*)(locals + ip [1]), error); + LOCAL_VAR (ip [1], MonoObject*) = ves_array_create (newobj_class, param_count, (stackval*)(locals + ip [2]), error); if (!is_ok (error)) THROW_EX (mono_error_convert_to_exception (error), ip); - ip += 4; + ip += 5; MINT_IN_BREAK; } MINT_IN_CASE(MINT_NEWOBJ_STRING) { - cmethod = (InterpMethod*)frame->imethod->data_items [ip [2]]; - call_args_offset = ip [1]; + cmethod = (InterpMethod*)frame->imethod->data_items [ip [3]]; + return_offset = ip [1]; + call_args_offset = ip [2]; - int param_size = ip [3]; + int param_size = ip [4]; if (param_size) memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); // `this` is implicit null. The created string will be returned // by the call, even though the call has void return (?!). LOCAL_VAR (call_args_offset, gpointer) = NULL; - ip += 4; + ip += 5; goto call; } MINT_IN_CASE(MINT_NEWOBJ_FAST) { - MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [3]]; + MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [4]]; INIT_VTABLE (vtable); - guint16 imethod_index = ip [2]; - guint16 param_size = ip [4]; - call_args_offset = ip [1]; + guint16 imethod_index = ip [3]; + guint16 param_size = ip [5]; + return_offset = ip [1]; + call_args_offset = ip [2]; const gboolean is_inlined = imethod_index == INLINED_METHOD_FLAG; - // Make room for two copies of o -- this parameter and return value. + // Make room for `this` parameter if (param_size) - memmove (locals + call_args_offset + 2 * MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); + memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); MonoObject *o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass)); if (G_UNLIKELY (!o)) { @@ -4749,9 +4766,8 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs } // This is return value - LOCAL_VAR (call_args_offset, MonoObject*) = o; + LOCAL_VAR (return_offset, MonoObject*) = o; // Set `this` arg for ctor call - call_args_offset += MINT_STACK_SLOT_SIZE; LOCAL_VAR (call_args_offset, MonoObject*) = o; ip += 6; if (!is_inlined) { @@ -4762,19 +4778,19 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs } MINT_IN_CASE(MINT_NEWOBJ_VT_FAST) { - guint16 imethod_index = ip [2]; - guint16 ret_size = ip [3]; - guint16 param_size = ip [4]; + guint16 imethod_index = ip [3]; + guint16 ret_size = ip [4]; + guint16 param_size = ip [5]; gboolean is_inlined = imethod_index == INLINED_METHOD_FLAG; - call_args_offset = ip [1]; - gpointer this_vt = locals + call_args_offset; + return_offset = ip [1]; + call_args_offset = ip [2]; + gpointer this_vt = locals + return_offset; if (param_size) - memmove (locals + call_args_offset + ret_size + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); + memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); // clear the valuetype memset (this_vt, 0, ret_size); - call_args_offset += ret_size; // pass the address of the valuetype LOCAL_VAR (call_args_offset, gpointer) = this_vt; @@ -4786,14 +4802,15 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } MINT_IN_CASE(MINT_NEWOBJ) { - guint32 const token = ip [2]; - guint16 param_size = ip [3]; - call_args_offset = ip [1]; + guint32 const token = ip [3]; + guint16 param_size = ip [4]; + return_offset = ip [1]; + call_args_offset = ip [2]; cmethod = (InterpMethod*)frame->imethod->data_items [token]; if (param_size) - memmove (locals + call_args_offset + 2 * MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); + memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); MonoClass * const newobj_class = cmethod->method->klass; @@ -4812,13 +4829,12 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs } error_init_reuse (error); MonoObject* o = mono_object_new_checked (newobj_class, error); - LOCAL_VAR (call_args_offset, MonoObject*) = o; // return value - call_args_offset += MINT_STACK_SLOT_SIZE; + LOCAL_VAR (return_offset, MonoObject*) = o; // return value LOCAL_VAR (call_args_offset, MonoObject*) = o; // first parameter mono_interp_error_cleanup (error); // FIXME: do not swallow the error EXCEPTION_CHECKPOINT; - ip += 4; + ip += 5; goto call; } MINT_IN_CASE(MINT_INTRINS_SPAN_CTOR) { @@ -5509,9 +5525,9 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } MINT_IN_CASE(MINT_LDELEMA) { - guint16 rank = ip [2]; - guint16 esize = ip [3]; - stackval *sp = (stackval*)(locals + ip [1]); + guint16 rank = ip [3]; + guint16 esize = ip [4]; + stackval *sp = (stackval*)(locals + ip [2]); MonoArray *ao = (MonoArray*) sp [0].data.o; NULL_CHECK (ao); @@ -5527,21 +5543,21 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs pos = (pos * len) + (guint32)(idx - lower); } - sp [0].data.p = mono_array_addr_with_size_fast (ao, esize, pos); - ip += 4; + LOCAL_VAR (ip [1], gpointer) = mono_array_addr_with_size_fast (ao, esize, pos); + ip += 5; MINT_IN_BREAK; } MINT_IN_CASE(MINT_LDELEMA_TC) { - stackval *sp = (stackval*)(locals + ip [1]); + stackval *sp = (stackval*)(locals + ip [2]); MonoObject *o = (MonoObject*) sp [0].data.o; NULL_CHECK (o); - MonoClass *klass = (MonoClass*)frame->imethod->data_items [ip [2]]; - MonoException *ex = ves_array_element_address (frame, klass, (MonoArray *) o, sp + 1, TRUE); + MonoClass *klass = (MonoClass*)frame->imethod->data_items [ip [3]]; + MonoException *ex = ves_array_element_address (frame, klass, (MonoArray *) o, (gpointer*)(locals + ip [1]), sp + 1, TRUE); if (ex) THROW_EX (ex, ip); - ip += 3; + ip += 4; MINT_IN_BREAK; } @@ -6099,25 +6115,31 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } MINT_IN_CASE(MINT_ICALL_V_V) - MINT_IN_CASE(MINT_ICALL_V_P) MINT_IN_CASE(MINT_ICALL_P_V) - MINT_IN_CASE(MINT_ICALL_P_P) MINT_IN_CASE(MINT_ICALL_PP_V) - MINT_IN_CASE(MINT_ICALL_PP_P) MINT_IN_CASE(MINT_ICALL_PPP_V) - MINT_IN_CASE(MINT_ICALL_PPP_P) MINT_IN_CASE(MINT_ICALL_PPPP_V) - MINT_IN_CASE(MINT_ICALL_PPPP_P) MINT_IN_CASE(MINT_ICALL_PPPPP_V) - MINT_IN_CASE(MINT_ICALL_PPPPP_P) MINT_IN_CASE(MINT_ICALL_PPPPPP_V) - MINT_IN_CASE(MINT_ICALL_PPPPPP_P) frame->state.ip = ip + 3; - do_icall_wrapper (frame, NULL, *ip, (stackval*)(locals + ip [1]), frame->imethod->data_items [ip [2]], FALSE); + do_icall_wrapper (frame, NULL, *ip, NULL, (stackval*)(locals + ip [1]), frame->imethod->data_items [ip [2]], FALSE); EXCEPTION_CHECKPOINT_GC_UNSAFE; CHECK_RESUME_STATE (context); ip += 3; MINT_IN_BREAK; + MINT_IN_CASE(MINT_ICALL_V_P) + MINT_IN_CASE(MINT_ICALL_P_P) + MINT_IN_CASE(MINT_ICALL_PP_P) + MINT_IN_CASE(MINT_ICALL_PPP_P) + MINT_IN_CASE(MINT_ICALL_PPPP_P) + MINT_IN_CASE(MINT_ICALL_PPPPP_P) + MINT_IN_CASE(MINT_ICALL_PPPPPP_P) + frame->state.ip = ip + 4; + do_icall_wrapper (frame, NULL, *ip, (stackval*)(locals + ip [1]), (stackval*)(locals + ip [2]), frame->imethod->data_items [ip [3]], FALSE); + EXCEPTION_CHECKPOINT_GC_UNSAFE; + CHECK_RESUME_STATE (context); + ip += 4; + MINT_IN_BREAK; MINT_IN_CASE(MINT_MONO_LDPTR) LOCAL_VAR (ip [1], gpointer) = frame->imethod->data_items [ip [2]]; ip += 3; @@ -6406,9 +6428,9 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs int i32 = READ32 (ip + 3); if (i32 == -1) { } else if (i32) { - memmove (frame->stack, locals + ip [1], i32); + memmove (frame->retval, locals + ip [1], i32); } else { - frame->stack [0] = LOCAL_VAR (ip [1], stackval); + frame->retval [0] = LOCAL_VAR (ip [1], stackval); } if ((flag & TRACING_FLAG) || ((flag & PROFILING_FLAG) && MONO_PROFILER_ENABLED (method_leave) && @@ -6417,7 +6439,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs prof_ctx->interp_frame = frame; prof_ctx->method = frame->imethod->method; if (i32 != -1) - prof_ctx->return_value = frame->stack; + prof_ctx->return_value = frame->retval; if (flag & TRACING_FLAG) mono_trace_leave_method (frame->imethod->method, frame->imethod->jinfo, prof_ctx); if (flag & PROFILING_FLAG) diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index f4bb2d7bbd6ee..159bed66b7bd7 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -294,11 +294,11 @@ OPDEF(MINT_JMP, "jmp", 2, 0, 0, MintOpMethodToken) OPDEF(MINT_ENDFILTER, "endfilter", 2, 0, 1, MintOpNoArgs) -OPDEF(MINT_NEWOBJ, "newobj", 4, CallArgs, 0, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_ARRAY, "newobj_array", 4, CallArgs, 0, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_STRING, "newobj_string", 4, CallArgs, 0, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_FAST, "newobj_fast", 6, CallArgs, 0, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_VT_FAST, "newobj_vt_fast", 6, CallArgs, 0, MintOpMethodToken) +OPDEF(MINT_NEWOBJ, "newobj", 5, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_ARRAY, "newobj_array", 5, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_STRING, "newobj_string", 5, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_FAST, "newobj_fast", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_VT_FAST, "newobj_vt_fast", 6, 1, 1, MintOpMethodToken) OPDEF(MINT_INITOBJ, "initobj", 3, 0, 1, MintOpShortInt) OPDEF(MINT_CASTCLASS, "castclass", 4, 1, 1, MintOpClassToken) OPDEF(MINT_ISINST, "isinst", 4, 1, 1, MintOpClassToken) @@ -339,8 +339,8 @@ OPDEF(MINT_LDELEM_REF, "ldelem.ref", 4, 1, 2, MintOpNoArgs) OPDEF(MINT_LDELEM_VT, "ldelem.vt", 5, 1, 2, MintOpShortInt) OPDEF(MINT_LDELEMA1, "ldelema1", 5, 1, 2, MintOpShortInt) -OPDEF(MINT_LDELEMA, "ldelema", 4, CallArgs, 0, MintOpTwoShorts) -OPDEF(MINT_LDELEMA_TC, "ldelema.tc", 3, CallArgs, 0, MintOpTwoShorts) +OPDEF(MINT_LDELEMA, "ldelema", 5, 1, 1, MintOpTwoShorts) +OPDEF(MINT_LDELEMA_TC, "ldelema.tc", 4, 1, 1, MintOpTwoShorts) OPDEF(MINT_STELEM_I, "stelem.i", 4, 0, 3, MintOpNoArgs) OPDEF(MINT_STELEM_I1, "stelem.i1", 4, 0, 3, MintOpNoArgs) @@ -605,34 +605,34 @@ OPDEF(MINT_ARRAY_ELEMENT_SIZE, "array_element_size", 3, 1, 1, MintOpNoArgs) OPDEF(MINT_ARRAY_IS_PRIMITIVE, "array_is_primitive", 3, 1, 1, MintOpNoArgs) /* Calls */ -OPDEF(MINT_CALL, "call", 3, CallArgs, 0, MintOpMethodToken) -OPDEF(MINT_CALLVIRT, "callvirt", 3, CallArgs, 0, MintOpMethodToken) -OPDEF(MINT_CALLVIRT_FAST, "callvirt.fast", 4, CallArgs, 0, MintOpMethodToken) -OPDEF(MINT_CALL_DELEGATE, "call.delegate", 4, CallArgs, 0, MintOpTwoShorts) -OPDEF(MINT_CALLI, "calli", 4, CallArgs, 1, MintOpMethodToken) -OPDEF(MINT_CALLI_NAT, "calli.nat", 7, CallArgs, 1, MintOpMethodToken) -OPDEF(MINT_CALLI_NAT_DYNAMIC, "calli.nat.dynamic", 4, CallArgs, 1, MintOpMethodToken) -OPDEF(MINT_CALLI_NAT_FAST, "calli.nat.fast", 6, CallArgs, 1, MintOpMethodToken) -OPDEF(MINT_CALL_VARARG, "call.vararg", 5, CallArgs, 0, MintOpMethodToken) -OPDEF(MINT_CALLRUN, "callrun", 4, CallArgs, 0, MintOpNoArgs) - -OPDEF(MINT_ICALL_V_V, "mono_icall_v_v", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_V_P, "mono_icall_v_p", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_P_V, "mono_icall_p_v", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_P_P, "mono_icall_p_p", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PP_V, "mono_icall_pp_v", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PP_P, "mono_icall_pp_p", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PPP_V, "mono_icall_ppp_v", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PPP_P, "mono_icall_ppp_p", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PPPP_V, "mono_icall_pppp_v", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PPPP_P, "mono_icall_pppp_p", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PPPPP_V, "mono_icall_ppppp_v", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PPPPP_P, "mono_icall_ppppp_p", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PPPPPP_V, "mono_icall_pppppp_v", 3, CallArgs, 0, MintOpShortInt) -OPDEF(MINT_ICALL_PPPPPP_P, "mono_icall_pppppp_p", 3, CallArgs, 0, MintOpShortInt) +OPDEF(MINT_CALL, "call", 4, 1, 1, MintOpMethodToken) +OPDEF(MINT_CALLVIRT, "callvirt", 4, 1, 1, MintOpMethodToken) +OPDEF(MINT_CALLVIRT_FAST, "callvirt.fast", 5, 1, 1, MintOpMethodToken) +OPDEF(MINT_CALL_DELEGATE, "call.delegate", 5, 1, 1, MintOpTwoShorts) +OPDEF(MINT_CALLI, "calli", 5, 1, 2, MintOpMethodToken) +OPDEF(MINT_CALLI_NAT, "calli.nat", 8, 1, 2, MintOpMethodToken) +OPDEF(MINT_CALLI_NAT_DYNAMIC, "calli.nat.dynamic", 5, 1, 2, MintOpMethodToken) +OPDEF(MINT_CALLI_NAT_FAST, "calli.nat.fast", 7, 1, 2, MintOpMethodToken) +OPDEF(MINT_CALL_VARARG, "call.vararg", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_CALLRUN, "callrun", 5, 1, 1, MintOpNoArgs) + +OPDEF(MINT_ICALL_V_V, "mono_icall_v_v", 3, 0, 1, MintOpShortInt) +OPDEF(MINT_ICALL_V_P, "mono_icall_v_p", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_ICALL_P_V, "mono_icall_p_v", 3, 0, 1, MintOpShortInt) +OPDEF(MINT_ICALL_P_P, "mono_icall_p_p", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PP_V, "mono_icall_pp_v", 3, 0, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PP_P, "mono_icall_pp_p", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PPP_V, "mono_icall_ppp_v", 3, 0, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PPP_P, "mono_icall_ppp_p", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PPPP_V, "mono_icall_pppp_v", 3, 0, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PPPP_P, "mono_icall_pppp_p", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PPPPP_V, "mono_icall_ppppp_v", 3, 0, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PPPPP_P, "mono_icall_ppppp_p", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PPPPPP_V, "mono_icall_pppppp_v", 3, 0, 1, MintOpShortInt) +OPDEF(MINT_ICALL_PPPPPP_P, "mono_icall_pppppp_p", 4, 1, 1, MintOpShortInt) // FIXME: MintOp -OPDEF(MINT_JIT_CALL, "mono_jit_call", 3, CallArgs, 0, MintOpNoArgs) -OPDEF(MINT_JIT_CALL2, "mono_jit_call2", 6, CallArgs, 0, MintOpNoArgs) +OPDEF(MINT_JIT_CALL, "mono_jit_call", 4, 1, 1, MintOpNoArgs) +OPDEF(MINT_JIT_CALL2, "mono_jit_call2", 7, 1, 1, MintOpNoArgs) OPDEF(MINT_MONO_LDPTR, "mono_ldptr", 3, 1, 0, MintOpShortInt) OPDEF(MINT_MONO_SGEN_THREAD_INFO, "mono_sgen_thread_info", 2, 1, 0, MintOpNoArgs) diff --git a/src/mono/mono/mini/interp/mintops.h b/src/mono/mono/mini/interp/mintops.h index 262062ee12ab5..bf4a0dc999937 100644 --- a/src/mono/mono/mini/interp/mintops.h +++ b/src/mono/mono/mini/interp/mintops.h @@ -68,6 +68,7 @@ typedef enum { #define MINT_IS_STFLD(op) ((op) >= MINT_STFLD_I1 && (op) <= MINT_STFLD_O) #define MINT_CALL_ARGS 2 +#define MINT_CALL_ARGS_SREG -2 extern unsigned char const mono_interp_oplen[]; extern int const mono_interp_op_dregs []; diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 21caccf441da1..43a1aea14cdac 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -1183,10 +1183,7 @@ interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *ta call_args [2] = -1; interp_add_ins (td, MINT_ICALL_PP_V); - // Allocate a dummy local to serve as dreg for this instruction - push_simple_type (td, STACK_TYPE_I4); - td->sp--; - interp_ins_set_dreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = call_args; } @@ -1197,10 +1194,7 @@ interp_generate_bie_throw (TransformData *td) MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_bad_image; interp_add_ins (td, MINT_ICALL_V_V); - // Allocate a dummy local to serve as dreg for this instruction - push_simple_type (td, STACK_TYPE_I4); - td->sp--; - interp_ins_set_dreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = NULL; } @@ -1211,10 +1205,7 @@ interp_generate_not_supported_throw (TransformData *td) MonoJitICallInfo *info = &mono_get_jit_icall_info ()->mono_throw_not_supported; interp_add_ins (td, MINT_ICALL_V_V); - // Allocate a dummy local to serve as dreg for this instruction - push_simple_type (td, STACK_TYPE_I4); - td->sp--; - interp_ins_set_dreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = NULL; } @@ -1251,10 +1242,7 @@ interp_generate_ipe_throw_with_msg (TransformData *td, MonoError *error_msg) call_args [1] = -1; interp_add_ins (td, MINT_ICALL_P_V); - // Allocate a dummy local to serve as dreg for this instruction - push_simple_type (td, STACK_TYPE_I4); - td->sp--; - interp_ins_set_dreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = call_args; } @@ -1770,7 +1758,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check push_simple_type (td, STACK_TYPE_MP); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); if (is_call_args) - td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); } static gboolean @@ -3086,7 +3074,8 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } } else { // Create a new dummy local to serve as the dreg of the call - // This dreg is only used to resolve the call args offset + // FIXME Consider adding special dreg type (ex -1), that is + // resolved to null offset. The opcode shouldn't really write to it push_simple_type (td, STACK_TYPE_I4); td->sp--; dreg = td->sp [0].local; @@ -3120,12 +3109,14 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } else if (!calli && !is_delegate_invoke && !is_virtual && mono_interp_jit_call_supported (target_method, csignature)) { interp_add_ins (td, MINT_JIT_CALL); interp_ins_set_dreg (td->last_ins, dreg); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (target_method, error)); mono_error_assert_ok (error); } else { if (is_delegate_invoke) { interp_add_ins (td, MINT_CALL_DELEGATE); interp_ins_set_dreg (td->last_ins, dreg); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = params_stack_size; td->last_ins->data [1] = get_data_item_index (td, (void *)csignature); } else if (calli) { @@ -3140,14 +3131,14 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (op != -1) { interp_add_ins (td, MINT_CALLI_NAT_FAST); interp_ins_set_dreg (td->last_ins, dreg); - interp_ins_set_sreg (td->last_ins, fp_sreg); + interp_ins_set_sregs2 (td->last_ins, fp_sreg, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (void *)csignature); td->last_ins->data [1] = op; td->last_ins->data [2] = save_last_error; } else if (native && method->dynamic && csignature->pinvoke) { interp_add_ins (td, MINT_CALLI_NAT_DYNAMIC); interp_ins_set_dreg (td->last_ins, dreg); - interp_ins_set_sreg (td->last_ins, fp_sreg); + interp_ins_set_sregs2 (td->last_ins, fp_sreg, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (void *)csignature); } else if (native) { interp_add_ins (td, MINT_CALLI_NAT); @@ -3172,7 +3163,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } interp_ins_set_dreg (td->last_ins, dreg); - interp_ins_set_sreg (td->last_ins, fp_sreg); + interp_ins_set_sregs2 (td->last_ins, fp_sreg, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, csignature); td->last_ins->data [1] = get_data_item_index (td, imethod); td->last_ins->data [2] = save_last_error; @@ -3181,7 +3172,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } else { interp_add_ins (td, MINT_CALLI); interp_ins_set_dreg (td->last_ins, dreg); - interp_ins_set_sreg (td->last_ins, fp_sreg); + interp_ins_set_sregs2 (td->last_ins, fp_sreg, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (void *)csignature); } } else { @@ -3204,6 +3195,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target interp_add_ins (td, MINT_CALL); } interp_ins_set_dreg (td->last_ins, dreg); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (void *)imethod); #ifdef ENABLE_EXPERIMENT_TIERED @@ -5395,7 +5387,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [1] = csignature->param_count; push_type (td, stack_type [ret_mt], klass); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->info.call_args = call_args; } else if (klass == mono_defaults.string_class) { int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); @@ -5414,7 +5406,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [1] = params_stack_size; push_type (td, stack_type [ret_mt], klass); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->info.call_args = call_args; } else if (m_class_get_image (klass) == mono_defaults.corlib && !strcmp (m_class_get_name (m->klass), "ByReference`1") && @@ -5527,6 +5519,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (m, error)); td->last_ins->data [1] = params_stack_size; } + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->info.call_args = call_args; goto_if_nok (error, exit); // Parameters and this pointer are popped of the stack. The return value remains @@ -6065,6 +6058,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; td->last_ins->data [0] = get_data_item_index (td, klass); td->last_ins->info.call_args = call_args; + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); } else { interp_add_ins (td, MINT_LDELEMA1); td->sp -= 2; @@ -6669,7 +6663,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, break; } case CEE_MONO_ICALL: { - int dreg; + int dreg = -1; MonoJitICallId const jit_icall_id = (MonoJitICallId)read32 (td->ip + 1); MonoJitICallInfo const * const info = mono_find_jit_icall_info (jit_icall_id); td->ip += 5; @@ -6686,13 +6680,6 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, int mt = mint_type (info->sig->ret); push_simple_type (td, stack_type [mt]); dreg = td->sp [-1].local; - td->locals [dreg].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; - } else { - // Create a new dummy local to serve as the dreg of the call - // This dreg is only used to resolve the call args offset - push_simple_type (td, STACK_TYPE_I4); - td->sp--; - dreg = td->sp [0].local; } if (jit_icall_id == MONO_JIT_ICALL_mono_threads_attach_coop) { rtm->needs_thread_attach = 1; @@ -6704,7 +6691,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, icall_op); // hash here is overkill - interp_ins_set_dreg (td->last_ins, dreg); + if (dreg != -1) + interp_ins_set_dreg (td->last_ins, dreg); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = call_args; } From 9e623153b347ff24e3fefe8ccdc2eb73d01d2140 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:08:36 +0200 Subject: [PATCH 05/17] [interp] Remove call args flag from code generation / optimization phases This flag should only be relevant to the var offset allocator --- src/mono/mono/mini/interp/transform.c | 29 ++++----------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 43a1aea14cdac..27d4d833bae58 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -1177,9 +1177,7 @@ interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *ta td->sp -= 2; int *call_args = (int*)mono_mempool_alloc (td->mempool, 3 * sizeof (int)); call_args [0] = td->sp [0].local; - td->locals [td->sp [0].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [1] = td->sp [1].local; - td->locals [td->sp [1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [2] = -1; interp_add_ins (td, MINT_ICALL_PP_V); @@ -1238,7 +1236,6 @@ interp_generate_ipe_throw_with_msg (TransformData *td, MonoError *error_msg) td->sp -= 1; int *call_args = (int*)mono_mempool_alloc (td->mempool, 2 * sizeof (int)); call_args [0] = td->sp [0].local; - td->locals [td->sp [0].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [1] = -1; interp_add_ins (td, MINT_ICALL_P_V); @@ -1732,7 +1729,6 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check interp_add_ins (td, MINT_LDELEMA); int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); for (int i = 0; i < rank + 1; i++) { - td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [i] = td->sp [i].local; } call_args [rank + 1] = -1; @@ -1746,7 +1742,6 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check interp_add_ins (td, MINT_LDELEMA_TC); int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); for (int i = 0; i < rank + 1; i++) { - td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [i] = td->sp [i].local; } call_args [rank + 1] = -1; @@ -3037,7 +3032,6 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target int num_args = csignature->param_count + !!csignature->hasthis; call_args = (int*) mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); for (int i = 0; i < num_args; i++) { - td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [i] = td->sp [i].local; } call_args [num_args] = -1; @@ -3068,10 +3062,6 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target res_size = MINT_STACK_SLOT_SIZE; } dreg = td->sp [-1].local; - if (op == -1 || mono_interp_op_dregs [op] == MINT_CALL_ARGS) { - // This dreg needs to be at the same offset as the call args - td->locals [dreg].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; - } } else { // Create a new dummy local to serve as the dreg of the call // FIXME Consider adding special dreg type (ex -1), that is @@ -5377,7 +5367,6 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); td->sp -= csignature->param_count; for (int i = 0; i < csignature->param_count; i++) { - td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [i] = td->sp [i].local; } call_args [csignature->param_count] = -1; @@ -5396,7 +5385,6 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, guint32 params_stack_size = tos_offset - get_tos_offset (td); for (int i = 0; i < csignature->param_count; i++) { - td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [i] = td->sp [i].local; } call_args [csignature->param_count] = -1; @@ -5442,11 +5430,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // We must not optimize out these locals, storing to them is part of the interp call convention // FIXME this affects inlining efficiency. We need to first remove the param moving by NEWOBJ - int *call_args = (int*) mono_mempool_alloc (td->mempool, csignature->param_count * sizeof (int)); - for (int i = 0; i < csignature->param_count; i++) { - td->locals [sp_params [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + int *call_args = (int*) mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); + for (int i = 0; i < csignature->param_count; i++) call_args [i] = sp_params [i].local; - } call_args [csignature->param_count] = -1; // Push the return value and `this` argument to the ctor @@ -5464,7 +5450,6 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type (td, stack_type [ret_mt], klass); } int dreg = td->sp [-2].local; - td->locals [dreg].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; // Push back the params to top of stack push_types (td, sp_params, csignature->param_count); @@ -6049,13 +6034,10 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->sp -= 2; int *call_args = (int*)mono_mempool_alloc (td->mempool, 3 * sizeof (int)); call_args [0] = td->sp [0].local; - td->locals [td->sp [0].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [1] = td->sp [1].local; - td->locals [td->sp [1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; call_args [2] = -1; push_simple_type (td, STACK_TYPE_MP); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - td->locals [td->sp [-1].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; td->last_ins->data [0] = get_data_item_index (td, klass); td->last_ins->info.call_args = call_args; interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); @@ -6671,16 +6653,15 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, CHECK_STACK (td, info->sig->param_count); td->sp -= info->sig->param_count; int *call_args = (int*)mono_mempool_alloc (td->mempool, (info->sig->param_count + 1) * sizeof (int)); - for (int i = 0; i < info->sig->param_count; i++) { - td->locals [td->sp [i].local].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + for (int i = 0; i < info->sig->param_count; i++) call_args [i] = td->sp [i].local; - } call_args [info->sig->param_count] = -1; if (!MONO_TYPE_IS_VOID (info->sig->ret)) { int mt = mint_type (info->sig->ret); push_simple_type (td, stack_type [mt]); dreg = td->sp [-1].local; } + if (jit_icall_id == MONO_JIT_ICALL_mono_threads_attach_coop) { rtm->needs_thread_attach = 1; } else if (jit_icall_id == MONO_JIT_ICALL_mono_threads_detach_coop) { @@ -7576,7 +7557,6 @@ interp_local_deadce (TransformData *td, int *local_ref_count) g_assert (td->locals [i].indirects >= 0); if (!local_ref_count [i] && !td->locals [i].indirects && - !(td->locals [i].flags & INTERP_LOCAL_FLAG_CALL_ARGS) && (td->locals [i].flags & INTERP_LOCAL_FLAG_DEAD) == 0) { needs_dce = TRUE; td->locals [i].flags |= INTERP_LOCAL_FLAG_DEAD; @@ -8096,7 +8076,6 @@ interp_cprop (TransformData *td) } } else if (local_defs [sreg].ins != NULL && (td->locals [sreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && - !(td->locals [sreg].flags & INTERP_LOCAL_FLAG_CALL_ARGS) && !(td->locals [dreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && interp_prev_ins (ins) == local_defs [sreg].ins) { // hackish temporary optimization that won't be necessary in the future From 738e0e37242e1779ef5eef2804e73aa7c6ac0051 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:13:54 +0200 Subject: [PATCH 06/17] [interp] Add new local offset allocator This change aims to simplify the handling of vars during optimizations. Before this change we had different types of vars : managed locals, var residing on the execution stack, vars that are the argument of a call. Multiple restrictions applied to vars residing on the execution stack and to call args. Following this change, all vars share the same semantics during optimizations passes. At the very end, we allocate offsets for them and we will end up with 3 types of vars : global vars (used from multiple bblocks), local vars (used in a single bblock) and call arg vars. Call arg vars are always local. The first step of the allocator is to detect all global vars and allocate offsets for them by doing a full iteration over the code. They will reside in the first section of the stack frame and they are allocated one after the other in the order they are detected. The param area (containing call arg vars) will have to be allocated after the local var space, otherwise a call would overwrite vars in the calling method. These vars are allocated for one basic block at a time. For simple local vars we do an initial iteration over the bblock instructions and we set the liveness information for each referenced var (live_start and live_end). We will maintain a list of active vars and the current top of stack. As a var becomes alive we allocate it at the current offset and add it to the active_vars array. As a var becomes dead, we remove entries from the active_vars array and update the current top of stack, if space has been freed at the end of the stack. For call args, because we must control the offset at which these vars are allocated, in the initial pass we generate MOVs from the var to a new local var, if the call arg was initially global. Afterwards, call arg vars are allocated in a similar manner to normal local vars. The space for them is tied to the param area of the call, so the entire space is allocated at once. A call become active when any of its args is first written. The liveness of the call ends when the actual call is done, at which point we resolve the offset of every arg relative to the start of the param area of the method. Once all normal local vars are allocated, we will compute the final offset of the call arg vars. --- src/mono/mono/mini/interp/interp.c | 12 +- src/mono/mono/mini/interp/transform.c | 530 ++++++++++++++++++++++---- src/mono/mono/mini/interp/transform.h | 17 +- 3 files changed, 464 insertions(+), 95 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 1953ec10bc52b..afe9f27e1bfc8 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -93,8 +93,6 @@ struct FrameClauseArgs { const guint16 *end_at_ip; /* When exiting this clause we also exit the frame */ int exit_clause; - /* Exception that we are filtering */ - MonoException *filter_exception; /* Frame that is executing this clause */ InterpFrame *exec_frame; }; @@ -3168,11 +3166,6 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs INIT_INTERP_STATE (frame, clause_args); - if (clause_args && clause_args->filter_exception) { - // Write the exception on to the first slot on the excecution stack - LOCAL_VAR (frame->imethod->total_locals_size, MonoException*) = clause_args->filter_exception; - } - #ifdef ENABLE_EXPERIMENT_TIERED mini_tiered_inc (frame->imethod->method, &frame->imethod->tiered_counter, 0); #endif @@ -6713,8 +6706,6 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs /* spec says stack should be empty at endfinally so it should be at the start too */ locals = (guchar*)frame->stack; g_assert (context->exc_gchandle); - // Write the exception on to the first slot on the excecution stack - LOCAL_VAR (frame->imethod->total_locals_size, MonoObject*) = mono_gchandle_get_target_internal (context->exc_gchandle); clear_resume_state (context); // goto main_loop instead of MINT_IN_DISPATCH helps the compiler and therefore conserves stack. @@ -6898,12 +6889,13 @@ interp_run_filter (StackFrameInfo *frame, MonoException *ex, int clause_index, g /* Copy the stack frame of the original method */ memcpy (child_frame.stack, iframe->stack, iframe->imethod->total_locals_size); + // Write the exception object in its reserved stack slot + *((MonoException**)((char*)child_frame.stack + iframe->imethod->clause_data_offsets [clause_index])) = ex; context->stack_pointer += iframe->imethod->alloca_size; memset (&clause_args, 0, sizeof (FrameClauseArgs)); clause_args.start_with_ip = (const guint16*)handler_ip; clause_args.end_at_ip = (const guint16*)handler_ip_end; - clause_args.filter_exception = ex; clause_args.exec_frame = &child_frame; interp_exec_method (&child_frame, context, &clause_args); diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 27d4d833bae58..b39e054864320 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -423,6 +423,8 @@ create_interp_local_explicit (TransformData *td, MonoType *type, int size) td->locals [td->locals_size].indirects = 0; td->locals [td->locals_size].offset = -1; td->locals [td->locals_size].size = size; + td->locals [td->locals_size].live_start = -1; + td->locals [td->locals_size].bb_index = -1; td->locals_size++; return td->locals_size - 1; @@ -434,7 +436,6 @@ create_interp_stack_local (TransformData *td, int type, MonoClass *k, int type_s int local = create_interp_local_explicit (td, get_type_from_stack (type, k), type_size); td->locals [local].flags |= INTERP_LOCAL_FLAG_EXECUTION_STACK; - td->locals [local].stack_offset = offset; return local; } @@ -1184,6 +1185,7 @@ interp_generate_mae_throw (TransformData *td, MonoMethod *method, MonoMethod *ta interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = call_args; + td->last_ins->flags |= INTERP_INST_FLAG_CALL; } static void @@ -1195,6 +1197,7 @@ interp_generate_bie_throw (TransformData *td) interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = NULL; + td->last_ins->flags |= INTERP_INST_FLAG_CALL; } static void @@ -1206,6 +1209,7 @@ interp_generate_not_supported_throw (TransformData *td) interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = NULL; + td->last_ins->flags |= INTERP_INST_FLAG_CALL; } static void @@ -1242,6 +1246,7 @@ interp_generate_ipe_throw_with_msg (TransformData *td, MonoError *error_msg) interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = call_args; + td->last_ins->flags |= INTERP_INST_FLAG_CALL; } static int @@ -1255,34 +1260,28 @@ create_interp_local (TransformData *td, MonoType *type) return create_interp_local_explicit (td, type, size); } +// Allocates var at the offset that tos points to, also updating it. static int -get_interp_local_offset (TransformData *td, int local) +alloc_var_offset (TransformData *td, int local, gint32 *ptos) { - // FIXME MINT_PROF_EXIT when void - if (local == -1) - return -1; + int size, offset; - if (td->locals [local].offset != -1) - return td->locals [local].offset; + offset = *ptos; + size = td->locals [local].size; - if (td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) { - td->locals [local].offset = td->total_locals_size + td->locals [local].stack_offset; - } else { - int size, offset; - - offset = td->total_locals_size; - size = td->locals [local].size; - - td->locals [local].offset = offset; + td->locals [local].offset = offset; - td->total_locals_size = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); - } - - //g_assert (td->total_locals_size < G_MAXUINT16); + *ptos = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); return td->locals [local].offset; } +static int +alloc_global_var_offset (TransformData *td, int var) +{ + return alloc_var_offset (td, var, &td->total_locals_size); +} + /* * ins_offset is the associated offset of this instruction * if ins is null, it means the data belongs to an instruction that was @@ -1713,7 +1712,6 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check MonoClass *element_class = m_class_get_element_class (array_class); int rank = m_class_get_rank (array_class); int size = mono_class_array_element_size (element_class); - gboolean is_call_args = FALSE; gboolean bounded = m_class_get_byval_arg (array_class) ? m_class_get_byval_arg (array_class)->type == MONO_TYPE_ARRAY : FALSE; @@ -1727,6 +1725,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check td->last_ins->data [0] = size; } else { interp_add_ins (td, MINT_LDELEMA); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); for (int i = 0; i < rank + 1; i++) { call_args [i] = td->sp [i].local; @@ -1736,10 +1735,11 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check g_assert (size < G_MAXUINT16); td->last_ins->data [1] = size; td->last_ins->info.call_args = call_args; - is_call_args = TRUE; + td->last_ins->flags |= INTERP_INST_FLAG_CALL; } } else { interp_add_ins (td, MINT_LDELEMA_TC); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); for (int i = 0; i < rank + 1; i++) { call_args [i] = td->sp [i].local; @@ -1747,13 +1747,11 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check call_args [rank + 1] = -1; td->last_ins->data [0] = get_data_item_index (td, check_class); td->last_ins->info.call_args = call_args; - is_call_args = TRUE; + td->last_ins->flags |= INTERP_INST_FLAG_CALL; } push_simple_type (td, STACK_TYPE_MP); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - if (is_call_args) - interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); } static gboolean @@ -3024,18 +3022,11 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target td->sp -= csignature->param_count + !!csignature->hasthis; guint32 params_stack_size = tos_offset - get_tos_offset (td); - int *call_args = NULL; - - if (op == -1 || mono_interp_op_dregs [op] == MINT_CALL_ARGS) { - // We must not optimize out these locals, storing to them is part of the interp call convention - // unless we already intrinsified this call - int num_args = csignature->param_count + !!csignature->hasthis; - call_args = (int*) mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); - for (int i = 0; i < num_args; i++) { - call_args [i] = td->sp [i].local; - } - call_args [num_args] = -1; - } + int num_args = csignature->param_count + !!csignature->hasthis; + int *call_args = (int*) mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); + for (int i = 0; i < num_args; i++) + call_args [i] = td->sp [i].local; + call_args [num_args] = -1; // We overwrite it with the return local, save it for future use if (csignature->param_count || csignature->hasthis) @@ -3100,6 +3091,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target interp_add_ins (td, MINT_JIT_CALL); interp_ins_set_dreg (td->last_ins, dreg); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); + td->last_ins->flags |= INTERP_INST_FLAG_CALL; td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (target_method, error)); mono_error_assert_ok (error); } else { @@ -3196,6 +3188,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } #endif } + td->last_ins->flags |= INTERP_INST_FLAG_CALL; } td->ip += 5; td->last_ins->info.call_args = call_args; @@ -3618,7 +3611,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet int mt = mint_type (type); td->locals [i].type = type; td->locals [i].offset = offset; - td->locals [i].flags = 0; + td->locals [i].flags = INTERP_LOCAL_FLAG_GLOBAL; td->locals [i].indirects = 0; td->locals [i].mt = mt; if (mt == MINT_TYPE_VT && (!sig->hasthis || i != 0)) { @@ -3646,7 +3639,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet imethod->local_offsets [i] = offset; td->locals [index].type = header->locals [i]; td->locals [index].offset = offset; - td->locals [index].flags = 0; + td->locals [index].flags = INTERP_LOCAL_FLAG_GLOBAL; td->locals [index].indirects = 0; td->locals [index].mt = mint_type (header->locals [i]); if (td->locals [index].mt == MINT_TYPE_VT) @@ -3658,16 +3651,17 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet } offset = ALIGN_TO (offset, MINT_VT_ALIGNMENT); td->il_locals_size = offset - td->il_locals_offset; + td->total_locals_size = offset; imethod->clause_data_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32)); + td->clause_vars = (int*)mono_mempool_alloc (td->mempool, sizeof (int) * header->num_clauses); for (i = 0; i < header->num_clauses; i++) { - imethod->clause_data_offsets [i] = offset; - offset += sizeof (MonoObject*); + int var = create_interp_local (td, mono_get_object_type ()); + td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + alloc_global_var_offset (td, var); + imethod->clause_data_offsets [i] = td->locals [var].offset; + td->clause_vars [i] = var; } - offset = ALIGN_TO (offset, MINT_VT_ALIGNMENT); - - //g_assert (offset < G_MAXUINT16); - td->total_locals_size = offset; } void @@ -3965,7 +3959,7 @@ initialize_clause_bblocks (TransformData *td) bb->stack_state [0].klass = NULL; /*FIX*/ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE; bb->stack_state [0].offset = 0; - bb->stack_state [0].local = create_interp_stack_local (td, STACK_TYPE_O, NULL, MINT_STACK_SLOT_SIZE, 0); + bb->stack_state [0].local = td->clause_vars [i]; } if (c->flags == MONO_EXCEPTION_CLAUSE_FILTER) { @@ -3978,7 +3972,7 @@ initialize_clause_bblocks (TransformData *td) bb->stack_state [0].klass = NULL; /*FIX*/ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE; bb->stack_state [0].offset = 0; - bb->stack_state [0].local = create_interp_stack_local (td, STACK_TYPE_O, NULL, MINT_STACK_SLOT_SIZE, 0); + bb->stack_state [0].local = td->clause_vars [i]; } else if (c->flags == MONO_EXCEPTION_CLAUSE_NONE) { /* * JIT doesn't emit sdb seq intr point at the start of catch clause, probably @@ -5377,6 +5371,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type (td, stack_type [ret_mt], klass); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); + td->last_ins->flags |= INTERP_INST_FLAG_CALL; td->last_ins->info.call_args = call_args; } else if (klass == mono_defaults.string_class) { int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); @@ -5395,6 +5390,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type (td, stack_type [ret_mt], klass); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); + td->last_ins->flags |= INTERP_INST_FLAG_CALL; td->last_ins->info.call_args = call_args; } else if (m_class_get_image (klass) == mono_defaults.corlib && !strcmp (m_class_get_name (m->klass), "ByReference`1") && @@ -5473,7 +5469,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, newobj_fast->data [2] = params_stack_size; newobj_fast->data [3] = csignature->param_count; - if ((mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m, csignature)) { + if (0 && (mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m, csignature)) { MonoMethodHeader *mheader = interp_method_get_header (m, error); goto_if_nok (error, exit); @@ -5505,6 +5501,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [1] = params_stack_size; } interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); + td->last_ins->flags |= INTERP_INST_FLAG_CALL; td->last_ins->info.call_args = call_args; goto_if_nok (error, exit); // Parameters and this pointer are popped of the stack. The return value remains @@ -6041,6 +6038,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [0] = get_data_item_index (td, klass); td->last_ins->info.call_args = call_args; interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); + td->last_ins->flags |= INTERP_INST_FLAG_CALL; } else { interp_add_ins (td, MINT_LDELEMA1); td->sp -= 2; @@ -6627,7 +6625,6 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // Push back to top of stack and fixup the local offset push_types (td, &tos, 1); td->sp [-1].local = saved_local; - td->locals [saved_local].stack_offset = td->sp [-1].offset; if (!interp_transform_call (td, method, NULL, generic_context, NULL, FALSE, error, FALSE, FALSE, FALSE)) goto exit; @@ -6675,6 +6672,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (dreg != -1) interp_ins_set_dreg (td->last_ins, dreg); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); + td->last_ins->flags |= INTERP_INST_FLAG_CALL; td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func); td->last_ins->info.call_args = call_args; } @@ -7329,7 +7327,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in *ip++ = opcode; if (opcode == MINT_SWITCH) { int labels = READ32 (&ins->data [0]); - *ip++ = get_interp_local_offset (td, ins->sregs [0]); + *ip++ = td->locals [ins->sregs [0]].offset; // Write number of switch labels *ip++ = ins->data [0]; *ip++ = ins->data [1]; @@ -7348,7 +7346,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in opcode == MINT_BR_S || opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK || opcode == MINT_CALL_HANDLER_S) { const int br_offset = start_ip - td->new_code; for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) - *ip++ = get_interp_local_offset (td, ins->sregs [i]); + *ip++ = td->locals [ins->sregs [i]].offset; if (ins->info.target_bb->native_offset >= 0) { // Backwards branch. We can already patch it. *ip++ = ins->info.target_bb->native_offset - br_offset; @@ -7369,7 +7367,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in opcode == MINT_BR || opcode == MINT_LEAVE || opcode == MINT_LEAVE_CHECK || opcode == MINT_CALL_HANDLER) { const int br_offset = start_ip - td->new_code; for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) - *ip++ = get_interp_local_offset (td, ins->sregs [i]); + *ip++ = td->locals [ins->sregs [i]].offset; if (ins->info.target_bb->native_offset >= 0) { // Backwards branch. We can already patch it int target_offset = ins->info.target_bb->native_offset - br_offset; @@ -7435,14 +7433,18 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in #endif } else { if (mono_interp_op_dregs [opcode]) - *ip++ = get_interp_local_offset (td, ins->dreg); + *ip++ = td->locals [ins->dreg].offset; if (mono_interp_op_sregs [opcode]) { - for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) - *ip++ = get_interp_local_offset (td, ins->sregs [i]); + for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { + if (ins->sregs [i] == MINT_CALL_ARGS_SREG) + *ip++ = td->locals [ins->info.call_args [0]].offset; + else + *ip++ = td->locals [ins->sregs [i]].offset; + } } else if (opcode == MINT_LDLOCA_S) { // This opcode receives a local but it is not viewed as a sreg since we don't load the value - *ip++ = get_interp_local_offset (td, ins->sregs [0]); + *ip++ = td->locals [ins->sregs [0]].offset; } int left = get_inst_length (ins) - (ip - start_ip); @@ -7454,28 +7456,6 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in return ip; } -static void -alloc_ins_locals (TransformData *td, InterpInst *ins) -{ - int opcode = ins->opcode; - if (mono_interp_op_sregs [opcode]) { - for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { - int local = ins->sregs [i]; - if (!(td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK)) - get_interp_local_offset (td, local); - } - } else if (opcode == MINT_LDLOCA_S) { - // This opcode receives a local but it is not viewed as a sreg since we don't load the value - get_interp_local_offset (td, ins->sregs [0]); - } - - if (mono_interp_op_dregs [opcode]) { - int local = ins->dreg; - if (!(td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK)) - get_interp_local_offset (td, local); - } -} - // Generates the final code, after we are done with all the passes static void generate_compacted_code (TransformData *td) @@ -7490,7 +7470,6 @@ generate_compacted_code (TransformData *td) InterpInst *ins = bb->first_ins; while (ins) { size += get_inst_length (ins); - alloc_ins_locals (td, ins); ins = ins->next; } } @@ -8217,13 +8196,400 @@ interp_optimize_code (TransformData *td) if (mono_interp_opt & INTERP_OPT_BBLOCKS) interp_optimize_bblocks (td); - if (mono_interp_opt & INTERP_OPT_CPROP) - MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td)); +// if (mono_interp_opt & INTERP_OPT_CPROP) +// MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td)); if (mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td)); } +static void +foreach_local_var (TransformData *td, InterpInst *ins, int data, void (*callback)(TransformData*, int, int)) +{ + int opcode = ins->opcode; + if (mono_interp_op_sregs [opcode]) { + for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { + int sreg = ins->sregs [i]; + + if (sreg == MINT_CALL_ARGS_SREG) { + int *call_args = ins->info.call_args; + if (call_args) { + int var = *call_args; + while (var != -1) { + callback (td, var, data); + call_args++; + var = *call_args; + } + } + } else { + callback (td, sreg, data); + } + } + } + + if (mono_interp_op_dregs [opcode]) + callback (td, ins->dreg, data); +} + +static void +set_var_live_range (TransformData *td, int var, int ins_index) +{ + // We don't track liveness yet for global vars + if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) + return; + if (td->locals [var].live_start == -1) + td->locals [var].live_start = ins_index; + td->locals [var].live_end = ins_index; +} + +static void +initialize_global_var (TransformData *td, int var, int bb_index) +{ + // Check if already handled + if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) + return; + + if (td->locals [var].bb_index == -1) { + td->locals [var].bb_index = bb_index; + } else if (td->locals [var].bb_index != bb_index) { + // var used in multiple basic blocks + if (td->verbose_level) + g_print ("alloc global var %d to offset %d\n", var, td->total_locals_size); + alloc_global_var_offset (td, var); + td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + } +} + +static void +initialize_global_vars (TransformData *td) +{ + InterpBasicBlock *bb; + + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + if (opcode == MINT_NOP) { + continue; + } else if (opcode == MINT_LDLOCA_S) { + int var = ins->sregs [0]; + // If global flag is set, it means its offset was already allocated + if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL)) { + if (td->verbose_level) + g_print ("alloc ldloca global var %d to offset %d\n", var, td->total_locals_size); + alloc_global_var_offset (td, var); + td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + } + } + foreach_local_var (td, ins, bb->index, initialize_global_var); + } + } +} + +// Data structure used for offset allocation of call args +typedef struct { + InterpInst *call; + int param_size; +} ActiveCall; + +typedef struct { + ActiveCall *active_calls; + int active_calls_count; + int active_calls_capacity; + int param_size; +} ActiveCalls; + +static void +init_active_calls (TransformData *td, ActiveCalls *ac) +{ + ac->active_calls_count = 0; + ac->active_calls_capacity = 5; + ac->active_calls = (ActiveCall*)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (ActiveCall)); + ac->param_size = 0; +} + +static void +reinit_active_calls (TransformData *td, ActiveCalls *ac) +{ + ac->active_calls_count = 0; + ac->param_size = 0; +} + +static int +get_call_param_size (TransformData *td, InterpInst *call) +{ + int *call_args = call->info.call_args; + if (!call_args) + return 0; + + int param_size = 0; + + int var = *call_args; + while (var != -1) { + param_size = ALIGN_TO (param_size + td->locals [var].size, MINT_STACK_SLOT_SIZE); + call_args++; + var = *call_args; + } + return param_size; +} + +static void +add_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) +{ + // Check if already added + if (call->flags & INTERP_INST_FLAG_ACTIVE_CALL) + return; + + if (ac->active_calls_count == ac->active_calls_capacity) { + ActiveCall *old = ac->active_calls; + ac->active_calls_capacity *= 2; + ac->active_calls = (ActiveCall*)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (ActiveCall)); + memcpy (ac->active_calls, old, ac->active_calls_count * sizeof (ActiveCall)); + } + + ac->active_calls [ac->active_calls_count].call = call; + ac->active_calls [ac->active_calls_count].param_size = get_call_param_size (td, call); + ac->param_size += ac->active_calls [ac->active_calls_count].param_size; + ac->active_calls_count++; + + // Mark a flag on it so we don't have to lookup the array with every argument store. + call->flags |= INTERP_INST_FLAG_ACTIVE_CALL; +} + +static void +end_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) +{ + // Remove call from array + for (int i = 0; i < ac->active_calls_count; i++) { + if (ac->active_calls [i].call == call) { + ac->active_calls_count--; + ac->param_size -= ac->active_calls [i].param_size; + // Since this entry is removed, move the last entry into it + if (ac->active_calls_count > 0 && i < ac->active_calls_count) + ac->active_calls [i] = ac->active_calls [ac->active_calls_count]; + } + } + // This is the relative offset (to the start of the call args stack) where the args + // for this call reside. + int start_offset = ac->param_size; + + // Compute to offset of each call argument + int *call_args = call->info.call_args; + if (call_args && (*call_args != -1)) { + int var = *call_args; + while (var != -1) { + alloc_var_offset (td, var, &start_offset); + call_args++; + var = *call_args; + } + } else { + // This call has no argument. Allocate a dummy one so when we resolve the + // offset for MINT_CALL_ARGS_SREG during compacted instruction emit, we can + // always use the offset of the first var in the call_args array + int new_var = create_interp_local (td, mono_get_int_type ()); + td->locals [new_var].call = call; + td->locals [new_var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + alloc_var_offset (td, new_var, &start_offset); + + call_args = (int*)mono_mempool_alloc (td->mempool, 3 * sizeof (int)); + call_args [0] = new_var; + call_args [1] = -1; + + call->info.call_args = call_args; + } +} + +// Data structure used for offset allocation of local vars + +typedef struct { + int var; + gboolean is_alive; +} ActiveVar; + +static int +compact_active_vars (TransformData *td, ActiveVar *active_vars, int active_vars_count, gint32 *current_offset) +{ + if (!active_vars_count) + return 0; + int i = active_vars_count - 1; + while (i >= 0 && !active_vars [i].is_alive) { + active_vars_count--; + *current_offset = td->locals [active_vars [i].var].offset; + i--; + } + return active_vars_count; +} + +static void +interp_alloc_offsets (TransformData *td) +{ + InterpBasicBlock *bb; + ActiveCalls ac; + + if (td->verbose_level) + g_print ("\nvar offset allocator iteration\n"); + + initialize_global_vars (td); + + int active_vars_capacity = MAX (td->locals_size / td->bb_count, 10); + ActiveVar *active_vars = (ActiveVar*)mono_mempool_alloc (td->mempool, active_vars_capacity * sizeof (ActiveVar)); + + init_active_calls (td, &ac); + + int final_total_locals_size = td->total_locals_size; + // We now have the top of stack offset. All local regs are allocated after this offset, with each basic block + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + int ins_index = 0; + if (td->verbose_level) + g_print ("BB%d\n", bb->index); + + reinit_active_calls (td, &ac); + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + if (ins->opcode == MINT_NOP) + continue; + if (ins->opcode == MINT_NEWOBJ_VT_FAST || ins->opcode == MINT_NEWOBJ_FAST || + ins->opcode == MINT_NEWOBJ || ins->opcode == MINT_NEWOBJ_STRING) { + // The offset allocator assumes that the liveness of destination var starts + // after the source vars, which means the destination var can be allocated + // at the same offset as some of the arguments. However, for newobj opcodes, + // the created object is set before the call is made. We solve this by making + // sure that the dreg is not allocated in the param area, so there is no + // risk of conflicts. + td->locals [ins->dreg].flags |= INTERP_LOCAL_FLAG_NO_CALL_ARGS; + } + if (ins->flags & INTERP_INST_FLAG_CALL) { + int *call_args = ins->info.call_args; + if (call_args) { + int var = *call_args; + while (var != -1) { + if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL || + td->locals [var].flags & INTERP_LOCAL_FLAG_NO_CALL_ARGS) { + // A global var is an argument to a call, which is not allowed. We need + // to copy the global var into a local var + int new_var = create_interp_local (td, td->locals [var].type); + td->locals [new_var].call = ins; + td->locals [new_var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + int opcode = get_mov_for_type (mint_type (td->locals [var].type), FALSE); + InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); + interp_ins_set_dreg (new_inst, new_var); + interp_ins_set_sreg (new_inst, var); + if (opcode == MINT_MOV_VT) + new_inst->data [0] = td->locals [var].size; + // The arg of the call is no longer global + *call_args = new_var; + // Also update liveness for this instruction + foreach_local_var (td, new_inst, ins_index, set_var_live_range); + ins_index++; + } else { + // Flag this var as it has special storage on the call args stack + td->locals [var].call = ins; + td->locals [var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + } + call_args++; + var = *call_args; + } + } + } + // Set live_start and live_end for every referenced local that is not global + foreach_local_var (td, ins, ins_index, set_var_live_range); + ins_index++; + } + gint32 current_offset = td->total_locals_size; + int active_vars_count = 0; + + ins_index = 0; + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + gboolean is_call = ins->flags & INTERP_INST_FLAG_CALL; + + if (opcode == MINT_NOP) + continue; + + if (td->verbose_level) { + g_print ("\tins_index %d\t", ins_index); + dump_interp_inst (ins); + } + + // Expire source vars. We first mark them as not alive and then compact the array + for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { + int var = ins->sregs [i]; + if (var == MINT_CALL_ARGS_SREG) + continue; + if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) { + g_assert (!(td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS)); + // Iterate over active vars, set the entry associated with var as !is_alive + for (int j = 0; j < active_vars_count; j++) { + if (active_vars [j].var == var) { + active_vars [j].is_alive = FALSE; + break; + } + } + } + } + + if (is_call) + end_active_call (td, &ac, ins); + + active_vars_count = compact_active_vars (td, active_vars, active_vars_count, ¤t_offset); + + // Alloc dreg local starting at the stack_offset + if (mono_interp_op_dregs [opcode]) { + int var = ins->dreg; + + if (td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { + add_active_call (td, &ac, td->locals [var].call); + } else if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].offset == -1) { + alloc_var_offset (td, var, ¤t_offset); + if (current_offset > final_total_locals_size) + final_total_locals_size = current_offset; + + if (td->verbose_level) + g_print ("alloc var %d to offset %d\n", var, td->locals [var].offset); + + if (td->locals [var].live_end > ins_index) { + // if dreg is still used in the basic block, add it to the active list + if (active_vars_count == active_vars_capacity) { + active_vars_capacity *= 2; + ActiveVar *new_array = (ActiveVar*)mono_mempool_alloc (td->mempool, active_vars_capacity * sizeof (ActiveVar)); + memcpy (new_array, active_vars, active_vars_count * sizeof (ActiveVar)); + active_vars = new_array; + } + active_vars [active_vars_count].var = var; + active_vars [active_vars_count].is_alive = TRUE; + active_vars_count++; + } else { + current_offset = td->locals [var].offset; + } + } + } + if (td->verbose_level) { + g_print ("active :"); + for (int i = 0; i < active_vars_count; i++) { + if (active_vars [i].is_alive) + g_print (" %d (end %d),", active_vars [i].var, td->locals [active_vars [i].var].live_end); + } + g_print ("\n"); + } + ins_index++; + } + } + + // Iterate over all call args locals, update their final offset (aka add td->total_locals_size to them) + // then also update td->total_locals_size to account for this space. + td->total_locals_size = final_total_locals_size; + for (int i = 0; i < td->locals_size; i++) { + // These are allocated separately at the end of the stack + if (td->locals [i].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { + td->locals [i].offset += td->total_locals_size; + final_total_locals_size = MAX (td->locals [i].offset + td->locals [i].size, final_total_locals_size); + } + } + td->total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_SLOT_SIZE); +} + /* * Very few methods have localloc. Handle it separately to not impact performance * of other methods. We replace the normal return opcodes with opcodes that also @@ -8336,6 +8702,8 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG interp_optimize_code (td); + interp_alloc_offsets (td); + generate_compacted_code (td); if (td->total_locals_size >= G_MAXUINT16) { diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 2ebb0401d02b2..940e6241fd2a8 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -9,10 +9,15 @@ #define INTERP_INST_FLAG_SEQ_POINT_METHOD_EXIT 4 #define INTERP_INST_FLAG_SEQ_POINT_NESTED_CALL 8 #define INTERP_INST_FLAG_RECORD_CALL_PATCH 16 +#define INTERP_INST_FLAG_CALL 32 +// Flag used internally by the var offset allocator +#define INTERP_INST_FLAG_ACTIVE_CALL 64 #define INTERP_LOCAL_FLAG_DEAD 1 #define INTERP_LOCAL_FLAG_EXECUTION_STACK 2 #define INTERP_LOCAL_FLAG_CALL_ARGS 4 +#define INTERP_LOCAL_FLAG_GLOBAL 8 +#define INTERP_LOCAL_FLAG_NO_CALL_ARGS 16 typedef struct _InterpInst InterpInst; typedef struct _InterpBasicBlock InterpBasicBlock; @@ -70,7 +75,7 @@ struct _InterpInst { union { InterpBasicBlock *target_bb; InterpBasicBlock **target_bb_table; - // For CallArgs instruction, this represents an array of all call arg vars + // For call instructions, this represents an array of all call arg vars // in the order they are pushed to the stack. This makes it easy to find // all source vars for these types of opcodes. This is terminated with -1. int *call_args; @@ -139,9 +144,12 @@ typedef struct { int indirects; int offset; int size; + int live_start, live_end; + // index of first basic block where this var is used + int bb_index; union { - // the offset from the start of the execution stack locals space - int stack_offset; + // If var is INTERP_LOCAL_FLAG_CALL_ARGS, this is the call instruction using it + InterpInst *call; }; } InterpLocal; @@ -166,7 +174,7 @@ typedef struct unsigned int max_stack_height; unsigned int stack_capacity; unsigned int max_stack_size; - unsigned int total_locals_size; + gint32 total_locals_size; InterpLocal *locals; unsigned int il_locals_offset; unsigned int il_locals_size; @@ -180,6 +188,7 @@ typedef struct GHashTable *patchsite_hash; #endif int *clause_indexes; + int *clause_vars; gboolean gen_sdb_seq_points; GPtrArray *seq_points; InterpBasicBlock **offset_to_bb; From 5db91b72b7951a4a8d0809731d83684683837ea6 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:14:57 +0200 Subject: [PATCH 07/17] [interp] Improve dumping for call instructions --- src/mono/mono/mini/interp/transform.c | 40 ++++++++++++++++----------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index b39e054864320..2d4b009bb2003 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -1383,9 +1383,7 @@ dump_interp_compacted_ins (const guint16 *ip, const guint16 *start) g_print ("IR_%04x: %-14s", ins_offset, mono_interp_opname (opcode)); ip++; - if (mono_interp_op_dregs [opcode] == MINT_CALL_ARGS) - g_print (" [call_args %d <-", *ip++); - else if (mono_interp_op_dregs [opcode] > 0) + if (mono_interp_op_dregs [opcode] > 0) g_print (" [%d <-", *ip++); else g_print (" [nil <-"); @@ -1418,20 +1416,30 @@ dump_interp_inst_no_newline (InterpInst *ins) int opcode = ins->opcode; g_print ("IL_%04x: %-14s", ins->il_offset, mono_interp_opname (opcode)); - if (mono_interp_op_dregs [opcode] == MINT_CALL_ARGS) - g_print (" [call_args %d <-", ins->dreg); - else if (mono_interp_op_dregs [opcode] > 0) - g_print (" [%d <-", ins->dreg); - else - g_print (" [nil <-"); + if (mono_interp_op_dregs [opcode] > 0) + g_print (" [%d <-", ins->dreg); + else + g_print (" [nil <-"); - if (mono_interp_op_sregs [opcode] > 0) { - for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) - g_print (" %d", ins->sregs [i]); - g_print ("],"); - } else { - g_print (" nil],"); - } + if (mono_interp_op_sregs [opcode] > 0) { + for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { + if (ins->sregs [i] == MINT_CALL_ARGS_SREG) { + g_print (" c:"); + int *call_args = ins->info.call_args; + if (call_args) { + while (*call_args != -1) { + g_print (" %d", *call_args); + call_args++; + } + } + } else { + g_print (" %d", ins->sregs [i]); + } + } + g_print ("],"); + } else { + g_print (" nil],"); + } if (opcode == MINT_LDLOCA_S) { // LDLOCA has special semantics, it has data in sregs [0], but it doesn't have any sregs From 4ebc821d2a76aa2cf1a1cb91e6dc0e23055b5fda Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:15:29 +0200 Subject: [PATCH 08/17] [interp] Fix var type of valuetype this --- src/mono/mono/mini/interp/transform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 2d4b009bb2003..10fedf04af9a9 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -3613,7 +3613,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet for (i = 0; i < num_args; i++) { MonoType *type; if (sig->hasthis && i == 0) - type = m_class_get_byval_arg (td->method->klass); + type = m_class_is_valuetype (td->method->klass) ? m_class_get_this_arg (td->method->klass) : m_class_get_byval_arg (td->method->klass); else type = mono_method_signature_internal (td->method)->params [i - sig->hasthis]; int mt = mint_type (type); @@ -3622,7 +3622,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet td->locals [i].flags = INTERP_LOCAL_FLAG_GLOBAL; td->locals [i].indirects = 0; td->locals [i].mt = mt; - if (mt == MINT_TYPE_VT && (!sig->hasthis || i != 0)) { + if (mt == MINT_TYPE_VT) { size = mono_type_size (type, &align); td->locals [i].size = size; offset += ALIGN_TO (size, MINT_STACK_SLOT_SIZE); From 0985aa810ee178080c7c768b98cf02898d24a99f Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:16:33 +0200 Subject: [PATCH 09/17] [interp] Re-enable copy propagation --- src/mono/mono/mini/interp/transform.c | 64 +++++++++++++++++---------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 10fedf04af9a9..63c38e4269e01 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -7955,6 +7955,30 @@ interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue return ins; } +static void +cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, int *local_ref_count, LocalValue *local_defs) +{ + int sreg = *psreg; + + local_ref_count [sreg]++; + if (local_defs [sreg].type == LOCAL_VALUE_LOCAL) { + int cprop_local = local_defs [sreg].local; + + // We are trying to replace sregs [i] with its def local (cprop_local), but cprop_local has since been + // modified, so we can't use it. + if (local_defs [cprop_local].ins != NULL && local_defs [cprop_local].def_index > local_defs [sreg].def_index) + return; + + if (td->verbose_level) + g_print ("cprop %d -> %d:\n\t", sreg, cprop_local); + local_ref_count [sreg]--; + *psreg = cprop_local; + local_ref_count [cprop_local]++; + if (td->verbose_level) + dump_interp_inst (ins); + } +} + static void interp_cprop (TransformData *td) { @@ -8001,27 +8025,21 @@ interp_cprop (TransformData *td) // FIXME MINT_PROF_EXIT when void if (sregs [i] == -1) continue; - local_ref_count [sregs [i]]++; - if (local_defs [sregs [i]].type == LOCAL_VALUE_LOCAL) { - int cprop_local = local_defs [sregs [i]].local; - // We are not allowed to extend the liveness of execution stack locals because - // it can end up conflicting with another such local. Once we will have our - // own offset allocator for these locals, this restriction can be lifted. - if (td->locals [cprop_local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) - continue; - - // We are trying to replace sregs [i] with its def local (cprop_local), but cprop_local has since been - // modified, so we can't use it. - if (local_defs [cprop_local].ins != NULL && local_defs [cprop_local].def_index > local_defs [sregs [i]].def_index) - continue; - - if (td->verbose_level) - g_print ("cprop %d -> %d:\n\t", sregs [i], cprop_local); - local_ref_count [sregs [i]]--; - sregs [i] = cprop_local; - local_ref_count [cprop_local]++; - if (td->verbose_level) - dump_interp_inst (ins); + if (sregs [i] == MINT_CALL_ARGS_SREG) { + int *call_args = ins->info.call_args; + if (call_args) { + while (*call_args != -1) { + cprop_sreg (td, ins, call_args, local_ref_count, local_defs); + call_args++; + } + } + } else { + cprop_sreg (td, ins, &sregs [i], local_ref_count, local_defs); + // This var is used as a source to a normal instruction. In case this var will + // also be used as source to a call, make sure the offset allocator will create + // a new temporary call arg var and not use this one. Call arg vars have special + // semantics. They can be assigned only once and they die once the call is made. + td->locals [sregs [i]].flags |= INTERP_LOCAL_FLAG_NO_CALL_ARGS; } } @@ -8204,8 +8222,8 @@ interp_optimize_code (TransformData *td) if (mono_interp_opt & INTERP_OPT_BBLOCKS) interp_optimize_bblocks (td); -// if (mono_interp_opt & INTERP_OPT_CPROP) -// MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td)); + if (mono_interp_opt & INTERP_OPT_CPROP) + MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td)); if (mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td)); From 10de44893ce756c3bc04053936fa25cfbfe3c51d Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:19:19 +0200 Subject: [PATCH 10/17] [interp] Rename MINT_NEWOBJ opcodes --- src/mono/mono/mini/interp/interp.c | 31 +++++++++++++++------------ src/mono/mono/mini/interp/mintops.def | 8 ++++--- src/mono/mono/mini/interp/mintops.h | 1 - src/mono/mono/mini/interp/transform.c | 20 +++++------------ 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index afe9f27e1bfc8..95be54e229fcd 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -4739,14 +4739,13 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs ip += 5; goto call; } - MINT_IN_CASE(MINT_NEWOBJ_FAST) { + MINT_IN_CASE(MINT_NEWOBJ) { MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [4]]; INIT_VTABLE (vtable); guint16 imethod_index = ip [3]; guint16 param_size = ip [5]; return_offset = ip [1]; call_args_offset = ip [2]; - const gboolean is_inlined = imethod_index == INLINED_METHOD_FLAG; // Make room for `this` parameter if (param_size) @@ -4763,18 +4762,20 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs // Set `this` arg for ctor call LOCAL_VAR (call_args_offset, MonoObject*) = o; ip += 6; - if (!is_inlined) { - cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index]; - goto call; - } + + cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index]; + goto call; + MINT_IN_BREAK; + } + MINT_IN_CASE(MINT_NEWOBJ_INLINED) { + g_error ("FIXME"); MINT_IN_BREAK; } - MINT_IN_CASE(MINT_NEWOBJ_VT_FAST) { + MINT_IN_CASE(MINT_NEWOBJ_VT) { guint16 imethod_index = ip [3]; guint16 ret_size = ip [4]; guint16 param_size = ip [5]; - gboolean is_inlined = imethod_index == INLINED_METHOD_FLAG; return_offset = ip [1]; call_args_offset = ip [2]; gpointer this_vt = locals + return_offset; @@ -4786,15 +4787,17 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs memset (this_vt, 0, ret_size); // pass the address of the valuetype LOCAL_VAR (call_args_offset, gpointer) = this_vt; - ip += 6; - if (!is_inlined) { - cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index]; - goto call; - } + + cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index]; + goto call; MINT_IN_BREAK; } - MINT_IN_CASE(MINT_NEWOBJ) { + MINT_IN_CASE(MINT_NEWOBJ_VT_INLINED) { + g_error ("FIXME"); + MINT_IN_BREAK; + } + MINT_IN_CASE(MINT_NEWOBJ_SLOW) { guint32 const token = ip [3]; guint16 param_size = ip [4]; return_offset = ip [1]; diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 159bed66b7bd7..a892115891dc3 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -294,11 +294,13 @@ OPDEF(MINT_JMP, "jmp", 2, 0, 0, MintOpMethodToken) OPDEF(MINT_ENDFILTER, "endfilter", 2, 0, 1, MintOpNoArgs) -OPDEF(MINT_NEWOBJ, "newobj", 5, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_SLOW, "newobj_slow", 5, 1, 1, MintOpMethodToken) OPDEF(MINT_NEWOBJ_ARRAY, "newobj_array", 5, 1, 1, MintOpMethodToken) OPDEF(MINT_NEWOBJ_STRING, "newobj_string", 5, 1, 1, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_FAST, "newobj_fast", 6, 1, 1, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_VT_FAST, "newobj_vt_fast", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ, "newobj", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_INLINED, "newobj_inlined", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_VT, "newobj_vt", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_VT_INLINED, "newobj_vt_inlined", 6, 1, 1, MintOpMethodToken) OPDEF(MINT_INITOBJ, "initobj", 3, 0, 1, MintOpShortInt) OPDEF(MINT_CASTCLASS, "castclass", 4, 1, 1, MintOpClassToken) OPDEF(MINT_ISINST, "isinst", 4, 1, 1, MintOpClassToken) diff --git a/src/mono/mono/mini/interp/mintops.h b/src/mono/mono/mini/interp/mintops.h index bf4a0dc999937..f2ae6dfc096fd 100644 --- a/src/mono/mono/mini/interp/mintops.h +++ b/src/mono/mono/mini/interp/mintops.h @@ -60,7 +60,6 @@ typedef enum { #define MINT_IS_BINOP_CONDITIONAL_BRANCH(op) ((op) >= MINT_BEQ_I4 && (op) <= MINT_BLT_UN_R8_S) #define MINT_IS_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_JIT_CALL) #define MINT_IS_PATCHABLE_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_VCALL) -#define MINT_IS_NEWOBJ(op) ((op) >= MINT_NEWOBJ && (op) <= MINT_NEWOBJ_MAGIC) #define MINT_IS_LDC_I4(op) ((op) >= MINT_LDC_I4_M1 && (op) <= MINT_LDC_I4) #define MINT_IS_UNOP(op) ((op) >= MINT_ADD1_I4 && (op) <= MINT_CEQ0_I4) #define MINT_IS_BINOP(op) ((op) >= MINT_ADD_I4 && (op) <= MINT_CLT_UN_R8) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 63c38e4269e01..6fffe5297ad79 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -5463,13 +5463,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, InterpInst *newobj_fast; if (is_vt) { - newobj_fast = interp_add_ins (td, MINT_NEWOBJ_VT_FAST); + newobj_fast = interp_add_ins (td, MINT_NEWOBJ_VT); interp_ins_set_dreg (newobj_fast, dreg); newobj_fast->data [1] = ALIGN_TO (vtsize, MINT_STACK_SLOT_SIZE); } else { MonoVTable *vtable = mono_class_vtable_checked (klass, error); goto_if_nok (error, exit); - newobj_fast = interp_add_ins (td, MINT_NEWOBJ_FAST); + newobj_fast = interp_add_ins (td, MINT_NEWOBJ); interp_ins_set_dreg (newobj_fast, dreg); newobj_fast->data [1] = get_data_item_index (td, vtable); } @@ -5502,7 +5502,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (!td->aggressive_inlining) INLINE_FAILURE; } else { - interp_add_ins (td, MINT_NEWOBJ); + interp_add_ins (td, MINT_NEWOBJ_SLOW); g_assert (!m_class_is_valuetype (klass)); interp_ins_set_dreg (td->last_ins, dreg); td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (m, error)); @@ -8141,16 +8141,6 @@ interp_cprop (TransformData *td) ins = interp_fold_binop (td, local_defs, local_ref_count, ins); } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode)) { ins = interp_fold_binop_cond_br (td, bb, local_defs, local_ref_count, ins); - } else if ((ins->opcode == MINT_NEWOBJ_FAST || ins->opcode == MINT_NEWOBJ_VT_FAST) && ins->data [0] == INLINED_METHOD_FLAG) { - // FIXME Drop the CALL_ARGS flag on the params so this will no longer be necessary - int param_count = ins->data [3]; - int *newobj_reg_map = ins->info.newobj_reg_map; - for (int i = 0; i < param_count; i++) { - int src = newobj_reg_map [i]; - int dst = newobj_reg_map [i + 1 + param_count]; - local_defs [dst] = local_defs [src]; - local_defs [dst].ins = NULL; - } } else if (MINT_IS_LDFLD (opcode) && ins->data [0] == 0) { InterpInst *ldloca = local_defs [sregs [0]].ins; if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S && @@ -8476,8 +8466,8 @@ interp_alloc_offsets (TransformData *td) for (ins = bb->first_ins; ins != NULL; ins = ins->next) { if (ins->opcode == MINT_NOP) continue; - if (ins->opcode == MINT_NEWOBJ_VT_FAST || ins->opcode == MINT_NEWOBJ_FAST || - ins->opcode == MINT_NEWOBJ || ins->opcode == MINT_NEWOBJ_STRING) { + if (ins->opcode == MINT_NEWOBJ || ins->opcode == MINT_NEWOBJ_VT || + ins->opcode == MINT_NEWOBJ_SLOW || ins->opcode == MINT_NEWOBJ_STRING) { // The offset allocator assumes that the liveness of destination var starts // after the source vars, which means the destination var can be allocated // at the same offset as some of the arguments. However, for newobj opcodes, From 8d23d86a2a0fade19f8327ecb6454895d0e5cfa0 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:21:01 +0200 Subject: [PATCH 11/17] [interp] Disable tracking of offsets on the execution stack during codegen They are no longer needed. We generate offsets for every var at the very end. --- src/mono/mono/mini/interp/interp-internals.h | 5 +- src/mono/mono/mini/interp/interp.c | 4 +- src/mono/mono/mini/interp/transform.c | 50 ++++++++------------ src/mono/mono/mini/interp/transform.h | 4 +- 4 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/mono/mono/mini/interp/interp-internals.h b/src/mono/mono/mini/interp/interp-internals.h index 4414a8300a172..2afac6e27ddc1 100644 --- a/src/mono/mono/mini/interp/interp-internals.h +++ b/src/mono/mono/mini/interp/interp-internals.h @@ -125,10 +125,7 @@ struct InterpMethod { MonoType **param_types; MonoJitInfo *jinfo; - // This doesn't include the size of stack locals - guint32 total_locals_size; - // The size of locals that map to the execution stack - guint32 stack_size; + guint32 locals_size; guint32 alloca_size; int num_clauses; // clauses int transformed; // boolean diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 95be54e229fcd..01801b22129c0 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -6891,7 +6891,7 @@ interp_run_filter (StackFrameInfo *frame, MonoException *ex, int clause_index, g child_frame.retval = &retval; /* Copy the stack frame of the original method */ - memcpy (child_frame.stack, iframe->stack, iframe->imethod->total_locals_size); + memcpy (child_frame.stack, iframe->stack, iframe->imethod->locals_size); // Write the exception object in its reserved stack slot *((MonoException**)((char*)child_frame.stack + iframe->imethod->clause_data_offsets [clause_index])) = ex; context->stack_pointer += iframe->imethod->alloca_size; @@ -6904,7 +6904,7 @@ interp_run_filter (StackFrameInfo *frame, MonoException *ex, int clause_index, g interp_exec_method (&child_frame, context, &clause_args); /* Copy back the updated frame */ - memcpy (iframe->stack, child_frame.stack, iframe->imethod->total_locals_size); + memcpy (iframe->stack, child_frame.stack, iframe->imethod->locals_size); context->stack_pointer = (guchar*)child_frame.stack; diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 6fffe5297ad79..65d6a5af9f6c8 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -377,12 +377,12 @@ realloc_stack (TransformData *td) } static int -get_tos_offset (TransformData *td) +get_stack_size (StackInfo *sp, int count) { - if (td->sp == td->stack) - return 0; - else - return td->sp [-1].offset + td->sp [-1].size; + int result = 0; + for (int i = 0; i < count; i++) + result += sp [i].size; + return result; } static MonoType* @@ -431,7 +431,7 @@ create_interp_local_explicit (TransformData *td, MonoType *type, int size) } static int -create_interp_stack_local (TransformData *td, int type, MonoClass *k, int type_size, int offset) +create_interp_stack_local (TransformData *td, int type, MonoClass *k, int type_size) { int local = create_interp_local_explicit (td, get_type_from_stack (type, k), type_size); @@ -451,11 +451,8 @@ push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) td->sp->type = type; td->sp->klass = k; td->sp->flags = 0; - td->sp->offset = get_tos_offset (td); - td->sp->local = create_interp_stack_local (td, type, k, type_size, td->sp->offset); + td->sp->local = create_interp_stack_local (td, type, k, type_size); td->sp->size = ALIGN_TO (type_size, MINT_STACK_SLOT_SIZE); - if ((td->sp->size + td->sp->offset) > td->max_stack_size) - td->max_stack_size = td->sp->size + td->sp->offset; td->sp++; } @@ -483,7 +480,7 @@ static void set_type_and_local (TransformData *td, StackInfo *sp, MonoClass *klass, int type) { SET_TYPE (sp, type, klass); - sp->local = create_interp_stack_local (td, type, NULL, MINT_STACK_SLOT_SIZE, sp->offset); + sp->local = create_interp_stack_local (td, type, NULL, MINT_STACK_SLOT_SIZE); } static void @@ -3026,11 +3023,10 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target fp_sreg = td->sp [0].local; } - guint32 tos_offset = get_tos_offset (td); - td->sp -= csignature->param_count + !!csignature->hasthis; - guint32 params_stack_size = tos_offset - get_tos_offset (td); - int num_args = csignature->param_count + !!csignature->hasthis; + td->sp -= num_args; + guint32 params_stack_size = get_stack_size (td->sp, num_args); + int *call_args = (int*) mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); for (int i = 0; i < num_args; i++) call_args [i] = td->sp [i].local; @@ -3966,7 +3962,6 @@ initialize_clause_bblocks (TransformData *td) bb->stack_state [0].type = STACK_TYPE_O; bb->stack_state [0].klass = NULL; /*FIX*/ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE; - bb->stack_state [0].offset = 0; bb->stack_state [0].local = td->clause_vars [i]; } @@ -3979,7 +3974,6 @@ initialize_clause_bblocks (TransformData *td) bb->stack_state [0].type = STACK_TYPE_O; bb->stack_state [0].klass = NULL; /*FIX*/ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE; - bb->stack_state [0].offset = 0; bb->stack_state [0].local = td->clause_vars [i]; } else if (c->flags == MONO_EXCEPTION_CLAUSE_NONE) { /* @@ -5383,9 +5377,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->info.call_args = call_args; } else if (klass == mono_defaults.string_class) { int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); - guint32 tos_offset = get_tos_offset (td); td->sp -= csignature->param_count; - guint32 params_stack_size = tos_offset - get_tos_offset (td); + guint32 params_stack_size = get_stack_size (td->sp, csignature->param_count); for (int i = 0; i < csignature->param_count; i++) { call_args [i] = td->sp [i].local; @@ -5424,9 +5417,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type_vt (td, klass, mono_class_value_size (klass, NULL)); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); } else { - guint32 tos_offset = get_tos_offset (td); td->sp -= csignature->param_count; - guint32 params_stack_size = tos_offset - get_tos_offset (td); + guint32 params_stack_size = get_stack_size (td->sp, csignature->param_count); // Move params types in temporary buffer StackInfo *sp_params = (StackInfo*) mono_mempool_alloc (td->mempool, sizeof (StackInfo) * csignature->param_count); @@ -8595,11 +8587,11 @@ interp_alloc_offsets (TransformData *td) // Iterate over all call args locals, update their final offset (aka add td->total_locals_size to them) // then also update td->total_locals_size to account for this space. - td->total_locals_size = final_total_locals_size; + td->param_area_offset = final_total_locals_size; for (int i = 0; i < td->locals_size; i++) { // These are allocated separately at the end of the stack if (td->locals [i].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { - td->locals [i].offset += td->total_locals_size; + td->locals [i].offset += td->param_area_offset; final_total_locals_size = MAX (td->locals [i].offset + td->locals [i].size, final_total_locals_size); } } @@ -8733,7 +8725,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG if (td->verbose_level) { g_print ("Runtime method: %s %p\n", mono_method_full_name (method, TRUE), rtm); - g_print ("Locals size %d, stack size: %d\n", td->total_locals_size, td->max_stack_size); + g_print ("Locals size %d\n", td->total_locals_size); g_print ("Calculated stack height: %d, stated height: %d\n", td->max_stack_height, header->max_stack); dump_interp_code (td->new_code, td->new_code_end); } @@ -8764,11 +8756,8 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER) c->data.filter_offset = get_native_offset (td, c->data.filter_offset); } - rtm->stack_size = td->max_stack_size; - // FIXME revisit whether we actually need this - rtm->stack_size += 2 * MINT_STACK_SLOT_SIZE; /* + 1 for returns of called functions + 1 for 0-ing in trace*/ - rtm->total_locals_size = ALIGN_TO (td->total_locals_size, MINT_VT_ALIGNMENT); - rtm->alloca_size = ALIGN_TO (rtm->total_locals_size + rtm->stack_size, 8); + rtm->alloca_size = td->total_locals_size; + rtm->locals_size = td->param_area_offset; rtm->data_items = (gpointer*)mono_mem_manager_alloc0 (td->mem_manager, 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])); @@ -8946,8 +8935,7 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon } if (nm == NULL) { mono_os_mutex_lock (&calc_section); - imethod->stack_size = sizeof (stackval); /* for tracing */ - imethod->alloca_size = imethod->stack_size; + imethod->alloca_size = sizeof (stackval); /* for tracing */ mono_memory_barrier (); imethod->transformed = TRUE; mono_interp_stats.methods_transformed++; diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 940e6241fd2a8..468da1b576e5f 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -32,8 +32,6 @@ typedef struct * the stack a new local is created. */ int local; - /* The offset from the execution stack start where this is stored */ - int offset; /* Saves how much stack this is using. It is a multiple of MINT_VT_ALIGNMENT */ int size; } StackInfo; @@ -173,7 +171,7 @@ typedef struct StackInfo *sp; unsigned int max_stack_height; unsigned int stack_capacity; - unsigned int max_stack_size; + gint32 param_area_offset; gint32 total_locals_size; InterpLocal *locals; unsigned int il_locals_offset; From 1bf4e3df297a831e7c4f051eda0d62396892913a Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:32:11 +0200 Subject: [PATCH 12/17] [interp] Remove memmove of args during newobj The offset allocator is allocating the vars at the right offset in the param area. We also used `push_types` to add the arguments back on the stack, which was allocating new vars for each argument. We no longer do this, so newobj_reg_map is not needed anymore. --- src/mono/mono/mini/interp/interp.c | 25 ++-------- src/mono/mono/mini/interp/mintops.def | 8 ++-- src/mono/mono/mini/interp/transform.c | 67 ++++++++++++--------------- src/mono/mono/mini/interp/transform.h | 3 -- 4 files changed, 38 insertions(+), 65 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 01801b22129c0..96cd8ca61117b 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -4729,28 +4729,19 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs return_offset = ip [1]; call_args_offset = ip [2]; - int param_size = ip [4]; - if (param_size) - memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); - // `this` is implicit null. The created string will be returned // by the call, even though the call has void return (?!). LOCAL_VAR (call_args_offset, gpointer) = NULL; - ip += 5; + ip += 4; goto call; } MINT_IN_CASE(MINT_NEWOBJ) { MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [4]]; INIT_VTABLE (vtable); guint16 imethod_index = ip [3]; - guint16 param_size = ip [5]; return_offset = ip [1]; call_args_offset = ip [2]; - // Make room for `this` parameter - if (param_size) - memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); - MonoObject *o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass)); if (G_UNLIKELY (!o)) { mono_error_set_out_of_memory (error, "Could not allocate %i bytes", m_class_get_instance_size (vtable->klass)); @@ -4761,7 +4752,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs LOCAL_VAR (return_offset, MonoObject*) = o; // Set `this` arg for ctor call LOCAL_VAR (call_args_offset, MonoObject*) = o; - ip += 6; + ip += 5; cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index]; goto call; @@ -4775,19 +4766,15 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_CASE(MINT_NEWOBJ_VT) { guint16 imethod_index = ip [3]; guint16 ret_size = ip [4]; - guint16 param_size = ip [5]; return_offset = ip [1]; call_args_offset = ip [2]; gpointer this_vt = locals + return_offset; - if (param_size) - memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); - // clear the valuetype memset (this_vt, 0, ret_size); // pass the address of the valuetype LOCAL_VAR (call_args_offset, gpointer) = this_vt; - ip += 6; + ip += 5; cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index]; goto call; @@ -4799,15 +4786,11 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs } MINT_IN_CASE(MINT_NEWOBJ_SLOW) { guint32 const token = ip [3]; - guint16 param_size = ip [4]; return_offset = ip [1]; call_args_offset = ip [2]; cmethod = (InterpMethod*)frame->imethod->data_items [token]; - if (param_size) - memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size); - MonoClass * const newobj_class = cmethod->method->klass; /* @@ -4830,7 +4813,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs mono_interp_error_cleanup (error); // FIXME: do not swallow the error EXCEPTION_CHECKPOINT; - ip += 5; + ip += 4; goto call; } MINT_IN_CASE(MINT_INTRINS_SPAN_CTOR) { diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index a892115891dc3..4c22cfaa1f483 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -294,12 +294,12 @@ OPDEF(MINT_JMP, "jmp", 2, 0, 0, MintOpMethodToken) OPDEF(MINT_ENDFILTER, "endfilter", 2, 0, 1, MintOpNoArgs) -OPDEF(MINT_NEWOBJ_SLOW, "newobj_slow", 5, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_SLOW, "newobj_slow", 4, 1, 1, MintOpMethodToken) OPDEF(MINT_NEWOBJ_ARRAY, "newobj_array", 5, 1, 1, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_STRING, "newobj_string", 5, 1, 1, MintOpMethodToken) -OPDEF(MINT_NEWOBJ, "newobj", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_STRING, "newobj_string", 4, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ, "newobj", 5, 1, 1, MintOpMethodToken) OPDEF(MINT_NEWOBJ_INLINED, "newobj_inlined", 6, 1, 1, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_VT, "newobj_vt", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_VT, "newobj_vt", 5, 1, 1, MintOpMethodToken) OPDEF(MINT_NEWOBJ_VT_INLINED, "newobj_vt_inlined", 6, 1, 1, MintOpMethodToken) OPDEF(MINT_INITOBJ, "initobj", 3, 0, 1, MintOpShortInt) OPDEF(MINT_CASTCLASS, "castclass", 4, 1, 1, MintOpClassToken) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 65d6a5af9f6c8..092bb65616d98 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -440,14 +440,20 @@ create_interp_stack_local (TransformData *td, int type, MonoClass *k, int type_s } static void -push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) +ensure_stack (TransformData *td, int additional) { - int sp_height; - sp_height = td->sp - td->stack + 1; - if (sp_height > td->max_stack_height) - td->max_stack_height = sp_height; - if (sp_height > td->stack_capacity) + int current_height = td->sp - td->stack; + int new_height = current_height + additional; + if (new_height > td->stack_capacity) realloc_stack (td); + if (new_height > td->max_stack_height) + td->max_stack_height = new_height; +} + +static void +push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) +{ + ensure_stack (td, 1); td->sp->type = type; td->sp->klass = k; td->sp->flags = 0; @@ -5376,19 +5382,20 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->flags |= INTERP_INST_FLAG_CALL; td->last_ins->info.call_args = call_args; } else if (klass == mono_defaults.string_class) { - int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); + int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 2) * sizeof (int)); td->sp -= csignature->param_count; - guint32 params_stack_size = get_stack_size (td->sp, csignature->param_count); + // First arg is dummy var, it is null when passed to the ctor + call_args [0] = create_interp_stack_local (td, stack_type [ret_mt], NULL, MINT_STACK_SLOT_SIZE); for (int i = 0; i < csignature->param_count; i++) { - call_args [i] = td->sp [i].local; + call_args [i + 1] = td->sp [i].local; } - call_args [csignature->param_count] = -1; + call_args [csignature->param_count + 1] = -1; interp_add_ins (td, MINT_NEWOBJ_STRING); td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (m, error)); - td->last_ins->data [1] = params_stack_size; push_type (td, stack_type [ret_mt], klass); + interp_ins_set_dreg (td->last_ins, td->sp [-1].local); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->flags |= INTERP_INST_FLAG_CALL; @@ -5418,19 +5425,11 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_ins_set_dreg (td->last_ins, td->sp [-1].local); } else { td->sp -= csignature->param_count; - guint32 params_stack_size = get_stack_size (td->sp, csignature->param_count); // Move params types in temporary buffer StackInfo *sp_params = (StackInfo*) mono_mempool_alloc (td->mempool, sizeof (StackInfo) * csignature->param_count); memcpy (sp_params, td->sp, sizeof (StackInfo) * csignature->param_count); - // We must not optimize out these locals, storing to them is part of the interp call convention - // FIXME this affects inlining efficiency. We need to first remove the param moving by NEWOBJ - int *call_args = (int*) mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); - for (int i = 0; i < csignature->param_count; i++) - call_args [i] = sp_params [i].local; - call_args [csignature->param_count] = -1; - // Push the return value and `this` argument to the ctor gboolean is_vt = m_class_is_valuetype (klass); int vtsize = 0; @@ -5447,8 +5446,10 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, } int dreg = td->sp [-2].local; - // Push back the params to top of stack - push_types (td, sp_params, csignature->param_count); + // Push back the params to top of stack. The original vars are maintained. + ensure_stack (td, csignature->param_count); + memcpy (td->sp, sp_params, sizeof (StackInfo) * csignature->param_count); + td->sp += csignature->param_count; if (!mono_class_has_finalizer (klass) && !m_class_has_weak_fields (klass)) { @@ -5465,26 +5466,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_ins_set_dreg (newobj_fast, dreg); newobj_fast->data [1] = get_data_item_index (td, vtable); } - // FIXME remove these once we have our own local offset allocator, even for execution stack locals - newobj_fast->data [2] = params_stack_size; - newobj_fast->data [3] = csignature->param_count; if (0 && (mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m, csignature)) { MonoMethodHeader *mheader = interp_method_get_header (m, error); goto_if_nok (error, exit); - // Add local mapping information for cprop to use, in case we inline - int param_count = csignature->param_count; - int *newobj_reg_map = (int*)mono_mempool_alloc (td->mempool, sizeof (int) * (param_count * 2 + 1)); - newobj_reg_map [param_count] = -1; - for (int i = 0; i < param_count; i++) { - newobj_reg_map [i] = sp_params [i].local; - newobj_reg_map [i + 1 + param_count] = td->sp [-param_count + i].local; - } - if (interp_inline_method (td, m, mheader, error)) { newobj_fast->data [0] = INLINED_METHOD_FLAG; - newobj_fast->info.newobj_reg_map = newobj_reg_map; break; } } @@ -5498,14 +5486,19 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, g_assert (!m_class_is_valuetype (klass)); interp_ins_set_dreg (td->last_ins, dreg); td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (m, error)); - td->last_ins->data [1] = params_stack_size; } + goto_if_nok (error, exit); + interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->flags |= INTERP_INST_FLAG_CALL; - td->last_ins->info.call_args = call_args; - goto_if_nok (error, exit); // Parameters and this pointer are popped of the stack. The return value remains td->sp -= csignature->param_count + 1; + // Save the arguments for the call + int *call_args = (int*) mono_mempool_alloc (td->mempool, (csignature->param_count + 2) * sizeof (int)); + for (int i = 0; i < csignature->param_count + 1; i++) + call_args [i] = td->sp [i].local; + call_args [csignature->param_count + 1] = -1; + td->last_ins->info.call_args = call_args; } break; } diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 468da1b576e5f..842f54442bd72 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -77,9 +77,6 @@ struct _InterpInst { // in the order they are pushed to the stack. This makes it easy to find // all source vars for these types of opcodes. This is terminated with -1. int *call_args; - // We handle newobj poorly due to not having our own local offset allocator. - // We temporarily use this array to let cprop know the values of the newobj args. - int *newobj_reg_map; } info; // Variable data immediately following the dreg/sreg information. This is represented exactly // in the final code stream as in this array. From 6bad3bcf00a3c74b1b3e7707b8a17ffdec67c85c Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:38:28 +0200 Subject: [PATCH 13/17] [interp] Re-enable inlining of constructors For object ctors, MINT_NEWOBJ_INLINED allocates an object which will be used both as a `this` arg to the ctor as well as the return var from the newobj operation. For valuetype ctors, we need to first inform the var offset allocator that the valuetype exists before the MINT_NEWOBJ_VT_INLINED invocation, which will take its address, which will be used as `this` arg to the inline method. We also need to dummy use the valuetype, so it never dies before the ctor is inlined, otherwise `this` points to garbage. We use this def/dummy_use mechanism in order to avoid promoting the valuetype to a global var, as it happens with normal vars that have their address taken (via ldloca). --- src/mono/mono/mini/interp/interp.c | 22 ++++- src/mono/mono/mini/interp/mintops.def | 6 +- src/mono/mono/mini/interp/transform.c | 119 +++++++++++++++++++++++--- 3 files changed, 133 insertions(+), 14 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 96cd8ca61117b..2c6bcc9e688c1 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -3193,6 +3193,8 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; MINT_IN_CASE(MINT_NOP) MINT_IN_CASE(MINT_NIY) + MINT_IN_CASE(MINT_DEF) + MINT_IN_CASE(MINT_DUMMY_USE) g_assert_not_reached (); MINT_IN_BREAK; MINT_IN_CASE(MINT_BREAK) @@ -4759,7 +4761,18 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } MINT_IN_CASE(MINT_NEWOBJ_INLINED) { - g_error ("FIXME"); + MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [2]]; + INIT_VTABLE (vtable); + + MonoObject *o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass)); + if (G_UNLIKELY (!o)) { + mono_error_set_out_of_memory (error, "Could not allocate %i bytes", m_class_get_instance_size (vtable->klass)); + THROW_EX (mono_error_convert_to_exception (error), ip); + } + + // This is return value + LOCAL_VAR (ip [1], MonoObject*) = o; + ip += 3; MINT_IN_BREAK; } @@ -4781,7 +4794,12 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_BREAK; } MINT_IN_CASE(MINT_NEWOBJ_VT_INLINED) { - g_error ("FIXME"); + guint16 ret_size = ip [3]; + gpointer this_vt = locals + ip [2]; + + memset (this_vt, 0, ret_size); + LOCAL_VAR (ip [1], gpointer) = this_vt; + ip += 4; MINT_IN_BREAK; } MINT_IN_CASE(MINT_NEWOBJ_SLOW) { diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 4c22cfaa1f483..03f5184c294b9 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -10,6 +10,8 @@ OPDEF(MINT_NOP, "nop", 1, 0, 0, MintOpNoArgs) OPDEF(MINT_NIY, "niy", 1, 0, 0, MintOpNoArgs) +OPDEF(MINT_DEF, "def", 2, 1, 0, MintOpNoArgs) +OPDEF(MINT_DUMMY_USE, "dummy_use", 2, 0, 1, MintOpNoArgs) OPDEF(MINT_BREAK, "break", 1, 0, 0, MintOpNoArgs) OPDEF(MINT_BREAKPOINT, "breakpoint", 1, 0, 0, MintOpNoArgs) OPDEF(MINT_LDNULL, "ldnull", 2, 1, 0, MintOpNoArgs) @@ -298,9 +300,9 @@ OPDEF(MINT_NEWOBJ_SLOW, "newobj_slow", 4, 1, 1, MintOpMethodToken) OPDEF(MINT_NEWOBJ_ARRAY, "newobj_array", 5, 1, 1, MintOpMethodToken) OPDEF(MINT_NEWOBJ_STRING, "newobj_string", 4, 1, 1, MintOpMethodToken) OPDEF(MINT_NEWOBJ, "newobj", 5, 1, 1, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_INLINED, "newobj_inlined", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_INLINED, "newobj_inlined", 3, 1, 0, MintOpMethodToken) OPDEF(MINT_NEWOBJ_VT, "newobj_vt", 5, 1, 1, MintOpMethodToken) -OPDEF(MINT_NEWOBJ_VT_INLINED, "newobj_vt_inlined", 6, 1, 1, MintOpMethodToken) +OPDEF(MINT_NEWOBJ_VT_INLINED, "newobj_vt_inlined", 4, 1, 1, MintOpMethodToken) OPDEF(MINT_INITOBJ, "initobj", 3, 0, 1, MintOpShortInt) OPDEF(MINT_CASTCLASS, "castclass", 4, 1, 1, MintOpClassToken) OPDEF(MINT_ISINST, "isinst", 4, 1, 1, MintOpClassToken) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 092bb65616d98..c69fbfb6c2136 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -462,6 +462,19 @@ push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) td->sp++; } +static void +push_var (TransformData *td, int var_index) +{ + InterpLocal *var = &td->locals [var_index]; + ensure_stack (td, 1); + td->sp->type = stack_type [var->mt]; + td->sp->klass = mono_class_from_mono_type_internal (var->type); + td->sp->flags = 0; + td->sp->local = var_index; + td->sp->size = ALIGN_TO (var->size, MINT_STACK_SLOT_SIZE); + td->sp++; +} + // This does not handle the size/offset of the entry. For those cases // we need to manually pop the top of the stack and push a new entry. #define SET_SIMPLE_TYPE(s, ty) \ @@ -2786,6 +2799,98 @@ interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHe return ret; } +static gboolean +interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSignature *csignature, int ret_mt, StackInfo *sp_params) +{ + ERROR_DECL(error); + InterpInst *newobj_fast, *prev_last_ins; + int dreg, this_reg = -1; + int prev_sp_offset; + MonoClass *klass = target_method->klass; + + if (!(mono_interp_opt & INTERP_OPT_INLINE) || + !interp_method_check_inlining (td, target_method, csignature)) + return FALSE; + + if (mono_class_has_finalizer (klass) || + m_class_has_weak_fields (klass)) + return FALSE; + + prev_last_ins = td->cbb->last_ins; + prev_sp_offset = td->sp - td->stack; + + // Allocate var holding the newobj result. We do it here, because the var has to be alive + // before the call, since newobj writes to it before executing the call. + gboolean is_vt = m_class_is_valuetype (klass); + int vtsize = 0; + if (is_vt) { + if (ret_mt == MINT_TYPE_VT) + vtsize = mono_class_value_size (klass, NULL); + else + vtsize = MINT_STACK_SLOT_SIZE; + + dreg = create_interp_stack_local (td, stack_type [ret_mt], klass, vtsize); + + // For valuetypes, we need to control the lifetime of the valuetype. + // MINT_NEWOBJ_VT_INLINED takes the address of this reg and we should keep + // the vt alive until the inlining is completed. + interp_add_ins (td, MINT_DEF); + interp_ins_set_dreg (td->last_ins, dreg); + } else { + dreg = create_interp_stack_local (td, stack_type [ret_mt], klass, MINT_STACK_SLOT_SIZE); + } + + // Allocate `this` pointer + if (is_vt) { + push_simple_type (td, STACK_TYPE_I); + this_reg = td->sp [-1].local; + } else { + push_var (td, dreg); + } + + // Push back the params to top of stack. The original vars are maintained. + ensure_stack (td, csignature->param_count); + memcpy (td->sp, sp_params, sizeof (StackInfo) * csignature->param_count); + td->sp += csignature->param_count; + + if (is_vt) { + // Receives the valuetype allocated with MINT_DEF, and returns its address + newobj_fast = interp_add_ins (td, MINT_NEWOBJ_VT_INLINED); + interp_ins_set_dreg (newobj_fast, this_reg); + interp_ins_set_sreg (newobj_fast, dreg); + newobj_fast->data [0] = ALIGN_TO (vtsize, MINT_STACK_SLOT_SIZE); + } else { + MonoVTable *vtable = mono_class_vtable_checked (klass, error); + goto_if_nok (error, fail); + newobj_fast = interp_add_ins (td, MINT_NEWOBJ_INLINED); + interp_ins_set_dreg (newobj_fast, dreg); + newobj_fast->data [0] = get_data_item_index (td, vtable); + } + + MonoMethodHeader *mheader = interp_method_get_header (target_method, error); + goto_if_nok (error, fail); + + if (!interp_inline_method (td, target_method, mheader, error)) + goto fail; + + if (is_vt) { + interp_add_ins (td, MINT_DUMMY_USE); + interp_ins_set_sreg (td->last_ins, dreg); + } + + push_var (td, dreg); + return TRUE; +fail: + // Restore the state + td->sp = td->stack + prev_sp_offset; + td->last_ins = prev_last_ins; + td->cbb->last_ins = prev_last_ins; + if (td->last_ins) + td->last_ins->next = NULL; + + return FALSE; +} + static void interp_constrained_box (TransformData *td, MonoClass *constrained_class, MonoMethodSignature *csignature, MonoError *error) { @@ -5430,6 +5535,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, StackInfo *sp_params = (StackInfo*) mono_mempool_alloc (td->mempool, sizeof (StackInfo) * csignature->param_count); memcpy (sp_params, td->sp, sizeof (StackInfo) * csignature->param_count); + if (interp_inline_newobj (td, m, csignature, ret_mt, sp_params)) + break; + // Push the return value and `this` argument to the ctor gboolean is_vt = m_class_is_valuetype (klass); int vtsize = 0; @@ -5467,15 +5575,6 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, newobj_fast->data [1] = get_data_item_index (td, vtable); } - if (0 && (mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m, csignature)) { - MonoMethodHeader *mheader = interp_method_get_header (m, error); - goto_if_nok (error, exit); - - if (interp_inline_method (td, m, mheader, error)) { - newobj_fast->data [0] = INLINED_METHOD_FLAG; - break; - } - } // Inlining failed. Set the method to be executed as part of newobj instruction newobj_fast->data [0] = get_data_item_index (td, mono_interp_get_imethod (m, error)); /* The constructor was not inlined, abort inlining of current method */ @@ -7314,7 +7413,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in g_array_append_val (td->line_numbers, lne); } - if (opcode == MINT_NOP) + if (opcode == MINT_NOP || opcode == MINT_DEF || opcode == MINT_DUMMY_USE) return ip; *ip++ = opcode; From f18dd20fb7161278ac949acdde8811699941abf0 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 12:40:50 +0200 Subject: [PATCH 14/17] [interp] Avoid optimization if newobj is guarded MINT_NEWOBJ* should not store into a local if the ctor might throw, because we set the return value before the ctor starts executing, and a guarding clause can see this variable as being set. --- src/mono/mono/mini/interp/transform.c | 24 +++++++++++++++++++++--- src/mono/mono/mini/interp/transform.h | 2 ++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index c69fbfb6c2136..79ef8b7b01deb 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -2800,7 +2800,7 @@ interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHe } static gboolean -interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSignature *csignature, int ret_mt, StackInfo *sp_params) +interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSignature *csignature, int ret_mt, StackInfo *sp_params, gboolean is_protected) { ERROR_DECL(error); InterpInst *newobj_fast, *prev_last_ins; @@ -2867,6 +2867,9 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi newobj_fast->data [0] = get_data_item_index (td, vtable); } + if (is_protected) + newobj_fast->flags |= INTERP_INST_FLAG_PROTECTED_NEWOBJ; + MonoMethodHeader *mheader = interp_method_get_header (target_method, error); goto_if_nok (error, fail); @@ -4154,6 +4157,17 @@ handle_stelem (TransformData *td, int op) ++td->ip; } +static gboolean +is_ip_protected (MonoMethodHeader *header, int offset) +{ + for (int i = 0; i < header->num_clauses; i++) { + MonoExceptionClause *clause = &header->clauses [i]; + if (clause->try_offset <= offset && offset < (clause->try_offset + clause->try_len)) + return TRUE; + } + return FALSE; +} + static gboolean generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoGenericContext *generic_context, MonoError *error) { @@ -5430,6 +5444,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_NEWOBJ: { MonoMethod *m; MonoMethodSignature *csignature; + gboolean is_protected = is_ip_protected (header, td->ip - header->code); td->ip++; token = read32 (td->ip); @@ -5535,7 +5550,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, StackInfo *sp_params = (StackInfo*) mono_mempool_alloc (td->mempool, sizeof (StackInfo) * csignature->param_count); memcpy (sp_params, td->sp, sizeof (StackInfo) * csignature->param_count); - if (interp_inline_newobj (td, m, csignature, ret_mt, sp_params)) + if (interp_inline_newobj (td, m, csignature, ret_mt, sp_params, is_protected)) break; // Push the return value and `this` argument to the ctor @@ -5590,6 +5605,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->flags |= INTERP_INST_FLAG_CALL; + if (is_protected) + td->last_ins->flags |= INTERP_INST_FLAG_PROTECTED_NEWOBJ; // Parameters and this pointer are popped of the stack. The return value remains td->sp -= csignature->param_count + 1; // Save the arguments for the call @@ -8166,7 +8183,8 @@ interp_cprop (TransformData *td) } else if (local_defs [sreg].ins != NULL && (td->locals [sreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && !(td->locals [dreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && - interp_prev_ins (ins) == local_defs [sreg].ins) { + interp_prev_ins (ins) == local_defs [sreg].ins && + !(interp_prev_ins (ins)->flags & INTERP_INST_FLAG_PROTECTED_NEWOBJ)) { // hackish temporary optimization that won't be necessary in the future // We replace `local1 <- ?, local2 <- local1` with `local2 <- ?, local1 <- local2` // if local1 is execution stack local and local2 is normal global local. This makes diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 842f54442bd72..0609d202a82bc 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -12,6 +12,8 @@ #define INTERP_INST_FLAG_CALL 32 // Flag used internally by the var offset allocator #define INTERP_INST_FLAG_ACTIVE_CALL 64 +// This instruction is protected by a clause +#define INTERP_INST_FLAG_PROTECTED_NEWOBJ 128 #define INTERP_LOCAL_FLAG_DEAD 1 #define INTERP_LOCAL_FLAG_EXECUTION_STACK 2 From 8ef7f965f47dce9a80da7950d2aa01e1b1c67894 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 3 Mar 2021 20:16:52 +0200 Subject: [PATCH 15/17] [interp] Refactor the active vars code a bit --- src/mono/mono/mini/interp/transform.c | 113 +++++++++++++++++--------- 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 79ef8b7b01deb..a08ceac52dbf1 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -8525,18 +8525,76 @@ typedef struct { gboolean is_alive; } ActiveVar; -static int -compact_active_vars (TransformData *td, ActiveVar *active_vars, int active_vars_count, gint32 *current_offset) +typedef struct { + ActiveVar *active_vars; + int active_vars_count; + int active_vars_capacity; +} ActiveVars; + +static void +init_active_vars (TransformData *td, ActiveVars *av) { - if (!active_vars_count) - return 0; - int i = active_vars_count - 1; - while (i >= 0 && !active_vars [i].is_alive) { - active_vars_count--; - *current_offset = td->locals [active_vars [i].var].offset; + av->active_vars_count = 0; + av->active_vars_capacity = MAX (td->locals_size / td->bb_count, 10); + av->active_vars = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVars)); +} + +static void +reinit_active_vars (TransformData *td, ActiveVars *av) +{ + av->active_vars_count = 0; +} + +static void +add_active_var (TransformData *td, ActiveVars *av, int var) +{ + if (av->active_vars_count == av->active_vars_capacity) { + av->active_vars_capacity *= 2; + ActiveVar *new_array = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVar)); + memcpy (new_array, av->active_vars, av->active_vars_count * sizeof (ActiveVar)); + av->active_vars = new_array; + } + av->active_vars [av->active_vars_count].var = var; + av->active_vars [av->active_vars_count].is_alive = TRUE; + av->active_vars_count++; +} + +static void +end_active_var (TransformData *td, ActiveVars *av, int var) +{ + // Iterate over active vars, set the entry associated with var as !is_alive + for (int i = 0; i < av->active_vars_count; i++) { + if (av->active_vars [i].var == var) { + av->active_vars [i].is_alive = FALSE; + return; + } + } +} + +static void +compact_active_vars (TransformData *td, ActiveVars *av, gint32 *current_offset) +{ + if (!av->active_vars_count) + return; + int i = av->active_vars_count - 1; + while (i >= 0 && !av->active_vars [i].is_alive) { + av->active_vars_count--; + *current_offset = td->locals [av->active_vars [i].var].offset; i--; } - return active_vars_count; +} + +static void +dump_active_vars (TransformData *td, ActiveVars *av) +{ + if (td->verbose_level) { + g_print ("active :"); + for (int i = 0; i < av->active_vars_count; i++) { + if (av->active_vars [i].is_alive) + g_print (" %d (end %d),", av->active_vars [i].var, td->locals [av->active_vars [i].var].live_end); + } + g_print ("\n"); + } } static void @@ -8544,15 +8602,14 @@ interp_alloc_offsets (TransformData *td) { InterpBasicBlock *bb; ActiveCalls ac; + ActiveVars av; if (td->verbose_level) g_print ("\nvar offset allocator iteration\n"); initialize_global_vars (td); - int active_vars_capacity = MAX (td->locals_size / td->bb_count, 10); - ActiveVar *active_vars = (ActiveVar*)mono_mempool_alloc (td->mempool, active_vars_capacity * sizeof (ActiveVar)); - + init_active_vars (td, &av); init_active_calls (td, &ac); int final_total_locals_size = td->total_locals_size; @@ -8564,6 +8621,7 @@ interp_alloc_offsets (TransformData *td) g_print ("BB%d\n", bb->index); reinit_active_calls (td, &ac); + reinit_active_vars (td, &av); for (ins = bb->first_ins; ins != NULL; ins = ins->next) { if (ins->opcode == MINT_NOP) @@ -8616,7 +8674,6 @@ interp_alloc_offsets (TransformData *td) ins_index++; } gint32 current_offset = td->total_locals_size; - int active_vars_count = 0; ins_index = 0; for (ins = bb->first_ins; ins != NULL; ins = ins->next) { @@ -8638,20 +8695,14 @@ interp_alloc_offsets (TransformData *td) continue; if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) { g_assert (!(td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS)); - // Iterate over active vars, set the entry associated with var as !is_alive - for (int j = 0; j < active_vars_count; j++) { - if (active_vars [j].var == var) { - active_vars [j].is_alive = FALSE; - break; - } - } + end_active_var (td, &av, var); } } if (is_call) end_active_call (td, &ac, ins); - active_vars_count = compact_active_vars (td, active_vars, active_vars_count, ¤t_offset); + compact_active_vars (td, &av, ¤t_offset); // Alloc dreg local starting at the stack_offset if (mono_interp_op_dregs [opcode]) { @@ -8669,28 +8720,14 @@ interp_alloc_offsets (TransformData *td) if (td->locals [var].live_end > ins_index) { // if dreg is still used in the basic block, add it to the active list - if (active_vars_count == active_vars_capacity) { - active_vars_capacity *= 2; - ActiveVar *new_array = (ActiveVar*)mono_mempool_alloc (td->mempool, active_vars_capacity * sizeof (ActiveVar)); - memcpy (new_array, active_vars, active_vars_count * sizeof (ActiveVar)); - active_vars = new_array; - } - active_vars [active_vars_count].var = var; - active_vars [active_vars_count].is_alive = TRUE; - active_vars_count++; + add_active_var (td, &av, var); } else { current_offset = td->locals [var].offset; } } } - if (td->verbose_level) { - g_print ("active :"); - for (int i = 0; i < active_vars_count; i++) { - if (active_vars [i].is_alive) - g_print (" %d (end %d),", active_vars [i].var, td->locals [active_vars [i].var].live_end); - } - g_print ("\n"); - } + if (td->verbose_level) + dump_active_vars (td, &av); ins_index++; } } From a30b921daf2205586e59b3b56565f49c9b7b8879 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Fri, 5 Mar 2021 13:51:40 +0200 Subject: [PATCH 16/17] [interp] Add missing implicit conversion When passing an argument or returning a value from a method. The stack contents are not necessarily matching the signature type, in which case we add conversions. --- src/mono/mono/mini/interp/transform.c | 92 +++++++++++++++++---------- 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index a08ceac52dbf1..f22cfb0f74723 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -2924,6 +2924,55 @@ interp_get_method (MonoMethod *method, guint32 token, MonoImage *image, MonoGene return (MonoMethod *)mono_method_get_wrapper_data (method, token); } +/* + * emit_convert: + * + * Emit some implicit conversions which are not part of the .net spec, but are allowed by MS.NET. + */ +static void +emit_convert (TransformData *td, StackInfo *sp, MonoType *target_type) +{ + int stype = sp->type; + target_type = mini_get_underlying_type (target_type); + + // FIXME: Add more + switch (target_type->type) { + case MONO_TYPE_I8: { + switch (stype) { + case STACK_TYPE_I4: + interp_add_conv (td, sp, NULL, STACK_TYPE_I8, MINT_CONV_I8_I4); + break; + default: + break; + } + break; + } +#if SIZEOF_VOID_P == 8 + case MONO_TYPE_I: + case MONO_TYPE_U: { + switch (stype) { + case STACK_TYPE_I4: + interp_add_conv (td, sp, NULL, STACK_TYPE_I8, MINT_CONV_I8_U4); + break; + default: + break; + } + } +#endif + default: + break; + } +} + +static void +interp_emit_arg_conv (TransformData *td, MonoMethodSignature *csignature) +{ + StackInfo *arg_start = td->sp - csignature->param_count; + + for (int i = 0; i < csignature->param_count; i++) + emit_convert (td, &arg_start [i], csignature->params [i]); +} + /* Return FALSE if error, including inline failure */ static gboolean interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target_method, MonoGenericContext *generic_context, MonoClass *constrained_class, gboolean readonly, MonoError *error, gboolean check_visibility, gboolean save_last_error, gboolean tailcall) @@ -3137,6 +3186,8 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target fp_sreg = td->sp [0].local; } + interp_emit_arg_conv (td, csignature); + int num_args = csignature->param_count + !!csignature->hasthis; td->sp -= num_args; guint32 params_stack_size = get_stack_size (td->sp, num_args); @@ -3922,37 +3973,6 @@ interp_emit_load_const (TransformData *td, gpointer field_addr, int mt) return TRUE; } -/* - * emit_convert: - * - * Emit some implicit conversions which are not part of the .net spec, but are allowed by MS.NET. - */ -static void -emit_convert (TransformData *td, int stype, MonoType *ftype) -{ - ftype = mini_get_underlying_type (ftype); - - // FIXME: Add more - switch (ftype->type) { - case MONO_TYPE_I8: { - switch (stype) { - case STACK_TYPE_I4: - td->sp--; - interp_add_ins (td, MINT_CONV_I8_I4); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); - push_simple_type (td, STACK_TYPE_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); - break; - default: - break; - } - break; - } - default: - break; - } -} - static void interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, gboolean is_load, MonoError *error) { @@ -4736,6 +4756,12 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, } case CEE_RET: { link_bblocks = FALSE; + MonoType *ult = mini_type_get_underlying_type (signature->ret); + if (ult->type != MONO_TYPE_VOID) { + // Convert stack contents to return type if necessary + CHECK_STACK (td, 1); + emit_convert (td, td->sp - 1, ult); + } /* Return from inlined method, return value is on top of stack */ if (inlining) { td->ip++; @@ -4748,9 +4774,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, } int vt_size = 0; - MonoType *ult = mini_type_get_underlying_type (signature->ret); if (ult->type != MONO_TYPE_VOID) { - CHECK_STACK (td, 1); --td->sp; if (mint_type (ult) == MINT_TYPE_VT) { MonoClass *klass = mono_class_from_mono_type_internal (ult); @@ -5948,7 +5972,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MonoType *ftype = mono_field_get_type_internal (field); mt = mint_type (ftype); - emit_convert (td, td->sp [-1].type, ftype); + emit_convert (td, td->sp - 1, ftype); /* the vtable of the field might not be initialized at this point */ MonoClass *fld_klass = mono_class_from_mono_type_internal (ftype); From 7cb1406ce0ed0ed05a871c613005b63eb6351af9 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Mon, 8 Mar 2021 13:34:51 +0200 Subject: [PATCH 17/17] [interp] Disable test using excessive stack space This test was exceeding the stack limit even before the new offset allocator, it was just not reported. --- src/tests/issues.targets | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 004c1fc48070e..1aa314e9e2283 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1935,6 +1935,9 @@ https://github.com/dotnet/runtime/issues/46622 + + https://github.com/dotnet/runtime/issues/46622 + https://github.com/dotnet/runtime/issues/37955