Skip to content

Commit efd3410

Browse files
authored
[interp] Optimize conditional branches (#52130)
* [interp] Try emitting ldc_0 instead of initlocal if possible Unlike initlocal ldc_0 are propagated, also having smaller instruction footprint. Signal retry of cprop when we are adding such instructions, also in other cases. We could dynamically update the state, but let's not overcomplicate the cprop pass, for now. * [interp] Also update def_index when swapping dreg * [interp] Refactor special static field access On netcore, there are no context static fields so all special static fields are thread static. These thread static accessors are not very common, so just remove redundant opcodes for them, and use ldind/ldobj.vt and stind/stobj.vt instead. * [interp] Remove MINT_LDFLD_VT_* opcodes These opcodes were used for loading a field of a VT var. This means the opcodes were loading a value from an immediate offset added to a var offset. We can use the standard MINT_MOV opcodes for this, however we need to resolve the offsets at the very end, so it doesn't interfere with other compilation passes, since the final offset is not the real offset of a variable, but rather an offset inside a VT var. * [interp] Remove MINT_CHECKPOINT opcode It was used to check for abort requests during backward branches. Also stop checking for abort requests in other places. * [interp] Add super instructions for conditional branches We include the safepoint in the instruction and also the immediate comparison value if possible. Doing the safepoint by default in branching opcodes is questionable since, in a typical program, forward branches are far more common than backward branches. We should really remove many of the branching opcodes. We now have the possibility to swap the src vars order, and use the opposite comparison instead.
1 parent 9136bce commit efd3410

File tree

4 files changed

+376
-233
lines changed

4 files changed

+376
-233
lines changed

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

+98-114
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,22 @@ interp_throw (ThreadContext *context, MonoException *ex, InterpFrame *frame, con
10501050
} \
10511051
} while (0)
10521052

1053+
// Reduce duplicate code in interp_exec_method
1054+
static void
1055+
do_safepoint (InterpFrame *frame, ThreadContext *context)
1056+
{
1057+
context_set_safepoint_frame (context, frame);
1058+
/* Poll safepoint */
1059+
mono_threads_safepoint ();
1060+
context_clear_safepoint_frame (context);
1061+
}
1062+
1063+
#define SAFEPOINT \
1064+
do { \
1065+
if (G_UNLIKELY (mono_polling_required)) \
1066+
do_safepoint (frame, context); \
1067+
} while (0)
1068+
10531069
static MonoObject*
10541070
ves_array_create (MonoClass *klass, int param_count, stackval *values, MonoError *error)
10551071
{
@@ -4164,6 +4180,79 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs
41644180
CONDBR(mono_isunordered (d1, d2) || d1 < d2)
41654181
MINT_IN_BREAK;
41664182
}
4183+
4184+
#define ZEROP_SP(datatype, op) \
4185+
if (LOCAL_VAR (ip [1], datatype) op 0) { \
4186+
gint16 br_offset = (gint16) ip [2]; \
4187+
BACK_BRANCH_PROFILE (br_offset); \
4188+
SAFEPOINT; \
4189+
ip += br_offset; \
4190+
} else \
4191+
ip += 3;
4192+
4193+
MINT_IN_CASE(MINT_BRFALSE_I4_SP) ZEROP_SP(gint32, ==); MINT_IN_BREAK;
4194+
MINT_IN_CASE(MINT_BRFALSE_I8_SP) ZEROP_SP(gint64, ==); MINT_IN_BREAK;
4195+
MINT_IN_CASE(MINT_BRTRUE_I4_SP) ZEROP_SP(gint32, !=); MINT_IN_BREAK;
4196+
MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
4197+
4198+
#define CONDBR_SP(cond) \
4199+
if (cond) { \
4200+
gint16 br_offset = (gint16) ip [3]; \
4201+
BACK_BRANCH_PROFILE (br_offset); \
4202+
SAFEPOINT; \
4203+
ip += br_offset; \
4204+
} else \
4205+
ip += 4;
4206+
#define BRELOP_SP(datatype, op) \
4207+
CONDBR_SP(LOCAL_VAR (ip [1], datatype) op LOCAL_VAR (ip [2], datatype))
4208+
4209+
MINT_IN_CASE(MINT_BEQ_I4_SP) BRELOP_SP(gint32, ==); MINT_IN_BREAK;
4210+
MINT_IN_CASE(MINT_BEQ_I8_SP) BRELOP_SP(gint64, ==); MINT_IN_BREAK;
4211+
MINT_IN_CASE(MINT_BGE_I4_SP) BRELOP_SP(gint32, >=); MINT_IN_BREAK;
4212+
MINT_IN_CASE(MINT_BGE_I8_SP) BRELOP_SP(gint64, >=); MINT_IN_BREAK;
4213+
MINT_IN_CASE(MINT_BGT_I4_SP) BRELOP_SP(gint32, >); MINT_IN_BREAK;
4214+
MINT_IN_CASE(MINT_BGT_I8_SP) BRELOP_SP(gint64, >); MINT_IN_BREAK;
4215+
MINT_IN_CASE(MINT_BLT_I4_SP) BRELOP_SP(gint32, <); MINT_IN_BREAK;
4216+
MINT_IN_CASE(MINT_BLT_I8_SP) BRELOP_SP(gint64, <); MINT_IN_BREAK;
4217+
MINT_IN_CASE(MINT_BLE_I4_SP) BRELOP_SP(gint32, <=); MINT_IN_BREAK;
4218+
MINT_IN_CASE(MINT_BLE_I8_SP) BRELOP_SP(gint64, <=); MINT_IN_BREAK;
4219+
4220+
MINT_IN_CASE(MINT_BNE_UN_I4_SP) BRELOP_SP(guint32, !=); MINT_IN_BREAK;
4221+
MINT_IN_CASE(MINT_BNE_UN_I8_SP) BRELOP_SP(guint64, !=); MINT_IN_BREAK;
4222+
MINT_IN_CASE(MINT_BGE_UN_I4_SP) BRELOP_SP(guint32, >=); MINT_IN_BREAK;
4223+
MINT_IN_CASE(MINT_BGE_UN_I8_SP) BRELOP_SP(guint64, >=); MINT_IN_BREAK;
4224+
MINT_IN_CASE(MINT_BGT_UN_I4_SP) BRELOP_SP(guint32, >); MINT_IN_BREAK;
4225+
MINT_IN_CASE(MINT_BGT_UN_I8_SP) BRELOP_SP(guint64, >); MINT_IN_BREAK;
4226+
MINT_IN_CASE(MINT_BLE_UN_I4_SP) BRELOP_SP(guint32, <=); MINT_IN_BREAK;
4227+
MINT_IN_CASE(MINT_BLE_UN_I8_SP) BRELOP_SP(guint64, <=); MINT_IN_BREAK;
4228+
MINT_IN_CASE(MINT_BLT_UN_I4_SP) BRELOP_SP(guint32, <); MINT_IN_BREAK;
4229+
MINT_IN_CASE(MINT_BLT_UN_I8_SP) BRELOP_SP(guint64, <); MINT_IN_BREAK;
4230+
4231+
#define BRELOP_IMM_SP(datatype, op) \
4232+
CONDBR_SP(LOCAL_VAR (ip [1], datatype) op (datatype)(gint16)ip [2])
4233+
4234+
MINT_IN_CASE(MINT_BEQ_I4_IMM_SP) BRELOP_IMM_SP(gint32, ==); MINT_IN_BREAK;
4235+
MINT_IN_CASE(MINT_BEQ_I8_IMM_SP) BRELOP_IMM_SP(gint64, ==); MINT_IN_BREAK;
4236+
MINT_IN_CASE(MINT_BGE_I4_IMM_SP) BRELOP_IMM_SP(gint32, >=); MINT_IN_BREAK;
4237+
MINT_IN_CASE(MINT_BGE_I8_IMM_SP) BRELOP_IMM_SP(gint64, >=); MINT_IN_BREAK;
4238+
MINT_IN_CASE(MINT_BGT_I4_IMM_SP) BRELOP_IMM_SP(gint32, >); MINT_IN_BREAK;
4239+
MINT_IN_CASE(MINT_BGT_I8_IMM_SP) BRELOP_IMM_SP(gint64, >); MINT_IN_BREAK;
4240+
MINT_IN_CASE(MINT_BLT_I4_IMM_SP) BRELOP_IMM_SP(gint32, <); MINT_IN_BREAK;
4241+
MINT_IN_CASE(MINT_BLT_I8_IMM_SP) BRELOP_IMM_SP(gint64, <); MINT_IN_BREAK;
4242+
MINT_IN_CASE(MINT_BLE_I4_IMM_SP) BRELOP_IMM_SP(gint32, <=); MINT_IN_BREAK;
4243+
MINT_IN_CASE(MINT_BLE_I8_IMM_SP) BRELOP_IMM_SP(gint64, <=); MINT_IN_BREAK;
4244+
4245+
MINT_IN_CASE(MINT_BNE_UN_I4_IMM_SP) BRELOP_IMM_SP(guint32, !=); MINT_IN_BREAK;
4246+
MINT_IN_CASE(MINT_BNE_UN_I8_IMM_SP) BRELOP_IMM_SP(guint64, !=); MINT_IN_BREAK;
4247+
MINT_IN_CASE(MINT_BGE_UN_I4_IMM_SP) BRELOP_IMM_SP(guint32, >=); MINT_IN_BREAK;
4248+
MINT_IN_CASE(MINT_BGE_UN_I8_IMM_SP) BRELOP_IMM_SP(guint64, >=); MINT_IN_BREAK;
4249+
MINT_IN_CASE(MINT_BGT_UN_I4_IMM_SP) BRELOP_IMM_SP(guint32, >); MINT_IN_BREAK;
4250+
MINT_IN_CASE(MINT_BGT_UN_I8_IMM_SP) BRELOP_IMM_SP(guint64, >); MINT_IN_BREAK;
4251+
MINT_IN_CASE(MINT_BLE_UN_I4_IMM_SP) BRELOP_IMM_SP(guint32, <=); MINT_IN_BREAK;
4252+
MINT_IN_CASE(MINT_BLE_UN_I8_IMM_SP) BRELOP_IMM_SP(guint64, <=); MINT_IN_BREAK;
4253+
MINT_IN_CASE(MINT_BLT_UN_I4_IMM_SP) BRELOP_IMM_SP(guint32, <); MINT_IN_BREAK;
4254+
MINT_IN_CASE(MINT_BLT_UN_I8_IMM_SP) BRELOP_IMM_SP(guint64, <); MINT_IN_BREAK;
4255+
41674256
MINT_IN_CASE(MINT_SWITCH) {
41684257
guint32 val = LOCAL_VAR (ip [1], guint32);
41694258
guint32 n = READ32 (ip + 2);
@@ -5117,20 +5206,8 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs
51175206
THROW_EX (ex, ip);
51185207
MINT_IN_BREAK;
51195208
}
5120-
MINT_IN_CASE(MINT_CHECKPOINT)
5121-
/* Do synchronous checking of abort requests */
5122-
EXCEPTION_CHECKPOINT;
5123-
++ip;
5124-
MINT_IN_BREAK;
51255209
MINT_IN_CASE(MINT_SAFEPOINT)
5126-
/* Do synchronous checking of abort requests */
5127-
EXCEPTION_CHECKPOINT;
5128-
if (G_UNLIKELY (mono_polling_required)) {
5129-
context_set_safepoint_frame (context, frame);
5130-
/* Poll safepoint */
5131-
mono_threads_safepoint ();
5132-
context_clear_safepoint_frame (context);
5133-
}
5210+
SAFEPOINT;
51345211
++ip;
51355212
MINT_IN_BREAK;
51365213
MINT_IN_CASE(MINT_LDFLDA_UNSAFE) {
@@ -5153,35 +5230,6 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs
51535230
MINT_IN_BREAK;
51545231
}
51555232

5156-
// FIXME squash to load directly field type, LDFLD_VT is just a LDLOC
5157-
#define LDFLD_VT_UNALIGNED(datatype, fieldtype, unaligned) do { \
5158-
if (unaligned) \
5159-
memcpy (locals + ip [1], (char *)locals + ip [2] + ip [3], sizeof (fieldtype)); \
5160-
else \
5161-
LOCAL_VAR (ip [1], datatype) = LOCAL_VAR (ip [2] + ip [3], fieldtype); \
5162-
ip += 4; \
5163-
} while (0)
5164-
5165-
#define LDFLD_VT(datatype, fieldtype) LDFLD_VT_UNALIGNED(datatype, fieldtype, FALSE)
5166-
5167-
MINT_IN_CASE(MINT_LDFLD_VT_I1) LDFLD_VT(gint32, gint8); MINT_IN_BREAK;
5168-
MINT_IN_CASE(MINT_LDFLD_VT_U1) LDFLD_VT(gint32, guint8); MINT_IN_BREAK;
5169-
MINT_IN_CASE(MINT_LDFLD_VT_I2) LDFLD_VT(gint32, gint16); MINT_IN_BREAK;
5170-
MINT_IN_CASE(MINT_LDFLD_VT_U2) LDFLD_VT(gint32, guint16); MINT_IN_BREAK;
5171-
MINT_IN_CASE(MINT_LDFLD_VT_I4) LDFLD_VT(gint32, gint32); MINT_IN_BREAK;
5172-
MINT_IN_CASE(MINT_LDFLD_VT_I8) LDFLD_VT(gint64, gint64); MINT_IN_BREAK;
5173-
MINT_IN_CASE(MINT_LDFLD_VT_R4) LDFLD_VT(float, float); MINT_IN_BREAK;
5174-
MINT_IN_CASE(MINT_LDFLD_VT_R8) LDFLD_VT(double, double); MINT_IN_BREAK;
5175-
MINT_IN_CASE(MINT_LDFLD_VT_O) LDFLD_VT(gpointer, gpointer); MINT_IN_BREAK;
5176-
MINT_IN_CASE(MINT_LDFLD_VT_I8_UNALIGNED) LDFLD_VT_UNALIGNED(gint64, gint64, TRUE); MINT_IN_BREAK;
5177-
MINT_IN_CASE(MINT_LDFLD_VT_R8_UNALIGNED) LDFLD_VT_UNALIGNED(double, double, TRUE); MINT_IN_BREAK;
5178-
5179-
MINT_IN_CASE(MINT_LDFLD_VT_VT) {
5180-
memmove (locals + ip [1], locals + ip [2] + ip [3], ip [4]);
5181-
ip += 5;
5182-
MINT_IN_BREAK;
5183-
}
5184-
51855233
#define LDFLD_UNALIGNED(datatype, fieldtype, unaligned) do { \
51865234
MonoObject *o = LOCAL_VAR (ip [2], MonoObject*); \
51875235
NULL_CHECK (o); \
@@ -5269,9 +5317,10 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs
52695317
MINT_IN_BREAK;
52705318
}
52715319

5272-
MINT_IN_CASE(MINT_LDSSFLDA) {
5273-
guint32 offset = READ32(ip + 2);
5274-
LOCAL_VAR (ip [1], gpointer) = mono_get_special_static_data (offset);
5320+
MINT_IN_CASE(MINT_LDTSFLDA) {
5321+
MonoInternalThread *thread = mono_thread_internal_current ();
5322+
guint32 offset = READ32 (ip + 2);
5323+
LOCAL_VAR (ip [1], gpointer) = ((char*)thread->static_data [offset & 0x3f]) + (offset >> 6);
52755324
ip += 4;
52765325
MINT_IN_BREAK;
52775326
}
@@ -5306,38 +5355,6 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs
53065355
MINT_IN_BREAK;
53075356
}
53085357

5309-
#define LDTSFLD(datatype, fieldtype) { \
5310-
MonoInternalThread *thread = mono_thread_internal_current (); \
5311-
guint32 offset = READ32 (ip + 2); \
5312-
gpointer addr = ((char*)thread->static_data [offset & 0x3f]) + (offset >> 6); \
5313-
LOCAL_VAR (ip [1], datatype) = *(fieldtype*)addr; \
5314-
ip += 4; \
5315-
}
5316-
MINT_IN_CASE(MINT_LDTSFLD_I1) LDTSFLD(gint32, gint8); MINT_IN_BREAK;
5317-
MINT_IN_CASE(MINT_LDTSFLD_U1) LDTSFLD(gint32, guint8); MINT_IN_BREAK;
5318-
MINT_IN_CASE(MINT_LDTSFLD_I2) LDTSFLD(gint32, gint16); MINT_IN_BREAK;
5319-
MINT_IN_CASE(MINT_LDTSFLD_U2) LDTSFLD(gint32, guint16); MINT_IN_BREAK;
5320-
MINT_IN_CASE(MINT_LDTSFLD_I4) LDTSFLD(gint32, gint32); MINT_IN_BREAK;
5321-
MINT_IN_CASE(MINT_LDTSFLD_I8) LDTSFLD(gint64, gint64); MINT_IN_BREAK;
5322-
MINT_IN_CASE(MINT_LDTSFLD_R4) LDTSFLD(float, float); MINT_IN_BREAK;
5323-
MINT_IN_CASE(MINT_LDTSFLD_R8) LDTSFLD(double, double); MINT_IN_BREAK;
5324-
MINT_IN_CASE(MINT_LDTSFLD_O) LDTSFLD(gpointer, gpointer); MINT_IN_BREAK;
5325-
5326-
MINT_IN_CASE(MINT_LDSSFLD) {
5327-
guint32 offset = READ32(ip + 3);
5328-
gpointer addr = mono_get_special_static_data (offset);
5329-
MonoClassField *field = (MonoClassField*)frame->imethod->data_items [ip [2]];
5330-
stackval_from_data (field->type, (stackval*)(locals + ip [1]), addr, FALSE);
5331-
ip += 5;
5332-
MINT_IN_BREAK;
5333-
}
5334-
MINT_IN_CASE(MINT_LDSSFLD_VT) {
5335-
guint32 offset = READ32(ip + 2);
5336-
gpointer addr = mono_get_special_static_data (offset);
5337-
memcpy (locals + ip [1], addr, ip [4]);
5338-
ip += 5;
5339-
MINT_IN_BREAK;
5340-
}
53415358
#define STSFLD(datatype, fieldtype) { \
53425359
MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [2]]; \
53435360
INIT_VTABLE (vtable); \
@@ -5364,40 +5381,6 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs
53645381
MINT_IN_BREAK;
53655382
}
53665383

5367-
#define STTSFLD(datatype, fieldtype) { \
5368-
MonoInternalThread *thread = mono_thread_internal_current (); \
5369-
guint32 offset = READ32 (ip + 2); \
5370-
gpointer addr = ((char*)thread->static_data [offset & 0x3f]) + (offset >> 6); \
5371-
*(fieldtype*)addr = LOCAL_VAR (ip [1], datatype); \
5372-
ip += 4; \
5373-
}
5374-
5375-
MINT_IN_CASE(MINT_STTSFLD_I1) STTSFLD(gint32, gint8); MINT_IN_BREAK;
5376-
MINT_IN_CASE(MINT_STTSFLD_U1) STTSFLD(gint32, guint8); MINT_IN_BREAK;
5377-
MINT_IN_CASE(MINT_STTSFLD_I2) STTSFLD(gint32, gint16); MINT_IN_BREAK;
5378-
MINT_IN_CASE(MINT_STTSFLD_U2) STTSFLD(gint32, guint16); MINT_IN_BREAK;
5379-
MINT_IN_CASE(MINT_STTSFLD_I4) STTSFLD(gint32, gint32); MINT_IN_BREAK;
5380-
MINT_IN_CASE(MINT_STTSFLD_I8) STTSFLD(gint64, gint64); MINT_IN_BREAK;
5381-
MINT_IN_CASE(MINT_STTSFLD_R4) STTSFLD(float, float); MINT_IN_BREAK;
5382-
MINT_IN_CASE(MINT_STTSFLD_R8) STTSFLD(double, double); MINT_IN_BREAK;
5383-
MINT_IN_CASE(MINT_STTSFLD_O) STTSFLD(gpointer, gpointer); MINT_IN_BREAK;
5384-
5385-
MINT_IN_CASE(MINT_STSSFLD) {
5386-
guint32 offset = READ32(ip + 3);
5387-
gpointer addr = mono_get_special_static_data (offset);
5388-
MonoClassField *field = (MonoClassField*)frame->imethod->data_items [ip [2]];
5389-
stackval_to_data (field->type, (stackval*)(locals + ip [1]), addr, FALSE);
5390-
ip += 5;
5391-
MINT_IN_BREAK;
5392-
}
5393-
MINT_IN_CASE(MINT_STSSFLD_VT) {
5394-
guint32 offset = READ32(ip + 2);
5395-
gpointer addr = mono_get_special_static_data (offset);
5396-
memcpy (addr, locals + ip [1], ip [4]);
5397-
ip += 5;
5398-
MINT_IN_BREAK;
5399-
}
5400-
54015384
MINT_IN_CASE(MINT_STOBJ_VT) {
54025385
MonoClass *c = (MonoClass*)frame->imethod->data_items [ip [3]];
54035386
mono_value_copy_internal (LOCAL_VAR (ip [1], gpointer), locals + ip [2], c);
@@ -6211,9 +6194,6 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs
62116194

62126195
gboolean const short_offset = opcode == MINT_LEAVE_S || opcode == MINT_LEAVE_S_CHECK;
62136196
ip += short_offset ? (gint16)*(ip + 1) : (gint32)READ32 (ip + 1);
6214-
// Check for any abort requests, once all finally blocks were invoked
6215-
if (!check)
6216-
EXCEPTION_CHECKPOINT;
62176197
MINT_IN_BREAK;
62186198
}
62196199
MINT_IN_CASE(MINT_ICALL_V_V)
@@ -6569,6 +6549,10 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs
65696549
ip += 3;
65706550
MINT_IN_BREAK;
65716551

6552+
MINT_IN_CASE(MINT_MOV_OFF)
6553+
// This opcode is resolved to a normal MINT_MOV when emitting compacted instructions
6554+
g_assert_not_reached ();
6555+
MINT_IN_BREAK;
65726556

65736557
#define MOV(argtype1,argtype2) \
65746558
LOCAL_VAR (ip [1], argtype1) = LOCAL_VAR (ip [2], argtype2); \

0 commit comments

Comments
 (0)