[wasm] Outline exception throws in the interp and ifdef out unreachable opcodes ...
authorKatelyn Gadd <kg@luminance.org>
Wed, 14 Dec 2022 17:53:36 +0000 (09:53 -0800)
committerGitHub <noreply@github.com>
Wed, 14 Dec 2022 17:53:36 +0000 (09:53 -0800)
* Outline THROW_EX_GENERAL and ifdef out unreachable opcodes in the wasm version of the mono interpreter for better performance
* Introduce IROPDEF for intermediate opcodes that are only used during code generation
* Move IR opcodes to the end of the table and don't generate jump targets for them
* Add an include guard to mintops.def to ensure that config.h is always included when it is used
* Make the jiterp opcodes platform specific, fix a bug in genmintops that produced wrong ordering
* Fix jiterpreter heuristic erroneously rejecting mov opcodes

src/mono/mono/mini/interp/interp.c
src/mono/mono/mini/interp/jiterpreter.c
src/mono/mono/mini/interp/mintops.c
src/mono/mono/mini/interp/mintops.def
src/mono/wasm/runtime/genmintops.py
src/mono/wasm/runtime/jiterpreter.ts

index 0976c9b..e083709 100644 (file)
@@ -1145,15 +1145,25 @@ INTERP_GET_EXCEPTION(array_type_mismatch)
 INTERP_GET_EXCEPTION(arithmetic)
 INTERP_GET_EXCEPTION_CHAR_ARG(argument_out_of_range)
 
+// Inlining throw logic into interp_exec_method makes it bigger and could push us up against
+//  internal limits in things like WASM compilers
+static MONO_NEVER_INLINE void
+interp_throw_ex_general (
+       MonoException *__ex, ThreadContext *context, InterpFrame *frame, const guint16 *ex_ip, gboolean rethrow
+)
+{
+       HANDLE_FUNCTION_ENTER ();
+       MonoExceptionHandle tmp_handle = MONO_HANDLE_NEW (MonoException, __ex);
+       interp_throw (context, MONO_HANDLE_RAW(tmp_handle), (frame), (ex_ip), (rethrow));
+       HANDLE_FUNCTION_RETURN ();
+}
+
 // We conservatively pin exception object here to avoid tweaking the
 // numerous call sites of this macro, even though, in a few cases,
 // this is not needed.
 #define THROW_EX_GENERAL(exception,ex_ip, rethrow)             \
        do {                                                    \
-               MonoException *__ex = (exception);              \
-               MONO_HANDLE_ASSIGN_RAW (tmp_handle, (MonoObject*)__ex); \
-               interp_throw (context, __ex, (frame), (ex_ip), (rethrow)); \
-               MONO_HANDLE_ASSIGN_RAW (tmp_handle, (MonoObject*)NULL); \
+               interp_throw_ex_general (exception, context, frame, ex_ip, rethrow); \
                goto resume;                                                      \
        } while (0)
 
@@ -3720,6 +3730,7 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
 #if USE_COMPUTED_GOTO
        static void * const in_labels[] = {
 #define OPDEF(a,b,c,d,e,f) &&LAB_ ## a,
+#define IROPDEF(a,b,c,d,e,f)
 #include "mintops.def"
        };
 #endif
@@ -3794,19 +3805,6 @@ main_loop:
                        memset (locals + ip [1], 0, ip [2]);
                        ip += 3;
                        MINT_IN_BREAK;
-               MINT_IN_CASE(MINT_NOP)
-               MINT_IN_CASE(MINT_IL_SEQ_POINT)
-               MINT_IN_CASE(MINT_NIY)
-               MINT_IN_CASE(MINT_DEF)
-               MINT_IN_CASE(MINT_DUMMY_USE)
-               MINT_IN_CASE(MINT_TIER_PATCHPOINT_DATA)
-#ifndef HOST_BROWSER
-               MINT_IN_CASE(MINT_TIER_NOP_JITERPRETER)
-               MINT_IN_CASE(MINT_TIER_PREPARE_JITERPRETER)
-               MINT_IN_CASE(MINT_TIER_ENTER_JITERPRETER)
-#endif
-                       g_assert_not_reached ();
-                       MINT_IN_BREAK;
                MINT_IN_CASE(MINT_BREAK)
                        ++ip;
                        SAVE_INTERP_STATE (frame);
@@ -7227,12 +7225,6 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
                        ip += 3;
                        MINT_IN_BREAK;
 
-               MINT_IN_CASE(MINT_MOV_SRC_OFF)
-               MINT_IN_CASE(MINT_MOV_DST_OFF)
-                       // This opcode is resolved to a normal MINT_MOV when emitting compacted instructions
-                       g_assert_not_reached ();
-                       MINT_IN_BREAK;
-
 #define MOV(argtype1,argtype2) \
        LOCAL_VAR (ip [1], argtype1) = LOCAL_VAR (ip [2], argtype2); \
        ip += 3;
@@ -7571,7 +7563,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
 #if !USE_COMPUTED_GOTO
                default:
                        interp_error_xsx ("Unimplemented opcode: %04x %s at 0x%x\n", *ip, mono_interp_opname (*ip), GPTRDIFF_TO_INT (ip - frame->imethod->code));
-#endif
+#endif // USE_COMPUTED_GOTO
                }
        }
 
index 5243d79..16ae18d 100644 (file)
@@ -714,6 +714,11 @@ jiterp_should_abort_trace (InterpInst *ins, gboolean *inside_branch_block)
                case MINT_SAFEPOINT:
                        return TRACE_ABORT;
 
+               case MINT_MOV_SRC_OFF:
+               case MINT_MOV_DST_OFF:
+                       // These opcodes will turn into supported MOVs later
+                       return TRACE_CONTINUE;
+
                default:
                if (
                        // branches
@@ -746,7 +751,7 @@ jiterp_should_abort_trace (InterpInst *ins, gboolean *inside_branch_block)
                )
                        return TRACE_CONTINUE;
                else if (
-                       (opcode >= MINT_MOV_SRC_OFF) &&
+                       (opcode >= MINT_MOV_I4_I1) &&
                        (opcode <= MINT_MOV_8_4)
                )
                        return TRACE_CONTINUE;
index 86cb256..e4b7805 100644 (file)
@@ -8,6 +8,7 @@
  */
 #include <glib.h>
 #include <stdio.h>
+#include "config.h"
 #include "mintops.h"
 
 // This, instead of an array of pointers, to optimize away a pointer and a relocation per string.
index 56080ef..74b167d 100644 (file)
@@ -6,19 +6,26 @@
  *
  * OPDEF (opsymbol, opstring, oplength (in uint16s), num_dregs (0 or 1), num_sregs, optype)
  * optype describes the contents of the instruction, following the dreg/sreg offsets.
- */
-
-/*
+ *
+ * IROPDEF is like OPDEF but for instructions that are never executed by the interpreter,
+ *  meant for use by transform.c and other internals. They *must* be defined at the end after
+ *  all regular OPDEFs.
+ *
  * This file is parsed by genmintops.py to generate typescript during the wasm build process,
  *  so if you make any changes to its syntax you will need to update that script.
+ * Any preprocessing directives are removed without being parsed!
  */
 
-OPDEF(MINT_NOP, "nop", 1, 0, 0, MintOpNoArgs)
-OPDEF(MINT_NIY, "niy", 1, 0, 0, MintOpNoArgs)
-OPDEF(MINT_DEF, "def", 2, 1, 0, MintOpNoArgs)
-OPDEF(MINT_IL_SEQ_POINT, "il_seq_point", 1, 0, 0, MintOpNoArgs)
-OPDEF(MINT_DUMMY_USE, "dummy_use", 2, 0, 1, MintOpNoArgs)
-OPDEF(MINT_TIER_PATCHPOINT_DATA, "tier_patchpoint_data", 2, 0, 0, MintOpShortInt)
+#ifndef __MONO_CONFIG_H__
+#error You must include config.h before including mintops.def
+#endif
+
+// Compatibility shim for old code
+#ifndef IROPDEF
+#define __DEFINED_IROPDEF__
+#define IROPDEF(opsymbol, opstring, oplength, num_dregs, num_sregs, optype) OPDEF(opsymbol, opstring, oplength, num_dregs, num_sregs, optype)
+#endif // IROPDEF
+
 OPDEF(MINT_BREAK, "break", 1, 0, 0, MintOpNoArgs)
 OPDEF(MINT_BREAKPOINT, "breakpoint", 1, 0, 0, MintOpNoArgs)
 
@@ -112,9 +119,6 @@ OPDEF(MINT_STSFLD_W, "stsfld.w", 8, 0, 1, MintOpTwoInts)
 OPDEF(MINT_LDSFLDA, "ldsflda", 4, 1, 0, MintOpTwoShorts)
 OPDEF(MINT_LDTSFLDA, "ldtsflda", 4, 1, 0, MintOpInt)
 
-OPDEF(MINT_MOV_SRC_OFF, "mov.src.off", 6, 1, 1, MintOpTwoShorts)
-OPDEF(MINT_MOV_DST_OFF, "mov.dst.off", 6, 1, 1, MintOpTwoShorts)
-
 OPDEF(MINT_MOV_I4_I1, "mov.i4.i1", 3, 1, 1, MintOpNoArgs)
 OPDEF(MINT_MOV_I4_U1, "mov.i4.u1", 3, 1, 1, MintOpNoArgs)
 OPDEF(MINT_MOV_I4_I2, "mov.i4.i2", 3, 1, 1, MintOpNoArgs)
@@ -794,7 +798,24 @@ OPDEF(MINT_INTRINS_WIDEN_ASCII_TO_UTF16, "intrins_widen_ascii_to_utf16", 5, 1, 3
 
 OPDEF(MINT_METADATA_UPDATE_LDFLDA, "metadata_update.ldflda", 5, 1, 1, MintOpTwoShorts)
 
-// TODO: Make this wasm only
+// This ifdef is fine because genmintops.py is generating output for HOST_BROWSER
+#if HOST_BROWSER
 OPDEF(MINT_TIER_PREPARE_JITERPRETER, "tier_prepare_jiterpreter", 3, 0, 0, MintOpInt)
 OPDEF(MINT_TIER_NOP_JITERPRETER, "tier_nop_jiterpreter", 3, 0, 0, MintOpInt)
 OPDEF(MINT_TIER_ENTER_JITERPRETER, "tier_enter_jiterpreter", 3, 0, 0, MintOpInt)
+#endif // HOST_BROWSER
+
+IROPDEF(MINT_NOP, "nop", 1, 0, 0, MintOpNoArgs)
+IROPDEF(MINT_NIY, "niy", 1, 0, 0, MintOpNoArgs)
+IROPDEF(MINT_DEF, "def", 2, 1, 0, MintOpNoArgs)
+IROPDEF(MINT_IL_SEQ_POINT, "il_seq_point", 1, 0, 0, MintOpNoArgs)
+IROPDEF(MINT_DUMMY_USE, "dummy_use", 2, 0, 1, MintOpNoArgs)
+IROPDEF(MINT_TIER_PATCHPOINT_DATA, "tier_patchpoint_data", 2, 0, 0, MintOpShortInt)
+// These two opcodes are resolved to a normal MINT_MOV when emitting compacted instructions
+IROPDEF(MINT_MOV_SRC_OFF, "mov.src.off", 6, 1, 1, MintOpTwoShorts)
+IROPDEF(MINT_MOV_DST_OFF, "mov.dst.off", 6, 1, 1, MintOpTwoShorts)
+
+#ifdef __DEFINED_IROPDEF__
+#undef IROPDEF
+#undef __DEFINED_IROPDEF__
+#endif // __DEFINED_IROPDEF__
index bef525c..510b1db 100755 (executable)
@@ -18,15 +18,17 @@ output_ts_path = sys.argv [2]
 src = open(src_header_path, 'r')
 
 tab = "    "
-header = tab + src.read().replace("\n", "\n" + tab)
+header_lines = src.read().splitlines()
+# strip preprocessing directives and add indentation for tslint/eslint
+header = "\n".join((tab + l) for l in header_lines if not l.startswith("#"))
 src.close()
 
-opdef_regex = r'OPDEF\((\w+),\s*(.+?),\s*(MintOp\w+)\)'
+opdef_regex = r'\s(IR)?OPDEF\((\w+),\s*(.+?),\s*(MintOp\w+)\)'
 enum_values = re.sub(
-    opdef_regex, lambda m : f"{m.group(1)}{' = 0' if (m.group(1) == 'MINT_NOP') else ''},", header
+    opdef_regex, lambda m : f"{m.group(2)},", header
 )
 metadata_table = re.sub(
-    opdef_regex, lambda m : f"[MintOpcode.{m.group(1)}]: [{m.group(2)}, MintOpArgType.{m.group(3)}],", header
+    opdef_regex, lambda m : f"[MintOpcode.{m.group(2)}]: [{m.group(3)}, MintOpArgType.{m.group(4)}],", header
 )
 
 generated = f"""
index c8f8e81..65db771 100644 (file)
@@ -1210,9 +1210,7 @@ function generate_wasm_body (
                 break;
 
             default:
-                if (
-                    opname.startsWith("ret")
-                ) {
+                if (opname.startsWith("ret")) {
                     if ((builder.branchTargets.size > 0) || trapTraceErrors || builder.options.countBailouts)
                         append_bailout(builder, ip, BailoutReason.Return);
                     else
@@ -1230,14 +1228,10 @@ function generate_wasm_body (
                 ) {
                     if (!emit_binop(builder, ip, opcode))
                         ip = abort;
-                } else if (
-                    unopTable[opcode]
-                ) {
+                } else if (unopTable[opcode]) {
                     if (!emit_unop(builder, ip, opcode))
                         ip = abort;
-                } else if (
-                    relopbranchTable[opcode]
-                ) {
+                } else if (relopbranchTable[opcode]) {
                     if (!emit_relop_branch(builder, ip, opcode))
                         ip = abort;
                 } else if (