Before this change, an InterpFrame contained 3 regions of data : args + locals, valuetype stack, execution stack. Each entry on the execution stack was a stackval structure. The space for valuetypes, was allocated separately, since they have various sizes. When pushing a valuetype on the stack, we allocated first the space for it on the vtstack and then pushed the address of the region on the execution stack. This change merges the execution stack with the valuetype stack, meaning we push now variable sized data on the execution stack. In order to keep track of the current stack location, whenever we push a type on stack, during transform phase, we also keep track of the offset where this value will reside on stack, as well as the size it occupies. All callsites need to be informed how much they need to pop the stack for the arguments. While called code can access this space normally (the args are special locals belonging to the frame and are accessed directly as such), external code needs a new mechanism to detect each argument for a given frame. This is achieved with the lazily initialized arg_offsets array on an InterpMethod. The method doesn't need to be compiled for this array to be correctly initialized.
Why :
- this simplifies handling of valuetypes, their storage follows the same rules as a normal objref/primitive type
- removes the common use of the vt_sp variable. The compiler no longer needs to reserve it in a register during the switch loop, we no longer need to save it with each call. The sp and ip become now the only variables describing the execution state in a method.
- the flow of the data on the execution stack is well behaved now (with the exception of a few opcodes that update directly based on the stack offset). We were using the vtstack for some magic storage (MINT_VTRESULT for example)
- this makes it such that the stack offset of every value is easily known at compile time, making it possible to completely drop the execution stack approach, and have every opcode have a unique dreg and a list of sregs (which are mapped to a certain stack offset). This will enable more advanced optimizations during compile stage.
Co-authored-by: BrzVlad <BrzVlad@users.noreply.github.com>
mono_mb_emit_ldarg (mb, 1);
mono_mb_emit_byte (mb, CEE_LDIND_REF);
- mono_mb_emit_byte (mb, CEE_DUP);
pos = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S);
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
mono_marshal_emit_xdomain_copy_value (mb, byte_array_class);
mono_mb_emit_managed_call (mb, method_rs_deserialize, NULL);
#ifndef __MONO_EE_H__
#define __MONO_EE_H__
-#define MONO_EE_API_VERSION 0xf
+#define MONO_EE_API_VERSION 0x10
typedef struct _MonoInterpStackIter MonoInterpStackIter;
MONO_EE_CALLBACK (gpointer, frame_get_arg, (MonoInterpFrameHandle frame, int pos)) \
MONO_EE_CALLBACK (gpointer, frame_get_local, (MonoInterpFrameHandle frame, int pos)) \
MONO_EE_CALLBACK (gpointer, frame_get_this, (MonoInterpFrameHandle frame)) \
- MONO_EE_CALLBACK (gpointer, frame_get_res, (MonoInterpFrameHandle frame)) \
MONO_EE_CALLBACK (void, frame_arg_to_data, (MonoInterpFrameHandle frame, MonoMethodSignature *sig, int index, gpointer data)) \
MONO_EE_CALLBACK (void, data_to_frame_arg, (MonoInterpFrameHandle frame, MonoMethodSignature *sig, int index, gconstpointer data)) \
MONO_EE_CALLBACK (gpointer, frame_arg_to_storage, (MonoInterpFrameHandle frame, MonoMethodSignature *sig, int index)) \
- MONO_EE_CALLBACK (void, frame_arg_set_storage, (MonoInterpFrameHandle frame, MonoMethodSignature *sig, int index, gpointer storage)) \
MONO_EE_CALLBACK (MonoInterpFrameHandle, frame_get_parent, (MonoInterpFrameHandle frame)) \
MONO_EE_CALLBACK (void, start_single_stepping, (void)) \
MONO_EE_CALLBACK (void, stop_single_stepping, (void)) \
}
static void
-stub_frame_arg_set_storage (MonoInterpFrameHandle frame, MonoMethodSignature *sig, int index, gpointer storage)
-{
- g_assert_not_reached ();
-}
-
-static void
stub_free_context (gpointer context)
{
g_assert_not_reached ();
#define PROFILING_FLAG 0x2
#define MINT_VT_ALIGNMENT 8
+#define MINT_STACK_SLOT_SIZE (sizeof (stackval))
#define INTERP_STACK_SIZE (1024*1024)
MonoExceptionClause *clauses; // num_clauses
void **data_items;
guint32 *local_offsets;
+ guint32 *arg_offsets;
guint32 *clause_data_offsets;
gpointer jit_call_info;
gpointer jit_entry;
guint32 total_locals_size;
guint32 stack_size;
- guint32 vt_stack_size;
guint32 alloca_size;
int num_clauses; // clauses
int transformed; // boolean
frame->state.ip = NULL;
}
+#define STACK_ADD_BYTES(sp,bytes) ((stackval*)((char*)(sp) + ALIGN_TO(bytes, MINT_STACK_SLOT_SIZE)))
+#define STACK_SUB_BYTES(sp,bytes) ((stackval*)((char*)(sp) - ALIGN_TO(bytes, MINT_STACK_SLOT_SIZE)))
+
/*
* List of classes whose methods will be executed by transitioning to JITted code.
* Used for testing.
}
}
-static void inline
+// Returns the size it uses on the interpreter stack
+static int
stackval_from_data (MonoType *type, stackval *result, const void *data, gboolean pinvoke)
{
type = mini_native_type_replace_type (type);
if (type->byref) {
- switch (type->type) {
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_STRING:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- break;
- default:
- break;
- }
result->data.p = *(gpointer*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
}
switch (type->type) {
case MONO_TYPE_VOID:
- return;
+ return 0;
case MONO_TYPE_I1:
result->data.i = *(gint8*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_U1:
case MONO_TYPE_BOOLEAN:
result->data.i = *(guint8*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_I2:
result->data.i = *(gint16*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_U2:
case MONO_TYPE_CHAR:
result->data.i = *(guint16*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_I4:
result->data.i = *(gint32*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_U:
case MONO_TYPE_I:
result->data.nati = *(mono_i*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
result->data.p = *(gpointer*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_U4:
result->data.i = *(guint32*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_R4:
/* memmove handles unaligned case */
memmove (&result->data.f_r4, data, sizeof (float));
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_I8:
case MONO_TYPE_U8:
memmove (&result->data.l, data, sizeof (gint64));
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_R8:
memmove (&result->data.f, data, sizeof (double));
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_STRING:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_CLASS:
case MONO_TYPE_OBJECT:
case MONO_TYPE_ARRAY:
result->data.p = *(gpointer*)data;
- return;
+ return MINT_STACK_SLOT_SIZE;
case MONO_TYPE_VALUETYPE:
if (m_class_is_enumtype (type->data.klass)) {
- stackval_from_data (mono_class_enum_basetype_internal (type->data.klass), result, data, pinvoke);
- return;
- } else if (pinvoke) {
- memcpy (result->data.vt, data, mono_class_native_size (type->data.klass, NULL));
+ return stackval_from_data (mono_class_enum_basetype_internal (type->data.klass), result, data, pinvoke);
} else {
- mono_value_copy_internal (result->data.vt, data, type->data.klass);
+ int size;
+ if (pinvoke)
+ size = mono_class_native_size (type->data.klass, NULL);
+ else
+ size = mono_class_value_size (type->data.klass, NULL);
+ memcpy (result, data, size);
+ return ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
}
- return;
case MONO_TYPE_GENERICINST: {
if (mono_type_generic_inst_is_valuetype (type)) {
MonoClass *klass = mono_class_from_mono_type_internal (type);
+ int size;
if (pinvoke)
- memcpy (result->data.vt, data, mono_class_native_size (klass, NULL));
+ size = mono_class_native_size (klass, NULL);
else
- mono_value_copy_internal (result->data.vt, data, klass);
- return;
+ size = mono_class_value_size (klass, NULL);
+ memcpy (result, data, size);
+ return ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
}
- stackval_from_data (m_class_get_byval_arg (type->data.generic_class->container_class), result, data, pinvoke);
- return;
+ return stackval_from_data (m_class_get_byval_arg (type->data.generic_class->container_class), result, data, pinvoke);
}
default:
g_error ("got type 0x%02x", type->type);
}
}
-static void inline
+static int
stackval_to_data (MonoType *type, stackval *val, void *data, gboolean pinvoke)
{
type = mini_native_type_replace_type (type);
if (type->byref) {
gpointer *p = (gpointer*)data;
*p = val->data.p;
- return;
+ return MINT_STACK_SLOT_SIZE;
}
/* printf ("TODAT0 %p\n", data); */
switch (type->type) {
case MONO_TYPE_U1: {
guint8 *p = (guint8*)data;
*p = val->data.i;
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_BOOLEAN: {
guint8 *p = (guint8*)data;
*p = (val->data.i != 0);
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_I2:
case MONO_TYPE_U2:
case MONO_TYPE_CHAR: {
guint16 *p = (guint16*)data;
*p = val->data.i;
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_I: {
mono_i *p = (mono_i*)data;
a native int - both by csc and mcs). Not sure what to do about sign extension
as it is outside the spec... doing the obvious */
*p = (mono_i)val->data.nati;
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_U: {
mono_u *p = (mono_u*)data;
/* see above. */
*p = (mono_u)val->data.nati;
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_I4:
case MONO_TYPE_U4: {
gint32 *p = (gint32*)data;
*p = val->data.i;
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_I8:
case MONO_TYPE_U8: {
memmove (data, &val->data.l, sizeof (gint64));
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_R4: {
/* memmove handles unaligned case */
memmove (data, &val->data.f_r4, sizeof (float));
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_R8: {
memmove (data, &val->data.f, sizeof (double));
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_STRING:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_ARRAY: {
gpointer *p = (gpointer *) data;
mono_gc_wbarrier_generic_store_internal (p, val->data.o);
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR: {
gpointer *p = (gpointer *) data;
*p = val->data.p;
- return;
+ return MINT_STACK_SLOT_SIZE;
}
case MONO_TYPE_VALUETYPE:
if (m_class_is_enumtype (type->data.klass)) {
- stackval_to_data (mono_class_enum_basetype_internal (type->data.klass), val, data, pinvoke);
- return;
- } else if (pinvoke) {
- memcpy (data, val->data.vt, mono_class_native_size (type->data.klass, NULL));
+ return stackval_to_data (mono_class_enum_basetype_internal (type->data.klass), val, data, pinvoke);
} else {
- mono_value_copy_internal (data, val->data.vt, type->data.klass);
+ int size;
+ if (pinvoke) {
+ size = mono_class_native_size (type->data.klass, NULL);
+ memcpy (data, val, size);
+ } else {
+ size = mono_class_value_size (type->data.klass, NULL);
+ mono_value_copy_internal (data, val, type->data.klass);
+ }
+ return ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
}
- return;
case MONO_TYPE_GENERICINST: {
MonoClass *container_class = type->data.generic_class->container_class;
if (m_class_is_valuetype (container_class) && !m_class_is_enumtype (container_class)) {
MonoClass *klass = mono_class_from_mono_type_internal (type);
- if (pinvoke)
- memcpy (data, val->data.vt, mono_class_native_size (klass, NULL));
- else
- mono_value_copy_internal (data, val->data.vt, klass);
- return;
+ int size;
+ if (pinvoke) {
+ size = mono_class_native_size (klass, NULL);
+ memcpy (data, val, size);
+ } else {
+ size = mono_class_value_size (klass, NULL);
+ mono_value_copy_internal (data, val, klass);
+ }
+ return ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
}
- stackval_to_data (m_class_get_byval_arg (type->data.generic_class->container_class), val, data, pinvoke);
- return;
- }
- default:
- g_error ("got type %x", type->type);
- }
-}
-
-/*
- * Same as stackval_to_data but return address of storage instead
- * of copying the value.
- */
-static gpointer
-stackval_to_data_addr (MonoType *type, stackval *val)
-{
- type = mini_native_type_replace_type (type);
- if (type->byref)
- return &val->data.p;
-
- switch (type->type) {
- case MONO_TYPE_I1:
- case MONO_TYPE_U1:
- case MONO_TYPE_BOOLEAN:
- case MONO_TYPE_I2:
- case MONO_TYPE_U2:
- case MONO_TYPE_CHAR:
- case MONO_TYPE_I4:
- case MONO_TYPE_U4:
- return &val->data.i;
- case MONO_TYPE_I:
- case MONO_TYPE_U:
- return &val->data.nati;
- case MONO_TYPE_I8:
- case MONO_TYPE_U8:
- return &val->data.l;
- case MONO_TYPE_R4:
- return &val->data.f_r4;
- case MONO_TYPE_R8:
- return &val->data.f;
- case MONO_TYPE_STRING:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_PTR:
- case MONO_TYPE_FNPTR:
- return &val->data.p;
- case MONO_TYPE_VALUETYPE:
- if (m_class_is_enumtype (type->data.klass))
- return stackval_to_data_addr (mono_class_enum_basetype_internal (type->data.klass), val);
- else
- return val->data.vt;
- case MONO_TYPE_TYPEDBYREF:
- return val->data.vt;
- case MONO_TYPE_GENERICINST: {
- MonoClass *container_class = type->data.generic_class->container_class;
-
- if (m_class_is_valuetype (container_class) && !m_class_is_enumtype (container_class))
- return val->data.vt;
- return stackval_to_data_addr (m_class_get_byval_arg (type->data.generic_class->container_class), val);
+ return stackval_to_data (m_class_get_byval_arg (type->data.generic_class->container_class), val, data, pinvoke);
}
default:
g_error ("got type %x", type->type);
return NULL;
}
+/* Does not handle `this` argument */
+static guint32
+compute_arg_offset (MonoMethodSignature *sig, int index, int prev_offset)
+{
+ if (index == 0)
+ return 0;
+
+ if (prev_offset == -1) {
+ guint32 offset = 0;
+ for (int i = 0; i < index; i++) {
+ int size, align;
+ MonoType *type = sig->params [i];
+ size = mono_type_size (type, &align);
+ offset += ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
+ }
+ return offset;
+ } else {
+ int size, align;
+ MonoType *type = sig->params [index - 1];
+ size = mono_type_size (type, &align);
+ return prev_offset + ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
+ }
+}
+
+static guint32*
+initialize_arg_offsets (InterpMethod *imethod)
+{
+ if (imethod->arg_offsets)
+ return imethod->arg_offsets;
+
+ MonoMethodSignature *sig = mono_method_signature_internal (imethod->method);
+ int arg_count = sig->hasthis + sig->param_count;
+ g_assert (arg_count);
+ guint32 *arg_offsets = (guint32*) g_malloc ((sig->hasthis + sig->param_count) * sizeof (int));
+ int index = 0, offset_addend = 0, prev_offset = 0;
+
+ if (sig->hasthis) {
+ arg_offsets [index++] = 0;
+ offset_addend = MINT_STACK_SLOT_SIZE;
+ }
+
+ for (int i = 0; i < sig->param_count; i++) {
+ prev_offset = compute_arg_offset (sig, i, prev_offset);
+ arg_offsets [index++] = prev_offset + offset_addend;
+ }
+
+ mono_memory_write_barrier ();
+ if (mono_atomic_cas_ptr ((gpointer*)&imethod->arg_offsets, arg_offsets, NULL) != NULL)
+ g_free (arg_offsets);
+ return imethod->arg_offsets;
+}
+
+static guint32
+get_arg_offset_fast (InterpMethod *imethod, int index)
+{
+ guint32 *arg_offsets = imethod->arg_offsets;
+ if (arg_offsets)
+ return arg_offsets [index];
+
+ arg_offsets = initialize_arg_offsets (imethod);
+ g_assert (arg_offsets);
+ return arg_offsets [index];
+}
+
+static guint32
+get_arg_offset (InterpMethod *imethod, MonoMethodSignature *sig, int index)
+{
+ if (imethod) {
+ return get_arg_offset_fast (imethod, index);
+ } else {
+ g_assert (!sig->hasthis);
+ return compute_arg_offset (sig, index, -1);
+ }
+}
+
#ifdef MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE
static MonoFuncV mono_native_to_interp_trampoline = NULL;
#endif
for (int i = 0; i < sig->param_count; i++) {
guint32 ptype = sig->params [i]->byref ? MONO_TYPE_PTR : sig->params [i]->type;
+ guint32 offset = get_arg_offset (frame->imethod, sig, i);
+ stackval *sp_arg = STACK_ADD_BYTES (frame->stack, offset);
switch (ptype) {
case MONO_TYPE_BOOLEAN:
case MONO_TYPE_CHAR:
case MONO_TYPE_I8:
case MONO_TYPE_U8:
#endif
- margs->iargs [int_i] = frame->stack [i].data.p;
+ margs->iargs [int_i] = sp_arg->data.p;
#if DEBUG_INTERP
g_print ("build_args_from_sig: margs->iargs [%d]: %p (frame @ %d)\n", int_i, margs->iargs [int_i], i);
#endif
#if SIZEOF_VOID_P == 4
case MONO_TYPE_I8:
case MONO_TYPE_U8: {
- stackval *sarg = &frame->stack [i];
#ifdef TARGET_ARM
/* pairs begin at even registers */
if (i8_align == 8 && int_i & 1)
int_i++;
#endif
- margs->iargs [int_i] = (gpointer) sarg->data.pair.lo;
+ margs->iargs [int_i] = (gpointer) sp_arg->data.pair.lo;
int_i++;
- margs->iargs [int_i] = (gpointer) sarg->data.pair.hi;
+ margs->iargs [int_i] = (gpointer) sp_arg->data.pair.hi;
#if DEBUG_INTERP
- g_print ("build_args_from_sig: margs->iargs [%d/%d]: 0x%016" PRIx64 ", hi=0x%08x lo=0x%08x (frame @ %d)\n", int_i - 1, int_i, *((guint64 *) &margs->iargs [int_i - 1]), sarg->data.pair.hi, sarg->data.pair.lo, i);
+ g_print ("build_args_from_sig: margs->iargs [%d/%d]: 0x%016" PRIx64 ", hi=0x%08x lo=0x%08x (frame @ %d)\n", int_i - 1, int_i, *((guint64 *) &margs->iargs [int_i - 1]), sp_arg->data.pair.hi, sp_arg->data.pair.lo, i);
#endif
int_i++;
break;
case MONO_TYPE_R4:
case MONO_TYPE_R8:
if (ptype == MONO_TYPE_R4)
- * (float *) &(margs->fargs [int_f]) = frame->stack [i].data.f_r4;
+ * (float *) &(margs->fargs [int_f]) = sp_arg->data.f_r4;
else
- margs->fargs [int_f] = frame->stack [i].data.f;
+ margs->fargs [int_f] = sp_arg->data.f;
#if DEBUG_INTERP
g_print ("build_args_from_sig: margs->fargs [%d]: %p (%f) (frame @ %d)\n", int_f, margs->fargs [int_f], margs->fargs [int_f], i);
#endif
case MONO_TYPE_U8:
case MONO_TYPE_VALUETYPE:
case MONO_TYPE_GENERICINST:
- margs->retval = &frame->retval->data.p;
+ margs->retval = &frame->stack->data.p;
margs->is_float_ret = 0;
break;
case MONO_TYPE_R4:
case MONO_TYPE_R8:
- margs->retval = &frame->retval->data.p;
+ margs->retval = &frame->stack->data.p;
margs->is_float_ret = 1;
break;
case MONO_TYPE_VOID:
interp_frame_arg_to_data (MonoInterpFrameHandle frame, MonoMethodSignature *sig, int index, gpointer data)
{
InterpFrame *iframe = (InterpFrame*)frame;
+ InterpMethod *imethod = iframe->imethod;
+ // If index == 1, we finished executing an InterpFrame, thus we always have imethod set,
+ // and the result is at the bottom of the execution stack.
if (index == -1)
- stackval_to_data (sig->ret, iframe->retval, data, sig->pinvoke);
+ stackval_to_data (sig->ret, STACK_ADD_BYTES (iframe->stack, imethod->total_locals_size), data, TRUE);
+ else if (sig->hasthis && index == 0)
+ *(gpointer*)data = iframe->stack->data.p;
else
- stackval_to_data (sig->params [index], &iframe->stack [index], data, sig->pinvoke);
+ stackval_to_data (sig->params [index - sig->hasthis], STACK_ADD_BYTES (iframe->stack, get_arg_offset (imethod, sig, index)), data, sig->pinvoke);
}
static void
interp_data_to_frame_arg (MonoInterpFrameHandle frame, MonoMethodSignature *sig, int index, gconstpointer data)
{
InterpFrame *iframe = (InterpFrame*)frame;
+ InterpMethod *imethod = iframe->imethod;
+ // 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->retval, data, sig->pinvoke);
+ stackval_from_data (sig->ret, iframe->stack, data, TRUE);
else if (sig->hasthis && index == 0)
- iframe->stack [index].data.p = *(gpointer*)data;
+ iframe->stack->data.p = *(gpointer*)data;
else
- stackval_from_data (sig->params [index - sig->hasthis], &iframe->stack [index], data, sig->pinvoke);
+ stackval_from_data (sig->params [index - sig->hasthis], STACK_ADD_BYTES (iframe->stack, get_arg_offset (imethod, sig, index)), data, sig->pinvoke);
}
static gpointer
interp_frame_arg_to_storage (MonoInterpFrameHandle frame, MonoMethodSignature *sig, int index)
{
InterpFrame *iframe = (InterpFrame*)frame;
+ InterpMethod *imethod = iframe->imethod;
if (index == -1)
- return stackval_to_data_addr (sig->ret, iframe->retval);
+ return STACK_ADD_BYTES (iframe->stack, imethod->total_locals_size);
else
- return stackval_to_data_addr (sig->params [index], &iframe->stack [index]);
-}
-
-static void
-interp_frame_arg_set_storage (MonoInterpFrameHandle frame, MonoMethodSignature *sig, int index, gpointer storage)
-{
- InterpFrame *iframe = (InterpFrame*)frame;
- stackval *val = (index == -1) ? iframe->retval : &iframe->stack [index];
- MonoType *type = (index == -1) ? sig->ret : sig->params [index];
-
- switch (type->type) {
- case MONO_TYPE_GENERICINST:
- if (!MONO_TYPE_IS_REFERENCE (type))
- val->data.vt = storage;
- break;
- case MONO_TYPE_VALUETYPE:
- val->data.vt = storage;
- break;
- default:
- g_assert_not_reached ();
- }
+ return STACK_ADD_BYTES (iframe->stack, get_arg_offset (imethod, sig, index));
}
static MonoPIFunc
#endif
static MONO_NO_OPTIMIZATION MONO_NEVER_INLINE void
ves_pinvoke_method (
+ InterpMethod *imethod,
MonoMethodSignature *sig,
MonoFuncV addr,
ThreadContext *context,
InterpFrame *parent_frame,
- stackval *retval,
+ stackval *sp,
gboolean save_last_error,
- gpointer *cache,
- stackval *sp)
+ gpointer *cache)
{
InterpFrame frame = {0};
frame.parent = parent_frame;
+ frame.imethod = imethod;
frame.stack = sp;
- frame.retval = retval;
MonoLMFExt ext;
gpointer args;
- g_assert (!frame.imethod);
-
/*
* When there's a calli in a pinvoke wrapper, we're in GC Safe mode.
* When we're called for some other calli, we may be in GC Unsafe mode.
g_free (ccontext.stack);
#else
- if (!context->has_resume_state && !MONO_TYPE_ISSTRUCT (sig->ret))
- stackval_from_data (sig->ret, frame.retval, (char*)&frame.retval->data.p, sig->pinvoke);
g_free (margs->iargs);
g_free (margs->fargs);
MonoType *ret = mono_method_signature_internal (inv->imethod->method)->ret;
if (ret->type != MONO_TYPE_VOID)
- dump_stackval (str, inv->retval, ret);
+ dump_stackval (str, STACK_ADD_BYTES (inv->stack, inv->imethod->total_locals_size), ret);
return g_string_free (str, FALSE);
}
{
ThreadContext *context = get_context ();
MonoMethodSignature *sig = mono_method_signature_internal (method);
- MonoClass *klass = mono_class_from_mono_type_internal (sig->ret);
- stackval result;
stackval *sp = (stackval*)context->stack_pointer;
MonoMethod *target_method = method;
//* <code>MonoObject *runtime_invoke (MonoObject *this_obj, void **params, MonoObject **exc, void* method)</code>
- result.data.vt = alloca (mono_class_instance_size (klass));
if (sig->hasthis)
sp [0].data.p = obj;
else
InterpFrame frame = {0};
frame.imethod = imethod;
frame.stack = sp;
- frame.retval = &result;
// 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
*/
return NULL;
}
- return (MonoObject*)result.data.p;
+ // The return value is at the bottom of the stack, after the locals
+ return STACK_ADD_BYTES (frame.stack, imethod->total_locals_size)->data.o;
}
typedef struct {
{
InterpMethod *rmethod;
ThreadContext *context;
- stackval *sp;
- stackval result;
+ stackval *sp, *sp_args;
MonoMethod *method;
MonoMethodSignature *sig;
MonoType *type;
orig_domain = mono_threads_attach_coop (mono_domain_get (), &attach_cookie);
context = get_context ();
- sp = (stackval*)context->stack_pointer;
+ sp_args = sp = (stackval*)context->stack_pointer;
method = rmethod->method;
sig = mono_method_signature_internal (method);
// FIXME: Optimize this
- if (sig->hasthis)
- sp [0].data.p = data->this_arg;
+ if (sig->hasthis) {
+ sp_args->data.p = data->this_arg;
+ sp_args++;
+ }
gpointer *params;
if (data->many_args)
else
params = data->args;
for (i = 0; i < sig->param_count; ++i) {
- int a_index = i + (sig->hasthis ? 1 : 0);
if (sig->params [i]->byref) {
- sp [a_index].data.p = params [i];
- continue;
- }
- type = rmethod->param_types [i];
- switch (type->type) {
- case MONO_TYPE_VALUETYPE:
- sp [a_index].data.p = params [i];
- break;
- case MONO_TYPE_GENERICINST:
- if (MONO_TYPE_IS_REFERENCE (type))
- sp [a_index].data.p = *(gpointer*)params [i];
- else
- sp [a_index].data.vt = params [i];
- break;
- default:
- stackval_from_data (type, &sp [a_index], params [i], FALSE);
- break;
+ sp_args->data.p = params [i];
+ sp_args++;
+ } else {
+ int size = stackval_from_data (sig->params [i], sp_args, params [i], FALSE);
+ sp_args = STACK_ADD_BYTES (sp_args, size);
}
}
- memset (&result, 0, sizeof (result));
-
InterpFrame frame = {0};
frame.imethod = data->rmethod;
frame.stack = sp;
- frame.retval = &result;
-
- type = rmethod->rtype;
- switch (type->type) {
- case MONO_TYPE_GENERICINST:
- if (!MONO_TYPE_IS_REFERENCE (type))
- result.data.vt = data->res;
- break;
- case MONO_TYPE_VALUETYPE:
- result.data.vt = data->res;
- break;
- default:
- break;
- }
context->stack_pointer = (guchar*)(sp + sig->hasthis + sig->param_count);
g_assert (!context->has_resume_state);
}
+ // The return value is at the bottom of the stack, after the locals space
type = rmethod->rtype;
- switch (type->type) {
- case MONO_TYPE_VOID:
- break;
- case MONO_TYPE_OBJECT:
- /* No need for a write barrier */
- *(MonoObject**)data->res = (MonoObject*)result.data.p;
- break;
- case MONO_TYPE_GENERICINST:
- if (MONO_TYPE_IS_REFERENCE (type)) {
- *(MonoObject**)data->res = (MonoObject*)result.data.p;
- } else {
- /* Already set before the call */
- }
- break;
- case MONO_TYPE_VALUETYPE:
- /* Already set before the call */
- break;
- default:
- stackval_to_data (type, &result, data->res, FALSE);
- break;
- }
+ if (type->type != MONO_TYPE_VOID)
+ stackval_to_data (type, STACK_ADD_BYTES (frame.stack, rmethod->total_locals_size), data->res, FALSE);
}
static stackval *
gpointer wrapper;
MonoMethodSignature *sig;
guint8 *arginfo;
- gint32 vt_res_size;
+ gint32 res_size;
int ret_mt;
};
* that could end up doing a jit call.
*/
gint32 size = mono_class_value_size (klass, NULL);
- cinfo->vt_res_size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
+ cinfo->res_size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
+ } else {
+ cinfo->res_size = MINT_STACK_SLOT_SIZE;
}
cinfo->ret_mt = mt;
} else {
int mt = mint_type (t);
if (sig->params [i]->byref) {
cinfo->arginfo [i] = JIT_ARG_BYVAL;
- } else if (mt == MINT_TYPE_VT) {
- cinfo->arginfo [i] = JIT_ARG_BYVAL;
} else if (mt == MINT_TYPE_O) {
cinfo->arginfo [i] = JIT_ARG_BYREF;
} else {
}
static MONO_NEVER_INLINE void
-do_jit_call (stackval *sp, unsigned char *vt_sp, InterpFrame *frame, InterpMethod *rmethod, MonoError *error)
+do_jit_call (stackval *sp, InterpFrame *frame, InterpMethod *rmethod, MonoError *error)
{
- guint8 res_buf [256];
MonoLMFExt ext;
JitCallInfo *cinfo;
args [pindex ++] = sp [0].data.p;
stack_index ++;
}
- switch (cinfo->ret_mt) {
- case -1:
- break;
- case MINT_TYPE_VT:
- args [pindex ++] = vt_sp;
- break;
- default:
- args [pindex ++] = res_buf;
- break;
- }
+ /* return address */
+ if (cinfo->ret_mt != -1)
+ args [pindex ++] = sp;
for (int i = 0; i < rmethod->param_count; ++i) {
- stackval *sval = &sp [stack_index + i];
+ stackval *sval = STACK_ADD_BYTES (sp, get_arg_offset_fast (rmethod, stack_index + i));
if (cinfo->arginfo [i] == JIT_ARG_BYVAL)
args [pindex ++] = sval->data.p;
else
/* data is an union, so can use 'p' for all types */
- args [pindex ++] = &sval->data.p;
+ args [pindex ++] = sval;
}
JitCallCbData cb_data;
mono_error_set_exception_instance (error, (MonoException*)obj);
return;
}
-
if (cinfo->ret_mt != -1) {
+ // Sign/zero extend if necessary
switch (cinfo->ret_mt) {
- case MINT_TYPE_O:
- sp->data.p = *(gpointer*)res_buf;
- break;
case MINT_TYPE_I1:
- sp->data.i = *(gint8*)res_buf;
+ sp->data.i = *(gint8*)sp;
break;
case MINT_TYPE_U1:
- sp->data.i = *(guint8*)res_buf;
+ sp->data.i = *(guint8*)sp;
break;
case MINT_TYPE_I2:
- sp->data.i = *(gint16*)res_buf;
+ sp->data.i = *(gint16*)sp;
break;
case MINT_TYPE_U2:
- sp->data.i = *(guint16*)res_buf;
+ sp->data.i = *(guint16*)sp;
break;
case MINT_TYPE_I4:
- sp->data.i = *(gint32*)res_buf;
- break;
case MINT_TYPE_I8:
- sp->data.l = *(gint64*)res_buf;
- break;
case MINT_TYPE_R4:
- sp->data.f_r4 = *(float*)res_buf;
- break;
case MINT_TYPE_R8:
- sp->data.f = *(double*)res_buf;
- break;
case MINT_TYPE_VT:
- /* The result was written to vt_sp */
- sp->data.p = vt_sp;
+ case MINT_TYPE_O:
+ /* The result was written to sp */
break;
default:
g_assert_not_reached ();
}
static void
-copy_varargs_vtstack (MonoMethodSignature *csig, stackval *sp, guchar *vt_sp_start)
+init_arglist (InterpFrame *frame, MonoMethodSignature *sig, stackval *sp, char *arglist)
{
- stackval *first_arg = sp - csig->param_count;
- guchar *vt_sp = vt_sp_start;
-
- /*
- * We need to have the varargs linearly on the stack so the ArgIterator
- * can iterate over them. We pass the signature first and then copy them
- * one by one on the vtstack. The callee (MINT_ARGLIST) will be able to
- * find this space by adding the current vt_sp pointer in the parent frame
- * with the amount of vtstack space used by the parameters.
- */
- *(gpointer*)vt_sp = csig;
- vt_sp += sizeof (gpointer);
+ *(gpointer*)arglist = sig;
+ arglist += sizeof (gpointer);
- for (int i = csig->sentinelpos; i < csig->param_count; i++) {
- int align, arg_size;
- arg_size = mono_type_stack_size (csig->params [i], &align);
- vt_sp = (guchar*)ALIGN_PTR_TO (vt_sp, align);
+ for (int i = sig->sentinelpos; i < sig->param_count; i++) {
+ int align, arg_size, sv_size;
+ arg_size = mono_type_stack_size (sig->params [i], &align);
+ arglist = (char*)ALIGN_PTR_TO (arglist, align);
- stackval_to_data (csig->params [i], &first_arg [i], vt_sp, FALSE);
- vt_sp += arg_size;
+ sv_size = stackval_to_data (sig->params [i], sp, arglist, FALSE);
+ arglist += arg_size;
+ sp = STACK_ADD_BYTES (sp, sv_size);
}
}
#ifdef MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE
-// inline so we can alloc on stack
-#define alloc_storage_for_stackval(s, t, p) do { \
- if ((t)->type == MONO_TYPE_GENERICINST && !MONO_TYPE_IS_REFERENCE (t)) { \
- (s)->data.vt = alloca (mono_class_value_size (mono_class_from_mono_type_internal (t), NULL)); \
- } else if ((t)->type == MONO_TYPE_VALUETYPE) { \
- if (p) \
- (s)->data.vt = alloca (mono_class_native_size ((t)->data.klass, NULL)); \
- else \
- (s)->data.vt = alloca (mono_class_value_size ((t)->data.klass, NULL)); \
- } \
- } while (0)
-
// Do not inline in case order of frame addresses matters.
static MONO_NEVER_INLINE void
interp_entry_from_trampoline (gpointer ccontext_untyped, gpointer rmethod_untyped)
{
ThreadContext *context;
stackval *sp;
- stackval result;
MonoMethod *method;
MonoMethodSignature *sig;
CallContext *ccontext = (CallContext*) ccontext_untyped;
sig = newsig;
}
- /* Allocate storage for value types */
- for (i = 0; i < sig->param_count; i++) {
- MonoType *type = sig->params [i];
- alloc_storage_for_stackval (&sp [i + sig->hasthis], type, sig->pinvoke);
- }
-
- if (sig->ret->type != MONO_TYPE_VOID)
- alloc_storage_for_stackval (&result, sig->ret, sig->pinvoke);
-
InterpFrame frame = {0};
frame.imethod = rmethod;
frame.stack = sp;
- frame.retval = &result;
/* Copy the args saved in the trampoline to the frame stack */
- mono_arch_get_native_call_context_args (ccontext, &frame, sig);
+ gpointer retp = mono_arch_get_native_call_context_args (ccontext, &frame, sig);
- context->stack_pointer = (guchar*)(sp + sig->hasthis + sig->param_count);
+ /* Allocate storage for value types */
+ stackval *newsp = sp;
+ /* FIXME we should reuse computation on imethod for this */
+ if (sig->hasthis)
+ newsp++;
+ for (i = 0; i < sig->param_count; i++) {
+ MonoType *type = sig->params [i];
+ int size;
+
+ if (type->type == MONO_TYPE_GENERICINST && !MONO_TYPE_IS_REFERENCE (type)) {
+ size = mono_class_value_size (mono_class_from_mono_type_internal (type), NULL);
+ } else if (type->type == MONO_TYPE_VALUETYPE) {
+ if (sig->pinvoke)
+ size = mono_class_native_size (type->data.klass, NULL);
+ else
+ size = mono_class_value_size (type->data.klass, NULL);
+ } else {
+ size = MINT_STACK_SLOT_SIZE;
+ }
+ newsp = STACK_ADD_BYTES (newsp, size);
+ }
+ context->stack_pointer = (guchar*)newsp;
interp_exec_method (&frame, context, NULL);
/* Write back the return value */
/* 'frame' is still valid */
- mono_arch_set_native_call_context_ret (ccontext, &frame, sig);
+ mono_arch_set_native_call_context_ret (ccontext, &frame, sig, retp);
}
#else
output_indent (); \
char *mn = mono_method_full_name (frame->imethod->method, FALSE); \
char *disasm = mono_interp_dis_mintop ((gint32)(ip - frame->imethod->code), TRUE, ip + 1, *ip); \
- g_print ("(%p) %s -> %s\t%d:%s\n", mono_thread_internal_current (), mn, disasm, vt_sp - vtalloc, ins); \
+ g_print ("(%p) %s -> %s\t%s\n", mono_thread_internal_current (), mn, disasm, ins); \
g_free (mn); \
g_free (ins); \
g_free (disasm); \
stackval_from_data (field->type, &sp [-1], addr, FALSE);
}
-static
-guchar* // Return new vt_sp instead of take-address.
+static stackval*
mono_interp_load_remote_field_vt (
InterpMethod* imethod,
MonoObject* o,
const guint16* ip,
- stackval* sp,
- guchar* vt_sp)
+ stackval* sp)
{
g_assert (o); // Caller checks and throws exception properly.
} else
#endif
addr = (char*)o + field->offset;
- sp [-1].data.p = vt_sp;
- memcpy (vt_sp, addr, i32);
- return vt_sp + ALIGN_TO (i32, MINT_VT_ALIGNMENT);
+ sp--;
+ memcpy ((char*)sp, addr, i32);
+ return STACK_ADD_BYTES (sp, i32);
}
static gboolean
sp->data.i = (a_val & b_val) == b_val;
}
-static int
-mono_interp_box_nullable (InterpFrame* frame, const guint16* ip, stackval* sp, MonoError* error)
-{
- InterpMethod* const imethod = frame->imethod;
- MonoClass* const c = (MonoClass*)imethod->data_items [ip [1]];
-
- int const size = mono_class_value_size (c, NULL);
-
- guint16 offset = ip [2];
- guint16 pop_vt_sp = !ip [3];
-
- sp [-1 - offset].data.o = mono_nullable_box (sp [-1 - offset].data.p, c, error);
- mono_interp_error_cleanup (error); /* FIXME: don't swallow the error */
-
- return pop_vt_sp ? ALIGN_TO (size, MINT_VT_ALIGNMENT) : 0;
-}
-
-static int
-mono_interp_box_vt (InterpFrame* frame, const guint16* ip, stackval* sp, MonoObjectHandle tmp_handle)
-{
- InterpMethod* const imethod = frame->imethod;
-
- MonoVTable * const vtable = (MonoVTable*)imethod->data_items [ip [1]];
- MonoClass* const c = vtable->klass;
-
- int const size = mono_class_value_size (c, NULL);
-
- guint16 offset = ip [2];
- guint16 pop_vt_sp = !ip [3];
-
- MonoObject* o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
- MONO_HANDLE_ASSIGN_RAW (tmp_handle, o);
- mono_value_copy_internal (mono_object_get_data (o), sp [-1 - offset].data.p, c);
- MONO_HANDLE_ASSIGN_RAW (tmp_handle, NULL);
-
- sp [-1 - offset].data.o = o;
- return pop_vt_sp ? ALIGN_TO (size, MINT_VT_ALIGNMENT) : 0;
-}
-
-static void
-mono_interp_box (InterpFrame* frame, const guint16* ip, stackval* sp, MonoObjectHandle tmp_handle)
-{
- MonoVTable * const vtable = (MonoVTable*)frame->imethod->data_items [ip [1]];
- guint16 const offset = ip [2];
-
- MonoObject *o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
- MONO_HANDLE_ASSIGN_RAW (tmp_handle, o);
- stackval_to_data (m_class_get_byval_arg (vtable->klass), &sp [-1 - offset], mono_object_get_data (o), FALSE);
- MONO_HANDLE_ASSIGN_RAW (tmp_handle, NULL);
-
- sp [-1 - offset].data.o = o;
-}
-
-static int
-mono_interp_store_remote_field_vt (InterpFrame* frame, const guint16* ip, stackval* sp, MonoError* error)
-{
- InterpMethod* const imethod = frame->imethod;
- MonoClassField *field;
-
- MonoObject* const o = sp [-2].data.o;
-
- field = (MonoClassField*)imethod->data_items[ip [1]];
- MonoClass *klass = mono_class_from_mono_type_internal (field->type);
- int const i32 = mono_class_value_size (klass, NULL);
-
-#ifndef DISABLE_REMOTING
- if (mono_object_is_transparent_proxy (o)) {
- MonoClass *klass = ((MonoTransparentProxy*)o)->remote_class->proxy_class;
- mono_store_remote_field_checked (o, klass, field, sp [-1].data.p, error);
- mono_interp_error_cleanup (error); /* FIXME: don't swallow the error */
- } else
-#endif
- mono_value_copy_internal ((char *) o + field->offset, sp [-1].data.p, klass);
-
- return ALIGN_TO (i32, MINT_VT_ALIGNMENT);
-}
-
// varargs in wasm consumes extra linear stack per call-site.
// These g_warning/g_error wrappers fix that. It is not the
// small wasm stack, but conserving it is still desirable.
#define SAVE_INTERP_STATE(frame) do { \
frame->state.ip = ip; \
frame->state.sp = sp; \
- frame->state.vt_sp = vt_sp; \
} while (0)
/* Load and clear state from FRAME */
#define LOAD_INTERP_STATE(frame) do { \
ip = frame->state.ip; \
sp = frame->state.sp; \
- vt_sp = frame->state.vt_sp; \
locals = (unsigned char *)frame->stack; \
frame->state.ip = NULL; \
} while (0)
#define INIT_INTERP_STATE(frame, _clause_args) do { \
ip = _clause_args ? ((FrameClauseArgs *)_clause_args)->start_with_ip : (frame)->imethod->code; \
locals = (unsigned char *)(frame)->stack; \
- vt_sp = (unsigned char *) locals + (frame)->imethod->total_locals_size; \
- sp = (stackval*)(vt_sp + (frame)->imethod->vt_stack_size); \
+ sp = (stackval*)(locals + (frame)->imethod->total_locals_size); \
} while (0)
#if PROFILE_INTERP
/* Interpreter main loop state (InterpState) */
const guint16 *ip = NULL;
stackval *sp;
- unsigned char *vt_sp;
unsigned char *locals = NULL;
#if DEBUG_INTERP
INIT_INTERP_STATE (frame, clause_args);
-#if DEBUG_INTERP
- vtalloc = vt_sp;
-#endif
-
if (clause_args && clause_args->filter_exception) {
sp->data.p = clause_args->filter_exception;
sp++;
#endif
MintOpcode opcode;
#ifdef ENABLE_CHECKED_BUILD
- guchar *vt_start = (guchar*)frame->stack + frame->imethod->total_locals_size;
- guchar *sp_start = vt_start + frame->imethod->vt_stack_size;
+ guchar *sp_start = (guchar*)frame->stack + frame->imethod->total_locals_size;
guchar *sp_end = sp_start + frame->imethod->stack_size;
g_assert (locals == (guchar*)frame->stack);
- g_assert (vt_sp >= vt_start);
- g_assert (vt_sp <= sp_start);
g_assert ((guchar*)sp >= sp_start);
g_assert ((guchar*)sp <= sp_end);
#endif
++ip;
++sp;
MINT_IN_BREAK;
- MINT_IN_CASE(MINT_ARGLIST)
- sp->data.p = vt_sp;
- /*
- * We know we have been called by an MINT_CALL_VARARG and the amount of vtstack
- * used by the parameters is at ip [-1] (the last argument to MINT_CALL_VARARG that
- * is embedded in the instruction stream).
- */
- *(gpointer*)sp->data.p = frame->parent->state.vt_sp + frame->parent->state.ip [-1];
- vt_sp += ALIGN_TO (sizeof (gpointer), MINT_VT_ALIGNMENT);
- ++ip;
- ++sp;
- MINT_IN_BREAK;
- MINT_IN_CASE(MINT_VTRESULT) {
- int ret_size = ip [1];
- unsigned char *ret_vt_sp = vt_sp;
- vt_sp -= READ32(ip + 2);
- if (ret_size > 0) {
- memmove (vt_sp, ret_vt_sp, ret_size);
- sp [-1].data.p = vt_sp;
- vt_sp += ALIGN_TO (ret_size, MINT_VT_ALIGNMENT);
- }
- ip += 4;
+ MINT_IN_CASE(MINT_INIT_ARGLIST) {
+ const guint16 *call_ip = frame->parent->state.ip - 4;
+ g_assert_checked (*call_ip == MINT_CALL_VARARG);
+ int params_stack_size = call_ip [2];
+ MonoMethodSignature *sig = (MonoMethodSignature*)frame->parent->imethod->data_items [call_ip [3]];
+
+ // 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);
+
+ init_arglist (frame, sig, STACK_ADD_BYTES (frame->stack, ip [2]), (char*)arglist);
+
+ // save the arglist for future access with MINT_ARGLIST
+ *(gpointer*)(locals + ip [1]) = arglist;
+
+ ip += 3;
MINT_IN_BREAK;
}
+
#define LDC(n) do { sp->data.i = (n); ++ip; ++sp; } while (0)
MINT_IN_CASE(MINT_LDC_I4_M1)
LDC(-1);
++ip;
MINT_IN_BREAK;
MINT_IN_CASE(MINT_DUP_VT) {
- int const i32 = READ32 (ip + 1);
- sp->data.p = vt_sp;
- memcpy(sp->data.p, sp [-1].data.p, i32);
- vt_sp += ALIGN_TO (i32, MINT_VT_ALIGNMENT);
- ++sp;
+ int i32 = READ32 (ip + 1);
+ memcpy (sp, STACK_SUB_BYTES (sp, i32), i32);
+ sp = STACK_ADD_BYTES (sp, i32);
ip += 3;
MINT_IN_BREAK;
}
}
MINT_IN_CASE(MINT_POP_VT) {
int i32 = READ32 (ip + 1);
- vt_sp -= ALIGN_TO (i32, MINT_VT_ALIGNMENT);
- sp--;
+ i32 = ALIGN_TO (i32, MINT_STACK_SLOT_SIZE);
+ sp = STACK_SUB_BYTES (sp, i32);
ip += 3;
MINT_IN_BREAK;
}
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_JMP) {
- g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+ g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size));
InterpMethod *new_method = (InterpMethod*)frame->imethod->data_items [ip [1]];
if (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL)
*/
context->stack_pointer = (guchar*)frame->stack + new_method->alloca_size;
frame->imethod = new_method;
- vt_sp = locals + frame->imethod->total_locals_size;
-#if DEBUG_INTERP
- vtalloc = vt_sp;
-#endif
- sp = (stackval*)(vt_sp + frame->imethod->vt_stack_size);
+ sp = (stackval*)(locals + frame->imethod->total_locals_size);
ip = frame->imethod->code;
MINT_IN_BREAK;
}
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 [1]];
int param_count = csignature->param_count;
- MonoDelegate *del = (MonoDelegate*) sp [-param_count - 1].data.o;
+ sp = STACK_SUB_BYTES (sp, ip [2]);
+ MonoDelegate *del = (MonoDelegate*) sp [0].data.o;
gboolean is_multicast = del->method == NULL;
InterpMethod *del_imethod = (InterpMethod*)del->interp_invoke_impl;
} else if (del_imethod->method->flags & METHOD_ATTRIBUTE_VIRTUAL && !del->target) {
// 'this' is passed dynamically, we need to recompute the target method
// with each call
- del_imethod = get_virtual_method (del_imethod, sp [-param_count].data.o->vtable);
+ del_imethod = get_virtual_method (del_imethod, sp [1].data.o->vtable);
} else {
del->interp_invoke_impl = del_imethod;
}
}
}
cmethod = del_imethod;
- vt_sp -= ip [2];
- sp -= param_count + 1;
if (!is_multicast) {
if (cmethod->param_count == param_count + 1) {
// Target method is static but the delegate has a target object. We handle
} else {
// skip the delegate pointer for static calls
// FIXME we could avoid memmove
- memmove (sp, sp + 1, param_count * sizeof (stackval));
+ memmove (sp, sp + 1, ip [2]);
}
}
ip += 3;
}
/* decrement by the actual number of args */
- sp -= csignature->param_count;
- if (csignature->hasthis)
- --sp;
- vt_sp -= ip [2];
+ sp = STACK_SUB_BYTES (sp, ip [2]);
if (csignature->hasthis) {
MonoObject *this_arg = (MonoObject*)sp->data.p;
guchar* code = (guchar*)sp->data.p;
/* decrement by the actual number of args */
- sp -= csignature->param_count;
- if (csignature->hasthis)
- --sp;
- vt_sp -= ip [2];
+ sp = STACK_SUB_BYTES (sp, ip [2]);
cmethod = mono_interp_get_native_func_wrapper (frame->imethod, csignature, code);
goto call;
}
MINT_IN_CASE(MINT_CALLI_NAT) {
- MonoMethodSignature* csignature;
- stackval retval;
-
- csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [1]];
+ MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [1]];
+ InterpMethod *imethod = (InterpMethod*)frame->imethod->data_items [ip [2]];
--sp;
guchar* const code = (guchar*)sp->data.p;
- /* decrement by the actual number of args */
- sp -= csignature->param_count;
- if (csignature->hasthis)
- --sp;
- vt_sp -= ip [2];
- /* If this is a vt return, the pinvoke will write the result directly to vt_sp */
- retval.data.p = vt_sp;
-
- gboolean save_last_error = ip [4];
- gpointer *cache = (gpointer*)&frame->imethod->data_items [ip [5]];
+ sp = STACK_SUB_BYTES (sp, ip [3]);
+
+ gboolean save_last_error = ip [5];
+ gpointer *cache = (gpointer*)&frame->imethod->data_items [ip [6]];
/* for calls, have ip pointing at the start of next instruction */
frame->state.ip = ip + 6;
- ves_pinvoke_method (csignature, (MonoFuncV)code, context, frame, &retval, save_last_error, cache, sp);
+ ves_pinvoke_method (imethod, csignature, (MonoFuncV)code, context, frame, sp, save_last_error, cache);
EXCEPTION_CHECKPOINT_GC_UNSAFE;
CHECK_RESUME_STATE (context);
- if (csignature->ret->type != MONO_TYPE_VOID) {
- *sp = retval;
- vt_sp += ip [3];
- sp++;
- }
- ip += 6;
+ // Result was written directly at top of stack
+ sp = STACK_ADD_BYTES (sp, ip [4]);
+ ip += 7;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_CALLVIRT_FAST) {
int slot;
cmethod = (InterpMethod*)frame->imethod->data_items [ip [1]];
- slot = (gint16)ip [2];
- /* decrement by the actual number of args */
- sp -= cmethod->param_count + cmethod->hasthis;
- vt_sp -= ip [3];
+ sp = STACK_SUB_BYTES (sp, ip [2]);
this_arg = (MonoObject*)sp->data.p;
- ip += 4;
+ slot = (gint16)ip [3];
+ ip += 4;
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 */
} else if (code_type == IMETHOD_CODE_COMPILED) {
frame->state.ip = ip;
error_init_reuse (error);
- do_jit_call (sp, vt_sp, frame, cmethod, error);
+ do_jit_call (sp, frame, cmethod, error);
if (!is_ok (error)) {
MonoException *ex = mono_error_convert_to_exception (error);
THROW_EX (ex, ip);
CHECK_RESUME_STATE (context);
- if (cmethod->rtype->type != MONO_TYPE_VOID) {
- sp++;
- vt_sp += ((JitCallInfo*)cmethod->jit_call_info)->vt_res_size;
- }
+ if (cmethod->rtype->type != MONO_TYPE_VOID)
+ sp = STACK_ADD_BYTES (sp, ((JitCallInfo*)cmethod->jit_call_info)->res_size);
}
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_CALL_VARARG) {
- MonoMethodSignature *csig;
-
+ // Same as MINT_CALL, except at ip [3] 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 [1]];
-
- /* The real signature for vararg calls */
- csig = (MonoMethodSignature*) frame->imethod->data_items [ip [2]];
-
- /* Push all vararg arguments from normal sp to vt_sp together with the signature */
- copy_varargs_vtstack (csig, sp, vt_sp);
- vt_sp -= ip [3];
-
- /* decrement by the actual number of args */
- // FIXME This seems excessive: frame and csig param_count.
- sp -= cmethod->param_count + cmethod->hasthis + csig->param_count - csig->sentinelpos;
-
+ sp = STACK_SUB_BYTES (sp, ip [2]);
ip += 4;
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 [1]];
- /* decrement by the actual number of args */
- sp -= ip [2];
- vt_sp -= ip [3];
+ sp = STACK_SUB_BYTES (sp, ip [2]);
MonoObject *this_arg = (MonoObject*)sp->data.p;
#ifdef ENABLE_EXPERIMENT_TIERED
ip += 5;
#else
- ip += 4;
+ ip += 3;
#endif
goto call;
}
MINT_IN_CASE(MINT_CALL) {
cmethod = (InterpMethod*)frame->imethod->data_items [ip [1]];
-
- /* decrement by the actual number of args */
- sp -= ip [2];
- vt_sp -= ip [3];
+ sp = STACK_SUB_BYTES (sp, ip [2]);
#ifdef ENABLE_EXPERIMENT_TIERED
ip += 5;
#else
- ip += 4;
+ ip += 3;
#endif
call:
/*
MINT_IN_CASE(MINT_JIT_CALL) {
InterpMethod *rmethod = (InterpMethod*)frame->imethod->data_items [ip [1]];
error_init_reuse (error);
- sp -= rmethod->param_count + rmethod->hasthis;
- vt_sp -= ip [2];
+ sp = STACK_SUB_BYTES (sp, ip [2]);
/* for calls, have ip pointing at the start of next instruction */
frame->state.ip = ip + 3;
- do_jit_call (sp, vt_sp, frame, rmethod, error);
+ do_jit_call (sp, frame, rmethod, error);
if (!is_ok (error)) {
MonoException *ex = mono_error_convert_to_exception (error);
THROW_EX (ex, ip);
CHECK_RESUME_STATE (context);
- if (rmethod->rtype->type != MONO_TYPE_VOID) {
- sp++;
- vt_sp += ((JitCallInfo*)rmethod->jit_call_info)->vt_res_size;
- }
+ if (rmethod->rtype->type != MONO_TYPE_VOID)
+ sp = STACK_ADD_BYTES (sp, ((JitCallInfo*)rmethod->jit_call_info)->res_size);
ip += 3;
MINT_IN_BREAK;
sp -= rmethod->param_count + rmethod->hasthis;
frame->state.ip = ip + 5;
- do_jit_call (sp, vt_sp, frame, rmethod, error);
+ do_jit_call (sp, frame, rmethod, error);
if (!is_ok (error)) {
MonoException *ex = mono_error_convert_to_exception (error);
THROW_EX (ex, ip);
MonoMethod *target_method = (MonoMethod*) frame->imethod->data_items [ip [1]];
MonoMethodSignature *sig = (MonoMethodSignature*) frame->imethod->data_items [ip [2]];
- sp->data.p = vt_sp;
stackval *retval = sp;
sp -= sig->param_count;
if (frame->parent) {
frame->parent->state.sp [0] = *sp;
frame->parent->state.sp++;
- } else {
- // FIXME This can only happen in a few wrappers. Add separate opcode for it
- *frame->retval = *sp;
}
- g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+ g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size));
goto exit_frame;
MINT_IN_CASE(MINT_RET_VOID)
- g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+ g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size));
goto exit_frame;
MINT_IN_CASE(MINT_RET_VT) {
int const i32 = READ32 (ip + 1);
- --sp;
+
+ sp = STACK_SUB_BYTES (sp, i32);
if (frame->parent) {
- gpointer dest_vt = frame->parent->state.vt_sp;
- // Push the valuetype in the parent frame. parent->state.sp [0] can be inside
- // vt to be returned, so we need to copy it before updating sp [0].
- memcpy (dest_vt, sp->data.p, i32);
- frame->parent->state.sp [0].data.p = dest_vt;
- frame->parent->state.sp++;
- frame->parent->state.vt_sp += ALIGN_TO (i32, MINT_VT_ALIGNMENT);
- } else {
- gpointer dest_vt = frame->retval->data.p;
- memcpy (dest_vt, sp->data.p, i32);
+ memmove (frame->parent->state.sp, sp, i32);
+ frame->parent->state.sp = STACK_ADD_BYTES (frame->parent->state.sp, i32);
}
- g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+ g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size));
goto exit_frame;
}
MINT_IN_CASE(MINT_RET_LOCALLOC)
if (frame->parent) {
frame->parent->state.sp [0] = *sp;
frame->parent->state.sp++;
- } else {
- // FIXME This can only happen in a few wrappers. Add separate opcode for it
- *frame->retval = *sp;
}
frame_data_allocator_pop (&context->data_stack, frame);
- g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+ g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size));
goto exit_frame;
MINT_IN_CASE(MINT_RET_VOID_LOCALLOC)
frame_data_allocator_pop (&context->data_stack, frame);
- g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+ g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size));
goto exit_frame;
MINT_IN_CASE(MINT_RET_VT_LOCALLOC) {
int const i32 = READ32 (ip + 1);
- --sp;
+ sp = STACK_SUB_BYTES (sp, i32);
if (frame->parent) {
- gpointer dest_vt = frame->parent->state.vt_sp;
- /* Push the valuetype in the parent frame */
- memcpy (dest_vt, sp->data.p, i32);
- frame->parent->state.sp [0].data.p = dest_vt;
- frame->parent->state.sp++;
- frame->parent->state.vt_sp += ALIGN_TO (i32, MINT_VT_ALIGNMENT);
- } else {
- memcpy (frame->retval->data.p, sp->data.p, i32);
+ memmove (frame->parent->state.sp, sp, i32);
+ frame->parent->state.sp = STACK_ADD_BYTES (frame->parent->state.sp, i32);
}
frame_data_allocator_pop (&context->data_stack, frame);
- g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+ g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size));
goto exit_frame;
}
MINT_IN_BREAK;
MINT_IN_CASE(MINT_LDIND_I) {
guint16 offset = ip [1];
- sp[-1 - offset].data.p = *(gpointer*)sp[-1 - offset].data.p;
+ // This doesn't follow the current stack based design, but we plan to switch to explicit offsets.
+ stackval *addr = (stackval*)(locals + frame->imethod->total_locals_size + offset);
+ addr->data.p = *(gpointer*)addr->data.p;
ip += 2;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_LDIND_I8) {
guint16 offset = ip [1];
+ // This doesn't follow the current stack based design, but we plan to switch to explicit offsets.
+ stackval *addr = (stackval*)(locals + frame->imethod->total_locals_size + offset);
#ifdef NO_UNALIGNED_ACCESS
- if ((gsize)sp [-1 - offset].data.p % SIZEOF_VOID_P)
- memcpy (&sp [-1 - offset].data.l, sp [-1 - offset].data.p, sizeof (gint64));
+ if ((gsize)addr->data.p % SIZEOF_VOID_P)
+ memcpy (&addr->data.l, addr->data.p, sizeof (gint64));
else
#endif
- sp[-1 - offset].data.l = *(gint64*)sp[-1 - offset].data.p;
+ addr->data.l = *(gint64*)addr->data.p;
ip += 2;
MINT_IN_BREAK;
}
}
MINT_IN_CASE(MINT_LDOBJ_VT) {
int size = READ32(ip + 1);
+ sp--;
+ memcpy (sp, sp [0].data.p, size);
+ sp = STACK_ADD_BYTES (sp, size);
ip += 3;
- memcpy (vt_sp, sp [-1].data.p, size);
- sp [-1].data.p = vt_sp;
- vt_sp += ALIGN_TO (size, MINT_VT_ALIGNMENT);
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_LDSTR)
MINT_IN_CASE(MINT_NEWOBJ_STRING) {
cmethod = (InterpMethod*)frame->imethod->data_items [ip [1]];
- const int param_count = ip [2];
- if (param_count) {
- sp -= param_count;
- memmove (sp + 1, sp, param_count * sizeof (stackval));
+ int param_size = ip [2];
+ if (param_size) {
+ sp = STACK_SUB_BYTES (sp, param_size);
+ memmove (sp + 1, sp, param_size);
}
// `this` is implicit null. The created string will be returned
// by the call, even though the call has void return (?!).
MINT_IN_CASE(MINT_NEWOBJ_FAST) {
MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [3]];
INIT_VTABLE (vtable);
- guint16 param_count;
guint16 imethod_index = ip [1];
-
+ guint16 param_size = ip [2];
const gboolean is_inlined = imethod_index == INLINED_METHOD_FLAG;
- param_count = ip [2];
-
// Make room for two copies of o -- this parameter and return value.
- if (param_count || !is_inlined) {
- sp -= param_count;
- memmove (sp + 2, sp, param_count * sizeof (stackval));
+ if (param_size) {
+ sp = STACK_SUB_BYTES (sp, param_size);
+ memmove (sp + 2, sp, param_size);
}
MonoObject *o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
THROW_EX (mono_error_convert_to_exception (error), ip);
}
- // Store o next to and before the parameters on the stack so GC will see it,
- // and where it is needed when the call returns.
sp [0].data.o = o;
- sp [1].data.o = o;
- ip += 4;
- if (is_inlined) {
- sp += param_count + 2;
- } else {
+ sp++;
+ sp [0].data.o = o;
+ ip += 5;
+ if (!is_inlined) {
cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index];
- goto call_newobj;
+ goto call;
}
+ sp = STACK_ADD_BYTES (sp, param_size + MINT_STACK_SLOT_SIZE);
MINT_IN_BREAK;
}
- MINT_IN_CASE(MINT_NEWOBJ_VT_FAST)
- MINT_IN_CASE(MINT_NEWOBJ_VTST_FAST) {
+ MINT_IN_CASE(MINT_NEWOBJ_VT_FAST) {
guint16 imethod_index = ip [1];
+ guint16 param_size = ip [2];
+ guint16 ret_size = ip [3];
gboolean is_inlined = imethod_index == INLINED_METHOD_FLAG;
- guint16 const param_count = ip [2];
-
// Make room for extra parameter and result.
- if (param_count) {
- sp -= param_count;
- memmove (sp + 2, sp, param_count * sizeof (stackval));
- }
-
- gboolean const vtst = *ip == MINT_NEWOBJ_VTST_FAST;
- if (vtst) {
- memset (vt_sp, 0, ip [3]);
- ip += 4;
- // Put extra parameter and result on stack, before other parameters,
- // and point stack to extra parameter, after result.
- // This pattern occurs for newobj_vt_fast and newobj_fast.
- sp [1].data.p = vt_sp;
- sp [0].data.p = vt_sp;
- } else {
- ip += 3;
- // Like newobj_fast, add valuetype_this parameter
- // and result and point stack to this after result.
- memset (sp, 0, sizeof (*sp));
- sp [1].data.p = &sp [0].data; // valuetype_this == result
+ if (param_size) {
+ sp = STACK_SUB_BYTES (sp, param_size);
+ memmove (STACK_ADD_BYTES (sp, ret_size + MINT_STACK_SLOT_SIZE), sp, param_size);
}
+ // Allocate return value on stack
+ stackval *retvt = sp;
+ memset (retvt, 0, ret_size);
+ sp = STACK_ADD_BYTES (sp, ret_size);
+ sp [0].data.p = retvt;
- if (is_inlined) {
- if (vtst)
- vt_sp += ALIGN_TO (ip [-1], MINT_VT_ALIGNMENT);
- sp += param_count + 2;
- MINT_IN_BREAK;
- }
- cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index];
+ ip += 5;
+ if (!is_inlined) {
+ cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index];
+ goto call;
}
- // call_newobj captures the pattern where the return value is placed
- // on the stack before the call, instead of the call forming it.
-call_newobj:
- ++sp; // Point sp at added extra param, after return value.
- goto call;
-
+ sp = STACK_ADD_BYTES (sp, param_size + MINT_STACK_SLOT_SIZE);
+ MINT_IN_BREAK;
+ }
MINT_IN_CASE(MINT_NEWOBJ) {
guint32 const token = ip [1];
+ guint16 param_size = ip [2];
cmethod = (InterpMethod*)frame->imethod->data_items [token];
- MonoMethodSignature* const csig = mono_method_signature_internal (cmethod->method);
-
- g_assert (csig->hasthis);
-
- // Make room for first parameter and return value.
- const int param_count = csig->param_count;
- if (param_count) {
- sp -= param_count;
- memmove (sp + 2, sp, param_count * sizeof (stackval));
+ // Make room for result and `this`
+ if (param_size) {
+ sp = STACK_SUB_BYTES (sp, param_size);
+ memmove (sp + 2, sp, param_size);
}
MonoClass * const newobj_class = cmethod->method->klass;
- /*if (profiling_classes) {
- guint count = GPOINTER_TO_UINT (g_hash_table_lookup (profiling_classes, newobj_class));
- count++;
- g_hash_table_insert (profiling_classes, newobj_class, GUINT_TO_POINTER (count));
- }*/
-
/*
* First arg is the object.
* a constructor returns void, but we need to return the object we created
mono_error_assert_ok (error);
}
#endif
- ip += 2;
- goto call_newobj;
+ ip += 3;
+ sp++;
+ goto call;
}
MINT_IN_CASE(MINT_NEWOBJ_MAGIC) {
ip += 2;
MINT_IN_BREAK;
}
- MINT_IN_CASE(MINT_INTRINS_BYREFERENCE_CTOR) {
- gpointer arg0 = sp [-1].data.p;
- gpointer *byreference_this = (gpointer*)vt_sp;
- *byreference_this = arg0;
-
- sp [-1].data.p = vt_sp;
- vt_sp += MINT_VT_ALIGNMENT;
- ip++;
- MINT_IN_BREAK;
- }
MINT_IN_CASE(MINT_INTRINS_SPAN_CTOR) {
- gpointer ptr = sp [-2].data.p;
- int len = sp [-1].data.i;
+ sp -= 2;
+ gpointer ptr = sp [0].data.p;
+ int len = sp [1].data.i;
if (len < 0)
THROW_EX (mono_get_exception_argument_out_of_range ("length"), ip);
- *(gpointer*)vt_sp = ptr;
- *(gint32*)((gpointer*)vt_sp + 1) = len;
- sp [-2].data.p = vt_sp;
+ *(gpointer*)sp = ptr;
+ *(gint32*)((gpointer*)sp + 1) = len;
#if SIZEOF_VOID_P == 8
- vt_sp += ALIGN_TO (12, MINT_VT_ALIGNMENT);
+ sp = STACK_ADD_BYTES (sp, 12);
#else
- vt_sp += ALIGN_TO (8, MINT_VT_ALIGNMENT);
+ sp = STACK_ADD_BYTES (sp, 8);
#endif
- sp--;
ip++;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_INTRINS_BYREFERENCE_GET_VALUE) {
- gpointer *byreference_this = (gpointer*)sp [-1].data.p;
- sp [-1].data.p = *byreference_this;
+ sp [-1].data.p = *(gpointer*)sp [-1].data.p;
++ip;
MINT_IN_BREAK;
}
}
MINT_IN_CASE(MINT_CKNULL_N) {
/* Same as CKNULL, but further down the stack */
- int const n = ip [1];
- MonoObject* const o = sp [-n].data.o;
+ int offset = ip [1];
+ // This doesn't follow the current stack based design, but we plan to switch to explicit offsets.
+ MonoObject *o = *(MonoObject**)(locals + frame->imethod->total_locals_size + offset);
NULL_CHECK (o);
ip += 2;
MINT_IN_BREAK;
}
#define LDFLD_VT_UNALIGNED(datamem, fieldtype, unaligned) do { \
- gpointer p = sp [-1].data.p; \
- vt_sp -= ip [2]; \
+ sp = STACK_SUB_BYTES (sp, ip [2]); \
if (unaligned) \
- memcpy (&sp[-1].data.datamem, (char *)p + ip [1], sizeof (fieldtype)); \
+ memcpy (&sp [0].data.datamem, (char *)sp + ip [1], sizeof (fieldtype)); \
else \
- sp [-1].data.datamem = * (fieldtype *)((char *)p + ip [1]); \
+ sp [0].data.datamem = * (fieldtype *)((char *)sp + ip [1]); \
+ sp++; \
ip += 3; \
} while (0)
MINT_IN_CASE(MINT_LDFLD_VT_R8_UNALIGNED) LDFLD_VT_UNALIGNED(f, double, TRUE); MINT_IN_BREAK;
MINT_IN_CASE(MINT_LDFLD_VT_VT) {
- gpointer p = sp [-1].data.p;
-
- vt_sp -= ip [2];
- sp [-1].data.p = vt_sp;
- memmove (vt_sp, (char *)p + ip [1], ip [3]);
- vt_sp += ip [3];
+ sp = STACK_SUB_BYTES (sp, ip [2]); \
+ memmove (sp, (char *)sp + ip [1], ip [3]);
+ sp = STACK_ADD_BYTES (sp, ip [3]);
ip += 4;
MINT_IN_BREAK;
}
NULL_CHECK (o);
int size = READ32(ip + 2);
- sp [-1].data.p = vt_sp;
- memcpy (sp [-1].data.p, (char *)o + ip [1], size);
- vt_sp += ALIGN_TO (size, MINT_VT_ALIGNMENT);
+ sp--;
+ memcpy (sp, (char *)o + ip [1], size);
+ sp = STACK_ADD_BYTES (sp, size);
ip += 4;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_LDRMFLD_VT) {
MonoObject* const o = sp [-1].data.o;
NULL_CHECK (o);
- vt_sp = mono_interp_load_remote_field_vt (frame->imethod, o, ip, sp, vt_sp);
+ sp = mono_interp_load_remote_field_vt (frame->imethod, o, ip, sp);
ip += 2;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_STFLD_R8_UNALIGNED) STFLD_UNALIGNED(f, double, TRUE); MINT_IN_BREAK;
MINT_IN_CASE(MINT_STFLD_VT_NOREF) {
- MonoObject* const o = sp [-2].data.o;
- NULL_CHECK (o);
- sp -= 2;
-
guint16 offset = ip [1];
guint16 vtsize = ip [2];
- memcpy ((char *) o + offset, sp [1].data.p, vtsize);
+ sp = STACK_SUB_BYTES (sp, MINT_STACK_SLOT_SIZE + vtsize);
+ MonoObject *o = sp [0].data.o;
+ NULL_CHECK (o);
+
+ memcpy ((char *) o + offset, sp + 1, vtsize);
- vt_sp -= ALIGN_TO (vtsize, MINT_VT_ALIGNMENT);
ip += 3;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_STFLD_VT) {
- MonoObject* const o = sp [-2].data.o;
- NULL_CHECK (o);
- sp -= 2;
-
MonoClass *klass = (MonoClass*)frame->imethod->data_items[ip [2]];
- int const i32 = mono_class_value_size (klass, NULL);
+ int vtsize = mono_class_value_size (klass, NULL);
+
+ sp = STACK_SUB_BYTES (sp, MINT_STACK_SLOT_SIZE + vtsize);
+ MonoObject *o = sp [0].data.o;
+ NULL_CHECK (o);
guint16 offset = ip [1];
- mono_value_copy_internal ((char *) o + offset, sp [1].data.p, klass);
+ mono_value_copy_internal ((char *) o + offset, sp + 1, klass);
- vt_sp -= ALIGN_TO (i32, MINT_VT_ALIGNMENT);
ip += 3;
MINT_IN_BREAK;
}
sp -= 2;
MINT_IN_BREAK;
}
- MINT_IN_CASE(MINT_STRMFLD_VT)
+ MINT_IN_CASE(MINT_STRMFLD_VT) {
+ MonoClassField *field = (MonoClassField*)frame->imethod->data_items [ip [1]];
+ MonoClass *klass = mono_class_from_mono_type_internal (field->type);
+ int vtsize = mono_class_value_size (klass, NULL);
+
+ sp = STACK_SUB_BYTES (sp, vtsize + MINT_STACK_SLOT_SIZE);
+ MonoObject *o = sp [0].data.o;
+ NULL_CHECK (o);
+
+#ifndef DISABLE_REMOTING
+ if (mono_object_is_transparent_proxy (o)) {
+ MonoClass *klass = ((MonoTransparentProxy*)o)->remote_class->proxy_class;
+ mono_store_remote_field_checked (o, klass, field, sp + 1, error);
+ mono_interp_error_cleanup (error); /* FIXME: don't swallow the error */
+ } else
+#endif
+ mono_value_copy_internal ((char *) o + field->offset, sp + 1, klass);
- NULL_CHECK (sp [-2].data.o);
- vt_sp -= mono_interp_store_remote_field_vt (frame, ip, sp, error);
ip += 2;
- sp -= 2;
MINT_IN_BREAK;
+ }
#define STLOCFLD(datamem, fieldtype) do { \
MonoObject *o = *(MonoObject**)(locals + ip [1]); \
MINT_IN_CASE(MINT_LDSFLD_VT) {
MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [1]];
INIT_VTABLE (vtable);
- sp->data.p = vt_sp;
gpointer addr = frame->imethod->data_items [ip [2]];
int const i32 = READ32 (ip + 3);
- memcpy (vt_sp, addr, i32);
- vt_sp += ALIGN_TO (i32, MINT_VT_ALIGNMENT);
+
+ memcpy (sp, addr, i32);
+ sp = STACK_ADD_BYTES (sp, i32);
ip += 5;
- ++sp;
MINT_IN_BREAK;
}
gpointer addr = mono_get_special_static_data (offset);
int size = READ32 (ip + 3);
- memcpy (vt_sp, addr, size);
- sp->data.p = vt_sp;
- vt_sp += ALIGN_TO (size, MINT_VT_ALIGNMENT);
+ memcpy (sp, addr, size);
+ sp = STACK_ADD_BYTES (sp, size);
ip += 5;
- ++sp;
MINT_IN_BREAK;
}
#define STSFLD(datamem, fieldtype) { \
int const i32 = READ32 (ip + 3);
gpointer addr = frame->imethod->data_items [ip [2]];
- memcpy (addr, sp [-1].data.vt, i32);
- vt_sp -= ALIGN_TO (i32, MINT_VT_ALIGNMENT);
+ sp = STACK_SUB_BYTES (sp, i32);
+ memcpy (addr, sp, i32);
+
ip += 5;
- --sp;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_STSSFLD_VT) {
guint32 offset = READ32(ip + 1);
gpointer addr = mono_get_special_static_data (offset);
- --sp;
int size = READ32 (ip + 3);
- memcpy (addr, sp->data.vt, size);
- vt_sp -= ALIGN_TO (size, MINT_VT_ALIGNMENT);
+
+ sp = STACK_SUB_BYTES (sp, size);
+ memcpy (addr, sp, size);
+
ip += 5;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_STOBJ_VT) {
- int size;
- MonoClass* const c = (MonoClass*)frame->imethod->data_items[ip [1]];
+ MonoClass *c = (MonoClass*)frame->imethod->data_items[ip [1]];
+ int size = mono_class_value_size (c, NULL);
+
+ sp = STACK_SUB_BYTES (sp, MINT_STACK_SLOT_SIZE + size);
+ mono_value_copy_internal (sp [0].data.p, sp + 1, c);
+
ip += 2;
- size = mono_class_value_size (c, NULL);
- mono_value_copy_internal (sp [-2].data.p, sp [-1].data.p, c);
- vt_sp -= ALIGN_TO (size, MINT_VT_ALIGNMENT);
- sp -= 2;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_CONV_OVF_I4_UN_R8)
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_BOX) {
- mono_interp_box (frame, ip, sp, tmp_handle);
- ip += 3;
+ MonoVTable *vtable = (MonoVTable*)frame->imethod->data_items [ip [1]];
+
+ MonoObject *o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, o);
+ stackval_to_data (m_class_get_byval_arg (vtable->klass), &sp [-1], mono_object_get_data (o), FALSE);
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, NULL);
+
+ sp [-1].data.o = o;
+ ip += 2;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_BOX_VT) {
- vt_sp -= mono_interp_box_vt (frame, ip, sp, tmp_handle);
- ip += 4;
+ MonoVTable *vtable = (MonoVTable*)frame->imethod->data_items [ip [1]];
+ MonoClass *c = vtable->klass;
+
+ int size = mono_class_value_size (c, NULL);
+
+ MonoObject* o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (c));
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, o);
+
+ sp = STACK_SUB_BYTES (sp, size);
+ mono_value_copy_internal (mono_object_get_data (o), sp, c);
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, NULL);
+
+ sp [0].data.o = o;
+ sp++;
+
+ ip += 2;
MINT_IN_BREAK;
}
- MINT_IN_CASE(MINT_BOX_NULLABLE) {
- vt_sp -= mono_interp_box_nullable (frame, ip, sp, error);
- ip += 4;
+ MINT_IN_CASE(MINT_BOX_PTR) {
+ MonoVTable *vtable = (MonoVTable*)frame->imethod->data_items [ip [1]];
+ MonoClass *c = vtable->klass;
+ // This doesn't follow the current stack based design, but we plan to switch to explicit offsets.
+ stackval *sp_ptr = (stackval*)(locals + frame->imethod->total_locals_size + ip [2]);
+
+ MonoObject* o = mono_gc_alloc_obj (vtable, m_class_get_instance_size (c));
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, o);
+ mono_value_copy_internal (mono_object_get_data (o), sp_ptr->data.p, c);
+ MONO_HANDLE_ASSIGN_RAW (tmp_handle, NULL);
+
+ sp_ptr->data.o = o;
+ ip += 3;
+ MINT_IN_BREAK;
+ }
+ MINT_IN_CASE(MINT_BOX_NULLABLE_PTR) {
+ MonoClass *c = (MonoClass*)frame->imethod->data_items [ip [1]];
+ // This doesn't follow the current stack based design, but we plan to switch to explicit offsets.
+ stackval *sp_ptr = (stackval*)(locals + frame->imethod->total_locals_size + ip [2]);
+
+ sp_ptr->data.o = mono_nullable_box (sp_ptr->data.p, c, error);
+ mono_interp_error_cleanup (error); /* FIXME: don't swallow the error */
+ ip += 3;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_NEWARR) {
MINT_IN_CASE(MINT_LDELEM_R8) LDELEM(f, double); MINT_IN_BREAK;
MINT_IN_CASE(MINT_LDELEM_REF) LDELEM(p, gpointer); MINT_IN_BREAK;
MINT_IN_CASE(MINT_LDELEM_VT) {
- sp--;
- MonoArray *o = (MonoArray*)sp [-1].data.p;
+ sp -= 2;
+ MonoArray *o = (MonoArray*)sp [0].data.p;
NULL_CHECK (o);
- mono_u aindex = sp [0].data.i;
+ mono_u aindex = sp [1].data.i;
if (aindex >= mono_array_length_internal (o))
THROW_EX (mono_get_exception_index_out_of_range (), ip);
int i32 = READ32 (ip + 1);
char *src_addr = mono_array_addr_with_size_fast ((MonoArray *) o, i32, aindex);
- sp [-1].data.vt = vt_sp;
- // Copying to vtstack. No wbarrier needed
- memcpy (sp [-1].data.vt, src_addr, i32);
- vt_sp += ALIGN_TO (i32, MINT_VT_ALIGNMENT);
+ memcpy (sp, src_addr, i32);
+ sp = STACK_ADD_BYTES (sp, i32);
ip += 3;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_STELEM_VT) {
- MonoArray *o;
- gint32 aindex;
- STELEM_PROLOG(o, aindex);
+ int i32 = READ32 (ip + 2);
+ sp = STACK_SUB_BYTES (sp, 2 * MINT_STACK_SLOT_SIZE + i32);
+ MonoArray *o = (MonoArray*)sp [0].data.p;
+ NULL_CHECK (o);
+ gint32 aindex = sp [1].data.i;
+ if (aindex >= mono_array_length_internal (o))
+ THROW_EX (mono_get_exception_index_out_of_range (), ip);
- MonoClass *klass_vt = (MonoClass*)frame->imethod->data_items [ip [1]];
- int const i32 = READ32 (ip + 2);
char *dst_addr = mono_array_addr_with_size_fast ((MonoArray *) o, i32, aindex);
-
- mono_value_copy_internal (dst_addr, sp [2].data.vt, klass_vt);
- vt_sp -= ALIGN_TO (i32, MINT_VT_ALIGNMENT);
+ MonoClass *klass_vt = (MonoClass*)frame->imethod->data_items [ip [1]];
+ mono_value_copy_internal (dst_addr, sp + 2, klass_vt);
ip += 4;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_MKREFANY) {
MonoClass* const c = (MonoClass*)frame->imethod->data_items [ip [1]];
+ sp--;
/* The value address is on the stack */
- gpointer addr = sp [-1].data.p;
+ gpointer addr = sp [0].data.p;
/* Push the typedref value on the stack */
- sp [-1].data.p = vt_sp;
- vt_sp += ALIGN_TO (sizeof (MonoTypedRef), MINT_VT_ALIGNMENT);
-
- MonoTypedRef *tref = (MonoTypedRef*)sp [-1].data.p;
+ MonoTypedRef *tref = (MonoTypedRef*)sp;
tref->klass = c;
tref->type = m_class_get_byval_arg (c);
tref->value = addr;
+ sp = STACK_ADD_BYTES (sp, sizeof (MonoTypedRef));
ip += 2;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_REFANYTYPE) {
- MonoTypedRef *tref = (MonoTypedRef*)sp [-1].data.p;
- MonoType *type = tref->type;
+ sp = STACK_SUB_BYTES (sp, sizeof (MonoTypedRef));
+ MonoTypedRef *tref = (MonoTypedRef*)sp;
- vt_sp -= ALIGN_TO (sizeof (MonoTypedRef), MINT_VT_ALIGNMENT);
- sp [-1].data.p = vt_sp;
- vt_sp += 8;
- *(gpointer*)sp [-1].data.p = type;
- ip ++;
+ sp [0].data.p = tref->type;
+ sp++;
+ ip++;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_REFANYVAL) {
- MonoTypedRef *tref = (MonoTypedRef*)sp [-1].data.p;
- gpointer addr = tref->value;
+ sp = STACK_SUB_BYTES (sp, sizeof (MonoTypedRef));
+ MonoTypedRef *tref = (MonoTypedRef*)sp;
MonoClass* const c = (MonoClass*)frame->imethod->data_items [ip [1]];
if (c != tref->klass)
THROW_EX (mono_get_exception_invalid_cast (), ip);
- vt_sp -= ALIGN_TO (sizeof (MonoTypedRef), MINT_VT_ALIGNMENT);
-
- sp [-1].data.p = addr;
+ sp [0].data.p = tref->value;
+ sp++;
ip += 2;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_LDTOKEN)
- sp->data.p = vt_sp;
- vt_sp += 8;
- * (gpointer *)sp->data.p = frame->imethod->data_items[ip [1]];
+ // FIXME same as MINT_MONO_LDPTR
+ sp->data.p = frame->imethod->data_items [ip [1]];
+ sp++;
ip += 2;
- ++sp;
MINT_IN_BREAK;
MINT_IN_CASE(MINT_ADD_OVF_I4)
if (CHECK_ADD_OVERFLOW (sp [-2].data.i, sp [-1].data.i))
guint16 clause_index = *(ip + 1);
// endfinally empties the stack
- vt_sp = (guchar*)frame->stack + frame->imethod->total_locals_size;
- sp = (stackval*)(vt_sp + frame->imethod->vt_stack_size);
+ sp = (stackval*)(locals + frame->imethod->total_locals_size);
guint16 *ret_ip = *(guint16**)(locals + frame->imethod->clause_data_offsets [clause_index]);
if (!ret_ip) {
MINT_IN_CASE(MINT_LEAVE_CHECK)
MINT_IN_CASE(MINT_LEAVE_S_CHECK) {
// leave empties the stack
- vt_sp = (guchar*)frame->stack + frame->imethod->total_locals_size;
- sp = (stackval*)(vt_sp + frame->imethod->vt_stack_size);
+ sp = (stackval*)(locals + frame->imethod->total_locals_size);
int opcode = *ip;
gboolean const check = opcode == MINT_LEAVE_CHECK || opcode == MINT_LEAVE_S_CHECK;
MINT_IN_CASE(MINT_MONO_RETOBJ)
++ip;
sp--;
+ g_assert_not_reached ();
stackval_from_data (mono_method_signature_internal (frame->imethod->method)->ret, frame->retval, sp->data.p,
mono_method_signature_internal (frame->imethod->method)->pinvoke);
if (sp > frame->stack)
ip++;
MINT_IN_BREAK;
}
-
- MINT_IN_CASE(MINT_LDARG_VT) {
- sp->data.p = vt_sp;
- int const i32 = READ32 (ip + 2);
- memcpy(sp->data.p, frame->stack [ip [1]].data.p, i32);
- vt_sp += ALIGN_TO (i32, MINT_VT_ALIGNMENT);
- ip += 4;
- ++sp;
- MINT_IN_BREAK;
- }
-
- MINT_IN_CASE(MINT_STARG_VT) {
- int const i32 = READ32 (ip + 2);
- --sp;
- memcpy(frame->stack [ip [1]].data.p, sp->data.p, i32);
- vt_sp -= ALIGN_TO (i32, MINT_VT_ALIGNMENT);
- ip += 4;
- MINT_IN_BREAK;
- }
-
MINT_IN_CASE(MINT_PROF_ENTER) {
guint16 flag = ip [1];
ip += 2;
int const i32 = READ32 (ip + 2);
if (i32 == -1) {
} else if (i32) {
- sp--;
+ sp = STACK_SUB_BYTES (sp, i32);
if (frame->parent) {
- gpointer dest_vt = frame->parent->state.vt_sp;
- /* Push the valuetype in the parent frame */
- memcpy (dest_vt, sp->data.p, i32);
- frame->parent->state.sp [0].data.p = dest_vt;
- frame->parent->state.sp++;
- frame->parent->state.vt_sp += ALIGN_TO (i32, MINT_VT_ALIGNMENT);
- } else {
- memcpy (frame->retval->data.p, sp->data.p, i32);
+ memmove (frame->parent->state.sp, sp, i32);
+ frame->parent->state.sp = STACK_ADD_BYTES (frame->parent->state.sp, i32);
}
} else {
sp--;
if (frame->parent) {
frame->parent->state.sp [0] = *sp;
frame->parent->state.sp++;
- } else {
- *frame->retval = *sp;
}
}
MonoProfilerCallContext *prof_ctx = g_new0 (MonoProfilerCallContext, 1);
prof_ctx->interp_frame = frame;
prof_ctx->method = frame->imethod->method;
- if (i32 != -1) {
- if (i32) {
- if (frame->parent)
- prof_ctx->return_value = frame->parent->state.sp [-1].data.p;
- else
- prof_ctx->return_value = frame->retval->data.p;
- } else {
- if (frame->parent)
- prof_ctx->return_value = frame->parent->state.sp - 1;
- else
- prof_ctx->return_value = frame->retval;
- }
- }
+ if (i32 != -1)
+ prof_ctx->return_value = sp;
if (flag & TRACING_FLAG)
mono_trace_leave_method (frame->imethod->method, frame->imethod->jinfo, prof_ctx);
if (flag & PROFILING_FLAG)
MINT_IN_BREAK;
}
- MINT_IN_CASE(MINT_LDARGA_VT)
- sp->data.p = frame->stack [ip [1]].data.p;
- ip += 2;
- ++sp;
- MINT_IN_BREAK;
-
#define LDLOC(datamem, argtype) \
sp->data.datamem = * (argtype *)(locals + ip [1]); \
ip += 2; \
MINT_IN_CASE(MINT_LDLOC_O) LDLOC(p, gpointer); MINT_IN_BREAK;
MINT_IN_CASE(MINT_LDLOC_VT) {
- sp->data.p = vt_sp;
int const i32 = READ32 (ip + 2);
- memcpy(sp->data.p, locals + ip [1], i32);
- vt_sp += ALIGN_TO (i32, MINT_VT_ALIGNMENT);
+ memcpy (sp, locals + ip [1], i32);
+ sp = STACK_ADD_BYTES (sp, i32);
ip += 4;
- ++sp;
MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_LDLOCA_S)
MINT_IN_CASE(MINT_STLOC_VT) {
int const i32 = READ32 (ip + 2);
- --sp;
- memcpy(locals + ip [1], sp->data.p, i32);
- vt_sp -= ALIGN_TO (i32, MINT_VT_ALIGNMENT);
+ sp = STACK_SUB_BYTES (sp, i32);
+ memcpy (locals + ip [1], sp, i32);
ip += 4;
MINT_IN_BREAK;
}
}
MINT_IN_CASE(MINT_LOCALLOC) {
- stackval *sp_start = (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size);
+ stackval *sp_start = (stackval*)(locals + frame->imethod->total_locals_size);
if (sp != sp_start + 1) /*FIX?*/
THROW_EX (mono_get_exception_execution_engine (NULL), ip);
ip += 1;
MINT_IN_BREAK;
}
- MINT_IN_CASE(MINT_LD_DELEGATE_INVOKE_IMPL) {
- MonoDelegate *del;
- int n = ip [1];
- del = (MonoDelegate*)sp [-n].data.p;
- if (!del->interp_invoke_impl) {
- /*
- * First time we are called. Set up the invoke wrapper. We might be able to do this
- * in ctor but we would need to handle AllocDelegateLike_internal separately
- */
- error_init_reuse (error);
- MonoMethod *invoke = mono_get_delegate_invoke_internal (del->object.vtable->klass);
- del->interp_invoke_impl = mono_interp_get_imethod (del->object.vtable->domain, mono_marshal_get_delegate_invoke (invoke, del), error);
- mono_error_assert_ok (error);
- }
- sp ++;
- sp [-1].data.p = del->interp_invoke_impl;
- ip += 2;
- MINT_IN_BREAK;
- }
#define MATH_UNOP(mathfunc) \
sp [-1].data.f = mathfunc (sp [-1].data.f); \
ip = context->handler_ip;
/* spec says stack should be empty at endfinally so it should be at the start too */
locals = (guchar*)frame->stack;
- vt_sp = locals + frame->imethod->total_locals_size;
- sp = (stackval*)(vt_sp + frame->imethod->vt_stack_size);
+ sp = (stackval*)(locals + frame->imethod->total_locals_size);
g_assert (context->exc_gchandle);
sp->data.p = mono_gchandle_get_target_internal (context->exc_gchandle);
++sp;
interp_frame_get_arg (MonoInterpFrameHandle frame, int pos)
{
InterpFrame *iframe = (InterpFrame*)frame;
- MonoMethodSignature *sig;
g_assert (iframe->imethod);
- sig = mono_method_signature_internal (iframe->imethod->method);
- return stackval_to_data_addr (sig->params [pos], &iframe->stack [pos + !!iframe->imethod->hasthis]);
+ return (char*)iframe->stack + get_arg_offset_fast (iframe->imethod, pos + iframe->imethod->hasthis);
}
static gpointer
g_assert (iframe->imethod);
g_assert (iframe->imethod->hasthis);
- return &iframe->stack [0].data.p;
+ return iframe->stack;
}
static MonoInterpFrameHandle
return iframe->parent;
}
-static gpointer
-interp_frame_get_res (MonoInterpFrameHandle frame)
-{
- InterpFrame *iframe = (InterpFrame*)frame;
- MonoMethodSignature *sig;
-
- g_assert (iframe->imethod);
- sig = mono_method_signature_internal (iframe->imethod->method);
- if (sig->ret->type == MONO_TYPE_VOID)
- return NULL;
- else if (iframe->parent)
- return stackval_to_data_addr (sig->ret, iframe->parent->state.sp - 1);
- else
- return stackval_to_data_addr (sig->ret, iframe->retval);
-}
-
static void
interp_start_single_stepping (void)
{
OPDEF(MINT_RET_VOID_LOCALLOC, "ret.void.localloc", 1, Pop0, Push0, MintOpNoArgs)
OPDEF(MINT_RET_VT_LOCALLOC, "ret.vt.localloc", 3, Pop1, Push0, MintOpInt)
-/* FIXME if ret_size != null it clobbers sp [-1] */
-OPDEF(MINT_VTRESULT, "vtresult", 4, Pop0, Push0, MintOpShortAndInt) /*FIX should be unsigned*/
-
OPDEF(MINT_LDC_I4_M1, "ldc.i4.m1", 1, Pop0, Push1, MintOpNoArgs)
OPDEF(MINT_LDC_I4_0, "ldc.i4.0", 1, Pop0, Push1, MintOpNoArgs)
OPDEF(MINT_LDC_I4_1, "ldc.i4.1", 1, Pop0, Push1, MintOpNoArgs)
OPDEF(MINT_LDC_R4, "ldc.r4", 3, Pop0, Push1, MintOpFloat)
OPDEF(MINT_LDC_R8, "ldc.r8", 5, Pop0, Push1, MintOpDouble)
-OPDEF(MINT_ARGLIST, "arglist", 1, Pop0, Push1, MintOpNoArgs)
-
-OPDEF(MINT_LDARG_VT, "ldarg.vt", 4, Pop0, Push1, MintOpShortAndInt)
-OPDEF(MINT_STARG_VT, "starg.vt", 4, Pop1, Push0, MintOpShortAndInt)
-OPDEF(MINT_LDARGA_VT, "ldarga.vt", 2, Pop0, Push1, MintOpUShortInt)
+OPDEF(MINT_INIT_ARGLIST, "init_arglist", 3, Pop0, Push0, MintOpNoArgs)
OPDEF(MINT_LDFLD_VT_I1, "ldfld.vt.i1", 3, Pop1, Push1, MintOpTwoShorts)
OPDEF(MINT_LDFLD_VT_U1, "ldfld.vt.u1", 3, Pop1, Push1, MintOpTwoShorts)
OPDEF(MINT_ENDFILTER, "endfilter", 1, Pop0, Push0, MintOpNoArgs)
-OPDEF(MINT_NEWOBJ, "newobj", 2, VarPop, Push1, MintOpMethodToken)
+OPDEF(MINT_NEWOBJ, "newobj", 3, VarPop, Push1, MintOpMethodToken)
OPDEF(MINT_NEWOBJ_ARRAY, "newobj_array", 3, VarPop, Push1, MintOpMethodToken)
OPDEF(MINT_NEWOBJ_STRING, "newobj_string", 3, VarPop, Push1, MintOpMethodToken)
-OPDEF(MINT_NEWOBJ_FAST, "newobj_fast", 4, VarPop, Push1, MintOpMethodToken)
-OPDEF(MINT_NEWOBJ_VT_FAST, "newobj_vt_fast", 3, VarPop, Push1, MintOpMethodToken)
-OPDEF(MINT_NEWOBJ_VTST_FAST, "newobj_vtst_fast", 4, VarPop, Push1, MintOpMethodToken)
+OPDEF(MINT_NEWOBJ_FAST, "newobj_fast", 5, VarPop, Push1, MintOpMethodToken)
+OPDEF(MINT_NEWOBJ_VT_FAST, "newobj_vt_fast", 5, VarPop, Push1, MintOpMethodToken)
OPDEF(MINT_NEWOBJ_MAGIC, "newobj_magic", 2, Pop0, Push0, MintOpMethodToken)
OPDEF(MINT_INITOBJ, "initobj", 3, Pop1, Push0, MintOpInt)
OPDEF(MINT_CASTCLASS, "castclass", 2, Pop0, Push0, MintOpClassToken)
OPDEF(MINT_CASTCLASS_COMMON, "castclass.common", 2, Pop0, Push0, MintOpClassToken)
OPDEF(MINT_ISINST_COMMON, "isinst.common", 2, Pop1, Push1, MintOpClassToken)
OPDEF(MINT_NEWARR, "newarr", 2, Pop1, Push1, MintOpClassToken)
-OPDEF(MINT_BOX, "box", 3, Pop0, Push0, MintOpTwoShorts)
-OPDEF(MINT_BOX_VT, "box.vt", 4, Pop0, Push0, MintOpTwoShorts)
-OPDEF(MINT_BOX_NULLABLE, "box.nullable", 4, Pop0, Push0, MintOpTwoShorts)
+OPDEF(MINT_BOX, "box", 2, Pop1, Push1, MintOpShortInt)
+OPDEF(MINT_BOX_VT, "box.vt", 2, Pop1, Push1, MintOpShortInt)
+OPDEF(MINT_BOX_PTR, "box.ptr", 3, Pop0, Push0, MintOpTwoShorts)
+OPDEF(MINT_BOX_NULLABLE_PTR, "box.nullable.ptr", 3, Pop0, Push0, MintOpTwoShorts)
OPDEF(MINT_UNBOX, "unbox", 2, Pop1, Push1, MintOpClassToken)
OPDEF(MINT_LDTOKEN, "ldtoken", 2, Pop0, Push1, MintOpClassToken) /* not really */
OPDEF(MINT_LDFTN, "ldftn", 2, Pop0, Push1, MintOpMethodToken)
OPDEF(MINT_ARRAY_IS_PRIMITIVE, "array_is_primitive", 1, Pop1, Push1, MintOpNoArgs)
/* Calls */
-OPDEF(MINT_CALL, "call", 4, VarPop, Push1, MintOpMethodToken)
-OPDEF(MINT_CALLVIRT, "callvirt", 4, VarPop, Push1, MintOpMethodToken)
+OPDEF(MINT_CALL, "call", 3, VarPop, Push1, MintOpMethodToken)
+OPDEF(MINT_CALLVIRT, "callvirt", 3, VarPop, Push1, MintOpMethodToken)
OPDEF(MINT_CALLVIRT_FAST, "callvirt.fast", 4, VarPop, Push1, MintOpMethodToken)
-OPDEF(MINT_CALL_DELEGATE, "call.delegate", 3, VarPop, VarPush, MintOpMethodToken)
+OPDEF(MINT_CALL_DELEGATE, "call.delegate", 3, VarPop, VarPush, MintOpTwoShorts)
OPDEF(MINT_CALLI, "calli", 3, VarPop, VarPush, MintOpMethodToken)
-OPDEF(MINT_CALLI_NAT, "calli.nat", 6, VarPop, VarPush, MintOpMethodToken)
+OPDEF(MINT_CALLI_NAT, "calli.nat", 7, VarPop, VarPush, MintOpMethodToken)
OPDEF(MINT_CALLI_NAT_DYNAMIC, "calli.nat.dynamic", 3, VarPop, VarPush, MintOpMethodToken)
OPDEF(MINT_CALLI_NAT_FAST, "calli.nat.fast", 4, VarPop, VarPush, MintOpMethodToken)
OPDEF(MINT_CALL_VARARG, "call.vararg", 4, VarPop, VarPush, MintOpMethodToken)
OPDEF(MINT_SDB_SEQ_POINT, "sdb_seq_point", 1, Pop0, Push0, MintOpNoArgs)
OPDEF(MINT_SDB_BREAKPOINT, "sdb_breakpoint", 1, Pop0, Push0, MintOpNoArgs)
OPDEF(MINT_LD_DELEGATE_METHOD_PTR, "ld_delegate_method_ptr", 1, Pop1, Push1, MintOpNoArgs)
-OPDEF(MINT_LD_DELEGATE_INVOKE_IMPL, "ld_delegate_invoke_impl", 2, Pop0, Push1, MintOpNoArgs)
OPDEF(MINT_START_ABORT_PROT, "start_abort_protected", 1, Pop0, Push0, MintOpNoArgs)
OPDEF(MINT_INTRINS_ENUM_HASFLAG, "intrins_enum_hasflag", 2, Pop2, Push1, MintOpClassToken)
OPDEF(MINT_INTRINS_GET_HASHCODE, "intrins_get_hashcode", 1, Pop1, Push1, MintOpNoArgs)
OPDEF(MINT_INTRINS_GET_TYPE, "intrins_get_type", 1, Pop1, Push1, MintOpNoArgs)
-OPDEF(MINT_INTRINS_BYREFERENCE_CTOR, "intrins_byreference_ctor", 1, Pop1, Push1, MintOpNoArgs)
OPDEF(MINT_INTRINS_SPAN_CTOR, "intrins_span_ctor", 1, Pop2, Push1, MintOpNoArgs)
OPDEF(MINT_INTRINS_BYREFERENCE_GET_VALUE, "intrins_byreference_get_value", 1, Pop1, Push1, MintOpNoArgs)
OPDEF(MINT_INTRINS_UNSAFE_ADD_BYTE_OFFSET, "intrins_unsafe_add_byte_offset", 1, Pop2, Push1, MintOpNoArgs)
#endif
+// 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) \
do { \
+ g_assert (ty != STACK_TYPE_VT); \
+ g_assert ((s)->type != STACK_TYPE_VT); \
(s)->type = (ty); \
(s)->flags = 0; \
(s)->klass = NULL; \
#define SET_TYPE(s, ty, k) \
do { \
+ g_assert (ty != STACK_TYPE_VT); \
+ g_assert ((s)->type != STACK_TYPE_VT); \
(s)->type = (ty); \
(s)->flags = 0; \
(s)->klass = k; \
} while (0)
-#define REALLOC_STACK(td, sppos) \
- do { \
- (td)->stack_capacity *= 2; \
- (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
- (td)->sp = (td)->stack + (sppos); \
- } while (0);
+static void
+realloc_stack (TransformData *td)
+{
+ int sppos = td->sp - td->stack;
-#define PUSH_SIMPLE_TYPE(td, ty) \
- do { \
- int sp_height; \
- (td)->sp++; \
- sp_height = (td)->sp - (td)->stack; \
- if (sp_height > (td)->max_stack_height) \
- (td)->max_stack_height = sp_height; \
- if (sp_height > (td)->stack_capacity) \
- REALLOC_STACK(td, sp_height); \
- SET_SIMPLE_TYPE((td)->sp - 1, ty); \
- } while (0)
+ td->stack_capacity *= 2;
+ td->stack = (StackInfo*) g_realloc (td->stack, td->stack_capacity * sizeof (td->stack [0]));
+ td->sp = td->stack + sppos;
+}
-#define PUSH_TYPE(td, ty, k) \
- do { \
- int sp_height; \
- (td)->sp++; \
- sp_height = (td)->sp - (td)->stack; \
- if (sp_height > (td)->max_stack_height) \
- (td)->max_stack_height = sp_height; \
- if (sp_height > (td)->stack_capacity) \
- REALLOC_STACK(td, sp_height); \
- SET_TYPE((td)->sp - 1, ty, k); \
- } while (0)
+static int
+get_tos_offset (TransformData *td)
+{
+ if (td->sp == td->stack)
+ return 0;
+ else
+ return td->sp [-1].offset + td->sp [-1].size;
+}
+
+static void
+push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size)
+{
+ 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)
+ realloc_stack (td);
+ td->sp->type = type;
+ td->sp->klass = k;
+ td->sp->flags = 0;
+ td->sp->offset = get_tos_offset (td);
+ 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++;
+}
+
+static void
+push_type (TransformData *td, int type, MonoClass *k)
+{
+ push_type_explicit (td, type, k, MINT_STACK_SLOT_SIZE);
+}
+
+static void
+push_simple_type (TransformData *td, int type)
+{
+ push_type (td, type, NULL);
+}
+
+static void
+push_type_vt (TransformData *td, MonoClass *k, int size)
+{
+ push_type_explicit (td, STACK_TYPE_VT, k, size);
+}
+
+static void
+push_types (TransformData *td, StackInfo *types, int count)
+{
+ for (int i = 0; i < count; i++)
+ push_type_explicit (td, types [i].type, types [i].klass, types [i].size);
+}
static void
mark_bb_as_dead (TransformData *td, InterpBasicBlock *bb)
bb->stack_state = (StackInfo*)mono_mempool_alloc (td->mempool, size);
memcpy (bb->stack_state, td->stack, size);
}
- bb->vt_stack_size = td->vt_sp;
}
static void
return st_value == vt_value;
}
-static void
-move_stack (TransformData *td, int start, int amount)
-{
- int sp_height = td->sp - td->stack;
- int to_move = sp_height - start;
-
- td->sp += amount;
- sp_height += amount;
- if (amount > 0) {
- if (sp_height > td->max_stack_height)
- td->max_stack_height = sp_height;
- if (sp_height > td->stack_capacity)
- REALLOC_STACK (td, sp_height);
- } else {
- g_assert (td->sp >= td->stack);
- }
-
- if (to_move > 0)
- memmove (td->stack + start + amount, td->stack + start, to_move * sizeof (StackInfo));
-}
-
-static void
-simulate_runtime_stack_increase (TransformData *td, int amount)
-{
- const int sp_height = td->sp - td->stack + amount;
-
- if (sp_height > td->max_stack_height)
- td->max_stack_height = sp_height;
-}
-
-#define PUSH_VT(td, size) \
- do { \
- (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
- if ((td)->vt_sp > (td)->max_vt_sp) \
- (td)->max_vt_sp = (td)->vt_sp; \
- } while (0)
-
-#define POP_VT(td, size) \
- do { \
- (td)->vt_sp -= ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
- } while (0)
-
static MonoType*
get_arg_type_exact (TransformData *td, int n, int *mt)
{
interp_add_ins (td, MINT_LDLOC_O);
td->last_ins->data [0] = 0;
klass = NULL;
+ push_type (td, stack_type [mt], klass);
} else {
- PUSH_VT (td, size);
- interp_add_ins (td, MINT_LDARG_VT);
+ interp_add_ins (td, MINT_LDLOC_VT);
td->last_ins->data [0] = n;
WRITE32_INS (td->last_ins, 1, &size);
+ push_type_vt (td, klass, size);
}
} else {
if ((hasthis || mt == MINT_TYPE_I) && n == 0) {
if (mt == MINT_TYPE_O)
klass = mono_class_from_mono_type_internal (type);
}
+ push_type (td, stack_type [mt], klass);
}
- PUSH_TYPE(td, stack_type[mt], klass);
}
static void
size = mono_class_native_size (klass, NULL);
else
size = mono_class_value_size (klass, NULL);
- interp_add_ins (td, MINT_STARG_VT);
+ interp_add_ins (td, MINT_STLOC_VT);
td->last_ins->data [0] = n;
WRITE32_INS (td->last_ins, 1, &size);
- if (td->sp [-1].type == STACK_TYPE_VT)
- POP_VT(td, size);
} else {
interp_add_ins (td, MINT_STLOC_I1 + (mt - MINT_TYPE_I1));
td->last_ins->data [0] = n;
if (mt == MINT_TYPE_VT) {
klass = mono_class_from_mono_type_internal (type);
gint32 size = mono_class_value_size (klass, NULL);
- PUSH_VT(td, size);
interp_add_ins (td, MINT_LDLOC_VT);
td->last_ins->data [0] = local;
WRITE32_INS (td->last_ins, 1, &size);
+ push_type_vt (td, klass, size);
} else {
g_assert (mt < MINT_TYPE_VT);
interp_add_ins (td, MINT_LDLOC_I1 + (mt - MINT_TYPE_I1));
td->last_ins->data [0] = local;
if (mt == MINT_TYPE_O)
klass = mono_class_from_mono_type_internal (type);
+ push_type (td, stack_type [mt], klass);
}
- PUSH_TYPE(td, stack_type[mt], klass);
}
static void
interp_add_ins (td, MINT_STLOC_VT);
td->last_ins->data [0] = local;
WRITE32_INS (td->last_ins, 1, &size);
- if (td->sp [-1].type == STACK_TYPE_VT)
- POP_VT(td, size);
} else {
g_assert (mt < MINT_TYPE_VT);
interp_add_ins (td, MINT_STLOC_I1 + (mt - MINT_TYPE_I1));
/* Inject code throwing MethodAccessException */
interp_add_ins (td, MINT_MONO_LDPTR);
td->last_ins->data [0] = get_data_item_index (td, method);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
interp_add_ins (td, MINT_MONO_LDPTR);
td->last_ins->data [0] = get_data_item_index (td, target_method);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
interp_add_ins (td, MINT_ICALL_PP_V);
td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
interp_add_ins (td, MINT_MONO_LDPTR);
td->last_ins->data [0] = get_data_item_index (td, msg);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
interp_add_ins (td, MINT_ICALL_P_V);
td->last_ins->data [0] = get_data_item_index (td, (gpointer)info->func);
* way, with an offset relative to the frame->locals.
*/
static int
-create_interp_local (TransformData *td, MonoType *type)
+create_interp_local_explicit (TransformData *td, MonoType *type, int size)
{
if (td->locals_size == td->locals_capacity) {
td->locals_capacity *= 2;
td->locals [td->locals_size].flags = 0;
td->locals [td->locals_size].indirects = 0;
td->locals [td->locals_size].offset = -1;
+ td->locals [td->locals_size].size = size;
td->locals_size++;
return td->locals_size - 1;
+
+}
+
+static int
+create_interp_local (TransformData *td, MonoType *type)
+{
+ int size, align;
+
+ size = mono_type_size (type, &align);
+ g_assert (align <= MINT_STACK_SLOT_SIZE);
+
+ return create_interp_local_explicit (td, type, size);
}
static int
get_interp_local_offset (TransformData *td, int local)
{
- int align, size, offset;
+ int size, offset;
if (td->locals [local].offset != -1)
return td->locals [local].offset;
offset = td->total_locals_size;
- size = mono_type_size (td->locals [local].type, &align);
- offset = ALIGN_TO (offset, align);
+ size = td->locals [local].size;
td->locals [local].offset = offset;
- td->total_locals_size = offset + size;
+ td->total_locals_size = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE);
//g_assert (td->total_locals_size < G_MAXUINT16);
return offset;
td->last_ins->data [0] = local;
td->locals [local].indirects++;
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
}
static gboolean
{
int mt = mint_type (m_class_get_byval_arg (klass));
int size;
+ td->sp--;
if (mt == MINT_TYPE_VT) {
interp_add_ins (td, MINT_LDOBJ_VT);
size = mono_class_value_size (klass, NULL);
WRITE32_INS (td->last_ins, 0, &size);
- PUSH_VT (td, size);
+ push_type_vt (td, klass, size);
} else {
int opcode = interp_get_ldind_for_mt (mt);
interp_add_ins (td, opcode);
+ push_type (td, stack_type [mt], klass);
}
-
- SET_TYPE (td->sp - 1, stack_type [mt], klass);
}
static void
int mt = mint_type (m_class_get_byval_arg (klass));
if (mt == MINT_TYPE_VT) {
- int size;
interp_add_ins (td, MINT_STOBJ_VT);
- td->last_ins->data [0] = get_data_item_index(td, klass);
- size = mono_class_value_size (klass, NULL);
- POP_VT (td, size);
+ td->last_ins->data [0] = get_data_item_index (td, klass);
} else {
int opcode;
switch (mt) {
int esize = mono_type_size (t, &align);
interp_add_ins (td, MINT_LDC_I4);
WRITE32_INS (td->last_ins, 0, &esize);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
td->ip += 5;
return TRUE;
} else if (!strcmp (tm, "AreSame")) {
int offset = MONO_STRUCT_OFFSET (MonoString, chars);
interp_add_ins (td, MINT_LDC_I4);
WRITE32_INS (td->last_ins, 0, &offset);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
td->ip += 5;
return TRUE;
} else if (!strcmp (tm, "IsBitwiseEquatable")) {
interp_add_ins (td, MINT_INTRINS_ENUM_HASFLAG);
td->last_ins->data [0] = get_data_item_index (td, base_klass);
td->sp -= 2;
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
td->ip += 5;
return TRUE;
}
const unsigned char *prev_ip, *prev_il_code, *prev_in_start;
int *prev_in_offsets;
gboolean ret;
- unsigned int prev_max_stack_height, prev_max_vt_sp, prev_locals_size;
+ unsigned int prev_max_stack_height, prev_locals_size;
int prev_n_data_items;
- int i, prev_vt_sp;
+ int i;
int prev_sp_offset;
MonoGenericContext *generic_context = NULL;
StackInfo *prev_param_area;
prev_il_code = td->il_code;
prev_in_start = td->in_start;
prev_sp_offset = td->sp - td->stack;
- prev_vt_sp = td->vt_sp;
prev_inlined_method = td->inlined_method;
prev_last_ins = td->last_ins;
prev_offset_to_bb = td->offset_to_bb;
td->inlined_method = target_method;
prev_max_stack_height = td->max_stack_height;
- prev_max_vt_sp = td->max_vt_sp;
prev_locals_size = td->locals_size;
prev_n_data_items = td->n_data_items;
if (td->verbose_level)
g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name);
td->max_stack_height = prev_max_stack_height;
- td->max_vt_sp = prev_max_vt_sp;
td->locals_size = prev_locals_size;
/* Remove any newly added items */
td->n_data_items = prev_n_data_items;
td->sp = td->stack + prev_sp_offset;
memcpy (&td->sp [-nargs], prev_param_area, nargs * sizeof (StackInfo));
- td->vt_sp = prev_vt_sp;
td->last_ins = prev_last_ins;
td->cbb = prev_cbb;
if (td->last_ins)
interp_constrained_box (TransformData *td, MonoDomain *domain, MonoClass *constrained_class, MonoMethodSignature *csignature, MonoError *error)
{
int mt = mint_type (m_class_get_byval_arg (constrained_class));
+ int ptr_offset = td->sp [-1 - csignature->param_count].offset;
if (mono_class_is_nullable (constrained_class)) {
g_assert (mt == MINT_TYPE_VT);
- interp_add_ins (td, MINT_BOX_NULLABLE);
+ interp_add_ins (td, MINT_BOX_NULLABLE_PTR);
td->last_ins->data [0] = get_data_item_index (td, constrained_class);
- td->last_ins->data [1] = csignature->param_count;
- td->last_ins->data [2] = 1;
+ td->last_ins->data [1] = ptr_offset;
} else {
MonoVTable *vtable = mono_class_vtable_checked (domain, constrained_class, error);
return_if_nok (error);
- if (mt == MINT_TYPE_VT) {
- interp_add_ins (td, MINT_BOX_VT);
- td->last_ins->data [0] = get_data_item_index (td, vtable);
- td->last_ins->data [1] = csignature->param_count;
- td->last_ins->data [2] = 1;
- } else {
- interp_add_ins (td, MINT_BOX);
- td->last_ins->data [0] = get_data_item_index (td, vtable);
- td->last_ins->data [1] = csignature->param_count;
- }
+ interp_add_ins (td, MINT_BOX_PTR);
+ td->last_ins->data [0] = get_data_item_index (td, vtable);
+ td->last_ins->data [1] = ptr_offset;
}
}
int is_virtual = *td->ip == CEE_CALLVIRT;
int calli = *td->ip == CEE_CALLI || *td->ip == CEE_MONO_CALLI_EXTRA_ARG;
int i;
- guint32 vt_stack_used = 0;
- guint32 vt_res_size = 0;
+ guint32 res_size = 0;
int op = -1;
int native = 0;
int need_null_check = is_virtual;
return_val_if_nok (error, FALSE);
mono_class_setup_vtable (target_method->klass);
+ // Follow the rules for constrained calls from ECMA spec
if (!m_class_is_valuetype (constrained_class)) {
/* managed pointer on the stack, we need to deref that puppy */
interp_add_ins (td, MINT_LDIND_I);
- td->last_ins->data [0] = csignature->param_count;
- } else if (target_method->klass == mono_defaults.object_class || target_method->klass == m_class_get_parent (mono_defaults.enum_class) || target_method->klass == mono_defaults.enum_class) {
+ td->last_ins->data [0] = td->sp [-1 - csignature->param_count].offset;
+ } else if (target_method->klass != constrained_class) {
/*
- * Constrained expects a managed pointer that normally needs dereferencing.
- * For value types that have their storage on the vtstack, a managed pointer
- * to it is identical to the internal pointer that is passed on the stack
- * when using the value type, not needing any dereferencing.
+ * The type parameter is instantiated as a valuetype,
+ * but that type doesn't override the method we're
+ * calling, so we need to box `this'.
*/
int this_type = (td->sp - csignature->param_count - 1)->type;
g_assert (this_type == STACK_TYPE_I || this_type == STACK_TYPE_MP);
- if (mint_type (m_class_get_byval_arg (constrained_class)) != MINT_TYPE_VT) {
- /* Always load the entire stackval, to handle also the case where the enum has long storage */
- interp_add_ins (td, MINT_LDIND_I8);
- td->last_ins->data [0] = csignature->param_count;
- }
-
interp_constrained_box (td, domain, constrained_class, csignature, error);
return_val_if_nok (error, FALSE);
} else {
- if (target_method->klass != constrained_class) {
- /*
- * The type parameter is instantiated as a valuetype,
- * but that type doesn't override the method we're
- * calling, so we need to box `this'.
- */
- int this_type = (td->sp - csignature->param_count - 1)->type;
- g_assert (this_type == STACK_TYPE_I || this_type == STACK_TYPE_MP);
- if (mint_type (m_class_get_byval_arg (constrained_class)) != MINT_TYPE_VT) {
- /* managed pointer on the stack, we need to deref that puppy */
- /* Always load the entire stackval, to handle also the case where the enum has long storage */
- interp_add_ins (td, MINT_LDIND_I8);
- td->last_ins->data [0] = csignature->param_count;
- }
-
- interp_constrained_box (td, domain, constrained_class, csignature, error);
- return_val_if_nok (error, FALSE);
- }
is_virtual = FALSE;
}
}
target_method = interp_transform_internal_calls (method, target_method, csignature, is_virtual);
- if (csignature->call_convention == MONO_CALL_VARARG) {
+ if (csignature->call_convention == MONO_CALL_VARARG)
csignature = mono_method_get_signature_checked (target_method, image, token, generic_context, error);
- int vararg_stack = 0;
- /*
- * For vararg calls, ArgIterator expects the signature and the varargs to be
- * stored in a linear memory. We allocate the necessary vt_stack space for
- * this. All varargs will be pushed to the vt_stack at call site.
- */
- vararg_stack += sizeof (gpointer);
- for (i = csignature->sentinelpos; i < csignature->param_count; ++i) {
- int align, arg_size;
- arg_size = mono_type_stack_size (csignature->params [i], &align);
- vararg_stack = ALIGN_TO (vararg_stack, align);
- vararg_stack += arg_size;
- }
- /*
- * MINT_CALL_VARARG needs this space on the vt stack. Make sure the
- * vtstack space is sufficient.
- */
- PUSH_VT (td, vararg_stack);
- POP_VT (td, vararg_stack);
- }
if (need_null_check) {
interp_add_ins (td, MINT_CKNULL_N);
- td->last_ins->data [0] = csignature->param_count + 1;
+ td->last_ins->data [0] = td->sp [-1 - csignature->param_count].offset;
}
g_assert (csignature->call_convention != MONO_CALL_FASTCALL);
if (calli)
--td->sp;
+ guint32 tos_offset = get_tos_offset (td);
td->sp -= csignature->param_count + !!csignature->hasthis;
- for (i = 0; i < csignature->param_count; ++i) {
- if (td->sp [i + !!csignature->hasthis].type == STACK_TYPE_VT) {
- gint32 size;
- MonoClass *klass = mono_class_from_mono_type_internal (csignature->params [i]);
- if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
- size = mono_class_native_size (klass, NULL);
- else
- size = mono_class_value_size (klass, NULL);
- size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
- vt_stack_used += size;
- }
- }
- /* Pop the vt stack used by the arguments */
- td->vt_sp -= vt_stack_used;
+ guint32 params_stack_size = tos_offset - get_tos_offset (td);
/* need to handle typedbyref ... */
if (csignature->ret->type != MONO_TYPE_VOID) {
MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret);
if (mt == MINT_TYPE_VT) {
if (csignature->pinvoke && method->wrapper_type != MONO_WRAPPER_NONE)
- vt_res_size = mono_class_native_size (klass, NULL);
+ res_size = mono_class_native_size (klass, NULL);
else
- vt_res_size = mono_class_value_size (klass, NULL);
- vt_res_size = ALIGN_TO (vt_res_size, MINT_VT_ALIGNMENT);
+ res_size = mono_class_value_size (klass, NULL);
+ res_size = ALIGN_TO (res_size, MINT_VT_ALIGNMENT);
if (mono_class_has_failure (klass)) {
mono_error_set_for_class_failure (error, klass);
return FALSE;
}
- PUSH_VT(td, vt_res_size);
+ push_type_vt (td, klass, res_size);
+ } else {
+ push_type (td, stack_type[mt], klass);
+ res_size = MINT_STACK_SLOT_SIZE;
}
- PUSH_TYPE(td, stack_type[mt], klass);
}
if (op >= 0) {
interp_add_ins (td, MINT_JIT_CALL);
td->last_ins->data [0] = get_data_item_index (td, (void *)mono_interp_get_imethod (domain, target_method, error));
mono_error_assert_ok (error);
- td->last_ins->data [1] = vt_stack_used;
+ td->last_ins->data [1] = params_stack_size;
} else {
if (is_delegate_invoke) {
interp_add_ins (td, MINT_CALL_DELEGATE);
td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
- td->last_ins->data [1] = vt_stack_used;
+ td->last_ins->data [1] = params_stack_size;
} else if (calli) {
#ifndef MONO_ARCH_HAS_NO_PROPER_MONOCTX
/* Try using fast icall path for simple signatures */
#endif
if (op != -1) {
interp_add_ins (td, MINT_CALLI_NAT_FAST);
+ 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);
- td->last_ins->data [1] = vt_stack_used;
+ td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
+ td->last_ins->data [1] = params_stack_size;
} else if (native) {
interp_add_ins (td, MINT_CALLI_NAT);
#ifdef TARGET_X86
/* Windows not tested/supported yet */
g_assertf (csignature->call_convention == MONO_CALL_DEFAULT || csignature->call_convention == MONO_CALL_C, "Interpreter supports only cdecl pinvoke on x86");
#endif
- td->last_ins->data [1] = vt_stack_used;
- td->last_ins->data [2] = vt_res_size;
- td->last_ins->data [3] = save_last_error;
+
+ InterpMethod *imethod = NULL;
+ /*
+ * We can have pinvoke calls outside M2N wrappers, in xdomain calls, where we can't easily get the called imethod.
+ * Those calls will be slower since we will not cache the arg offsets on the imethod, and have to compute them
+ * every time based on the signature.
+ */
+ if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
+ WrapperInfo *info = mono_marshal_get_wrapper_info (method);
+ if (info) {
+ MonoMethod *pinvoke_method = info->d.managed_to_native.method;
+ imethod = mono_interp_get_imethod (domain, pinvoke_method, error);
+ return_val_if_nok (error, FALSE);
+ }
+ }
+
+ 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] = params_stack_size;
+ td->last_ins->data [3] = res_size;
+ td->last_ins->data [4] = save_last_error;
/* Cache slot */
- td->last_ins->data [4] = get_data_item_index_nonshared (td, NULL);
+ td->last_ins->data [5] = get_data_item_index_nonshared (td, NULL);
} else {
interp_add_ins (td, MINT_CALLI);
- td->last_ins->data [1] = vt_stack_used;
+ td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
+ td->last_ins->data [1] = params_stack_size;
}
- td->last_ins->data [0] = get_data_item_index (td, (void *)csignature);
} else {
InterpMethod *imethod = mono_interp_get_imethod (domain, target_method, error);
return_val_if_nok (error, FALSE);
if (csignature->call_convention == MONO_CALL_VARARG) {
interp_add_ins (td, MINT_CALL_VARARG);
- td->last_ins->data [1] = get_data_item_index (td, (void *)csignature);
+ td->last_ins->data [2] = get_data_item_index (td, (void *)csignature);
} else if (is_virtual && !mono_class_is_marshalbyref (target_method->klass)) {
interp_add_ins (td, MINT_CALLVIRT_FAST);
if (mono_class_is_interface (target_method->klass))
- td->last_ins->data [1] = -2 * MONO_IMT_SIZE + mono_method_get_imt_slot (target_method);
+ td->last_ins->data [2] = -2 * MONO_IMT_SIZE + mono_method_get_imt_slot (target_method);
else
- td->last_ins->data [1] = mono_method_get_vtable_slot (target_method);
+ td->last_ins->data [2] = mono_method_get_vtable_slot (target_method);
} else if (is_virtual) {
interp_add_ins (td, MINT_CALLVIRT);
- td->last_ins->data [1] = imethod->param_count + imethod->hasthis;
} else {
interp_add_ins (td, MINT_CALL);
- td->last_ins->data [1] = imethod->param_count + imethod->hasthis;
}
td->last_ins->data [0] = get_data_item_index (td, (void *)imethod);
- td->last_ins->data [2] = vt_stack_used;
+ td->last_ins->data [1] = params_stack_size;
#ifdef ENABLE_EXPERIMENT_TIERED
if (MINT_IS_PATCHABLE_CALL (td->last_ins->opcode)) {
td->locals_capacity = td->locals_size;
offset = 0;
- g_assert (sizeof (stackval) == MINT_VT_ALIGNMENT);
+ g_assert (MINT_STACK_SLOT_SIZE == MINT_VT_ALIGNMENT);
/*
* We will load arguments as if they are locals. Unlike normal locals, every argument
type = 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);
td->locals [i].offset = offset;
td->locals [i].flags = 0;
td->locals [i].indirects = 0;
td->locals [i].type = type;
- td->locals [i].mt = mint_type (type);
- offset += sizeof (stackval);
+ td->locals [i].mt = mt;
+ if (mt == MINT_TYPE_VT && (!sig->hasthis || i != 0)) {
+ size = mono_type_size (type, &align);
+ offset += ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
+ } else {
+ offset += MINT_STACK_SLOT_SIZE;
+ }
}
td->il_locals_offset = offset;
}
}
-static gboolean
-signature_has_vt_params (MonoMethodSignature *csignature)
-{
- int i;
- for (i = 0; i < csignature->param_count; ++i) {
- int mt = mint_type (csignature->params [i]);
- if (mt == MINT_TYPE_VT)
- return TRUE;
- }
- return FALSE;
-}
-
static void
initialize_clause_bblocks (TransformData *td)
{
bb = td->offset_to_bb [c->handler_offset];
g_assert (bb);
bb->eh_block = TRUE;
- bb->vt_stack_size = 0;
if (c->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
bb->stack_height = 0;
bb->stack_state = (StackInfo*) mono_mempool_alloc0 (td->mempool, sizeof (StackInfo));
bb->stack_state [0].type = STACK_TYPE_O;
bb->stack_state [0].klass = NULL; /*FIX*/
+ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE;
}
if (c->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
g_assert (bb);
bb->eh_block = TRUE;
bb->stack_height = 1;
- bb->vt_stack_size= 0;
bb->stack_state = (StackInfo*) mono_mempool_alloc0 (td->mempool, sizeof (StackInfo));
bb->stack_state [0].type = STACK_TYPE_O;
bb->stack_state [0].klass = NULL; /*FIX*/
+ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE;
} else if (c->flags == MONO_EXCEPTION_CLAUSE_NONE) {
/*
* JIT doesn't emit sdb seq intr point at the start of catch clause, probably
MonoDomain *domain = rtm->domain;
MonoMethodSignature *signature = mono_method_signature_internal (method);
int num_args = signature->hasthis + signature->param_count;
+ int arglist_local = -1;
gboolean ret = TRUE;
gboolean emitted_funccall_seq_point = FALSE;
guint32 *arg_locals = NULL;
g_free (name);
}
+ if (rtm->vararg) {
+ // vararg calls are identical to normal calls on the call site. However, the
+ // first instruction in a vararg method needs to copy the variable arguments
+ // into a special region so they can be accessed by MINT_ARGLIST. This region
+ // is localloc'ed so we have compile time static offsets for all locals/stack.
+ arglist_local = create_interp_local (td, m_class_get_byval_arg (mono_defaults.int_class));
+ interp_add_ins (td, MINT_INIT_ARGLIST);
+ td->last_ins->data [0] = arglist_local;
+ // This is the offset where the variable args are on stack. After this instruction
+ // which copies them to localloc'ed memory, this space will be overwritten by normal
+ // locals
+ td->last_ins->data [1] = td->il_locals_offset;
+ td->has_localloc = TRUE;
+ }
+
/*
* We initialize the locals regardless of the presence of the init_locals
* flag. Locals holding references need to be zeroed so we don't risk
td->dont_inline = g_list_prepend (td->dont_inline, method);
while (td->ip < end) {
g_assert (td->sp >= td->stack);
- g_assert (td->vt_sp < 0x10000000);
in_offset = td->ip - header->code;
if (!inlining)
td->current_il_offset = in_offset;
if (new_bb->stack_height > 0)
memcpy (td->stack, new_bb->stack_state, new_bb->stack_height * sizeof(td->stack [0]));
td->sp = td->stack + new_bb->stack_height;
- td->vt_sp = new_bb->vt_stack_size;
} else if (link_bblocks) {
/* This bblock is not branched to. Initialize its stack state */
init_bb_stack_state (td, new_bb);
}
if (td->verbose_level > 1) {
- g_print ("IL_%04lx %-10s, sp %ld, %s %-12s vt_sp %u (max %u)\n",
+ g_print ("IL_%04lx %-10s, sp %ld, %s %-12s\n",
td->ip - td->il_code,
mono_opcode_name (*td->ip), td->sp - td->stack,
td->sp > td->stack ? stack_type_string [td->sp [-1].type] : " ",
- (td->sp > td->stack && (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_VT)) ? (td->sp [-1].klass == NULL ? "?" : m_class_get_name (td->sp [-1].klass)) : "",
- td->vt_sp, td->max_vt_sp);
+ (td->sp > td->stack && (td->sp [-1].type == STACK_TYPE_O || td->sp [-1].type == STACK_TYPE_VT)) ? (td->sp [-1].klass == NULL ? "?" : m_class_get_name (td->sp [-1].klass)) : "");
}
if (sym_seq_points && mono_bitset_test_fast (seq_point_locs, td->ip - header->code)) {
int n = ((guint8 *) td->ip) [1];
if (!inlining) {
- get_arg_type_exact (td, n, &mt);
- if (mt == MINT_TYPE_VT) {
- interp_add_ins (td, MINT_LDARGA_VT);
- } else {
- interp_add_ins (td, MINT_LDLOCA_S);
- td->locals [n].indirects++;
- }
+ interp_add_ins (td, MINT_LDLOCA_S);
td->last_ins->data [0] = n;
+ td->locals [n].indirects++;
} else {
int loc_n = arg_locals [n];
interp_add_ins (td, MINT_LDLOCA_S);
td->last_ins->data [0] = loc_n;
td->locals [loc_n].indirects++;
}
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
td->ip += 2;
break;
}
loc_n = local_locals [loc_n];
td->last_ins->data [0] = loc_n;
td->locals [loc_n].indirects++;
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
td->ip += 2;
break;
}
}
case CEE_LDNULL:
SIMPLE_OP(td, MINT_LDNULL);
- PUSH_TYPE(td, STACK_TYPE_O, NULL);
+ push_type (td, STACK_TYPE_O, NULL);
break;
case CEE_LDC_I4_M1:
SIMPLE_OP(td, MINT_LDC_I4_M1);
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
break;
case CEE_LDC_I4_0:
if (in_offset + 2 < td->code_size && interp_ip_in_cbb (td, in_offset + 1) && td->ip [1] == 0xfe && td->ip [2] == CEE_CEQ &&
td->ip += 2;
} else {
SIMPLE_OP(td, MINT_LDC_I4_0);
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
}
break;
case CEE_LDC_I4_1:
td->ip += 2;
} else {
SIMPLE_OP(td, MINT_LDC_I4_1);
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
}
break;
case CEE_LDC_I4_2:
case CEE_LDC_I4_7:
case CEE_LDC_I4_8:
SIMPLE_OP(td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0);
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
break;
case CEE_LDC_I4_S:
interp_add_ins (td, MINT_LDC_I4_S);
td->last_ins->data [0] = ((gint8 *) td->ip) [1];
td->ip += 2;
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
break;
case CEE_LDC_I4:
i32 = read32 (td->ip + 1);
interp_add_ins (td, MINT_LDC_I4);
WRITE32_INS (td->last_ins, 0, &i32);
td->ip += 5;
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
break;
case CEE_LDC_I8: {
gint64 val = read64 (td->ip + 1);
interp_add_ins (td, MINT_LDC_I8);
WRITE64_INS (td->last_ins, 0, &val);
td->ip += 9;
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_I8);
+ push_simple_type (td, STACK_TYPE_I8);
break;
}
case CEE_LDC_R4: {
interp_add_ins (td, MINT_LDC_R4);
WRITE32_INS (td->last_ins, 0, &val);
td->ip += 5;
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_R4);
+ push_simple_type (td, STACK_TYPE_R4);
break;
}
case CEE_LDC_R8: {
interp_add_ins (td, MINT_LDC_R8);
WRITE64_INS (td->last_ins, 0, &val);
td->ip += 9;
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_R8);
+ push_simple_type (td, STACK_TYPE_R8);
break;
}
case CEE_DUP: {
MonoClass *klass = td->sp [-1].klass;
if (td->sp [-1].type == STACK_TYPE_VT) {
gint32 size = mono_class_value_size (klass, NULL);
- PUSH_VT(td, size);
interp_add_ins (td, MINT_DUP_VT);
WRITE32_INS (td->last_ins, 0, &size);
td->ip ++;
- } else
+ push_type_vt (td, klass, size);
+ } else {
SIMPLE_OP(td, MINT_DUP);
- PUSH_TYPE(td, type, klass);
+ push_type (td, type, klass);
+ }
break;
}
case CEE_POP:
size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
interp_add_ins (td, MINT_POP_VT);
WRITE32_INS (td->last_ins, 0, &size);
- td->vt_sp -= size;
td->ip++;
} else {
SIMPLE_OP(td, MINT_POP);
mono_error_set_generic_error (error, "System", "InvalidProgramException", "");
goto exit;
}
- if (td->vt_sp != ALIGN_TO (vt_size, MINT_VT_ALIGNMENT))
- g_error ("%s: CEE_RET: value type stack: %d vs. %d", mono_method_full_name (td->method, TRUE), td->vt_sp, vt_size);
if (sym_seq_points) {
last_seq_point = interp_add_ins (td, MINT_SDB_SEQ_POINT);
}
td->last_ins->data [0] = exit_profiling;
WRITE32_INS (td->last_ins, 1, &vt_size);
- POP_VT (td, vt_size);
++td->ip;
} else {
if (vt_size == 0)
else {
interp_add_ins (td, MINT_RET_VT);
WRITE32_INS (td->last_ins, 0, &vt_size);
- POP_VT (td, vt_size);
++td->ip;
}
}
interp_add_ins (td, MINT_LDSTR_TOKEN);
td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token));
}
- PUSH_TYPE(td, STACK_TYPE_O, mono_defaults.string_class);
+ push_type (td, STACK_TYPE_O, mono_defaults.string_class);
break;
}
case CEE_NEWOBJ: {
MonoMethod *m;
MonoMethodSignature *csignature;
- guint32 vt_stack_used = 0;
- guint32 vt_res_size = 0;
td->ip++;
token = read32 (td->ip);
goto_if_nok (error, exit);
}
+ int ret_mt = mint_type (m_class_get_byval_arg (klass));
if (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass)) {
td->sp -= csignature->param_count;
#if SIZEOF_VOID_P == 8
td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
goto_if_nok (error, exit);
- PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
+ push_type (td, stack_type [ret_mt], klass);
} else if (klass == mono_defaults.int_class && csignature->param_count == 1) {
td->sp--;
#if SIZEOF_VOID_P == 8
interp_add_ins (td, MINT_CONV_OVF_I4_I8);
#endif
- PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
+ push_type (td, stack_type [ret_mt], klass);
+ } else if (m_class_get_parent (klass) == mono_defaults.array_class) {
+ interp_add_ins (td, MINT_NEWOBJ_ARRAY);
+ td->last_ins->data [0] = get_data_item_index (td, m->klass);
+ td->last_ins->data [1] = csignature->param_count;
+ td->sp -= csignature->param_count;
+ push_type (td, stack_type [ret_mt], klass);
+ } else if (klass == mono_defaults.string_class) {
+ guint32 tos_offset = get_tos_offset (td);
+ td->sp -= csignature->param_count;
+ guint32 params_stack_size = tos_offset - get_tos_offset (td);
+
+ interp_add_ins (td, MINT_NEWOBJ_STRING);
+ td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
+ td->last_ins->data [1] = params_stack_size;
+ push_type (td, stack_type [ret_mt], klass);
+ } else if (m_class_get_image (klass) == mono_defaults.corlib &&
+ !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
+ !strcmp (m->name, ".ctor")) {
+ /* public ByReference(ref T value) */
+ g_assert (csignature->hasthis && csignature->param_count == 1);
+ /* We already have the vt on top of the stack */
+ interp_add_ins (td, MINT_NOP);
+ td->sp--;
+ push_type_vt (td, klass, mono_class_value_size (klass, NULL));
+ } else if (m_class_get_image (klass) == mono_defaults.corlib &&
+ (!strcmp (m_class_get_name (m->klass), "Span`1") ||
+ !strcmp (m_class_get_name (m->klass), "ReadOnlySpan`1")) &&
+ csignature->param_count == 2 &&
+ csignature->params [0]->type == MONO_TYPE_PTR &&
+ !type_has_references (mono_method_get_context (m)->class_inst->type_argv [0])) {
+ /* ctor frequently used with ReadOnlySpan over static arrays */
+ interp_add_ins (td, MINT_INTRINS_SPAN_CTOR);
+ td->sp -= 2;
+ push_type_vt (td, klass, mono_class_value_size (klass, NULL));
} else {
- gboolean can_inline = TRUE;
- if (m_class_get_parent (klass) == mono_defaults.array_class) {
- interp_add_ins (td, MINT_NEWOBJ_ARRAY);
- td->last_ins->data [0] = get_data_item_index (td, m->klass);
- td->last_ins->data [1] = csignature->param_count;
- } else if (klass == mono_defaults.string_class) {
- interp_add_ins (td, MINT_NEWOBJ_STRING);
- td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
- td->last_ins->data [1] = csignature->param_count;
- } else if (m_class_get_image (klass) == mono_defaults.corlib &&
- !strcmp (m_class_get_name (m->klass), "ByReference`1") &&
- !strcmp (m->name, ".ctor")) {
- /* public ByReference(ref T value) */
- g_assert (csignature->hasthis && csignature->param_count == 1);
- interp_add_ins (td, MINT_INTRINS_BYREFERENCE_CTOR);
- } else if (m_class_get_image (klass) == mono_defaults.corlib &&
- (!strcmp (m_class_get_name (m->klass), "Span`1") ||
- !strcmp (m_class_get_name (m->klass), "ReadOnlySpan`1")) &&
- csignature->param_count == 2 &&
- csignature->params [0]->type == MONO_TYPE_PTR &&
- !type_has_references (mono_method_get_context (m)->class_inst->type_argv [0])) {
- /* ctor frequently used with ReadOnlySpan over static arrays */
- interp_add_ins (td, MINT_INTRINS_SPAN_CTOR);
- } else if (klass != mono_defaults.string_class &&
- !mono_class_is_marshalbyref (klass) &&
+ guint32 tos_offset = get_tos_offset (td);
+ td->sp -= csignature->param_count;
+ guint32 params_stack_size = tos_offset - get_tos_offset (td);
+
+ // Move params types in temporary buffer
+ StackInfo *sp_params = (StackInfo*) g_malloc (sizeof (StackInfo) * csignature->param_count);
+ memcpy (sp_params, td->sp, sizeof (StackInfo) * csignature->param_count);
+
+ // Push the return value and `this` argument to the ctor
+ gboolean is_vt = m_class_is_valuetype (klass);
+ int vtsize = mono_class_value_size (klass, NULL);
+ if (is_vt) {
+ if (ret_mt == MINT_TYPE_VT)
+ push_type_vt (td, klass, vtsize);
+ else
+ push_type (td, stack_type [ret_mt], klass);
+ push_simple_type (td, STACK_TYPE_I);
+ } else {
+ push_type (td, stack_type [ret_mt], klass);
+ push_type (td, stack_type [ret_mt], klass);
+ }
+
+ // Push back the params to top of stack
+ push_types (td, sp_params, csignature->param_count);
+
+ if (!mono_class_is_marshalbyref (klass) &&
!mono_class_has_finalizer (klass) &&
!m_class_has_weak_fields (klass)) {
- gboolean is_vt = m_class_is_valuetype (klass);
- int mt = mint_type (m_class_get_byval_arg (klass));
- gboolean is_vtst = is_vt ? mt == MINT_TYPE_VT : FALSE;
- int vtsize = mono_class_value_size (klass, NULL);
InterpInst *newobj_fast;
if (is_vt) {
- newobj_fast = interp_add_ins (td, is_vtst ? MINT_NEWOBJ_VTST_FAST : MINT_NEWOBJ_VT_FAST);
- if (is_vtst)
- newobj_fast->data [2] = vtsize;
+ newobj_fast = interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
+ newobj_fast->data [2] = vtsize;
+ // FIXME Remove this once we dump stack based design. It is here only to inform cprop
+ // how many args this instruction receives in inlined case and it is a bad pattern.
+ newobj_fast->data [3] = csignature->param_count;
} else {
MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
goto_if_nok (error, exit);
newobj_fast = interp_add_ins (td, MINT_NEWOBJ_FAST);
newobj_fast->data [2] = get_data_item_index (td, vtable);
+ newobj_fast->data [3] = csignature->param_count;
}
- newobj_fast->data [1] = csignature->param_count;
-
- move_stack (td, (td->sp - td->stack) - csignature->param_count, 2);
- StackInfo *tmp_sp = td->sp - csignature->param_count - 2;
- SET_TYPE (tmp_sp, stack_type [mt], klass);
- // for MINT_TYPE_VT, we will push a value type on vtstack
- if (is_vtst)
- PUSH_VT (td, vtsize);
- // In vt case we pass indirect pointer as this
- if (is_vt)
- SET_SIMPLE_TYPE (tmp_sp + 1, STACK_TYPE_I);
- else
- SET_TYPE (tmp_sp + 1, STACK_TYPE_O, klass);
+ newobj_fast->data [1] = params_stack_size;
// We don't support inlining ctors of MINT_TYPE_VT which also receive a MINT_TYPE_VT
// as an argument. The reason is that we would need to push this on the vtstack before
// the argument, which is very awkward for uncommon scenario.
- if ((mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m, csignature) &&
- (!is_vtst || !signature_has_vt_params (csignature))) {
+ if ((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);
break;
}
}
- // If inlining failed, restore the stack.
- // At runtime, interp.c newobj_fast uses an extra stack element
- // after the parameters to store `o` across the non-recursive call
- // where GC will see it.
- // move_stack with the last parameter negative does not reduce max_stack.
- if (is_vtst)
- POP_VT (td, vtsize);
- move_stack (td, (td->sp - td->stack) - csignature->param_count, -2);
- // Set the method to be executed as part of newobj instruction
+ // 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 (domain, m, error));
- can_inline = FALSE;
+ /* The constructor was not inlined, abort inlining of current method */
+ INLINE_FAILURE;
} else {
- // Runtime (interp_exec_method_full in interp.c) inserts
- // extra stack to hold this and return value, before call.
- simulate_runtime_stack_increase (td, 2);
interp_add_ins (td, MINT_NEWOBJ);
g_assert (!m_class_is_valuetype (klass));
td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
+ td->last_ins->data [1] = params_stack_size;
}
goto_if_nok (error, exit);
- if (!can_inline) {
- /* The constructor was not inlined, abort inlining of current method */
- INLINE_FAILURE;
- }
-
- td->sp -= csignature->param_count;
- if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
- vt_res_size = mono_class_value_size (klass, NULL);
- PUSH_VT (td, vt_res_size);
- }
- for (i = 0; i < csignature->param_count; ++i) {
- int mt = mint_type(csignature->params [i]);
- if (mt == MINT_TYPE_VT) {
- MonoClass *k = mono_class_from_mono_type_internal (csignature->params [i]);
- gint32 size = mono_class_value_size (k, NULL);
- size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
- vt_stack_used += size;
- }
- }
- if ((vt_stack_used != 0 || vt_res_size != 0) &&
- td->last_ins->opcode != MINT_INTRINS_BYREFERENCE_CTOR &&
- td->last_ins->opcode != MINT_INTRINS_SPAN_CTOR) {
- /* FIXME Remove this once vtsp and sp are unified */
- interp_add_ins (td, MINT_VTRESULT);
- td->last_ins->data [0] = vt_res_size;
- WRITE32_INS (td->last_ins, 1, &vt_stack_used);
- td->vt_sp -= vt_stack_used;
- }
- PUSH_TYPE (td, stack_type [mint_type (m_class_get_byval_arg (klass))], klass);
+ // Parameters and this pointer are popped of the stack. The return value remains
+ td->sp -= csignature->param_count + 1;
}
break;
}
interp_add_ins (td, MINT_LDLOCA_S);
td->last_ins->data [0] = local;
td->locals [local].indirects++;
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
} else {
interp_add_ins (td, MINT_UNBOX);
td->last_ins->data [0] = get_data_item_index (td, klass);
// box T + unbox.any T -> nop
if ((td->last_ins->opcode == MINT_BOX || td->last_ins->opcode == MINT_BOX_VT) &&
(td->sp - 1)->klass == klass && td->last_ins == td->cbb->last_ins) {
- gboolean is_vt = td->last_ins->opcode == MINT_BOX_VT;
interp_clear_ins (td->last_ins);
- if (is_vt)
- PUSH_VT(td, mono_class_value_size(klass, NULL));
- int mt = mint_type(m_class_get_byval_arg(klass));
- SET_TYPE(td->sp - 1, stack_type[mt], klass);
+ int mt = mint_type (m_class_get_byval_arg (klass));
+ td->sp--;
+ // Push back the original value that was boxed. We should handle this in CEE_BOX instead
+ if (mt == MINT_TYPE_VT)
+ push_type_vt (td, klass, mono_class_value_size (klass, NULL));
+ else
+ push_type (td, stack_type [mt], klass);
td->ip += 5;
break;
}
interp_add_ins (td, MINT_MONO_LDPTR);
td->last_ins->data [0] = get_data_item_index (td, klass);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
interp_add_ins (td, MINT_MONO_LDPTR);
td->last_ins->data [0] = get_data_item_index (td, field);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
interp_add_ins (td, MINT_LDC_I4);
WRITE32_INS (td->last_ins, 0, &offset);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
#if SIZEOF_VOID_P == 8
interp_add_ins (td, MINT_CONV_I8_I4);
#endif
td->last_ins->data [1] = obj_size;
if (mt == MINT_TYPE_VT)
td->last_ins->data [2] = field_size;
- POP_VT (td, obj_size);
} else {
int opcode = MINT_LDFLD_I1 + mt - MINT_TYPE_I1;
#ifdef NO_UNALIGNED_ACCESS
}
}
}
+ td->sp--;
if (mt == MINT_TYPE_VT)
- PUSH_VT (td, field_size);
+ push_type_vt (td, field_klass, field_size);
+ else
+ push_type (td, stack_type [mt], field_klass);
td->ip += 5;
- SET_TYPE (td->sp - 1, stack_type [mt], field_klass);
BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_ACQ);
break;
}
}
}
}
- if (mt == MINT_TYPE_VT) {
- int size = mono_class_value_size (field_klass, NULL);
- POP_VT (td, size);
- }
td->ip += 5;
td->sp -= 2;
break;
interp_emit_ldsflda (td, field, error);
goto_if_nok (error, exit);
td->ip += 5;
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
break;
}
case CEE_LDSFLD: {
if (mt == MINT_TYPE_VT) {
int size = mono_class_value_size (klass, NULL);
- PUSH_VT(td, size);
+ push_type_vt (td, klass, size);
+ } else {
+ push_type (td, stack_type [mt], klass);
}
td->ip += 5;
- PUSH_TYPE(td, stack_type [mt], klass);
break;
}
case CEE_STSFLD: {
interp_emit_sfld_access (td, field, fld_klass, mt, FALSE, error);
goto_if_nok (error, exit);
- if (mt == MINT_TYPE_VT) {
- int size = mono_class_value_size (fld_klass, NULL);
- POP_VT(td, size);
- }
td->ip += 5;
--td->sp;
break;
++td->ip;
break;
case CEE_BOX: {
- int size;
CHECK_STACK (td, 1);
token = read32 (td->ip + 1);
if (method->wrapper_type != MONO_WRAPPER_NONE)
const gboolean vt = mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT;
- if (vt) {
- size = mono_class_value_size (klass, NULL);
- size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
- td->vt_sp -= size;
- } else if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4) {
+ if (td->sp [-1].type == STACK_TYPE_R8 && m_class_get_byval_arg (klass)->type == MONO_TYPE_R4)
interp_add_ins (td, MINT_CONV_R4_R8);
- }
MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
goto_if_nok (error, exit);
+ td->sp--;
interp_add_ins (td, vt ? MINT_BOX_VT : MINT_BOX);
td->last_ins->data [0] = get_data_item_index (td, vtable);
td->last_ins->data [1] = 0;
- SET_TYPE(td->sp - 1, STACK_TYPE_O, klass);
+ push_type (td, STACK_TYPE_O, klass);
td->ip += 5;
}
ENSURE_I4 (td, 1);
SIMPLE_OP (td, MINT_LDELEM_VT);
WRITE32_INS (td->last_ins, 0, &size);
- --td->sp;
- SET_TYPE (td->sp - 1, STACK_TYPE_VT, klass);
- PUSH_VT (td, size);
+ td->sp -= 2;
+ push_type_vt (td, klass, size);
break;
}
default: {
SIMPLE_OP (td, MINT_STELEM_VT);
td->last_ins->data [0] = get_data_item_index (td, klass);
WRITE32_INS (td->last_ins, 1, &size);
- POP_VT (td, size);
break;
}
default: {
td->last_ins->data [0] = get_data_item_index (td, klass);
td->ip += 5;
- PUSH_VT (td, sizeof (MonoTypedRef));
- SET_TYPE(td->sp - 1, STACK_TYPE_VT, mono_defaults.typed_reference_class);
+ td->sp--;
+ push_type_vt (td, mono_defaults.typed_reference_class, sizeof (MonoTypedRef));
break;
case CEE_REFANYVAL: {
CHECK_STACK (td, 1);
interp_add_ins (td, MINT_REFANYVAL);
td->last_ins->data [0] = get_data_item_index (td, klass);
- POP_VT (td, sizeof (MonoTypedRef));
- SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
+ td->sp--;
+ push_simple_type (td, STACK_TYPE_MP);
+ SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_MP);
td->ip += 5;
break;
interp_add_ins (td, MINT_LDC_I4_1);
else
interp_add_ins (td, MINT_LDC_I4_0);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
td->ip = next_next_ip + 5;
break;
}
gpointer systype = mono_type_get_object_checked (domain, (MonoType*)handle, error);
goto_if_nok (error, exit);
td->last_ins->data [0] = get_data_item_index (td, systype);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
td->ip = next_ip + 5;
} else {
- PUSH_VT (td, sizeof(gpointer));
interp_add_ins (td, MINT_LDTOKEN);
td->last_ins->data [0] = get_data_item_index (td, handle);
- PUSH_TYPE (td, stack_type [mt], klass);
+ push_type_vt (td, klass, sizeof (gpointer));
td->ip += 5;
}
--td->sp;
td->ip += 1;
interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
break;
case CEE_MONO_CALLI_EXTRA_ARG:
/* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
interp_add_ins (td, MINT_LDFTN);
td->last_ins->data [0] = get_data_item_index (td, (gpointer)func);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
break;
}
case CEE_MONO_ICALL: {
if (!MONO_TYPE_IS_VOID (info->sig->ret)) {
int mt = mint_type (info->sig->ret);
- PUSH_SIMPLE_TYPE(td, stack_type [mt]);
+ push_simple_type (td, stack_type [mt]);
}
break;
}
case CEE_MONO_VTADDR: {
int size;
CHECK_STACK (td, 1);
+ MonoClass *klass = td->sp [-1].klass;
if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
- size = mono_class_native_size(td->sp [-1].klass, NULL);
+ size = mono_class_native_size (klass, NULL);
else
- size = mono_class_value_size(td->sp [-1].klass, NULL);
- size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
- interp_add_ins (td, MINT_VTRESULT);
- td->last_ins->data [0] = 0;
+ size = mono_class_value_size (klass, NULL);
+
+ int local = create_interp_local_explicit (td, m_class_get_byval_arg (klass), size);
+ interp_add_ins (td, MINT_STLOC_VT);
+ td->last_ins->data [0] = local;
WRITE32_INS (td->last_ins, 1, &size);
- td->vt_sp -= size;
+ td->sp--;
+
+ interp_add_ins (td, MINT_LDLOCA_S);
+ td->last_ins->data [0] = local;
+ push_simple_type (td, STACK_TYPE_MP);
+
++td->ip;
- SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
break;
}
case CEE_MONO_LDPTR:
td->ip += 5;
interp_add_ins (td, MINT_MONO_LDPTR);
td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
break;
case CEE_MONO_PINVOKE_ADDR_CACHE: {
token = read32 (td->ip + 1);
/* This is a memory slot used by the wrapper */
gpointer addr = mono_mem_manager_alloc0 (td->mem_manager, sizeof (gpointer));
td->last_ins->data [0] = get_data_item_index (td, addr);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
break;
}
case CEE_MONO_OBJADDR:
td->ip += 5;
interp_add_ins (td, MINT_MONO_NEWOBJ);
td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token));
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_O);
+ push_simple_type (td, STACK_TYPE_O);
break;
case CEE_MONO_RETOBJ:
CHECK_STACK (td, 1);
if (td->sp > td->stack)
g_warning ("CEE_MONO_RETOBJ: more values on stack: %d", td->sp-td->stack);
break;
- case CEE_MONO_LDNATIVEOBJ:
+ case CEE_MONO_LDNATIVEOBJ: {
token = read32 (td->ip + 1);
td->ip += 5;
klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
- g_assert(m_class_is_valuetype (klass));
- SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
+ g_assert (m_class_is_valuetype (klass));
+ td->sp--;
+
+ int size = mono_class_native_size (klass, NULL);
+ interp_add_ins (td, MINT_LDOBJ_VT);
+ WRITE32_INS (td->last_ins, 0, &size);
+ push_type_vt (td, klass, size);
break;
+ }
case CEE_MONO_TLS: {
gint32 key = read32 (td->ip + 1);
td->ip += 5;
g_assertf (key == TLS_KEY_SGEN_THREAD_INFO, "%d", key);
interp_add_ins (td, MINT_MONO_SGEN_THREAD_INFO);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
break;
}
case CEE_MONO_ATOMIC_STORE_I4:
case CEE_MONO_LDPTR_INT_REQ_FLAG:
interp_add_ins (td, MINT_MONO_LDPTR);
td->last_ins->data [0] = get_data_item_index (td, &mono_thread_interruption_request_flag);
- PUSH_TYPE (td, STACK_TYPE_MP, NULL);
+ push_type (td, STACK_TYPE_MP, NULL);
++td->ip;
break;
case CEE_MONO_MEMORY_BARRIER:
break;
case CEE_MONO_LDDOMAIN:
interp_add_ins (td, MINT_MONO_LDDOMAIN);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
++td->ip;
break;
case CEE_MONO_SAVE_LAST_ERROR:
break;
case CEE_MONO_GET_SP:
interp_add_ins (td, MINT_MONO_GET_SP);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+ push_simple_type (td, STACK_TYPE_I);
++td->ip;
break;
default:
++td->ip;
switch (*td->ip) {
case CEE_ARGLIST:
- interp_add_ins (td, MINT_ARGLIST);
- PUSH_VT (td, SIZEOF_VOID_P);
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_VT);
+ load_local (td, arglist_local);
++td->ip;
break;
case CEE_CEQ:
interp_generate_not_supported_throw (td);
interp_add_ins (td, MINT_LDNULL);
td->ip += 5;
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
break;
}
#endif
}
td->ip += 5;
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
break;
}
td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
goto_if_nok (error, exit);
td->ip += 5;
- PUSH_SIMPLE_TYPE (td, STACK_TYPE_F);
+ push_simple_type (td, STACK_TYPE_F);
break;
}
case CEE_LDARG: {
int n = read16 (td->ip + 1);
if (!inlining) {
- get_arg_type_exact (td, n, &mt);
- if (mt == MINT_TYPE_VT) {
- interp_add_ins (td, MINT_LDARGA_VT);
- } else {
- interp_add_ins (td, MINT_LDLOCA_S);
- td->locals [n].indirects++;
- }
+ interp_add_ins (td, MINT_LDLOCA_S);
td->last_ins->data [0] = n;
+ td->locals [n].indirects++;
} else {
int loc_n = arg_locals [n];
interp_add_ins (td, MINT_LDLOCA_S);
td->last_ins->data [0] = loc_n;
td->locals [loc_n].indirects++;
}
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
td->ip += 3;
break;
}
loc_n = local_locals [loc_n];
td->last_ins->data [0] = loc_n;
td->locals [loc_n].indirects++;
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_MP);
+ push_simple_type (td, STACK_TYPE_MP);
td->ip += 3;
break;
}
--td->sp;
} else {
interp_add_ins (td, MINT_LDNULL);
- PUSH_TYPE(td, STACK_TYPE_O, NULL);
+ push_type (td, STACK_TYPE_O, NULL);
interp_add_ins (td, MINT_STIND_REF);
td->sp -= 2;
}
}
interp_add_ins (td, MINT_LDC_I4);
WRITE32_INS (td->last_ins, 0, &size);
- PUSH_SIMPLE_TYPE(td, STACK_TYPE_I4);
+ push_simple_type (td, STACK_TYPE_I4);
break;
}
case CEE_REFANYTYPE:
interp_add_ins (td, MINT_REFANYTYPE);
td->ip += 1;
- POP_VT (td, sizeof (MonoTypedRef));
- PUSH_VT (td, sizeof (gpointer));
- SET_TYPE(td->sp - 1, STACK_TYPE_VT, NULL);
+ td->sp--;
+ push_type_vt (td, NULL, sizeof (gpointer));
break;
default:
g_error ("transform.c: Unimplemented opcode: 0xFE %02x (%s) at 0x%x\n", *td->ip, mono_opcode_name (256 + *td->ip), td->ip-header->code);
if (exit_bb->stack_height > 0)
memcpy (td->stack, exit_bb->stack_state, exit_bb->stack_height * sizeof(td->stack [0]));
td->sp = td->stack + exit_bb->stack_height;
- td->vt_sp = exit_bb->vt_stack_size;
}
}
}
case MINT_CALL_VARARG: {
InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
- MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [1]];
+ MonoMethodSignature *csignature = (MonoMethodSignature*) td->data_items [ins->data [2]];
*pop = imethod->param_count + imethod->hasthis + csignature->param_count - csignature->sentinelpos;
*push = imethod->rtype->type != MONO_TYPE_VOID;
break;
}
case MINT_NEWOBJ_VT_FAST:
- case MINT_NEWOBJ_VTST_FAST:
case MINT_NEWOBJ_FAST: {
- int param_count = ins->data [1];
gboolean is_inlined = ins->data [0] == INLINED_METHOD_FLAG;
if (is_inlined) {
// This needs to be handled explictly during cprop, in order to properly
*pop = 0;
*push = 2;
} else {
- *pop = param_count;
+ InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
+ *pop = imethod->param_count;
*push = 1;
}
break;
}
case MINT_NEWOBJ_ARRAY:
- case MINT_NEWOBJ_STRING:
*pop = ins->data [1];
*push = 1;
break;
+ case MINT_NEWOBJ_STRING: {
+ InterpMethod *imethod = (InterpMethod*) td->data_items [ins->data [0]];
+ *pop = imethod->param_count;
+ *push = 1;
+ break;
+ }
case MINT_LDELEMA:
case MINT_LDELEMA_TC:
*pop = ins->data [0] + 1;
} else if (MINT_IS_MOVLOC (opcode)) {
ins->data [0] = get_interp_local_offset (td, ins->data [0]);
ins->data [1] = get_interp_local_offset (td, ins->data [1]);
+ } else if (opcode == MINT_INIT_ARGLIST) {
+ ins->data [0] = get_interp_local_offset (td, ins->data [0]);
}
int size = get_inst_length (ins) - 1;
// propagated instruction, so we remove the top of stack dependency
sp [-1].ins = NULL;
sp++;
- } else if (ins->opcode >= MINT_BOX && ins->opcode <= MINT_BOX_NULLABLE) {
- int offset = ins->data [1];
- // Clear the stack slot that is boxed
- memset (&sp [-1 - offset], 0, sizeof (StackContentInfo));
- // Make sure that the instructions that pushed this stack slot can't be
- // optimized away. If we would optimize them away, we would also need to
- // update the offset in the box instruction, which we can't, for now.
- for (int i = 1; i <= offset; i++)
- sp [-i].ins = NULL;
+ } else if (ins->opcode == MINT_BOX_PTR || ins->opcode == MINT_BOX_NULLABLE_PTR) {
+ // These opcodes violate the stack based design, just clear the whole stack
+ // for now since we will get rid of the stack design anyway.
+ memset (stack, 0, (sp - stack) * sizeof (StackContentInfo));
} else if (ins->opcode == MINT_CKNULL_N) {
- int offset = ins->data [0];
- for (int i = 1; i <= offset; i++)
- sp [-i].ins = NULL;
- } else if (ins->opcode == MINT_LD_DELEGATE_INVOKE_IMPL) {
- int offset = ins->data [0];
- for (int i = 1; i <= offset; i++)
- sp [-i].ins = NULL;
- memset (sp, 0, sizeof (StackContentInfo));
- sp++;
+ // This opcode violates the stack based design, just clear the whole stack
+ // for now since we will get rid of the stack design anyway.
+ for (StackContentInfo *spit = stack; spit < sp; spit++)
+ spit->ins = NULL;
} else if (ins->opcode == MINT_POP || ins->opcode == MINT_POP_VT) {
sp--;
if (sp->ins) {
// value of the stack, so it is not considered for further optimizations.
sp->val.type = STACK_VALUE_NONE;
}
- } else if ((ins->opcode >= MINT_NEWOBJ_FAST && ins->opcode <= MINT_NEWOBJ_VTST_FAST) && ins->data [0] == INLINED_METHOD_FLAG) {
- int param_count = ins->data [1];
+ } else if ((ins->opcode == MINT_NEWOBJ_FAST || ins->opcode == MINT_NEWOBJ_VT_FAST) && ins->data [0] == INLINED_METHOD_FLAG) {
+ int param_count = ins->data [3];
// memmove the stack values while clearing ins, to prevent instruction removal
for (int i = 1; i <= param_count; i++) {
sp [-i + 2] = sp [-i];
if (td->verbose_level) {
g_print ("Runtime method: %s %p\n", mono_method_full_name (method, TRUE), rtm);
- g_print ("Locals size %d, VT stack size: %d\n", td->total_locals_size, td->max_vt_sp);
- g_print ("Calculated stack size: %d, stated size: %d\n", td->max_stack_height, header->max_stack);
+ g_print ("Locals size %d, stack size: %d\n", td->total_locals_size, td->max_stack_size);
+ g_print ("Calculated stack height: %d, stated height: %d\n", td->max_stack_height, header->max_stack);
dump_mint_code (td->new_code, td->new_code_end);
}
if (c->flags & MONO_EXCEPTION_CLAUSE_FILTER)
c->data.filter_offset = get_native_offset (td, c->data.filter_offset);
}
- rtm->stack_size = (sizeof (stackval)) * (td->max_stack_height + 2); /* + 1 for returns of called functions + 1 for 0-ing in trace*/
- rtm->stack_size = ALIGN_TO (rtm->stack_size, MINT_VT_ALIGNMENT);
- rtm->vt_stack_size = td->max_vt_sp;
+ 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->vt_stack_size + rtm->stack_size, 8);
+ rtm->alloca_size = ALIGN_TO (rtm->total_locals_size + rtm->stack_size, 8);
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]));
MonoClass *klass;
unsigned char type;
unsigned char flags;
+ /* 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;
#define STACK_VALUE_NONE 0
*/
int stack_height;
StackInfo *stack_state;
- int vt_stack_size;
int index;
int flags;
int indirects;
int offset;
+ int size;
} InterpLocal;
typedef struct
StackInfo *sp;
unsigned int max_stack_height;
unsigned int stack_capacity;
- unsigned int vt_sp;
- unsigned int max_vt_sp;
+ unsigned int max_stack_size;
unsigned int total_locals_size;
InterpLocal *locals;
unsigned int il_locals_offset;
}
void
-mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
+mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig, gpointer retp)
{
const MonoEECallbacks *interp_cb;
CallInfo *cinfo;
cinfo = get_call_info (NULL, sig);
ainfo = &cinfo->ret;
- if (cinfo->ret.storage != ArgValuetypeAddrInIReg) {
+ if (retp) {
+ g_assert (cinfo->ret.storage == ArgValuetypeAddrInIReg);
+ interp_cb->frame_arg_to_data ((MonoInterpFrameHandle)frame, sig, -1, retp);
+#ifdef TARGET_WIN32
+ // Windows x64 ABI ainfo implementation includes info on how to return value type address.
+ // back to caller.
+ storage = arg_get_storage (ccontext, ainfo);
+ *(gpointer *)storage = retp;
+#endif
+ } else {
+ g_assert (cinfo->ret.storage != ArgValuetypeAddrInIReg);
int temp_size = arg_need_temp (ainfo);
if (temp_size)
if (temp_size)
arg_set_val (ccontext, ainfo, storage);
}
-#ifdef TARGET_WIN32
- // Windows x64 ABI ainfo implementation includes info on how to return value type address.
- // back to caller.
- else {
- storage = arg_get_storage (ccontext, ainfo);
- *(gpointer *)storage = interp_cb->frame_arg_to_storage (frame, sig, -1);
- }
-#endif
g_free (cinfo);
}
-void
+gpointer
mono_arch_get_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
{
const MonoEECallbacks *interp_cb = mini_get_interp_callbacks ();
gpointer storage;
ArgInfo *ainfo;
- if (sig->ret->type != MONO_TYPE_VOID) {
- ainfo = &cinfo->ret;
- if (ainfo->storage == ArgValuetypeAddrInIReg) {
- storage = (gpointer) ccontext->gregs [cinfo->ret.reg];
- interp_cb->frame_arg_set_storage ((MonoInterpFrameHandle)frame, sig, -1, storage);
- }
- }
-
for (int i = 0; i < sig->param_count + sig->hasthis; i++) {
ainfo = &cinfo->args [i];
interp_cb->data_to_frame_arg ((MonoInterpFrameHandle)frame, sig, i, storage);
}
+ storage = NULL;
+ if (sig->ret->type != MONO_TYPE_VOID) {
+ ainfo = &cinfo->ret;
+ if (ainfo->storage == ArgValuetypeAddrInIReg)
+ storage = (gpointer) ccontext->gregs [cinfo->ret.reg];
+ }
g_free (cinfo);
+ return storage;
}
void
/* Set return value in the ccontext (for n2i return) */
void
-mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
+mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig, gpointer retp)
{
const MonoEECallbacks *interp_cb;
CallInfo *cinfo;
cinfo = get_call_info (NULL, sig);
ainfo = &cinfo->ret;
- if (ainfo->storage != RegTypeStructByAddr) {
+ if (retp) {
+ g_assert (ainfo->storage == RegTypeStructByAddr);
+ interp_cb->frame_arg_to_data ((MonoInterpFrameHandle)frame, sig, -1, retp);
+ } else {
+ g_assert (ainfo->storage != RegTypeStructByAddr);
g_assert (!arg_need_temp (ainfo));
storage = arg_get_storage (ccontext, ainfo);
memset (ccontext, 0, sizeof (CallContext)); // FIXME
}
/* Gets the arguments from ccontext (for n2i entry) */
-void
+gpointer
mono_arch_get_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
{
const MonoEECallbacks *interp_cb = mini_get_interp_callbacks ();
gpointer storage;
ArgInfo *ainfo;
- if (sig->ret->type != MONO_TYPE_VOID) {
- ainfo = &cinfo->ret;
- if (ainfo->storage == RegTypeStructByAddr) {
- storage = (gpointer)(gsize)ccontext->gregs [cinfo->ret.reg];
- interp_cb->frame_arg_set_storage ((MonoInterpFrameHandle)frame, sig, -1, storage);
- }
- }
-
for (int i = 0; i < sig->param_count + sig->hasthis; i++) {
ainfo = &cinfo->args [i];
int temp_size = arg_need_temp (ainfo);
interp_cb->data_to_frame_arg ((MonoInterpFrameHandle)frame, sig, i, storage);
}
+ storage = NULL;
+ if (sig->ret->type != MONO_TYPE_VOID) {
+ ainfo = &cinfo->ret;
+ if (ainfo->storage == RegTypeStructByAddr)
+ storage = (gpointer)(gsize)ccontext->gregs [cinfo->ret.reg];
+ }
+
g_free (cinfo);
+ return storage;
}
/* Gets the return value from ccontext (for i2n exit) */
/* Set return value in the ccontext (for n2i return) */
void
-mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
+mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig, gpointer retp)
{
const MonoEECallbacks *interp_cb;
CallInfo *cinfo;
cinfo = get_call_info (NULL, sig);
ainfo = &cinfo->ret;
- if (ainfo->storage != ArgVtypeByRef) {
+ if (retp) {
+ g_assert (ainfo->storage == ArgVtypeByRef);
+ interp_cb->frame_arg_to_data ((MonoInterpFrameHandle)frame, sig, -1, retp);
+ } else {
+ g_assert (ainfo->storage != ArgVtypeByRef);
int temp_size = arg_need_temp (ainfo);
if (temp_size)
}
/* Gets the arguments from ccontext (for n2i entry) */
-void
+gpointer
mono_arch_get_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig)
{
const MonoEECallbacks *interp_cb = mini_get_interp_callbacks ();
gpointer storage;
ArgInfo *ainfo;
- if (sig->ret->type != MONO_TYPE_VOID) {
- ainfo = &cinfo->ret;
- if (ainfo->storage == ArgVtypeByRef) {
- storage = (gpointer) ccontext->gregs [cinfo->ret.reg];
- interp_cb->frame_arg_set_storage ((MonoInterpFrameHandle)frame, sig, -1, storage);
- }
- }
-
for (int i = 0; i < sig->param_count + sig->hasthis; i++) {
ainfo = &cinfo->args [i];
int temp_size = arg_need_temp (ainfo);
interp_cb->data_to_frame_arg ((MonoInterpFrameHandle)frame, sig, i, storage);
}
+ storage = NULL;
+ if (sig->ret->type != MONO_TYPE_VOID) {
+ ainfo = &cinfo->ret;
+ if (ainfo->storage == ArgVtypeByRef)
+ storage = (gpointer) ccontext->gregs [cinfo->ret.reg];
+ }
g_free (cinfo);
+ return storage;
}
/* Gets the return value from ccontext (for i2n exit) */
{
MonoType *ret = mono_method_signature_internal (ctx->method)->ret;
- if (ctx->interp_frame)
- ctx->return_value = mini_get_interp_callbacks ()->frame_get_res (ctx->interp_frame);
-
if (!ctx->return_value)
return NULL;
gpointer mono_arch_get_native_to_interp_trampoline (MonoTrampInfo **info);
#ifdef MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP
+// Moves data (arguments and return vt address) from the InterpFrame to the CallContext so a pinvoke call can be made.
void mono_arch_set_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig);
-void mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig);
-void mono_arch_get_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig);
+// Moves the return value from the InterpFrame to the ccontext, or to the retp (if native code passed the retvt address)
+void mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig, gpointer retp);
+// When entering interp from native, this moves the arguments from the ccontext to the InterpFrame. If we have a return
+// vt address, we return it. This ret vt address needs to be passed to mono_arch_set_native_call_context_ret.
+gpointer mono_arch_get_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig);
+// After the pinvoke call is done, this moves return value from the ccontext to the InterpFrame.
void mono_arch_get_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig);
#endif