diff --git a/src/mono/browser/runtime/jiterpreter-tables.ts b/src/mono/browser/runtime/jiterpreter-tables.ts index 0ab6ce675267be..a345844e9d9048 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 @@ -93,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/interp.c b/src/mono/mono/mini/interp/interp.c index 070278d2cd0e84..61521990f1e8a1 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]; @@ -5308,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) + (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; @@ -5316,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) + (gint32)READ32 (ip + 3); + ip += 5; + MINT_IN_BREAK; MINT_IN_CASE(MINT_SUB_I4) BINOP(gint32, -); MINT_IN_BREAK; @@ -5346,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) * (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) * (gint32)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; @@ -5457,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/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 diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def index 87108ca73bff38..d79d5cd30acac2 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) @@ -654,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/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-opt.c b/src/mono/mono/mini/interp/transform-opt.c index 1b5e2b7a026b8b..aafb2504bc23dd 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); @@ -3207,8 +3188,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 +3207,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,45 +3354,67 @@ 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) { + opcode == MINT_MUL_I4 || opcode == MINT_MUL_I8 || + opcode == MINT_OR_I4 || opcode == MINT_AND_I4) { 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)) { 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)) { 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] = 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 @@ -3438,14 +3426,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 +3467,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 @@ -3498,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, MINT_TYPE_I2)) { - // 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); } } } @@ -3675,9 +3660,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 +3672,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); @@ -3924,8 +3910,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); 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;