Skip to content

Commit 60d9b98

Browse files
authored
[mono][interp] Fixes for compilation of huge methods (#57346)
* [interp] Disable inlining if interpreter var limit is excedeed Currently, the interpreter has a 64k limit for the stack space to be used by vars. If we reach this limit, retry compilation with inlining disabled, since inlining can increase significantly the method code. * [interp] Improve cprop speed by removing redundant memclear Instead of clearing the defs for all vars, clear only for the vars used within a basic block, as the jit does. Otherwise, for large methods with many locals and many bblocks, we end up spending most of the time clearing memory. Improves speed of cprop by 20-30%, while for huge methods it can improve it by orders of magnitude.
1 parent 8327778 commit 60d9b98

File tree

2 files changed

+80
-39
lines changed

2 files changed

+80
-39
lines changed

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

Lines changed: 77 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2779,6 +2779,9 @@ interp_method_check_inlining (TransformData *td, MonoMethod *method, MonoMethodS
27792779
{
27802780
MonoMethodHeaderSummary header;
27812781

2782+
if (td->disable_inlining)
2783+
return FALSE;
2784+
27822785
if (method->flags & METHOD_ATTRIBUTE_REQSECOBJ)
27832786
/* Used to mark methods containing StackCrawlMark locals */
27842787
return FALSE;
@@ -8292,6 +8295,42 @@ cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, LocalValue *local_de
82928295
}
82938296
}
82948297

8298+
static void
8299+
foreach_local_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int, gpointer))
8300+
{
8301+
int opcode = ins->opcode;
8302+
if (mono_interp_op_sregs [opcode]) {
8303+
for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) {
8304+
int sreg = ins->sregs [i];
8305+
8306+
if (sreg == MINT_CALL_ARGS_SREG) {
8307+
int *call_args = ins->info.call_args;
8308+
if (call_args) {
8309+
int var = *call_args;
8310+
while (var != -1) {
8311+
callback (td, var, data);
8312+
call_args++;
8313+
var = *call_args;
8314+
}
8315+
}
8316+
} else {
8317+
callback (td, sreg, data);
8318+
}
8319+
}
8320+
}
8321+
8322+
if (mono_interp_op_dregs [opcode])
8323+
callback (td, ins->dreg, data);
8324+
}
8325+
8326+
static void
8327+
clear_local_defs (TransformData *td, int var, void *data)
8328+
{
8329+
LocalValue *local_defs = (LocalValue*) data;
8330+
local_defs [var].type = LOCAL_VALUE_NONE;
8331+
local_defs [var].ins = NULL;
8332+
}
8333+
82958334
static void
82968335
interp_cprop (TransformData *td)
82978336
{
@@ -8316,8 +8355,8 @@ interp_cprop (TransformData *td)
83168355
// Set cbb since we do some instruction inserting below
83178356
td->cbb = bb;
83188357

8319-
// FIXME This is excessive. Remove this once we have SSA
8320-
memset (local_defs, 0, td->locals_size * sizeof (LocalValue));
8358+
for (ins = bb->first_ins; ins != NULL; ins = ins->next)
8359+
foreach_local_var (td, ins, local_defs, clear_local_defs);
83218360

83228361
if (td->verbose_level)
83238362
g_print ("BB%d\n", bb->index);
@@ -8947,34 +8986,6 @@ interp_optimize_code (TransformData *td)
89478986
MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td));
89488987
}
89498988

8950-
static void
8951-
foreach_local_var (TransformData *td, InterpInst *ins, int data, void (*callback)(TransformData*, int, int))
8952-
{
8953-
int opcode = ins->opcode;
8954-
if (mono_interp_op_sregs [opcode]) {
8955-
for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) {
8956-
int sreg = ins->sregs [i];
8957-
8958-
if (sreg == MINT_CALL_ARGS_SREG) {
8959-
int *call_args = ins->info.call_args;
8960-
if (call_args) {
8961-
int var = *call_args;
8962-
while (var != -1) {
8963-
callback (td, var, data);
8964-
call_args++;
8965-
var = *call_args;
8966-
}
8967-
}
8968-
} else {
8969-
callback (td, sreg, data);
8970-
}
8971-
}
8972-
}
8973-
8974-
if (mono_interp_op_dregs [opcode])
8975-
callback (td, ins->dreg, data);
8976-
}
8977-
89788989
static void
89798990
set_var_live_range (TransformData *td, int var, int ins_index)
89808991
{
@@ -8986,6 +8997,12 @@ set_var_live_range (TransformData *td, int var, int ins_index)
89868997
td->locals [var].live_end = ins_index;
89878998
}
89888999

9000+
static void
9001+
set_var_live_range_cb (TransformData *td, int var, gpointer data)
9002+
{
9003+
set_var_live_range (td, var, (int)(gsize)data);
9004+
}
9005+
89899006
static void
89909007
initialize_global_var (TransformData *td, int var, int bb_index)
89919008
{
@@ -9002,6 +9019,12 @@ initialize_global_var (TransformData *td, int var, int bb_index)
90029019
alloc_global_var_offset (td, var);
90039020
td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL;
90049021
}
9022+
}
9023+
9024+
static void
9025+
initialize_global_var_cb (TransformData *td, int var, gpointer data)
9026+
{
9027+
initialize_global_var (td, var, (int)(gsize)data);
90059028
}
90069029

90079030
static void
@@ -9026,7 +9049,7 @@ initialize_global_vars (TransformData *td)
90269049
td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL;
90279050
}
90289051
}
9029-
foreach_local_var (td, ins, bb->index, initialize_global_var);
9052+
foreach_local_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb);
90309053
}
90319054
}
90329055
}
@@ -9296,7 +9319,7 @@ interp_alloc_offsets (TransformData *td)
92969319
// The arg of the call is no longer global
92979320
*call_args = new_var;
92989321
// Also update liveness for this instruction
9299-
foreach_local_var (td, new_inst, ins_index, set_var_live_range);
9322+
foreach_local_var (td, new_inst, (gpointer)(gsize)ins_index, set_var_live_range_cb);
93009323
ins_index++;
93019324
}
93029325
} else {
@@ -9334,7 +9357,7 @@ interp_alloc_offsets (TransformData *td)
93349357
}
93359358
}
93369359
// Set live_start and live_end for every referenced local that is not global
9337-
foreach_local_var (td, ins, ins_index, set_var_live_range);
9360+
foreach_local_var (td, ins, (gpointer)(gsize)ins_index, set_var_live_range_cb);
93389361
ins_index++;
93399362
}
93409363
gint32 current_offset = td->total_locals_size;
@@ -9448,6 +9471,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
94489471
int i;
94499472
TransformData transform_data;
94509473
TransformData *td;
9474+
gboolean retry_compilation = FALSE;
94519475
static gboolean verbose_method_inited;
94529476
static char* verbose_method_name;
94539477

@@ -9456,6 +9480,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
94569480
verbose_method_inited = TRUE;
94579481
}
94589482

9483+
retry:
94599484
memset (&transform_data, 0, sizeof(transform_data));
94609485
td = &transform_data;
94619486

@@ -9480,6 +9505,8 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
94809505
td->seq_points = g_ptr_array_new ();
94819506
td->verbose_level = mono_interp_traceopt;
94829507
td->prof_coverage = mono_profiler_coverage_instrumentation_enabled (method);
9508+
if (retry_compilation)
9509+
td->disable_inlining = TRUE;
94839510
rtm->data_items = td->data_items;
94849511

94859512
if (td->prof_coverage)
@@ -9527,12 +9554,21 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
95279554
generate_compacted_code (td);
95289555

95299556
if (td->total_locals_size >= G_MAXUINT16) {
9530-
char *name = mono_method_get_full_name (method);
9531-
char *msg = g_strdup_printf ("Unable to run method '%s': locals size too big.", name);
9532-
g_free (name);
9533-
mono_error_set_generic_error (error, "System", "InvalidProgramException", "%s", msg);
9534-
g_free (msg);
9535-
goto exit;
9557+
if (td->disable_inlining) {
9558+
char *name = mono_method_get_full_name (method);
9559+
char *msg = g_strdup_printf ("Unable to run method '%s': locals size too big.", name);
9560+
g_free (name);
9561+
mono_error_set_generic_error (error, "System", "InvalidProgramException", "%s", msg);
9562+
g_free (msg);
9563+
retry_compilation = FALSE;
9564+
goto exit;
9565+
} else {
9566+
// We give the method another chance to compile with inlining disabled
9567+
retry_compilation = TRUE;
9568+
goto exit;
9569+
}
9570+
} else {
9571+
retry_compilation = FALSE;
95369572
}
95379573

95389574
if (td->verbose_level) {
@@ -9623,6 +9659,8 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
96239659
if (td->line_numbers)
96249660
g_array_free (td->line_numbers, TRUE);
96259661
mono_mempool_destroy (td->mempool);
9662+
if (retry_compilation)
9663+
goto retry;
96269664
}
96279665

96289666
gboolean

src/mono/mono/mini/interp/transform.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ typedef struct
208208
GList *dont_inline;
209209
int inline_depth;
210210
int has_localloc : 1;
211+
// If method compilation fails due to certain limits being exceeded, we disable inlining
212+
// and retry compilation.
213+
int disable_inlining : 1;
211214
// If the current method (inlined_method) has the aggressive inlining attribute, we no longer
212215
// bail out of inlining when having to generate certain opcodes (like call, throw).
213216
int aggressive_inlining : 1;

0 commit comments

Comments
 (0)