#define TRACING_FLAG 0x1
#define PROFILING_FLAG 0x2
-#define MINT_VT_ALIGNMENT 8
#define MINT_STACK_SLOT_SIZE (sizeof (stackval))
+// This alignment provides us with straight forward support for Vector128
+#define MINT_STACK_ALIGNMENT (2 * MINT_STACK_SLOT_SIZE)
#define INTERP_STACK_SIZE (1024*1024)
#define INTERP_REDZONE_SIZE (8*1024)
ThreadContext *context = (ThreadContext *) mono_native_tls_get_value (thread_context_id);
if (context == NULL) {
context = g_new0 (ThreadContext, 1);
- context->stack_start = (guchar*)mono_valloc (0, INTERP_STACK_SIZE, MONO_MMAP_READ | MONO_MMAP_WRITE, MONO_MEM_ACCOUNT_INTERP_STACK);
+ context->stack_start = (guchar*)mono_valloc_aligned (INTERP_STACK_SIZE, MINT_STACK_ALIGNMENT, MONO_MMAP_READ | MONO_MMAP_WRITE, MONO_MEM_ACCOUNT_INTERP_STACK);
context->stack_end = context->stack_start + INTERP_STACK_SIZE - INTERP_REDZONE_SIZE;
context->stack_real_end = context->stack_start + INTERP_STACK_SIZE;
/* We reserve a stack slot at the top of the interp stack to make temp objects visible to GC */
- context->stack_pointer = context->stack_start + MINT_STACK_SLOT_SIZE;
+ context->stack_pointer = context->stack_start + MINT_STACK_ALIGNMENT;
frame_data_allocator_init (&context->data_stack, 8192);
/* Make sure all data is initialized before publishing the context */
sp_args = STACK_ADD_BYTES (sp_args, size);
}
}
+ sp_args = (stackval*)ALIGN_TO (sp_args, MINT_STACK_ALIGNMENT);
InterpFrame frame = {0};
frame.imethod = data->rmethod;
* that could end up doing a jit call.
*/
gint32 size = mono_class_value_size (klass, NULL);
- cinfo->res_size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
+ cinfo->res_size = ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
} else {
cinfo->res_size = MINT_STACK_SLOT_SIZE;
}
}
newsp = STACK_ADD_BYTES (newsp, size);
}
+ newsp = (stackval*)ALIGN_TO (newsp, MINT_STACK_ALIGNMENT);
context->stack_pointer = (guchar*)newsp;
g_assert (context->stack_pointer < context->stack_end);
ip = frame->imethod->code;
MINT_IN_BREAK;
}
+ MINT_IN_CASE(MINT_CALL_ALIGN_STACK) {
+ int call_offset = ip [1];
+ int aligned_call_offset = call_offset + MINT_STACK_SLOT_SIZE;
+ int params_stack_size = ip [2];
+
+ memmove (locals + aligned_call_offset, locals + call_offset, params_stack_size);
+ ip += 3;
+ 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 [4]];
}
context->stack_pointer = (guchar*)frame->stack + cmethod->alloca_size;
+ g_assert_checked (((gsize)context->stack_pointer % MINT_STACK_ALIGNMENT) == 0);
+
if (G_UNLIKELY (context->stack_pointer >= context->stack_end)) {
context->stack_end = context->stack_real_end;
THROW_EX (mono_domain_get ()->stack_overflow_ex, ip);
cmethod = (InterpMethod*)frame->imethod->data_items [ip [2]];
return_offset = ip [1];
call_args_offset = ip [1];
+ int aligned_call_args_offset = ALIGN_TO (call_args_offset, MINT_STACK_ALIGNMENT);
int param_size = ip [3];
if (param_size)
- memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size);
+ memmove (locals + aligned_call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size);
+ call_args_offset = aligned_call_args_offset;
LOCAL_VAR (call_args_offset, gpointer) = NULL;
ip += 4;
goto call;
gboolean is_vt = ret_size != 0;
if (!is_vt)
ret_size = MINT_STACK_SLOT_SIZE;
+ return_offset = call_args_offset;
cmethod = (InterpMethod*)frame->imethod->data_items [ip [2]];
MonoClass *newobj_class = cmethod->method->klass;
+ call_args_offset = ALIGN_TO (call_args_offset + ret_size, MINT_STACK_ALIGNMENT);
// We allocate space on the stack for return value and for this pointer, that is passed to ctor
+ // Here we use return_offset as meaning original call_args_offset
if (param_size)
- memmove (locals + call_args_offset + ret_size + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size);
+ memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + return_offset, param_size);
if (is_vt) {
- this_ptr = locals + call_args_offset;
+ this_ptr = locals + return_offset;
memset (this_ptr, 0, ret_size);
- call_args_offset += ret_size;
} else {
// FIXME push/pop LMF
MonoVTable *vtable = mono_class_vtable_checked (newobj_class, error);
error_init_reuse (error);
this_ptr = mono_object_new_checked (newobj_class, error);
mono_interp_error_cleanup (error); // FIXME: do not swallow the error
- LOCAL_VAR (call_args_offset, gpointer) = this_ptr; // return value
- call_args_offset += MINT_STACK_SLOT_SIZE;
+ LOCAL_VAR (return_offset, gpointer) = this_ptr; // return value
}
LOCAL_VAR (call_args_offset, gpointer) = this_ptr;
- return_offset = call_args_offset; // unused, prevent warning
ip += 5;
goto call;
}
int len = LOCAL_VAR (ip [2], gint32);
gpointer mem;
if (len > 0) {
- mem = frame_data_allocator_alloc (&context->data_stack, frame, ALIGN_TO (len, MINT_VT_ALIGNMENT));
+ // We align len to 8 so we can safely load all primitive types on all platforms
+ mem = frame_data_allocator_alloc (&context->data_stack, frame, ALIGN_TO (len, sizeof (gint64)));
if (frame->imethod->init_locals)
memset (mem, 0, len);
}
findex ++;
}
+ sp_args = (stackval*)ALIGN_TO (sp_args, MINT_STACK_ALIGNMENT);
/* Allocate frame */
InterpFrame frame = {0};
ThreadContext *context = mono_jiterp_get_context();
gpointer mem;
if (len > 0) {
- mem = mono_jiterp_frame_data_allocator_alloc (&context->data_stack, frame, ALIGN_TO (len, MINT_VT_ALIGNMENT));
+ mem = mono_jiterp_frame_data_allocator_alloc (&context->data_stack, frame, ALIGN_TO (len, sizeof (gint64)));
if (frame->imethod->init_locals)
memset (mem, 0, len);
OPDEF(MINT_ARRAY_RANK, "array_rank", 3, 1, 1, MintOpNoArgs)
OPDEF(MINT_ARRAY_ELEMENT_SIZE, "array_element_size", 3, 1, 1, MintOpNoArgs)
+OPDEF(MINT_CALL_ALIGN_STACK, "call_align_stack", 3, 1, 0, MintOpShortInt)
+
/* Calls */
OPDEF(MINT_CALL, "call", 4, 1, 1, MintOpMethodToken)
OPDEF(MINT_CALLVIRT_FAST, "callvirt.fast", 5, 1, 1, MintOpMethodToken)
return ret; \
} while (0)
+// We want to allow any block of stack slots to get moved in order for them to be aligned to MINT_STACK_ALIGNMENT
#define ENSURE_STACK_SIZE(td, size) \
do { \
- if ((size) > td->max_stack_size) \
- td->max_stack_size = size; \
+ if ((size) >= td->max_stack_size) \
+ td->max_stack_size = ALIGN_TO (size + MINT_STACK_ALIGNMENT - MINT_STACK_SLOT_SIZE, MINT_STACK_ALIGNMENT); \
} while (0)
#define ENSURE_I4(td, sp_off) \
MonoMethodSignature *csignature;
int is_virtual = *td->ip == CEE_CALLVIRT;
int calli = *td->ip == CEE_CALLI || *td->ip == CEE_MONO_CALLI_EXTRA_ARG;
- guint32 res_size = 0;
int op = -1;
int native = 0;
int need_null_check = is_virtual;
MonoClass *klass = mono_class_from_mono_type_internal (csignature->ret);
if (mt == MINT_TYPE_VT) {
+ guint32 res_size;
if (csignature->pinvoke && !csignature->marshalling_disabled && method->wrapper_type != MONO_WRAPPER_NONE)
res_size = mono_class_native_size (klass, NULL);
else
res_size = mono_class_value_size (klass, NULL);
push_type_vt (td, klass, res_size);
- 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;
}
} else {
push_type (td, stack_type[mt], klass);
- res_size = MINT_STACK_SLOT_SIZE;
}
dreg = td->sp [-1].local;
} else {
td->last_ins->flags |= INTERP_INST_FLAG_CALL;
}
td->ip += 5;
- td->last_ins->info.call_args = call_args;
+ if (td->last_ins->flags & INTERP_INST_FLAG_CALL) {
+ td->last_ins->info.call_args = call_args;
+ if (!td->optimized) {
+ int call_dreg = td->last_ins->dreg;
+ int call_offset = td->locals [call_dreg].stack_offset;
+ if ((call_offset % MINT_STACK_ALIGNMENT) != 0) {
+ InterpInst *align_ins = interp_insert_ins_bb (td, td->cbb, interp_prev_ins (td->last_ins), MINT_CALL_ALIGN_STACK);
+ interp_ins_set_dreg (align_ins, call_dreg);
+ align_ins->data [0] = params_stack_size;
+ if (calli) {
+ // fp_sreg is at the top of the stack, make sure it is not overwritten by MINT_CALL_ALIGN_STACK
+ int offset = ALIGN_TO (call_offset, MINT_STACK_ALIGNMENT) - call_offset;
+ td->locals [fp_sreg].stack_offset += offset;
+ }
+ }
+ }
+ }
return TRUE;
}
td->locals_capacity = td->locals_size;
offset = 0;
- g_assert (MINT_STACK_SLOT_SIZE == MINT_VT_ALIGNMENT);
-
/*
* We will load arguments as if they are locals. Unlike normal locals, every argument
* is stored in a stackval sized slot and valuetypes have special semantics since we
offset += MINT_STACK_SLOT_SIZE;
}
}
+ offset = ALIGN_TO (offset, MINT_STACK_ALIGNMENT);
td->il_locals_offset = offset;
for (int i = 0; i < num_il_locals; ++i) {
// Every local takes a MINT_STACK_SLOT_SIZE so IL locals have same behavior as execution locals
offset += ALIGN_TO (size, MINT_STACK_SLOT_SIZE);
}
- offset = ALIGN_TO (offset, MINT_VT_ALIGNMENT);
+ offset = ALIGN_TO (offset, MINT_STACK_ALIGNMENT);
+
td->il_locals_size = offset - td->il_locals_offset;
td->total_locals_size = offset;
MonoClass *field_klass = mono_class_from_mono_type_internal (ftype);
mt = mint_type (ftype);
int field_size = mono_class_value_size (field_klass, NULL);
- int obj_size = mono_class_value_size (klass, NULL);
- obj_size = ALIGN_TO (obj_size, MINT_VT_ALIGNMENT);
{
if (is_static) {
foreach_local_var (td, ins, NULL, alloc_unopt_global_local);
}
}
+
+ if (!td->optimized)
+ td->total_locals_size = ALIGN_TO (td->total_locals_size, MINT_STACK_ALIGNMENT);
return noe;
}
// same offset. Use the dreg offset so we don't need to rely on existing call_args.
if (td->optimized)
offset = get_local_offset (td, ins->info.call_args [0]);
- else
+ else if (opcode == MINT_NEWOBJ_ARRAY || opcode == MINT_LDELEMA_TC || opcode == MINT_LDELEMA)
+ // no alignment required since this is not a real call
offset = get_local_offset (td, ins->dreg);
+ else
+ offset = ALIGN_TO (get_local_offset (td, ins->dreg), MINT_STACK_ALIGNMENT);
*ip++ = GINT_TO_UINT16 (offset);
} else {
*ip++ = GINT_TO_UINT16 (get_local_offset (td, ins->sregs [i]));
foreach_local_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb);
}
}
+ td->total_locals_size = ALIGN_TO (td->total_locals_size, MINT_STACK_ALIGNMENT);
}
// Data structure used for offset allocation of call args
call_args++;
var = *call_args;
}
+ param_size = ALIGN_TO (param_size, MINT_STACK_ALIGNMENT);
return param_size;
}
ins_index++;
}
}
+ final_total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT);
// Iterate over all call args locals, update their final offset (aka add td->total_locals_size to them)
// then also update td->total_locals_size to account for this space.
final_total_locals_size = MAX (td->locals [i].offset + td->locals [i].size, final_total_locals_size);
}
}
- td->total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_SLOT_SIZE);
+ td->total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT);
}
/*
// When unoptimized, the param area is stored in the same order, within the IL execution stack.
g_assert (!td->optimized || !td->max_stack_size);
rtm->alloca_size = td->total_locals_size + td->max_stack_size;
+ g_assert ((rtm->alloca_size % MINT_STACK_ALIGNMENT) == 0);
rtm->locals_size = td->optimized ? td->param_area_offset : td->total_locals_size;
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]));