Attempt to bypass emscripten's generated wrappers for cwraps (#85919)
authorKatelyn Gadd <kg@luminance.org>
Tue, 9 May 2023 13:22:51 +0000 (06:22 -0700)
committerGitHub <noreply@github.com>
Tue, 9 May 2023 13:22:51 +0000 (06:22 -0700)
Bypass emscripten's generated js wrappers when invoking cwraps, this may improve cross-language inlining
Fix signature for a cwrap that was missing an arg

src/mono/wasm/runtime/cwraps.ts

index 370bac0..9540663 100644 (file)
@@ -57,7 +57,7 @@ const fn_signatures: SigLine[] = [
     [false, "mono_wasm_invoke_method_ref", "void", ["number", "number", "number", "number", "number"]],
     [true, "mono_wasm_string_from_utf16_ref", "void", ["number", "number", "number"]],
     [true, "mono_wasm_intern_string_ref", "void", ["number"]],
-    [true, "mono_wasm_assembly_get_entry_point", "number", ["number"]],
+    [true, "mono_wasm_assembly_get_entry_point", "number", ["number", "number"]],
     [true, "mono_wasm_class_get_type", "number", ["number"]],
 
     // MONO.diagnostics
@@ -98,10 +98,10 @@ const fn_signatures: SigLine[] = [
     [true, "mono_jiterp_get_trace_bailout_count", "number", ["number"]],
     [true, "mono_jiterp_value_copy", "void", ["number", "number", "number"]],
     [true, "mono_jiterp_get_member_offset", "number", ["number"]],
-    [false, "mono_jiterp_encode_leb52", "number", ["number", "number", "number"]],
-    [false, "mono_jiterp_encode_leb64_ref", "number", ["number", "number", "number"]],
-    [false, "mono_jiterp_encode_leb_signed_boundary", "number", ["number", "number", "number"]],
-    [false, "mono_jiterp_write_number_unaligned", "void", ["number", "number", "number"]],
+    [true, "mono_jiterp_encode_leb52", "number", ["number", "number", "number"]],
+    [true, "mono_jiterp_encode_leb64_ref", "number", ["number", "number", "number"]],
+    [true, "mono_jiterp_encode_leb_signed_boundary", "number", ["number", "number", "number"]],
+    [true, "mono_jiterp_write_number_unaligned", "void", ["number", "number", "number"]],
     [true, "mono_jiterp_type_is_byref", "number", ["number"]],
     [true, "mono_jiterp_get_size_of_stackval", "number", []],
     [true, "mono_jiterp_parse_option", "number", ["string"]],
@@ -268,6 +268,42 @@ export const enum I52Error {
     OUT_OF_RANGE = 2,
 }
 
+const fastCwrapTypes = ["void", "number", null];
+
+function cwrap (name: string, returnType: string | null, argTypes: string[] | undefined, opts: any, throwOnError: boolean) : Function {
+    // Attempt to bypass emscripten's generated wrapper if it is safe to do so
+    let fce =
+        // Special cwrap options disable the fast path
+        (typeof (opts) === "undefined") &&
+        // Only attempt to do fast calls if all the args and the return type are either number or void
+        (fastCwrapTypes.indexOf(returnType) >= 0) &&
+        (!argTypes || argTypes.every(atype => fastCwrapTypes.indexOf(atype) >= 0)) &&
+        // Module["asm"] may not be defined yet if we are early enough in the startup process
+        //  in that case, we need to rely on emscripten's lazy wrappers
+        Module["asm"]
+            ? <Function>((<any>Module["asm"])[name])
+            : undefined;
+
+    // If the argument count for the wasm function doesn't match the signature, fall back to cwrap
+    if (fce && argTypes && (fce.length !== argTypes.length)) {
+        console.error(`MONO_WASM: argument count mismatch for cwrap ${name}`);
+        fce = undefined;
+    }
+
+    // We either failed to find the raw wasm func or for some reason we can't use it directly
+    if (typeof (fce) !== "function")
+        fce = Module.cwrap(name, returnType, argTypes, opts);
+
+    if (typeof (fce) !== "function") {
+        const msg = `cwrap ${name} not found or not a function`;
+        if (throwOnError)
+            throw new Error(msg);
+        else
+            console.error("MONO_WASM: " + msg);
+    }
+    return fce;
+}
+
 export function init_c_exports(): void {
     const lfns = WasmEnableLegacyJsInterop && !disableLegacyJsInterop ? legacy_interop_cwraps : [];
     const fns = [...fn_signatures, ...lfns];
@@ -277,18 +313,12 @@ export function init_c_exports(): void {
         if (lazy) {
             // lazy init on first run
             wf[name] = function (...args: any[]) {
-                const fce = Module.cwrap(name, returnType, argTypes, opts);
-                if (typeof (fce) !== "function")
-                    throw new Error(`cwrap ${name} not found or not a function`);
+                const fce = cwrap(name, returnType, argTypes, opts, true);
                 wf[name] = fce;
                 return fce(...args);
             };
         } else {
-            const fce = Module.cwrap(name, returnType, argTypes, opts);
-            // throw would be preferable, but it causes really hard to debug startup errors and
-            //  unhandled promise rejections so this is more useful
-            if (typeof (fce) !== "function")
-                console.error(`cwrap ${name} not found or not a function`);
+            const fce = cwrap(name, returnType, argTypes, opts, false);
             wf[name] = fce;
         }
     }