From f576c78591bf4293bcdf983b56ffa2db625a9d3d Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Tue, 9 May 2023 06:22:51 -0700 Subject: [PATCH] Attempt to bypass emscripten's generated wrappers for cwraps (#85919) 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 | 56 +++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts index 370bac015ca58..9540663fbd459 100644 --- a/src/mono/wasm/runtime/cwraps.ts +++ b/src/mono/wasm/runtime/cwraps.ts @@ -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"] + ? ((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; } }