Skip to content

Commit 01444f7

Browse files
authored
[mono][interp] Improve tiering performance (#70649)
* [mono][interp] Don't allocate some vars as execution stack It is not really true and it serves no purpose here. * [mono][interp] Print code before any optimizations take place Fix bitrotten mono_interp_print_td_code. Don't print IL_SEQ_POINT opcodes since they are too noisy. * [mono][interp] Use td->optimized directly in more places * [mono][interp] Add dummy MINT_LDNULL instruction We were pushing local that wasn't defined by any instruction, potentially confusing the var offset allocator. * [mono][interp] Add fast offset allocator, to be used by unoptimized code This is the old offset allocation scheme that we were using originally, before the var offset allocator was added. Vars have the same offset as they would have in IL code, based on their position on the execution stack at the moment when they are pushed. Whenever we push/pop on the execution stack we keep track of the used stack size. Every var pushed on the execution stack will therefore have this stack_offset remembered. Once the entire IL code is traversed and we have all the global locals allocated, the real offset of the execution stack locals can be determined. It is computed as the originally determined stack_offset added with the offset of the execution stack start (size of the global locals space). With this offset allocator, calls no longer need to store all the call args sregs and the return_offset is always the same as call_args_offset. This is because all vars are directly placed in the right position and no optimizations can move them around. The offset of the return value will therefore be also the offset where all the args are placed. The limitation with this way of allocating offsets is that we run into the same problems with opcodes that don't have typical stack usage (use values, pop them, store result). This happens with newobj opcodes. The opcode receives the params, and then it needs to call a ctor with these same params and a newly allocated this object. Since we can't use a var offset allocation pass to compute the offset ideally, the newobj opcodes in the case of unoptimized code must move these params around on the stack, in order to make room for `this`. * [mono][interp] Add dreg to all calls in unoptimized code All calls need to have a dreg (a dummy one if it is void call), in order for unoptimized offset allocator to determine the offset of the call. In unoptimized code, the offset of the first argument is always the same as the offset of the return, if any. * [mono][interp] Fix issue with passing of exvars Unoptimized code can't use a global local directly (like the exvar), it must first be pushed to a new var on the execution stack. Add a mov instruction when we start executing the basic block for a handler.
1 parent 65af9d0 commit 01444f7

4 files changed

Lines changed: 357 additions & 107 deletions

File tree

src/mono/mono/mini/interp/interp.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5374,6 +5374,19 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
53745374
ip += 4;
53755375
goto call;
53765376
}
5377+
MINT_IN_CASE(MINT_NEWOBJ_STRING_UNOPT) {
5378+
// Same as MINT_NEWOBJ_STRING but copy params into right place on stack
5379+
cmethod = (InterpMethod*)frame->imethod->data_items [ip [2]];
5380+
return_offset = ip [1];
5381+
call_args_offset = ip [1];
5382+
5383+
int param_size = ip [3];
5384+
if (param_size)
5385+
memmove (locals + call_args_offset + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size);
5386+
LOCAL_VAR (call_args_offset, gpointer) = NULL;
5387+
ip += 4;
5388+
goto call;
5389+
}
53775390
MINT_IN_CASE(MINT_NEWOBJ) {
53785391
MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [4]];
53795392
INIT_VTABLE (vtable);
@@ -5474,6 +5487,49 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
54745487
ip += 4;
54755488
goto call;
54765489
}
5490+
MINT_IN_CASE(MINT_NEWOBJ_SLOW_UNOPT) {
5491+
call_args_offset = ip [1];
5492+
guint16 param_size = ip [3];
5493+
guint16 ret_size = ip [4];
5494+
gpointer this_ptr;
5495+
5496+
// Should only be called in unoptimized code. This opcode moves the params around
5497+
// to compensate for the lack of use of a proper offset allocator in unoptimized code.
5498+
gboolean is_vt = ret_size != 0;
5499+
if (!is_vt)
5500+
ret_size = MINT_STACK_SLOT_SIZE;
5501+
5502+
cmethod = (InterpMethod*)frame->imethod->data_items [ip [2]];
5503+
5504+
MonoClass *newobj_class = cmethod->method->klass;
5505+
5506+
// We allocate space on the stack for return value and for this pointer, that is passed to ctor
5507+
if (param_size)
5508+
memmove (locals + call_args_offset + ret_size + MINT_STACK_SLOT_SIZE, locals + call_args_offset, param_size);
5509+
5510+
if (is_vt) {
5511+
this_ptr = locals + call_args_offset;
5512+
memset (this_ptr, 0, ret_size);
5513+
call_args_offset += ret_size;
5514+
} else {
5515+
// FIXME push/pop LMF
5516+
MonoVTable *vtable = mono_class_vtable_checked (newobj_class, error);
5517+
if (!is_ok (error) || !mono_runtime_class_init_full (vtable, error)) {
5518+
MonoException *exc = interp_error_convert_to_exception (frame, error, ip);
5519+
g_assert (exc);
5520+
THROW_EX (exc, ip);
5521+
}
5522+
error_init_reuse (error);
5523+
this_ptr = mono_object_new_checked (newobj_class, error);
5524+
mono_interp_error_cleanup (error); // FIXME: do not swallow the error
5525+
LOCAL_VAR (call_args_offset, gpointer) = this_ptr; // return value
5526+
call_args_offset += MINT_STACK_SLOT_SIZE;
5527+
}
5528+
LOCAL_VAR (call_args_offset, gpointer) = this_ptr;
5529+
return_offset = call_args_offset; // unused, prevent warning
5530+
ip += 5;
5531+
goto call;
5532+
}
54775533
MINT_IN_CASE(MINT_INTRINS_SPAN_CTOR) {
54785534
gpointer ptr = LOCAL_VAR (ip [2], gpointer);
54795535
int len = LOCAL_VAR (ip [3], gint32);

src/mono/mono/mini/interp/mintops.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,8 @@ OPDEF(MINT_JMP, "jmp", 2, 0, 0, MintOpMethodToken)
338338

339339
OPDEF(MINT_ENDFILTER, "endfilter", 2, 0, 1, MintOpNoArgs)
340340

341+
OPDEF(MINT_NEWOBJ_SLOW_UNOPT, "newobj_slow_unopt", 5, 1, 0, MintOpMethodToken)
342+
OPDEF(MINT_NEWOBJ_STRING_UNOPT, "newobj_string_unopt", 4, 1, 0, MintOpMethodToken)
341343
OPDEF(MINT_NEWOBJ_SLOW, "newobj_slow", 4, 1, 1, MintOpMethodToken)
342344
OPDEF(MINT_NEWOBJ_ARRAY, "newobj_array", 5, 1, 1, MintOpMethodToken)
343345
OPDEF(MINT_NEWOBJ_STRING, "newobj_string", 4, 1, 1, MintOpMethodToken)

0 commit comments

Comments
 (0)