Skip to content

Commit

Permalink
Optimize JS function proxying (emscripten-core#9164)
Browse files Browse the repository at this point in the history
  • Loading branch information
juj authored Aug 18, 2019
1 parent 5deb076 commit 0ea89ab
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 220 deletions.
60 changes: 38 additions & 22 deletions src/jsifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ var NEED_ALL_ASM2WASM_IMPORTS = BINARYEN_TRAP_MODE == 'js';
// the current compilation unit.
var HAS_MAIN = ('_main' in IMPLEMENTED_FUNCTIONS) || MAIN_MODULE || SIDE_MODULE;

// Mangles the given C/JS side function name to assembly level function name (adds an underscore)
function mangleCSymbolName(f) {
return f[0] == '$' ? f.substr(1) : '_' + f;
}

// Reverses C/JS name mangling: _foo -> foo, and foo -> $foo.
function demangleCSymbolName(f) {
return f[0] == '_' ? f.substr(1) : '$' + f;
}

// JSifier
function JSify(data, functionsOnly) {
var mainPass = !functionsOnly;
Expand All @@ -72,7 +82,7 @@ function JSify(data, functionsOnly) {
}
libFuncsToInclude.forEach(function(ident) {
data.functionStubs.push({
ident: '_' + ident
ident: mangleCSymbolName(ident)
});
});
}
Expand Down Expand Up @@ -129,12 +139,7 @@ function JSify(data, functionsOnly) {
return '';
}

// $ident's are special, we do not prefix them with a '_'.
if (ident[0] === '$') {
var finalName = ident.substr(1);
} else {
var finalName = '_' + ident;
}
var finalName = mangleCSymbolName(ident);

// if the function was implemented in compiled code, we just need to export it so we can reach it from JS
if (finalName in IMPLEMENTED_FUNCTIONS) {
Expand Down Expand Up @@ -175,7 +180,7 @@ function JSify(data, functionsOnly) {
realIdent = realIdent.substr(2);
}

var target = (SIDE_MODULE ? 'parent' : '') + "Module['_" + realIdent + "']";
var target = (SIDE_MODULE ? 'parent' : '') + "Module['" + mangleCSymbolName(realIdent) + "']";
var assertion = '';
if (ASSERTIONS) {
var what = 'function';
Expand Down Expand Up @@ -211,18 +216,20 @@ function JSify(data, functionsOnly) {
var isFunction = false;

if (typeof snippet === 'string') {
var target = LibraryManager.library[snippet];
if (target) {
// Redirection for aliases. We include the parent, and at runtime make ourselves equal to it.
// This avoid having duplicate functions with identical content.
redirectedIdent = snippet;
deps.push(snippet);
snippet = '_' + snippet;
}
// In asm, we need to know about library functions. If there is a target, though, then no
// need to consider this a library function - we will call directly to it anyhow
if (!redirectedIdent && (typeof target == 'function' || /Math_\w+/.exec(snippet))) {
Functions.libraryFunctions[finalName] = 1;
if (snippet[0] != '=') {
var target = LibraryManager.library[snippet];
if (target) {
// Redirection for aliases. We include the parent, and at runtime make ourselves equal to it.
// This avoid having duplicate functions with identical content.
redirectedIdent = snippet;
deps.push(snippet);
snippet = mangleCSymbolName(snippet);
}
// In asm, we need to know about library functions. If there is a target, though, then no
// need to consider this a library function - we will call directly to it anyhow
if (!redirectedIdent && (typeof target == 'function' || /Math_\w+/.exec(snippet))) {
Functions.libraryFunctions[finalName] = 1;
}
}
} else if (typeof snippet === 'object') {
snippet = stringifyWithFunctions(snippet);
Expand Down Expand Up @@ -282,9 +289,18 @@ function JSify(data, functionsOnly) {
contentText = snippet; // Regular JS function that will be executed in the context of the calling thread.
}
} else if (typeof snippet === 'string' && snippet.indexOf(';') == 0) {
// In JS libraries
// foo: ';[code here verbatim]'
// emits
// 'var foo;[code here verbatim];'
contentText = 'var ' + finalName + snippet;
if (snippet[snippet.length-1] != ';' && snippet[snippet.length-1] != '}') contentText += ';';
} else {
// In JS libraries
// foo: '=[value]'
// emits
// 'var foo = [value];'
if (typeof snippet === 'string' && snippet[0] == '=') snippet = snippet.substr(1);
contentText = 'var ' + finalName + '=' + snippet + ';';
}
var sig = LibraryManager.library[ident + '__sig'];
Expand All @@ -307,15 +323,15 @@ function JSify(data, functionsOnly) {
// We set EXPORTED_FUNCTIONS here to tell emscripten.py to do that.
deps.forEach(function(dep) {
if (LibraryManager.library[dep + '__asm']) {
EXPORTED_FUNCTIONS['_' + dep] = 0;
EXPORTED_FUNCTIONS[mangleCSymbolName(dep)] = 0;
}
});
}
return depsText + contentText;
}

itemsDict.functionStub.push(item);
var shortident = item.ident.substr(1);
var shortident = demangleCSymbolName(item.ident);
// If this is not linkable, anything not in the library is definitely missing
if (item.ident in DEAD_FUNCTIONS) {
if (LibraryManager.library[shortident + '__asm']) {
Expand Down
36 changes: 17 additions & 19 deletions src/library_pthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -1157,40 +1157,38 @@ var LibraryPThread = {
// all the args here.
// We also pass 'sync' to C separately, since C needs to look at it.
var numCallArgs = arguments.length - 2;
#if ASSERTIONS
if (numCallArgs > {{{ cDefine('EM_QUEUED_JS_CALL_MAX_ARGS') }}}-1) throw 'emscripten_proxy_to_main_thread_js: Too many arguments ' + numCallArgs + ' to proxied function idx=' + index + ', maximum supported is ' + ({{{ cDefine('EM_QUEUED_JS_CALL_MAX_ARGS') }}}-1) + '!';
#endif
// Allocate a buffer, which will be copied by the C code.
var stack = stackSave();
var buffer = stackAlloc(numCallArgs * 8);
// First passed parameter specifies the number of arguments to the function.
var args = stackAlloc(numCallArgs * 8);
var b = args >> 3;
for (var i = 0; i < numCallArgs; i++) {
HEAPF64[(buffer >> 3) + i] = arguments[2 + i];
HEAPF64[b + i] = arguments[2 + i];
}
var ret = _emscripten_run_in_main_runtime_thread_js(index, numCallArgs, buffer, sync);
var ret = _emscripten_run_in_main_runtime_thread_js(index, numCallArgs, args, sync);
stackRestore(stack);
return ret;
},

emscripten_receive_on_main_thread_js__deps: ['emscripten_proxy_to_main_thread_js'],
emscripten_receive_on_main_thread_js: function(index, numCallArgs, buffer) {
// Avoid garbage by reusing a single JS array for call arguments.
if (!_emscripten_receive_on_main_thread_js.callArgs) {
_emscripten_receive_on_main_thread_js.callArgs = [];
}
var callArgs = _emscripten_receive_on_main_thread_js.callArgs;
callArgs.length = numCallArgs;
emscripten_receive_on_main_thread_js_callArgs: '=[]',

emscripten_receive_on_main_thread_js__deps: ['emscripten_proxy_to_main_thread_js', 'emscripten_receive_on_main_thread_js_callArgs'],
emscripten_receive_on_main_thread_js: function(index, numCallArgs, args) {
_emscripten_receive_on_main_thread_js_callArgs.length = numCallArgs;
var b = args >> 3;
for (var i = 0; i < numCallArgs; i++) {
callArgs[i] = HEAPF64[(buffer >> 3) + i];
_emscripten_receive_on_main_thread_js_callArgs[i] = HEAPF64[b + i];
}
// Proxied JS library funcs are encoded as positive values, and
// EM_ASMs as negative values (see include_asm_consts)
var func;
if (index > 0) {
func = proxiedFunctionTable[index];
} else {
func = ASM_CONSTS[-index - 1];
}
var func = index > 0 ? proxiedFunctionTable[index] : ASM_CONSTS[-index - 1];
#if ASSERTIONS
assert(func.length == numCallArgs);
#endif
return func.apply(null, callArgs);
return func.apply(null, _emscripten_receive_on_main_thread_js_callArgs);
},

#if MODULARIZE
Expand Down
4 changes: 2 additions & 2 deletions src/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ var LibraryManager = {
if (typeof lib[x] === 'string') {
var target = x;
while (typeof lib[target] === 'string') {
// ignore code, aliases are just simple names
if (lib[target].search(/[({; ]/) >= 0) continue libloop;
// ignore code and variable assignments, aliases are just simple names
if (lib[target].search(/[=({; ]/) >= 0) continue libloop;
// ignore trivial pass-throughs to Math.*
if (lib[target].indexOf('Math_') == 0) continue libloop;
target = lib[target];
Expand Down
3 changes: 3 additions & 0 deletions src/struct_info.json
Original file line number Diff line number Diff line change
Expand Up @@ -1512,6 +1512,9 @@
"EM_PROXIED_SYSCALL",
"EM_PROXIED_CREATE_CONTEXT",
"EM_PROXIED_RESIZE_OFFSCREENCANVAS",
"EM_PROXIED_JS_FUNCTION",
"EM_QUEUED_CALL_MAX_ARGS",
"EM_QUEUED_JS_CALL_MAX_ARGS",
"EM_THREAD_STATUS_NOTSTARTED",
"EM_THREAD_STATUS_RUNNING",
"EM_THREAD_STATUS_SLEEPING",
Expand Down
10 changes: 6 additions & 4 deletions system/include/emscripten/threading.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,17 @@ typedef union em_variant_val
char *cp;
} em_variant_val;

// Proxied C/C++ functions support at most this many arguments. Dispatch is static/strongly typed by signature.
#define EM_QUEUED_CALL_MAX_ARGS 11
// Proxied JS function can support a few more arguments than proxied C/C++ functions, because the dispatch
// is variadic and signature independent.
#define EM_QUEUED_JS_CALL_MAX_ARGS 20
typedef struct em_queued_call
{
int functionEnum;
void *functionPtr;
int operationDone;
// If set, this is a JS call: number of arguments are in functionEnum, the index in functionPtr,
// and args in args.
int js;
em_variant_val args[EM_QUEUED_CALL_MAX_ARGS];
em_variant_val args[EM_QUEUED_JS_CALL_MAX_ARGS];
em_variant_val returnValue;

// An optional pointer to a secondary data block that should be free()d when this queued call is freed.
Expand Down Expand Up @@ -254,6 +255,7 @@ typedef int (*em_func_iiiiiiiiii)(int, int, int, int, int, int, int, int, int);
#define EM_PROXIED_SYSCALL (EM_PROXIED_FUNC_SPECIAL(1) | EM_FUNC_SIG_III)
#define EM_PROXIED_CREATE_CONTEXT (EM_PROXIED_FUNC_SPECIAL(2) | EM_FUNC_SIG_III)
#define EM_PROXIED_RESIZE_OFFSCREENCANVAS (EM_PROXIED_FUNC_SPECIAL(3) | EM_FUNC_SIG_IIII)
#define EM_PROXIED_JS_FUNCTION (EM_PROXIED_FUNC_SPECIAL(4) | EM_FUNC_SIG_D)

// Runs the given function synchronously on the main Emscripten runtime thread.
// If this thread is the main thread, the operation is immediately performed, and the result is returned.
Expand Down
Loading

0 comments on commit 0ea89ab

Please sign in to comment.