[wasm] Implement more opcodes in the jiterpreter (#81575)
authorKatelyn Gadd <kg@luminance.org>
Fri, 3 Feb 2023 02:46:32 +0000 (18:46 -0800)
committerGitHub <noreply@github.com>
Fri, 3 Feb 2023 02:46:32 +0000 (18:46 -0800)
* Implement the new add_mul_imm interpreter opcodes
* Implement MINT_LDFTN and MINT_INTRINS_ENUM_HASFLAG
* Implement MINT_ARRAY_RANK

src/mono/mono/mini/interp/interp.c
src/mono/mono/mini/interp/jiterpreter.c
src/mono/mono/mini/interp/jiterpreter.h
src/mono/wasm/runtime/cwraps.ts
src/mono/wasm/runtime/jiterpreter.ts

index 751a072..0753162 100644 (file)
@@ -8716,4 +8716,16 @@ mono_jiterp_auto_safepoint (InterpFrame *frame, guint16 *ip)
                do_safepoint (frame, get_context(), ip);
 }
 
+EMSCRIPTEN_KEEPALIVE gpointer
+mono_jiterp_imethod_to_ftnptr (InterpMethod *imethod)
+{
+       return imethod_to_ftnptr (imethod, FALSE);
+}
+
+EMSCRIPTEN_KEEPALIVE void
+mono_jiterp_enum_hasflag (MonoClass *klass, gint32 *dest, stackval *sp1, stackval *sp2)
+{
+       *dest = mono_interp_enum_hasflag (sp1, sp2, klass);
+}
+
 #endif
index 0ef798e..633684f 100644 (file)
@@ -669,6 +669,7 @@ jiterp_should_abort_trace (InterpInst *ins, gboolean *inside_branch_block)
                case MINT_LDLOCA_S:
                case MINT_LDTOKEN:
                case MINT_LDSTR:
+               case MINT_LDFTN:
                case MINT_LDFTN_ADDR:
                case MINT_MONO_LDPTR:
                case MINT_CPOBJ_VT:
@@ -699,6 +700,10 @@ jiterp_should_abort_trace (InterpInst *ins, gboolean *inside_branch_block)
                case MINT_SAFEPOINT:
                case MINT_INTRINS_GET_HASHCODE:
                case MINT_INTRINS_RUNTIMEHELPERS_OBJECT_HAS_COMPONENT_SIZE:
+               case MINT_INTRINS_ENUM_HASFLAG:
+               case MINT_ADD_MUL_I4_IMM:
+               case MINT_ADD_MUL_I8_IMM:
+               case MINT_ARRAY_RANK:
                        return TRACE_CONTINUE;
 
                case MINT_BR:
@@ -1016,6 +1021,18 @@ mono_jiterp_type_to_stind (MonoType *type)
        return mono_type_to_stind (type);
 }
 
+EMSCRIPTEN_KEEPALIVE int
+mono_jiterp_get_array_rank (gint32 *dest, MonoObject **src)
+{
+       if (!src || !*src) {
+               *dest = 0;
+               return 0;
+       }
+
+       *dest = m_class_get_rank (mono_object_class (*src));
+       return 1;
+}
+
 // HACK: fix C4206
 EMSCRIPTEN_KEEPALIVE
 #endif
index 8150408..4def33e 100644 (file)
@@ -122,6 +122,12 @@ mono_jiterp_auto_safepoint (InterpFrame *frame, guint16 *ip);
 void
 mono_jiterp_interp_entry (JiterpEntryData *_data, stackval *sp_args, void *res);
 
+gpointer
+mono_jiterp_imethod_to_ftnptr (InterpMethod *imethod);
+
+void
+mono_jiterp_enum_hasflag (MonoClass *klass, gint32 *dest, stackval *sp1, stackval *sp2);
+
 #endif // __MONO_MINI_INTERPRETER_INTERNALS_H__
 
 extern WasmDoJitCall jiterpreter_do_jit_call;
index b5b25a5..e37160a 100644 (file)
@@ -121,6 +121,7 @@ const fn_signatures: SigLine[] = [
     [true, "mono_jiterp_get_signature_params", "number", ["number"]],
     [true, "mono_jiterp_type_to_ldind", "number", ["number"]],
     [true, "mono_jiterp_type_to_stind", "number", ["number"]],
+    [true, "mono_jiterp_imethod_to_ftnptr", "number", ["number"]],
 ];
 
 export interface t_Cwraps {
@@ -260,6 +261,7 @@ export interface t_Cwraps {
     mono_jiterp_get_signature_params(sig: VoidPtr): VoidPtr;
     mono_jiterp_type_to_ldind(type: MonoType): number;
     mono_jiterp_type_to_stind(type: MonoType): number;
+    mono_jiterp_imethod_to_ftnptr(imethod: VoidPtr): VoidPtr;
 }
 
 const wrapped_c_functions: t_Cwraps = <any>{};
index f8ff785..192b49a 100644 (file)
@@ -263,6 +263,8 @@ function getTraceImports () {
         ["safepoint", "safepoint", getRawCwrap("mono_jiterp_auto_safepoint")],
         ["hashcode", "hashcode", getRawCwrap("mono_jiterp_get_hashcode")],
         ["hascsize", "hascsize", getRawCwrap("mono_jiterp_object_has_component_size")],
+        ["hasflag", "hasflag", getRawCwrap("mono_jiterp_enum_hasflag")],
+        ["array_rank", "array_rank", getRawCwrap("mono_jiterp_get_array_rank")],
     ];
 
     if (instrumentedMethodNames.length > 0) {
@@ -532,6 +534,20 @@ function generate_wasm (
                 "ppObj": WasmValtype.i32,
             }, WasmValtype.i32
         );
+        builder.defineType(
+            "hasflag", {
+                "klass": WasmValtype.i32,
+                "dest": WasmValtype.i32,
+                "sp1": WasmValtype.i32,
+                "sp2": WasmValtype.i32,
+            }, WasmValtype.void
+        );
+        builder.defineType(
+            "array_rank", {
+                "destination": WasmValtype.i32,
+                "source": WasmValtype.i32,
+            }, WasmValtype.i32
+        );
 
         builder.generateTypeSection();
 
@@ -612,7 +628,7 @@ function generate_wasm (
         compileStarted = _now();
         const buffer = builder.getArrayView();
         if (trace > 0)
-            console.log(`${traceName} generated ${buffer.length} byte(s) of wasm`);
+            console.log(`${(<any>(builder.base)).toString(16)} ${traceName} generated ${buffer.length} byte(s) of wasm`);
         counters.bytesGenerated += buffer.length;
         const traceModule = new WebAssembly.Module(buffer);
 
@@ -664,6 +680,11 @@ function generate_wasm (
         else if (trace >= 2)
             console.log(`${traceName} -> fn index ${idx}`);
 
+        // Ensure that a bit of ongoing diagnostic output is printed for very long-running test
+        //  suites or benchmarks if you've enabled stats
+        if (builder.options.enableStats && (counters.tracesCompiled % 500) === 0)
+            jiterpreter_dump_stats(false, true);
+
         return idx;
     } catch (exc: any) {
         threw = true;
@@ -926,15 +947,20 @@ function generate_wasm_body (
                 append_ldloca(builder, getArgU16(ip, 2));
                 append_stloc_tail(builder, getArgU16(ip, 1), WasmOpcode.i32_store);
                 break;
+
             case MintOpcode.MINT_LDTOKEN:
             case MintOpcode.MINT_LDSTR:
+            case MintOpcode.MINT_LDFTN:
             case MintOpcode.MINT_LDFTN_ADDR:
             case MintOpcode.MINT_MONO_LDPTR: {
                 // Pre-load locals for the store op
                 builder.local("pLocals");
 
                 // frame->imethod->data_items [ip [2]]
-                const data = get_imethod_data(frame, getArgU16(ip, 2));
+                let data = get_imethod_data(frame, getArgU16(ip, 2));
+                if (opcode === MintOpcode.MINT_LDFTN)
+                    data = <any>cwraps.mono_jiterp_imethod_to_ftnptr(<any>data);
+
                 builder.ptr_const(data);
 
                 append_stloc_tail(builder, getArgU16(ip, 1), WasmOpcode.i32_store);
@@ -1088,6 +1114,15 @@ function generate_wasm_body (
                 append_bailout(builder, ip, BailoutReason.NullCheck);
                 builder.endBlock();
                 break;
+            case MintOpcode.MINT_INTRINS_ENUM_HASFLAG: {
+                const klass = get_imethod_data(frame, getArgU16(ip, 4));
+                builder.ptr_const(klass);
+                append_ldloca(builder, getArgU16(ip, 1));
+                append_ldloca(builder, getArgU16(ip, 2));
+                append_ldloca(builder, getArgU16(ip, 3));
+                builder.callImport("hasflag");
+                break;
+            }
             case MintOpcode.MINT_INTRINS_MEMORYMARSHAL_GETARRAYDATAREF: {
                 const offset = cwraps.mono_jiterp_get_offset_of_array_data();
                 builder.local("pLocals");
@@ -1109,6 +1144,19 @@ function generate_wasm_body (
                 builder.callImport("hascsize");
                 append_stloc_tail(builder, getArgU16(ip, 1), WasmOpcode.i32_store);
                 break;
+            case MintOpcode.MINT_ARRAY_RANK: {
+                builder.block();
+                // dest, src
+                append_ldloca(builder, getArgU16(ip, 1));
+                append_ldloca(builder, getArgU16(ip, 2));
+                builder.callImport("array_rank");
+                // If the array was null we will bail out, otherwise continue
+                builder.appendU8(WasmOpcode.br_if);
+                builder.appendULeb(0);
+                append_bailout(builder, ip, BailoutReason.NullCheck);
+                builder.endBlock();
+                break;
+            }
 
             case MintOpcode.MINT_CASTCLASS:
             case MintOpcode.MINT_ISINST:
@@ -1282,6 +1330,27 @@ function generate_wasm_body (
                 builder.endBlock();
                 break;
 
+            case MintOpcode.MINT_ADD_MUL_I4_IMM:
+            case MintOpcode.MINT_ADD_MUL_I8_IMM: {
+                const isI32 = opcode === MintOpcode.MINT_ADD_MUL_I4_IMM;
+                builder.local("pLocals");
+                append_ldloc(builder, getArgU16(ip, 2), isI32 ? WasmOpcode.i32_load : WasmOpcode.i64_load);
+                const rhs = getArgI16(ip, 3),
+                    multiplier = getArgI16(ip, 4);
+                if (isI32)
+                    builder.i32_const(rhs);
+                else
+                    builder.i52_const(rhs);
+                builder.appendU8(isI32 ? WasmOpcode.i32_add : WasmOpcode.i64_add);
+                if (isI32)
+                    builder.i32_const(multiplier);
+                else
+                    builder.i52_const(multiplier);
+                builder.appendU8(isI32? WasmOpcode.i32_mul : WasmOpcode.i64_mul);
+                append_stloc_tail(builder, getArgU16(ip, 1), isI32 ? WasmOpcode.i32_store : WasmOpcode.i64_store);
+                break;
+            }
+
             default:
                 if (opname.startsWith("ret")) {
                     if ((builder.branchTargets.size > 0) || trapTraceErrors || builder.options.countBailouts)
@@ -3095,7 +3164,7 @@ export function mono_interp_tier_prepare_jiterpreter (
         return JITERPRETER_TRAINING;
 }
 
-export function jiterpreter_dump_stats (b?: boolean) {
+export function jiterpreter_dump_stats (b?: boolean, concise?: boolean) {
     if (!mostRecentOptions || (b !== undefined))
         mostRecentOptions = getOptions();
 
@@ -3104,6 +3173,9 @@ export function jiterpreter_dump_stats (b?: boolean) {
 
     console.log(`// generated: ${counters.bytesGenerated} wasm bytes; ${counters.tracesCompiled} traces (${counters.traceCandidates} candidates, ${(counters.tracesCompiled / counters.traceCandidates * 100).toFixed(1)}%); ${counters.jitCallsCompiled} jit_calls (${(counters.directJitCallsCompiled / counters.jitCallsCompiled * 100).toFixed(1)}% direct); ${counters.entryWrappersCompiled} interp_entries`);
     console.log(`// time spent: ${elapsedTimes.generation | 0}ms generating, ${elapsedTimes.compilation | 0}ms compiling wasm`);
+    if (concise)
+        return;
+
     if (mostRecentOptions.countBailouts) {
         for (let i = 0; i < BailoutReasonNames.length; i++) {
             const bailoutCount = cwraps.mono_jiterp_get_trace_bailout_count(i);