From 6c7b9a8878198fbb0ebbd6d4d302743c59d4225e Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Mon, 4 Mar 2024 18:09:06 +0200 Subject: [PATCH 1/7] [mono][interp] Remove most of hardcoded LDC instructions They have little benefit. Also fix oversight which resulted in using MINT_LDC_I4_S only for byte sized integers. --- .../browser/runtime/jiterpreter-tables.ts | 8 ---- src/mono/mono/mini/interp/interp.c | 31 ++----------- src/mono/mono/mini/interp/mintops.def | 8 ---- src/mono/mono/mini/interp/mintops.h | 2 +- src/mono/mono/mini/interp/transform.c | 46 ++++++------------- 5 files changed, 20 insertions(+), 75 deletions(-) diff --git a/src/mono/browser/runtime/jiterpreter-tables.ts b/src/mono/browser/runtime/jiterpreter-tables.ts index 0ab6ce675267be..7bde9631964a48 100644 --- a/src/mono/browser/runtime/jiterpreter-tables.ts +++ b/src/mono/browser/runtime/jiterpreter-tables.ts @@ -9,16 +9,8 @@ import { } from "./mintops"; export const ldcTable: { [opcode: number]: [WasmOpcode, number] } = { - [MintOpcode.MINT_LDC_I4_M1]: [WasmOpcode.i32_const, -1], [MintOpcode.MINT_LDC_I4_0]: [WasmOpcode.i32_const, 0], [MintOpcode.MINT_LDC_I4_1]: [WasmOpcode.i32_const, 1], - [MintOpcode.MINT_LDC_I4_2]: [WasmOpcode.i32_const, 2], - [MintOpcode.MINT_LDC_I4_3]: [WasmOpcode.i32_const, 3], - [MintOpcode.MINT_LDC_I4_4]: [WasmOpcode.i32_const, 4], - [MintOpcode.MINT_LDC_I4_5]: [WasmOpcode.i32_const, 5], - [MintOpcode.MINT_LDC_I4_6]: [WasmOpcode.i32_const, 6], - [MintOpcode.MINT_LDC_I4_7]: [WasmOpcode.i32_const, 7], - [MintOpcode.MINT_LDC_I4_8]: [WasmOpcode.i32_const, 8], }; // operator, loadOperator, storeOperator diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 070278d2cd0e84..08ba6f24d10a26 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -3997,36 +3997,13 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause MINT_IN_BREAK; } -#define LDC(n) do { LOCAL_VAR (ip [1], gint32) = (n); ip += 2; } while (0) - MINT_IN_CASE(MINT_LDC_I4_M1) - LDC(-1); - MINT_IN_BREAK; MINT_IN_CASE(MINT_LDC_I4_0) - LDC(0); + LOCAL_VAR (ip [1], gint32) = 0; + ip += 2; MINT_IN_BREAK; MINT_IN_CASE(MINT_LDC_I4_1) - LDC(1); - MINT_IN_BREAK; - MINT_IN_CASE(MINT_LDC_I4_2) - LDC(2); - MINT_IN_BREAK; - MINT_IN_CASE(MINT_LDC_I4_3) - LDC(3); - MINT_IN_BREAK; - MINT_IN_CASE(MINT_LDC_I4_4) - LDC(4); - MINT_IN_BREAK; - MINT_IN_CASE(MINT_LDC_I4_5) - LDC(5); - MINT_IN_BREAK; - MINT_IN_CASE(MINT_LDC_I4_6) - LDC(6); - MINT_IN_BREAK; - MINT_IN_CASE(MINT_LDC_I4_7) - LDC(7); - MINT_IN_BREAK; - MINT_IN_CASE(MINT_LDC_I4_8) - LDC(8); + LOCAL_VAR (ip [1], gint32) = 1; + ip += 2; MINT_IN_BREAK; MINT_IN_CASE(MINT_LDC_I4_S) LOCAL_VAR (ip [1], gint32) = (short)ip [2]; diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 87108ca73bff38..856cf7d006b8d7 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -42,16 +42,8 @@ OPDEF(MINT_RET_U1, "ret.u1", 2, 0, 1, MintOpNoArgs) OPDEF(MINT_RET_I2, "ret.i2", 2, 0, 1, MintOpNoArgs) OPDEF(MINT_RET_U2, "ret.u2", 2, 0, 1, MintOpNoArgs) -OPDEF(MINT_LDC_I4_M1, "ldc.i4.m1", 2, 1, 0, MintOpNoArgs) OPDEF(MINT_LDC_I4_0, "ldc.i4.0", 2, 1, 0, MintOpNoArgs) OPDEF(MINT_LDC_I4_1, "ldc.i4.1", 2, 1, 0, MintOpNoArgs) -OPDEF(MINT_LDC_I4_2, "ldc.i4.2", 2, 1, 0, MintOpNoArgs) -OPDEF(MINT_LDC_I4_3, "ldc.i4.3", 2, 1, 0, MintOpNoArgs) -OPDEF(MINT_LDC_I4_4, "ldc.i4.4", 2, 1, 0, MintOpNoArgs) -OPDEF(MINT_LDC_I4_5, "ldc.i4.5", 2, 1, 0, MintOpNoArgs) -OPDEF(MINT_LDC_I4_6, "ldc.i4.6", 2, 1, 0, MintOpNoArgs) -OPDEF(MINT_LDC_I4_7, "ldc.i4.7", 2, 1, 0, MintOpNoArgs) -OPDEF(MINT_LDC_I4_8, "ldc.i4.8", 2, 1, 0, MintOpNoArgs) OPDEF(MINT_LDC_I4_S, "ldc.i4.s", 3, 1, 0, MintOpShortInt) OPDEF(MINT_LDC_I4, "ldc.i4", 4, 1, 0, MintOpInt) diff --git a/src/mono/mono/mini/interp/mintops.h b/src/mono/mono/mini/interp/mintops.h index a50a7d409215b3..0062e8d81438e8 100644 --- a/src/mono/mono/mini/interp/mintops.h +++ b/src/mono/mono/mini/interp/mintops.h @@ -220,7 +220,7 @@ typedef enum { #define MINT_IS_SUPER_BRANCH(op) ((op) >= MINT_BRFALSE_I4_SP && (op) <= MINT_BLT_UN_I8_IMM_SP) #define MINT_IS_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_JIT_CALL) #define MINT_IS_PATCHABLE_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_VCALL) -#define MINT_IS_LDC_I4(op) ((op) >= MINT_LDC_I4_M1 && (op) <= MINT_LDC_I4) +#define MINT_IS_LDC_I4(op) ((op) >= MINT_LDC_I4_0 && (op) <= MINT_LDC_I4) #define MINT_IS_LDC_I8(op) ((op) >= MINT_LDC_I8_0 && (op) <= MINT_LDC_I8) #define MINT_IS_UNOP(op) ((op) >= MINT_ADD1_I4 && (op) <= MINT_CEQ0_I4) #define MINT_IS_BINOP(op) ((op) >= MINT_ADD_I4 && (op) <= MINT_CLT_UN_R8) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index d791ea9d579e4d..27904e52fd4165 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -1604,24 +1604,16 @@ interp_ip_in_cbb (TransformData *td, int il_offset) static gboolean interp_ins_is_ldc (InterpInst *ins) { - return ins->opcode >= MINT_LDC_I4_M1 && ins->opcode <= MINT_LDC_I8; + return ins->opcode >= MINT_LDC_I4_0 && ins->opcode <= MINT_LDC_I8; } gint32 interp_get_const_from_ldc_i4 (InterpInst *ins) { switch (ins->opcode) { - case MINT_LDC_I4_M1: return -1; case MINT_LDC_I4_0: return 0; case MINT_LDC_I4_1: return 1; - case MINT_LDC_I4_2: return 2; - case MINT_LDC_I4_3: return 3; - case MINT_LDC_I4_4: return 4; - case MINT_LDC_I4_5: return 5; - case MINT_LDC_I4_6: return 6; - case MINT_LDC_I4_7: return 7; - case MINT_LDC_I4_8: return 8; - case MINT_LDC_I4_S: return (gint32)(gint8)ins->data [0]; + case MINT_LDC_I4_S: return (gint32)(gint16)ins->data [0]; case MINT_LDC_I4: return READ32 (&ins->data [0]); default: g_assert_not_reached (); @@ -1633,24 +1625,14 @@ InterpInst* interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct, int dreg) { guint16 opcode; - switch (ct) { - case -1: opcode = MINT_LDC_I4_M1; break; - case 0: opcode = MINT_LDC_I4_0; break; - case 1: opcode = MINT_LDC_I4_1; break; - case 2: opcode = MINT_LDC_I4_2; break; - case 3: opcode = MINT_LDC_I4_3; break; - case 4: opcode = MINT_LDC_I4_4; break; - case 5: opcode = MINT_LDC_I4_5; break; - case 6: opcode = MINT_LDC_I4_6; break; - case 7: opcode = MINT_LDC_I4_7; break; - case 8: opcode = MINT_LDC_I4_8; break; - default: - if (ct >= -128 && ct <= 127) - opcode = MINT_LDC_I4_S; - else - opcode = MINT_LDC_I4; - break; - } + if (!ct) + opcode = MINT_LDC_I4_0; + else if (ct == 1) + opcode = MINT_LDC_I4_1; + else if (ct >= G_MININT16 && ct <= G_MAXINT16) + opcode = MINT_LDC_I4_S; + else + opcode = MINT_LDC_I4; int new_size = mono_interp_oplen [opcode]; @@ -1668,7 +1650,7 @@ interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct, int interp_ins_set_dreg (ins, dreg); if (new_size == 3) - ins->data [0] = (gint8)ct; + ins->data [0] = (gint16)ct; else if (new_size == 4) WRITE32_INS (ins, 0, &ct); @@ -5232,7 +5214,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, ++td->ip; break; case CEE_LDC_I4_M1: - interp_add_ins (td, MINT_LDC_I4_M1); + interp_add_ins (td, MINT_LDC_I4_S); + td->last_ins->data [0] = (guint16)-1; push_simple_type (td, STACK_TYPE_I4); interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; @@ -5276,7 +5259,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_LDC_I4_6: case CEE_LDC_I4_7: case CEE_LDC_I4_8: - interp_add_ins (td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0); + interp_add_ins (td, MINT_LDC_I4_S); + td->last_ins->data [0] = *td->ip - CEE_LDC_I4_0; push_simple_type (td, STACK_TYPE_I4); interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; From f7d534d375a8c806ef4a589b49972013f42aadaa Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 5 Mar 2024 17:52:53 +0200 Subject: [PATCH 2/7] [mono][interp] Refactor fetching of immediate value for superinstructions Make `get_sreg_imm` more flexible, by returning the immediate value and the mint type that this value can fit in. Simplify return immediate handling by also applying conversion to immediate value, if necessary. --- src/mono/mono/mini/interp/transform-opt.c | 106 +++++++++++----------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index 1b5e2b7a026b8b..9362d0c2056a4e 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -3207,8 +3207,10 @@ mono_test_interp_cprop (TransformData *td) interp_cprop (td); } +// If sreg is constant, it returns the value in `imm` and the smallest +// containing type for it in `imm_mt`. static gboolean -get_sreg_imm (TransformData *td, int sreg, gint16 *imm, int result_mt) +get_sreg_imm (TransformData *td, int sreg, gint32 *imm, int *imm_mt) { if (var_has_indirects (td, sreg)) return FALSE; @@ -3224,32 +3226,15 @@ get_sreg_imm (TransformData *td, int sreg, gint16 *imm, int result_mt) ct = interp_get_const_from_ldc_i8 (def); else return FALSE; - gint64 min_val, max_val; - // We only propagate the immediate only if it fits into the desired type, - // so we don't accidentaly handle conversions wrong - switch (result_mt) { - case MINT_TYPE_I1: - min_val = G_MININT8; - max_val = G_MAXINT8; - break; - case MINT_TYPE_I2: - min_val = G_MININT16; - max_val = G_MAXINT16; - break; - case MINT_TYPE_U1: - min_val = 0; - max_val = G_MAXUINT8; - break; - case MINT_TYPE_U2: - min_val = 0; - max_val = G_MAXINT16; - break; - default: - g_assert_not_reached (); - - } - if (ct >= min_val && ct <= max_val) { + if (ct >= G_MININT16 && ct <= G_MAXINT16) { *imm = (gint16)ct; + if (imm_mt) + *imm_mt = MINT_TYPE_I2; + return TRUE; + } else if (ct >= G_MININT32 && ct <= G_MAXINT32) { + *imm = (gint32)ct; + if (imm_mt) + *imm_mt = MINT_TYPE_I4; return TRUE; } } @@ -3388,29 +3373,43 @@ interp_super_instructions (TransformData *td) if (opcode == MINT_RET || (opcode >= MINT_RET_I1 && opcode <= MINT_RET_U2)) { // ldc + ret -> ret.imm int sreg = ins->sregs [0]; - gint16 imm; - if (get_sreg_imm (td, sreg, &imm, (opcode == MINT_RET) ? MINT_TYPE_I2 : opcode - MINT_RET_I1)) { - InterpInst *def = td->var_values [sreg].def; - int ret_op = MINT_IS_LDC_I4 (def->opcode) ? MINT_RET_I4_IMM : MINT_RET_I8_IMM; - InterpInst *new_inst = interp_insert_ins (td, ins, ret_op); - new_inst->data [0] = imm; - interp_clear_ins (def); - interp_clear_ins (ins); - td->var_values [sreg].ref_count--; // 0 - if (td->verbose_level) { - g_print ("superins: "); - interp_dump_ins (new_inst, td->data_items); + gint32 imm; + if (get_sreg_imm (td, sreg, &imm, NULL)) { + // compute the casting as done by the ret opcode + int ret_mt = (opcode == MINT_RET) ? MINT_TYPE_I8 : opcode - MINT_RET_I1; + if (ret_mt == MINT_TYPE_I1) + imm = (gint8)imm; + else if (ret_mt == MINT_TYPE_U1) + imm = (guint8)imm; + else if (ret_mt == MINT_TYPE_I2) + imm = (gint16)imm; + else if (ret_mt == MINT_TYPE_U2) + imm = (guint16)imm; + + if (imm >= G_MININT16 && imm <= G_MAXINT16) { + InterpInst *def = td->var_values [sreg].def; + int ret_op = MINT_IS_LDC_I4 (def->opcode) ? MINT_RET_I4_IMM : MINT_RET_I8_IMM; + InterpInst *new_inst = interp_insert_ins (td, ins, ret_op); + new_inst->data [0] = (gint16)imm; + interp_clear_ins (def); + interp_clear_ins (ins); + td->var_values [sreg].ref_count--; // 0 + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } } } } else if (opcode == MINT_ADD_I4 || opcode == MINT_ADD_I8 || opcode == MINT_MUL_I4 || opcode == MINT_MUL_I8) { int sreg = -1; int sreg_imm = -1; - gint16 imm; - if (get_sreg_imm (td, ins->sregs [0], &imm, MINT_TYPE_I2)) { + int imm_mt; + gint32 imm; + if (get_sreg_imm (td, ins->sregs [0], &imm, &imm_mt) && imm_mt == MINT_TYPE_I2) { sreg = ins->sregs [1]; sreg_imm = ins->sregs [0]; - } else if (get_sreg_imm (td, ins->sregs [1], &imm, MINT_TYPE_I2)) { + } else if (get_sreg_imm (td, ins->sregs [1], &imm, &imm_mt) && imm_mt == MINT_TYPE_I2) { sreg = ins->sregs [0]; sreg_imm = ins->sregs [1]; } @@ -3426,7 +3425,7 @@ interp_super_instructions (TransformData *td) InterpInst *new_inst = interp_insert_ins (td, ins, binop); new_inst->dreg = ins->dreg; new_inst->sregs [0] = sreg; - new_inst->data [0] = imm; + new_inst->data [0] = (gint16)imm; interp_clear_ins (td->var_values [sreg_imm].def); interp_clear_ins (ins); td->var_values [sreg_imm].ref_count--; // 0 @@ -3438,14 +3437,15 @@ interp_super_instructions (TransformData *td) } } else if (opcode == MINT_SUB_I4 || opcode == MINT_SUB_I8) { // ldc + sub -> add.-imm - gint16 imm; + gint32 imm; + int imm_mt; int sreg_imm = ins->sregs [1]; - if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2) && imm != G_MININT16) { + if (get_sreg_imm (td, sreg_imm, &imm, &imm_mt) && imm_mt == MINT_TYPE_I2 && imm != G_MININT16) { int add_op = opcode == MINT_SUB_I4 ? MINT_ADD_I4_IMM : MINT_ADD_I8_IMM; InterpInst *new_inst = interp_insert_ins (td, ins, add_op); new_inst->dreg = ins->dreg; new_inst->sregs [0] = ins->sregs [0]; - new_inst->data [0] = -imm; + new_inst->data [0] = (gint16)-imm; interp_clear_ins (td->var_values [sreg_imm].def); interp_clear_ins (ins); td->var_values [sreg_imm].ref_count--; // 0 @@ -3478,15 +3478,16 @@ interp_super_instructions (TransformData *td) } } } else if (MINT_IS_BINOP_SHIFT (opcode)) { - gint16 imm; + gint32 imm; + int imm_mt; int sreg_imm = ins->sregs [1]; - if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { + if (get_sreg_imm (td, sreg_imm, &imm, &imm_mt) && imm_mt == MINT_TYPE_I2) { // ldc + sh -> sh.imm int shift_op = MINT_SHR_UN_I4_IMM + (opcode - MINT_SHR_UN_I4); InterpInst *new_inst = interp_insert_ins (td, ins, shift_op); new_inst->dreg = ins->dreg; new_inst->sregs [0] = ins->sregs [0]; - new_inst->data [0] = imm; + new_inst->data [0] = (gint16)imm; interp_clear_ins (td->var_values [sreg_imm].def); interp_clear_ins (ins); td->var_values [sreg_imm].ref_count--; // 0 @@ -3500,7 +3501,7 @@ interp_super_instructions (TransformData *td) InterpInst *amount_def = get_var_value_def (td, amount_var); if (amount_def != NULL && td->var_values [amount_var].ref_count == 1 && amount_def->opcode == MINT_AND_I4) { int mask_var = amount_def->sregs [1]; - if (get_sreg_imm (td, mask_var, &imm, MINT_TYPE_I2)) { + if (get_sreg_imm (td, mask_var, &imm, NULL)) { // ldc + and + shl -> shl_and_imm int new_opcode = -1; if (opcode == MINT_SHL_I4 && imm == 31) @@ -3675,9 +3676,10 @@ interp_super_instructions (TransformData *td) td->var_values [obj_sreg].ref_count--; } } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode) && interp_is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { - gint16 imm; + gint32 imm; + int imm_mt; int sreg_imm = ins->sregs [1]; - if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { + if (get_sreg_imm (td, sreg_imm, &imm, &imm_mt) && imm_mt == MINT_TYPE_I2) { int condbr_op = get_binop_condbr_imm_sp (opcode); if (condbr_op != MINT_NOP) { InterpInst *prev_ins = interp_prev_ins (ins); @@ -3686,7 +3688,7 @@ interp_super_instructions (TransformData *td) interp_clear_ins (prev_ins); InterpInst *new_ins = interp_insert_ins (td, ins, condbr_op); new_ins->sregs [0] = ins->sregs [0]; - new_ins->data [0] = imm; + new_ins->data [0] = (gint16)imm; new_ins->info.target_bb = ins->info.target_bb; interp_clear_ins (td->var_values [sreg_imm].def); interp_clear_ins (ins); From d215c8b9f800bb729bd0df6f2939f2eeb294ac72 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 5 Mar 2024 18:30:05 +0200 Subject: [PATCH 3/7] [mono][interp] Avoid running super instruction pass more than once We will avoid running it if we know will do another iteration later (especially useful when first iteration is ssa disabled). It can still happen for it to run multiple times (when bblock optimization requests retry), but this should be very rare. This will save compilation time and should also remove the necessity of handling super instructions in constant folding pass. Reverts https://github.com/dotnet/runtime/pull/99055 since it is no longer necessary. --- src/mono/mono/mini/interp/transform-opt.c | 32 +++++++---------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index 9362d0c2056a4e..c77fc5e6e39e17 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -2130,12 +2130,6 @@ interp_get_mt_for_ldind (int ldind_op) result.field = op val->field; \ break; -#define INTERP_FOLD_SHIFTOP_IMM(opcode,local_type,field,shift_op,cast_type) \ - case opcode: \ - result.type = local_type; \ - result.field = (cast_type)val->field shift_op ins->data [0]; \ - break; - #define INTERP_FOLD_CONV(opcode,val_type_dst,field_dst,val_type_src,field_src,cast_type) \ case opcode: \ result.type = val_type_dst; \ @@ -2175,19 +2169,6 @@ interp_fold_unop (TransformData *td, InterpInst *ins) INTERP_FOLD_UNOP (MINT_NOT_I8, VAR_VALUE_I8, l, ~); INTERP_FOLD_UNOP (MINT_CEQ0_I4, VAR_VALUE_I4, i, 0 ==); - INTERP_FOLD_UNOP (MINT_ADD_I4_IMM, VAR_VALUE_I4, i, ((gint32)(gint16)ins->data [0])+); - INTERP_FOLD_UNOP (MINT_ADD_I8_IMM, VAR_VALUE_I8, l, ((gint64)(gint16)ins->data [0])+); - - INTERP_FOLD_UNOP (MINT_MUL_I4_IMM, VAR_VALUE_I4, i, ((gint32)(gint16)ins->data [0])*); - INTERP_FOLD_UNOP (MINT_MUL_I8_IMM, VAR_VALUE_I8, l, ((gint64)(gint16)ins->data [0])*); - - INTERP_FOLD_SHIFTOP_IMM (MINT_SHR_UN_I4_IMM, VAR_VALUE_I4, i, >>, guint32); - INTERP_FOLD_SHIFTOP_IMM (MINT_SHR_UN_I8_IMM, VAR_VALUE_I8, l, >>, guint64); - INTERP_FOLD_SHIFTOP_IMM (MINT_SHL_I4_IMM, VAR_VALUE_I4, i, <<, gint32); - INTERP_FOLD_SHIFTOP_IMM (MINT_SHL_I8_IMM, VAR_VALUE_I8, l, <<, gint64); - INTERP_FOLD_SHIFTOP_IMM (MINT_SHR_I4_IMM, VAR_VALUE_I4, i, >>, gint32); - INTERP_FOLD_SHIFTOP_IMM (MINT_SHR_I8_IMM, VAR_VALUE_I8, l, >>, gint64); - INTERP_FOLD_CONV (MINT_CONV_I1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint8); INTERP_FOLD_CONV (MINT_CONV_I1_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint8); INTERP_FOLD_CONV (MINT_CONV_U1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint8); @@ -2921,7 +2902,7 @@ interp_cprop (TransformData *td) td->var_values [dreg].type = VAR_VALUE_I4; td->var_values [dreg].i = (gint32)td->data_items [ins->data [0]]; #endif - } else if (MINT_IS_UNOP (opcode) || MINT_IS_BINOP_IMM (opcode)) { + } else if (MINT_IS_UNOP (opcode)) { ins = interp_fold_unop (td, ins); } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode)) { ins = interp_fold_unop_cond_br (td, bb, ins); @@ -3926,8 +3907,15 @@ interp_optimize_code (TransformData *td) // We run this after var deadce to detect more single use vars. This pass will clear // unnecessary instruction on the fly so deadce is no longer needed to run. - if (mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) - MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td)); + if (mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) { + // This pass is enough to be called only once, after all cprop and other optimizations + // are done. The problem is that currently it needs to run over code in SSA form, so we + // can't just run it at the very end of optimization cycles. Also bblock optimization can + // lead to another optimization iteration, so we can still end up running it multiple times. + // Basic block optimization currently needs to run after we exited SSA. + if (!ssa_enabled_retry && !td->need_optimization_retry) + MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td)); + } if (!td->disable_ssa) interp_exit_ssa (td); From 0fb834947d622811c3c23e1583c74e4d04798a12 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 5 Mar 2024 18:51:46 +0200 Subject: [PATCH 4/7] [mono][interp] Add a few super instructions with 32 bit immediates Also add instructions for and/or + immediates. --- src/mono/mono/mini/interp/interp.c | 32 +++++++++++++++++++++++ src/mono/mono/mini/interp/mintops.def | 10 +++++++ src/mono/mono/mini/interp/transform-opt.c | 24 +++++++++++------ 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 08ba6f24d10a26..8214a0e2909cf8 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -5285,6 +5285,10 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) + (gint16)ip [3]; ip += 4; MINT_IN_BREAK; + MINT_IN_CASE(MINT_ADD_I4_IMM2) + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) + READ32 (ip + 3); + ip += 5; + MINT_IN_BREAK; MINT_IN_CASE(MINT_ADD1_I8) LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) + 1; ip += 3; @@ -5293,6 +5297,10 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) + (gint16)ip [3]; ip += 4; MINT_IN_BREAK; + MINT_IN_CASE(MINT_ADD_I8_IMM2) + LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) + READ32 (ip + 3); + ip += 5; + MINT_IN_BREAK; MINT_IN_CASE(MINT_SUB_I4) BINOP(gint32, -); MINT_IN_BREAK; @@ -5323,10 +5331,18 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) * (gint16)ip [3]; ip += 4; MINT_IN_BREAK; + MINT_IN_CASE(MINT_MUL_I4_IMM2) + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) * READ32 (ip + 3); + ip += 5; + MINT_IN_BREAK; MINT_IN_CASE(MINT_MUL_I8_IMM) LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) * (gint16)ip [3]; ip += 4; MINT_IN_BREAK; + MINT_IN_CASE(MINT_MUL_I8_IMM2) + LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) * READ32 (ip + 3); + ip += 5; + MINT_IN_BREAK; MINT_IN_CASE(MINT_ADD_MUL_I4_IMM) LOCAL_VAR (ip [1], gint32) = (LOCAL_VAR (ip [2], gint32) + (gint16)ip [3]) * (gint16)ip [4]; ip += 5; @@ -5434,12 +5450,28 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; MINT_IN_CASE(MINT_AND_I4) BINOP(gint32, &); MINT_IN_BREAK; + MINT_IN_CASE(MINT_AND_I4_IMM) + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) & (gint16)ip [3]; + ip += 4; + MINT_IN_BREAK; + MINT_IN_CASE(MINT_AND_I4_IMM2) + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) & READ32 (ip + 3); + ip += 5; + MINT_IN_BREAK; MINT_IN_CASE(MINT_AND_I8) BINOP(gint64, &); MINT_IN_BREAK; MINT_IN_CASE(MINT_OR_I4) BINOP(gint32, |); MINT_IN_BREAK; + MINT_IN_CASE(MINT_OR_I4_IMM) + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) | (gint16)ip [3]; + ip += 4; + MINT_IN_BREAK; + MINT_IN_CASE(MINT_OR_I4_IMM2) + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) | READ32 (ip + 3); + ip += 5; + MINT_IN_BREAK; MINT_IN_CASE(MINT_OR_I8) BINOP(gint64, |); MINT_IN_BREAK; diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 856cf7d006b8d7..d79d5cd30acac2 100644 --- a/src/mono/mono/mini/interp/mintops.def +++ b/src/mono/mono/mini/interp/mintops.def @@ -646,10 +646,20 @@ OPDEF(MINT_RET_I4_IMM, "ret.i4.imm", 2, 0, 0, MintOpShortInt) OPDEF(MINT_RET_I8_IMM, "ret.i8.imm", 2, 0, 0, MintOpShortInt) OPDEF(MINT_ADD_I4_IMM, "add.i4.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_ADD_I4_IMM2, "add.i4.imm2", 5, 1, 1, MintOpInt) OPDEF(MINT_ADD_I8_IMM, "add.i8.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_ADD_I8_IMM2, "add.i8.imm2", 5, 1, 1, MintOpInt) OPDEF(MINT_MUL_I4_IMM, "mul.i4.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_MUL_I4_IMM2, "mul.i4.imm2", 5, 1, 1, MintOpInt) OPDEF(MINT_MUL_I8_IMM, "mul.i8.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_MUL_I8_IMM2, "mul.i8.imm2", 5, 1, 1, MintOpInt) + +OPDEF(MINT_AND_I4_IMM, "and.i4.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_AND_I4_IMM2, "and.i4.imm2", 5, 1, 1, MintOpInt) + +OPDEF(MINT_OR_I4_IMM, "or.i4.imm", 4, 1, 1, MintOpShortInt) +OPDEF(MINT_OR_I4_IMM2, "or.i4.imm2", 5, 1, 1, MintOpInt) OPDEF(MINT_SHR_UN_I4_IMM, "shr.un.i4.imm", 4, 1, 1, MintOpShortInt) OPDEF(MINT_SHR_UN_I8_IMM, "shr.un.i8.imm", 4, 1, 1, MintOpShortInt) diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index c77fc5e6e39e17..8bb3c89df41b07 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -3382,31 +3382,39 @@ interp_super_instructions (TransformData *td) } } } else if (opcode == MINT_ADD_I4 || opcode == MINT_ADD_I8 || - opcode == MINT_MUL_I4 || opcode == MINT_MUL_I8) { + opcode == MINT_MUL_I4 || opcode == MINT_MUL_I8 || + opcode == MINT_OR_I4 || opcode == MINT_AND_I4) { int sreg = -1; int sreg_imm = -1; int imm_mt; gint32 imm; - if (get_sreg_imm (td, ins->sregs [0], &imm, &imm_mt) && imm_mt == MINT_TYPE_I2) { + if (get_sreg_imm (td, ins->sregs [0], &imm, &imm_mt)) { sreg = ins->sregs [1]; sreg_imm = ins->sregs [0]; - } else if (get_sreg_imm (td, ins->sregs [1], &imm, &imm_mt) && imm_mt == MINT_TYPE_I2) { + } else if (get_sreg_imm (td, ins->sregs [1], &imm, &imm_mt)) { sreg = ins->sregs [0]; sreg_imm = ins->sregs [1]; } if (sreg != -1) { int binop; switch (opcode) { - case MINT_ADD_I4: binop = MINT_ADD_I4_IMM; break; - case MINT_ADD_I8: binop = MINT_ADD_I8_IMM; break; - case MINT_MUL_I4: binop = MINT_MUL_I4_IMM; break; - case MINT_MUL_I8: binop = MINT_MUL_I8_IMM; break; + case MINT_ADD_I4: binop = (imm_mt == MINT_TYPE_I2) ? MINT_ADD_I4_IMM : MINT_ADD_I4_IMM2; break; + case MINT_ADD_I8: binop = (imm_mt == MINT_TYPE_I2) ? MINT_ADD_I8_IMM : MINT_ADD_I8_IMM2; break; + case MINT_MUL_I4: binop = (imm_mt == MINT_TYPE_I2) ? MINT_MUL_I4_IMM : MINT_MUL_I4_IMM2; break; + case MINT_MUL_I8: binop = (imm_mt == MINT_TYPE_I2) ? MINT_MUL_I8_IMM : MINT_MUL_I8_IMM2; break; + case MINT_OR_I4: binop = (imm_mt == MINT_TYPE_I2) ? MINT_OR_I4_IMM : MINT_OR_I4_IMM2; break; + case MINT_AND_I4: binop = (imm_mt == MINT_TYPE_I2) ? MINT_AND_I4_IMM : MINT_AND_I4_IMM2; break; default: g_assert_not_reached (); } InterpInst *new_inst = interp_insert_ins (td, ins, binop); new_inst->dreg = ins->dreg; new_inst->sregs [0] = sreg; - new_inst->data [0] = (gint16)imm; + if (imm_mt == MINT_TYPE_I2) + new_inst->data [0] = (gint16)imm; + else if (imm_mt == MINT_TYPE_I4) + WRITE32_INS (new_inst, 0, &imm); + else + g_assert_not_reached (); interp_clear_ins (td->var_values [sreg_imm].def); interp_clear_ins (ins); td->var_values [sreg_imm].ref_count--; // 0 From 7679330fa4cfbcf1a3bd0330276d40116114bf93 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 5 Mar 2024 19:42:40 +0200 Subject: [PATCH 5/7] [mono][interp] Fix MINT_SHL_AND_ generation and + ldc will already be transformed to a superinstruction, so this can also be simplified. --- src/mono/mono/mini/interp/transform-opt.c | 49 ++++++++++------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index 8bb3c89df41b07..aafb2504bc23dd 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -3488,33 +3488,28 @@ interp_super_instructions (TransformData *td) } else if (opcode == MINT_SHL_I4 || opcode == MINT_SHL_I8) { int amount_var = ins->sregs [1]; InterpInst *amount_def = get_var_value_def (td, amount_var); - if (amount_def != NULL && td->var_values [amount_var].ref_count == 1 && amount_def->opcode == MINT_AND_I4) { - int mask_var = amount_def->sregs [1]; - if (get_sreg_imm (td, mask_var, &imm, NULL)) { - // ldc + and + shl -> shl_and_imm - int new_opcode = -1; - if (opcode == MINT_SHL_I4 && imm == 31) - new_opcode = MINT_SHL_AND_I4; - else if (opcode == MINT_SHL_I8 && imm == 63) - new_opcode = MINT_SHL_AND_I8; - - if (new_opcode != -1) { - InterpInst *new_inst = interp_insert_ins (td, ins, new_opcode); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; - new_inst->sregs [1] = amount_def->sregs [0]; - - td->var_values [amount_var].ref_count--; // 0 - td->var_values [mask_var].ref_count--; // 0 - td->var_values [new_inst->dreg].def = new_inst; - - interp_clear_ins (td->var_values [mask_var].def); - interp_clear_ins (amount_def); - interp_clear_ins (ins); - if (td->verbose_level) { - g_print ("superins: "); - interp_dump_ins (new_inst, td->data_items); - } + if (amount_def != NULL && td->var_values [amount_var].ref_count == 1 && amount_def->opcode == MINT_AND_I4_IMM) { + // and_imm + shl -> shl_and_imm + int new_opcode = -1; + if (opcode == MINT_SHL_I4 && amount_def->data [0] == 31) + new_opcode = MINT_SHL_AND_I4; + else if (opcode == MINT_SHL_I8 && amount_def->data [0] == 63) + new_opcode = MINT_SHL_AND_I8; + + if (new_opcode != -1) { + InterpInst *new_inst = interp_insert_ins (td, ins, new_opcode); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->sregs [1] = amount_def->sregs [0]; + + td->var_values [amount_var].ref_count--; // 0 + td->var_values [new_inst->dreg].def = new_inst; + + interp_clear_ins (amount_def); + interp_clear_ins (ins); + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); } } } From 5decd50ca97cc32e1f289711b42a580b3b2b522d Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Tue, 5 Mar 2024 13:35:12 -0800 Subject: [PATCH 6/7] Implement new superinsns in jiterpreter --- src/mono/browser/runtime/jiterpreter-tables.ts | 10 ++++++++++ .../runtime/jiterpreter-trace-generator.ts | 18 +++++++++++++++++- .../mini/interp/jiterpreter-opcode-values.h | 8 +++++--- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/mono/browser/runtime/jiterpreter-tables.ts b/src/mono/browser/runtime/jiterpreter-tables.ts index 7bde9631964a48..a345844e9d9048 100644 --- a/src/mono/browser/runtime/jiterpreter-tables.ts +++ b/src/mono/browser/runtime/jiterpreter-tables.ts @@ -85,6 +85,16 @@ export const unopTable: { [opcode: number]: OpRec3 | undefined } = { [MintOpcode.MINT_CLZ_I8]: [WasmOpcode.i64_clz, WasmOpcode.i64_load, WasmOpcode.i64_store], [MintOpcode.MINT_CTZ_I8]: [WasmOpcode.i64_ctz, WasmOpcode.i64_load, WasmOpcode.i64_store], [MintOpcode.MINT_POPCNT_I8]: [WasmOpcode.i64_popcnt, WasmOpcode.i64_load, WasmOpcode.i64_store], + + [MintOpcode.MINT_ADD_I4_IMM2]: [WasmOpcode.i32_add, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_MUL_I4_IMM2]: [WasmOpcode.i32_mul, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_ADD_I8_IMM2]: [WasmOpcode.i64_add, WasmOpcode.i64_load, WasmOpcode.i64_store], + [MintOpcode.MINT_MUL_I8_IMM2]: [WasmOpcode.i64_mul, WasmOpcode.i64_load, WasmOpcode.i64_store], + + [MintOpcode.MINT_AND_I4_IMM]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_AND_I4_IMM2]: [WasmOpcode.i32_and, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_OR_I4_IMM]: [WasmOpcode.i32_or, WasmOpcode.i32_load, WasmOpcode.i32_store], + [MintOpcode.MINT_OR_I4_IMM2]: [WasmOpcode.i32_or, WasmOpcode.i32_load, WasmOpcode.i32_store], }; // HACK: Generating correct wasm for these is non-trivial so we hand them off to C. diff --git a/src/mono/browser/runtime/jiterpreter-trace-generator.ts b/src/mono/browser/runtime/jiterpreter-trace-generator.ts index 9b3002eae107ab..201b9008c33d54 100644 --- a/src/mono/browser/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/browser/runtime/jiterpreter-trace-generator.ts @@ -1525,7 +1525,7 @@ export function generateWasmBody( } else ip = abort; } else if ( - (opcode >= MintOpcode.MINT_LDC_I4_M1) && + (opcode >= MintOpcode.MINT_LDC_I4_0) && (opcode <= MintOpcode.MINT_LDC_R8) ) { if (!emit_ldc(builder, ip, opcode)) @@ -2610,6 +2610,8 @@ function emit_unop(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): case MintOpcode.MINT_ADD_I4_IMM: case MintOpcode.MINT_MUL_I4_IMM: + case MintOpcode.MINT_AND_I4_IMM: + case MintOpcode.MINT_OR_I4_IMM: case MintOpcode.MINT_SHL_I4_IMM: case MintOpcode.MINT_SHR_I4_IMM: case MintOpcode.MINT_SHR_UN_I4_IMM: @@ -2619,6 +2621,14 @@ function emit_unop(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): builder.i32_const(getArgI16(ip, 3)); break; + case MintOpcode.MINT_ADD_I4_IMM2: + case MintOpcode.MINT_MUL_I4_IMM2: + case MintOpcode.MINT_AND_I4_IMM2: + case MintOpcode.MINT_OR_I4_IMM2: + append_ldloc(builder, getArgU16(ip, 2), loadOp); + builder.i32_const(getArgI32(ip, 3)); + break; + case MintOpcode.MINT_ADD_I8_IMM: case MintOpcode.MINT_MUL_I8_IMM: case MintOpcode.MINT_SHL_I8_IMM: @@ -2630,6 +2640,12 @@ function emit_unop(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): builder.i52_const(getArgI16(ip, 3)); break; + case MintOpcode.MINT_ADD_I8_IMM2: + case MintOpcode.MINT_MUL_I8_IMM2: + append_ldloc(builder, getArgU16(ip, 2), loadOp); + builder.i52_const(getArgI32(ip, 3)); + break; + default: append_ldloc(builder, getArgU16(ip, 2), loadOp); break; diff --git a/src/mono/mono/mini/interp/jiterpreter-opcode-values.h b/src/mono/mono/mini/interp/jiterpreter-opcode-values.h index fb677b438d3871..c3926ccb4c4791 100644 --- a/src/mono/mono/mini/interp/jiterpreter-opcode-values.h +++ b/src/mono/mono/mini/interp/jiterpreter-opcode-values.h @@ -35,7 +35,7 @@ OPRANGE(MINT_RET_I4_IMM, MINT_RET_I8_IMM, ABORT_OUTSIDE_BRANCH_BLOCK_NONE) // High value because interp has to do a memory load for the immediate // but we can inline it into the trace -OPRANGE(MINT_LDC_I4_M1, MINT_LDC_R8, HIGH) +OPRANGE(MINT_LDC_I4_0, MINT_LDC_R8, HIGH) OPRANGE(MINT_MOV_I4_I1, MINT_MOV_4, NORMAL) // High value for large/complex moves @@ -43,8 +43,10 @@ OPRANGE(MINT_MOV_8, MINT_MOV_8_4, HIGH) // Binops. Assume most of them are not any faster in jiterp OPRANGE(MINT_ADD_I4, MINT_CLT_UN_R8, NORMAL) -// Unops and some superinsns. Most will not be faster in jiterp. -OPRANGE(MINT_ADD1_I4, MINT_SHR_I8_IMM, NORMAL) +// Unops. Most will not be faster in jiterp. +OPRANGE(MINT_ADD1_I4, MINT_CEQ0_I4, NORMAL) +// Some superinsns that will be faster in jiterp due to inline constants +OPRANGE(MINT_ADD_I4_IMM, MINT_ADD_MUL_I8_IMM, HIGH) // Math intrinsics. We implement most of these by calling libc or using wasm opcodes OPRANGE(MINT_ASIN, MINT_MAXF, NORMAL) // Field operations. Null check optimization makes these more efficient than interp From 09ff7cdbdfa55b375bf0b3f9c9e7491e8f2c875e Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 6 Mar 2024 11:40:20 +0200 Subject: [PATCH 7/7] [mono][interp] Add explicit cast for clarity --- src/mono/mono/mini/interp/interp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 8214a0e2909cf8..61521990f1e8a1 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -5286,7 +5286,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; ip += 4; MINT_IN_BREAK; MINT_IN_CASE(MINT_ADD_I4_IMM2) - LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) + READ32 (ip + 3); + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) + (gint32)READ32 (ip + 3); ip += 5; MINT_IN_BREAK; MINT_IN_CASE(MINT_ADD1_I8) @@ -5298,7 +5298,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; ip += 4; MINT_IN_BREAK; MINT_IN_CASE(MINT_ADD_I8_IMM2) - LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) + READ32 (ip + 3); + LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) + (gint32)READ32 (ip + 3); ip += 5; MINT_IN_BREAK; MINT_IN_CASE(MINT_SUB_I4) @@ -5332,7 +5332,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; ip += 4; MINT_IN_BREAK; MINT_IN_CASE(MINT_MUL_I4_IMM2) - LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) * READ32 (ip + 3); + LOCAL_VAR (ip [1], gint32) = LOCAL_VAR (ip [2], gint32) * (gint32)READ32 (ip + 3); ip += 5; MINT_IN_BREAK; MINT_IN_CASE(MINT_MUL_I8_IMM) @@ -5340,7 +5340,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK; ip += 4; MINT_IN_BREAK; MINT_IN_CASE(MINT_MUL_I8_IMM2) - LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) * READ32 (ip + 3); + LOCAL_VAR (ip [1], gint64) = LOCAL_VAR (ip [2], gint64) * (gint32)READ32 (ip + 3); ip += 5; MINT_IN_BREAK; MINT_IN_CASE(MINT_ADD_MUL_I4_IMM)