diff --git a/AUTHORS b/AUTHORS index 3df78225cce76..d1a35abb83668 100644 --- a/AUTHORS +++ b/AUTHORS @@ -181,4 +181,4 @@ a license to everyone to use it as detailed in LICENSE.) * Thaddée Tyl * Philipp Wiesemann * Jan Jongboom (copyright owned by Telenor Digital AS) - +* Tiago Quelhas diff --git a/emcc b/emcc index 58696863276b1..ade759083e390 100755 --- a/emcc +++ b/emcc @@ -1070,6 +1070,10 @@ try: memory_init_file = True assert closure is not 2, 'EMTERPRETIFY requires valid asm.js, and is incompatible with closure 2 which disables that' + if shared.Settings.DEAD_FUNCTIONS and not js_opts: + js_opts = True + logging.warning('enabling js opts for DEAD_FUNCTIONS') + if proxy_to_worker: shared.Settings.PROXY_TO_WORKER = 1 @@ -1491,6 +1495,10 @@ try: js_optimizer_queue = [] js_optimizer_extra_info = {} + if shared.Settings.DEAD_FUNCTIONS: + js_optimizer_queue += ['eliminateDeadFuncs'] + js_optimizer_extra_info['dead_functions'] = shared.Settings.DEAD_FUNCTIONS + if opt_level >= 1 and js_opts: logging.debug('running js post-opts') diff --git a/emscripten-version.txt b/emscripten-version.txt index d8c0559525c6b..f22efa6b70168 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.30.4 +1.30.5 diff --git a/emscripten.py b/emscripten.py index 8adeb1923759f..59e8c86fbb636 100755 --- a/emscripten.py +++ b/emscripten.py @@ -456,7 +456,6 @@ def fix_item(item): if settings['PRECISE_F32']: maths += ['Math.fround'] basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs] - if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall') if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_FT_MASK'] if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8'] if settings['ASSERTIONS']: @@ -495,7 +494,7 @@ def fix_item(item): ''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret)) for i in range(settings['RESERVED_FUNCTION_POINTERS']): - jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0], settings) + jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall_%s(%d%s%s)' % (sig, i, ',' if coerced_args else '', coerced_args), sig[0], settings) function_tables_impls.append(''' function jsCall_%s_%s(%s) { %s @@ -506,6 +505,9 @@ def fix_item(item): shared.Settings.copy(settings) asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' basic_funcs.append('invoke_%s' % sig) + if settings.get('RESERVED_FUNCTION_POINTERS'): + asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n' + basic_funcs.append('jsCall_%s' % sig) if settings.get('DLOPEN_SUPPORT'): asm_setup += '\n' + shared.JS.make_extcall(sig) + '\n' basic_funcs.append('extCall_%s' % sig) @@ -1135,7 +1137,6 @@ def make_emulated_param(i): if provide_fround: maths += ['Math.fround'] basic_funcs = ['abort', 'assert'] + [m.replace('.', '_') for m in math_envs] - if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall') if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_FT_MASK'] if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8'] if settings['ASSERTIONS']: @@ -1220,7 +1221,7 @@ def keyfunc(other): ffi_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings, ffi_arg=True) for i in range(1, len(sig))]) for i in range(settings['RESERVED_FUNCTION_POINTERS']): - jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall(%d%s%s)' % (i, ',' if ffi_args else '', ffi_args), sig[0], settings, ffi_result=True) + jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall_%s(%d%s%s)' % (sig, i, ',' if ffi_args else '', ffi_args), sig[0], settings, ffi_result=True) function_tables_impls.append(''' function jsCall_%s_%s(%s) { %s @@ -1231,6 +1232,9 @@ def keyfunc(other): shared.Settings.copy(settings) asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' basic_funcs.append('invoke_%s' % sig) + if settings.get('RESERVED_FUNCTION_POINTERS'): + asm_setup += '\n' + shared.JS.make_jscall(sig) + '\n' + basic_funcs.append('jsCall_%s' % sig) if settings.get('DLOPEN_SUPPORT'): asm_setup += '\n' + shared.JS.make_extcall(sig) + '\n' basic_funcs.append('extCall_%s' % sig) diff --git a/site/source/docs/api_reference/emscripten.h.rst b/site/source/docs/api_reference/emscripten.h.rst index dff34a3adbd68..b47698c4ff542 100644 --- a/site/source/docs/api_reference/emscripten.h.rst +++ b/site/source/docs/api_reference/emscripten.h.rst @@ -34,8 +34,8 @@ Defines .. note:: - Double-quotes (") cannot be used in the inline assembly/JavaScript. Single-quotes (‘) can be used, as shown above. - Newlines (\\n, \\r etc.) are supported in the inline Javascript. Note that any platform-specific issues with line endings in normal JavaScript also apply to inline JavaScript declared using ``EM_ASM``. - - This works with **asm.js** (it outlines the code and does a function call to reach it). - You can’t access C variables with :c:macro:`EM_ASM`, nor receive a value back. Instead use :c:macro:`EM_ASM_ARGS`, :c:macro:`EM_ASM_INT`, or :c:macro:`EM_ASM_DOUBLE`. + - As of ``1.30.4``, ``EM_ASM`` contents appear as normal JS, outside of the compiled code. Previously we had them as a string that was ``eval``ed. The newer approach avoids the overhead of ``eval``, and also allows for better optimization of ``EM_ASM`` contents by things like closure compiler, as their contents are now visible. Note that this means that closure compiler will optimize them as if they were written together with the rest of the codebase, which is a change from before - you may need to use safety quotes in some places (``a['b']`` instead of ``a.b``). .. c:macro:: EM_ASM_(code, ...) diff --git a/src/jsifier.js b/src/jsifier.js index a7bae00eaa66c..9918c5422f32d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -496,7 +496,6 @@ function JSify(data, functionsOnly) { } itemsDict.functionStub.push(item); - if (IGNORED_FUNCTIONS.indexOf(item.ident) >= 0) return; var shortident = item.ident.substr(1); if (BUILD_AS_SHARED_LIB) { // Shared libraries reuse the runtime of their parents. @@ -578,8 +577,6 @@ function JSify(data, functionsOnly) { function functionReconstructor(func) { // We have this function all reconstructed, go and finalize it's JS! - if (IGNORED_FUNCTIONS.indexOf(func.ident) >= 0) return null; - func.JS = '\n'; var paramIdents = func.params.map(function(param) { @@ -1933,9 +1930,7 @@ function JSify(data, functionsOnly) { print(processMacros(preprocess(shellParts[1]))); // Print out some useful metadata if (RUNNING_JS_OPTS || PGO) { - var generatedFunctions = JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { - return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; - })); + var generatedFunctions = JSON.stringify(keys(Functions.implementedFunctions)); if (PGO) { print('PGOMonitor.allGenerated = ' + generatedFunctions + ';\nremoveRunDependency("pgo");\n'); } diff --git a/src/library.js b/src/library.js index a0d4b97cb42d6..3d0f625d461cb 100644 --- a/src/library.js +++ b/src/library.js @@ -925,12 +925,6 @@ LibraryManager.library = { return buf; } }, - getwd__deps: ['getcwd'], - getwd: function(path_name) { - // char *getwd(char *path_name); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/getwd.html - return _getcwd(path_name, 4096); // PATH_MAX. - }, isatty__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], isatty: function(fildes) { // int isatty(int fildes); diff --git a/src/library_async.js b/src/library_async.js index 121b48f637620..8b08ae4d358d1 100644 --- a/src/library_async.js +++ b/src/library_async.js @@ -278,7 +278,7 @@ mergeInto(LibraryManager.library, { }); EmterpreterAsync.setState(1); #if ASSERTIONS - EmterpreterAsync.saveStack = stackTrace(); + EmterpreterAsync.saveStack = new Error().stack; // we can't call stackTrace() as it calls compiled code #endif // Pause the main loop, until we resume if (Browser.mainLoop.func) { diff --git a/src/preamble.js b/src/preamble.js index 95e51684203bc..c995ed817b556 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -20,15 +20,6 @@ Runtime['addFunction'] = Runtime.addFunction; Runtime['removeFunction'] = Runtime.removeFunction; #endif -#if ASM_JS -#if RESERVED_FUNCTION_POINTERS -function jsCall() { - var args = Array.prototype.slice.call(arguments); - return Runtime.functionPointers[args[0]].apply(null, args.slice(1)); -} -#endif -#endif - #if BENCHMARK Module.realPrint = Module.print; Module.print = Module.printErr = function(){}; @@ -393,6 +384,11 @@ var cwrap, ccall; } } var ret = func.apply(null, cArgs); +#if ASSERTIONS + if (typeof EmterpreterAsync === 'object') { + assert(!EmterpreterAsync.state, 'cannot start async op with normal JS calling ccall'); + } +#endif if (returnType === 'string') ret = Pointer_stringify(ret); if (stack !== 0) Runtime.stackRestore(stack); return ret; @@ -452,6 +448,9 @@ var cwrap, ccall; var strgfy = parseJSFunc(function(){return Pointer_stringify}).returnValue; funcstr += 'ret = ' + strgfy + '(ret);'; } +#if ASSERTIONS + funcstr += "if (typeof EmterpreterAsync === 'object') { assert(!EmterpreterAsync.state, 'cannot start async op with normal JS calling cwrap') }"; +#endif if (!numericArgs) { // If we had a stack, restore it funcstr += JSsource['stackRestore'].body.replace('()', '(stack)') + ';'; diff --git a/src/settings.js b/src/settings.js index 5c7b0643bad72..faf042e4bd360 100644 --- a/src/settings.js +++ b/src/settings.js @@ -443,13 +443,6 @@ var LIBRARY_DEPS_TO_AUTOEXPORT = ['memcpy']; // This list is also used to determ // so we must export so that if they are implemented in C // they will be accessible, in ASM_JS mode). -var IGNORED_FUNCTIONS = []; // Functions that we should not generate, neither a stub nor a complete function. - // This is useful if your project code includes a function, and you want to replace - // that in the compiled code with your own handwritten JS. (Of course even without - // this option, you could just override the generated function at runtime. However, - // JS engines might optimize better if the function is defined once in a single - // place in your code.) - var EXPORTED_GLOBALS = []; // Global non-function variables that are explicitly // exported, so they are guaranteed to be // accessible outside of the generated code. diff --git a/system/include/compat/unistd.h b/system/include/compat/unistd.h deleted file mode 100644 index 07c3afde1efdd..0000000000000 --- a/system/include/compat/unistd.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _COMPAT_UNISTD_H -#define _COMPAT_UNISTD_H - -#ifdef __cplusplus -extern "C" { -#endif - -char * getwd(char *__buf ); - -#ifdef __cplusplus -} -#endif - -#include_next - -#endif diff --git a/tests/core/test_ccall.in b/tests/core/test_ccall.in index c0ad0b07afc3d..ef9c6a48fc12b 100644 --- a/tests/core/test_ccall.in +++ b/tests/core/test_ccall.in @@ -30,7 +30,7 @@ int get_stack() { int i; return (int)&i; } int uses_stack(test_struct* t1) { if (stackChecker == 0) stackChecker = (int*)malloc(sizeof(int)); *stackChecker = get_stack(); - EM_ASM(Module.ccall('get_int', 'number')); + EM_ASM(Module['ccall']('get_int', 'number')); printf("stack is %s.\n", *stackChecker == get_stack() ? "ok" : "messed up"); } void call_ccall_again() { diff --git a/tests/test_core.py b/tests/test_core.py index 155f80879b713..1afdf90d6ef02 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -978,11 +978,17 @@ def test_stack_align(self): if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('fastcomp-only') Settings.INLINING_LIMIT = 50 src = path_from_root('tests', 'core', 'test_stack_align.cpp') - self.do_run(open(src).read(), ['''align 4: 0 + def test(): + self.do_run(open(src).read(), ['''align 4: 0 align 8: 0 align 16: 0 align 32: 0 base align: 0, 0, 0, 0''']) + test() + if '-O' in str(self.emcc_args): + print 'outlining' + Settings.OUTLINING_LIMIT = 60 + test() def test_stack_restore(self): if self.is_emterpreter(): return self.skip('generated code not available in emterpreter') @@ -6119,6 +6125,38 @@ def process(filename): self.emcc_args += ['--closure', '1'] self.do_run_from_file(src, output, post_build=post) + def test_dead_functions(self): + src = r''' + #include + extern "C" { + __attribute__((noinline)) int unused(int x) { + return x; + } + } + int main(int argc, char **argv) { + printf("*%d*\n", argc > 1 ? unused(1) : 2); + return 0; + } + ''' + def test(expected, args=[], no_build=False): + self.do_run(src, expected, args=args, no_build=no_build) + return open(self.in_dir('src.cpp.o.js')).read() + + # Sanity check that it works and the dead function is emitted + js = test('*1*', ['x']) + test('*2*', no_build=True) + if self.run_name in ['default', 'asm1', 'asm2g']: assert 'function _unused($' in js + + # Kill off the dead function, and check a code path using it aborts + Settings.DEAD_FUNCTIONS = ['_unused'] + test('*2*') + test('abort(-1) at', args=['x'], no_build=True) + + # Kill off a library function, check code aborts + Settings.DEAD_FUNCTIONS = ['_printf'] + test('abort(-1) at') + test('abort(-1) at', args=['x'], no_build=True) + def test_pgo(self): if Settings.ASM_JS: return self.skip('PGO does not work in asm mode') @@ -6282,7 +6320,7 @@ def test_add_function(self): printf("fp: %d\n", fp); void (*f)(int) = reinterpret_cast(fp); f(7); - EM_ASM_(Module.Runtime.removeFunction($0), f); + EM_ASM_(Module['Runtime']['removeFunction']($0), f); printf("ok\n"); return 0; } @@ -7307,6 +7345,22 @@ def test_async(self): self.do_run(src, 'HelloWorld!99'); + if self.is_emterpreter(): + print 'check bad ccall use' + Settings.ASSERTIONS = 1 + Settings.INVOKE_RUN = 0 + open('post.js', 'w').write(''' +try { + Module['ccall']('main', 'number', ['number', 'string'], [2, 'waka']); + var never = true; +} catch(e) { + Module.print(e); + assert(!never); +} +''') + self.emcc_args += ['--post-js', 'post.js'] + self.do_run(src, 'cannot start async op with normal JS'); + def test_coroutine(self): if not Settings.ASM_JS: return self.skip('asyncify requires asm.js') if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('asyncify requires fastcomp') diff --git a/tests/unistd/curdir.c b/tests/unistd/curdir.c index b9f22dd74f491..c266fc35ddbcb 100644 --- a/tests/unistd/curdir.c +++ b/tests/unistd/curdir.c @@ -16,9 +16,6 @@ int main() { ); char buffer[256]; - printf("getwd: %s\n", getwd(buffer)); - printf("errno: %d\n", errno); - errno = 0; printf("getcwd: %s\n", getcwd(buffer, 256)); printf("errno: %d\n", errno); errno = 0; @@ -27,9 +24,6 @@ int main() { printf("chdir(file): %d\n", chdir("/file")); printf("errno: %d\n", errno); if (!errno) { - errno = 0; - printf("getwd: %s\n", getwd(buffer)); - printf("errno: %d\n", errno); errno = 0; printf("getcwd: %s\n", getcwd(buffer, 256)); printf("errno: %d\n", errno); @@ -40,9 +34,6 @@ int main() { printf("chdir(device): %d\n", chdir("/device")); printf("errno: %d\n", errno); if (!errno) { - errno = 0; - printf("getwd: %s\n", getwd(buffer)); - printf("errno: %d\n", errno); errno = 0; printf("getcwd: %s\n", getcwd(buffer, 256)); printf("errno: %d\n", errno); @@ -53,9 +44,6 @@ int main() { printf("chdir(folder): %d\n", chdir("/folder")); printf("errno: %d\n", errno); if (!errno) { - errno = 0; - printf("getwd: %s\n", getwd(buffer)); - printf("errno: %d\n", errno); errno = 0; printf("getcwd: %s\n", getcwd(buffer, 256)); printf("errno: %d\n", errno); @@ -66,9 +54,6 @@ int main() { printf("chdir(nonexistent): %d\n", chdir("/nonexistent")); printf("errno: %d\n", errno); if (!errno) { - errno = 0; - printf("getwd: %s\n", getwd(buffer)); - printf("errno: %d\n", errno); errno = 0; printf("getcwd: %s\n", getcwd(buffer, 256)); printf("errno: %d\n", errno); @@ -79,9 +64,6 @@ int main() { printf("chdir(link): %d\n", chdir("/link")); printf("errno: %d\n", errno); if (!errno) { - errno = 0; - printf("getwd: %s\n", getwd(buffer)); - printf("errno: %d\n", errno); errno = 0; printf("getcwd: %s\n", getcwd(buffer, 256)); printf("errno: %d\n", errno); @@ -93,9 +75,6 @@ int main() { printf("fchdir(/): %d\n", fchdir(open("/", O_RDONLY, 0777))); printf("errno: %d\n", errno); if (!errno) { - errno = 0; - printf("getwd: %s\n", getwd(buffer)); - printf("errno: %d\n", errno); errno = 0; printf("getcwd: %s\n", getcwd(buffer, 256)); printf("errno: %d\n", errno); diff --git a/tests/unistd/curdir.out b/tests/unistd/curdir.out index e353f1c40ccf1..65fab9eba573d 100644 --- a/tests/unistd/curdir.out +++ b/tests/unistd/curdir.out @@ -1,5 +1,3 @@ -getwd: / -errno: 0 getcwd: / errno: 0 @@ -11,8 +9,6 @@ errno: 20 chdir(folder): 0 errno: 0 -getwd: /folder -errno: 0 getcwd: /folder errno: 0 @@ -21,14 +17,10 @@ errno: 2 chdir(link): 0 errno: 0 -getwd: /folder -errno: 0 getcwd: /folder errno: 0 fchdir(/): 0 errno: 0 -getwd: / -errno: 0 getcwd: / errno: 0 diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 306f1b4fed34e..bbdd04b0fa76a 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1,5 +1,5 @@ // -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ; js-indent-level : 2 ; js-curly-indent-offset: 0 -*- -// vim: set ts=2 et sw=2 tw=80: +// vim: set ts=2 et sw=2: //============================================================================== // Optimizer tool. This is meant to be run after the emscripten compiler has @@ -2525,7 +2525,9 @@ function getStackBumpNode(ast) { if (type === 'assign' && node[2][0] === 'name' && node[2][1] === 'STACKTOP') { var value = node[3]; if (value[0] === 'name') return true; - assert(value[0] == 'binary' && value[1] == '|' && value[2][0] == 'binary' && value[2][1] == '+' && value[2][2][0] == 'name' && value[2][2][1] == 'STACKTOP' && value[2][3][0] == 'num'); + if (value[0] == 'binary' && value[1] == '&') return; // this is an alignment fix, ignore + assert(value[0] == 'binary' && value[1] == '|' && value[2][0] == 'binary' && value[2][1] == '+' && value[2][2][0] == 'name' && value[2][2][1] == 'STACKTOP'); + if (value[2][3][0] !== 'num') return; // non-constant bump, ignore found = node; return true; } @@ -5335,6 +5337,8 @@ function outline(ast) { // the value after they return. var size = measureSize(func); asmData.maxOutlinings = Math.min(Math.round(3*size/extraInfo.sizeToOutline), maxTotalOutlinings); + asmData.maxAttemptedOutlinings = Infinity; + if (extraInfo.sizeToOutline < 100) asmData.maxAttemptedOutlinings = Math.min(50, asmData.maxAttemptedOutlinings); // tiny sizes, be careful of too many attempts asmData.intendedPieces = Math.ceil(size/extraInfo.sizeToOutline); asmData.totalStackSize = stackSize + (stack.length + 2*asmData.maxOutlinings)*8; asmData.controlStackPos = function(i) { return stackSize + (stack.length + i)*8 }; @@ -5678,6 +5682,7 @@ function outline(ast) { } function outlineStatements(func, asmData, stats, maxSize) { + asmData.maxAttemptedOutlinings--; level++; //printErr('outlineStatements: ' + [func[1], level, measureSize(func)]); var lastSize = measureSize(stats); @@ -5708,7 +5713,7 @@ function outline(ast) { } } function done() { - return asmData.splitCounter >= asmData.maxOutlinings || measureSize(func) <= extraInfo.sizeToOutline; + return asmData.splitCounter >= asmData.maxOutlinings || measureSize(func) <= extraInfo.sizeToOutline || asmData.maxAttemptedOutlinings < 0; } while (1) { i--; @@ -7911,6 +7916,23 @@ function asmLastOpts(ast) { }); } +// Contrary to the name this does not eliminate actual dead functions, only +// those marked as such with DEAD_FUNCTIONS +function eliminateDeadFuncs(ast) { + assert(asm); + assert(extraInfo && extraInfo.dead_functions); + var deadFunctions = set(extraInfo.dead_functions); + traverseGeneratedFunctions(ast, function (fun, type) { + if (!(fun[1] in deadFunctions)) { + return; + } + var asmData = normalizeAsm(fun); + fun[3] = [['stat', ['call', ['name', 'abort'], [['num', -1]]]]]; + asmData.vars = {}; + denormalizeAsm(fun, asmData); + }); +} + // Passes table var minifyWhitespace = false, printMetadata = true, asm = false, asmPreciseF32 = false, emitJSON = false, last = false; @@ -7931,6 +7953,7 @@ var passes = { loopOptimizer: loopOptimizer, registerize: registerize, registerizeHarder: registerizeHarder, + eliminateDeadFuncs: eliminateDeadFuncs, eliminate: eliminate, eliminateMemSafe: eliminateMemSafe, aggressiveVariableElimination: aggressiveVariableElimination, diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index 915cfbc32da04..b6e24572b8941 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -9,7 +9,7 @@ def path_from_root(*pathelems): return os.path.join(__rootpath__, *pathelems) -NATIVE_PASSES = set(['asm', 'asmPreciseF32', 'receiveJSON', 'emitJSON', 'eliminate', 'eliminateMemSafe', 'simplifyExpressions', 'simplifyIfs', 'optimizeFrounds', 'registerize', 'registerizeHarder', 'minifyNames', 'minifyLocals', 'minifyWhitespace', 'cleanup', 'asmLastOpts', 'last', 'noop', 'closure']) +NATIVE_PASSES = set(['asm', 'asmPreciseF32', 'receiveJSON', 'emitJSON', 'eliminateDeadFuncs', 'eliminate', 'eliminateMemSafe', 'simplifyExpressions', 'simplifyIfs', 'optimizeFrounds', 'registerize', 'registerizeHarder', 'minifyNames', 'minifyLocals', 'minifyWhitespace', 'cleanup', 'asmLastOpts', 'last', 'noop', 'closure']) JS_OPTIMIZER = path_from_root('tools', 'js-optimizer.js') diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp index 6e63ac9b9524a..726d4f9f7248d 100644 --- a/tools/optimizer/optimizer.cpp +++ b/tools/optimizer/optimizer.cpp @@ -3869,6 +3869,29 @@ void asmLastOpts(Ref ast) { }); } +// Contrary to the name this does not eliminate actual dead functions, only +// those marked as such with DEAD_FUNCTIONS +void eliminateDeadFuncs(Ref ast) { + assert(!!extraInfo); + IString DEAD_FUNCTIONS("dead_functions"); + IString ABORT("abort"); + assert(extraInfo->has(DEAD_FUNCTIONS)); + StringSet deadFunctions; + for (size_t i = 0; i < extraInfo[DEAD_FUNCTIONS]->size(); i++) { + deadFunctions.insert(extraInfo[DEAD_FUNCTIONS][i]->getIString()); + } + traverseFunctions(ast, [&](Ref fun) { + if (!deadFunctions.has(fun[1].get()->getIString())) { + return; + } + AsmData asmData(fun); + fun[3]->setSize(1); + fun[3][0] = make1(STAT, make2(CALL, makeName(ABORT), &(makeArray())->push_back(makeNum(-1)))); + asmData.vars.clear(); + asmData.denormalize(); + }); +} + //================== // Main //================== @@ -3925,6 +3948,7 @@ int main(int argc, char **argv) { if (str == "asm") {} // the default for us else if (str == "asmPreciseF32") {} else if (str == "receiveJSON" || str == "emitJSON") {} + else if (str == "eliminateDeadFuncs") eliminateDeadFuncs(doc); else if (str == "eliminate") eliminate(doc); else if (str == "eliminateMemSafe") eliminateMemSafe(doc); else if (str == "simplifyExpressions") simplifyExpressions(doc); diff --git a/tools/shared.py b/tools/shared.py index 27aac239d9eb0..636ed753c0771 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1900,6 +1900,26 @@ def make_extcall(sig, named=True): }''') return ret + @staticmethod + def make_jscall(sig, named=True): + fnargs = ','.join(['a' + str(i) for i in range(1, len(sig))]) + args = 'index' + (',' if fnargs else '') + fnargs + ret = '''function%s(%s) { + %sRuntime.functionPointers[index](%s); +}''' % ((' jsCall_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', fnargs) + + if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS: + # guard against cross-module stack leaks + ret = ret.replace(') {\n', ''') { + try { + var preStack = asm.stackSave(); +''').replace(';\n}', '''; + } finally { + assert(asm.stackSave() == preStack); + } +}''') + return ret + @staticmethod def make_invoke(sig, named=True): args = ','.join(['a' + str(i) for i in range(1, len(sig))]) diff --git a/tools/webidl_binder.py b/tools/webidl_binder.py index df1d591724d6f..04a70db63078b 100644 --- a/tools/webidl_binder.py +++ b/tools/webidl_binder.py @@ -319,7 +319,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, %sEM_ASM_%s({ var self = Module['getCache'](Module['%s'])[$0]; if (!self.hasOwnProperty('%s')) throw 'a JSImplementation must implement all functions, you forgot %s::%s.'; - %sself.%s(%s)%s; + %sself['%s'](%s)%s; }, (int)this%s); }''' % (c_return_type, func_name, dec_args, basic_return, 'INT' if c_return_type not in C_FLOATS else 'DOUBLE', @@ -381,7 +381,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, continue if not constructor: mid_js += [r''' -%s.prototype['%s'] = ''' % (name, m.identifier.name)] +%s.prototype['%s'] = %s.prototype.%s = ''' % (name, m.identifier.name, name, m.identifier.name)] sigs = {} return_type = None for ret, args in m.signatures(): @@ -427,7 +427,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, get_name = 'get_' + attr mid_js += [r''' - %s.prototype['%s']= ''' % (name, get_name)] + %s.prototype['%s'] = %s.prototype.%s = ''' % (name, get_name, name, get_name)] render_function(name, get_name, get_sigs, m.type.name, None, @@ -441,7 +441,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, if not m.readonly: set_name = 'set_' + attr mid_js += [r''' - %s.prototype['%s']= ''' % (name, set_name)] + %s.prototype['%s'] = %s.prototype.%s = ''' % (name, set_name, name, set_name)] render_function(name, set_name, set_sigs, 'Void', None, @@ -454,7 +454,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, if not interface.getExtendedAttribute('NoDelete'): mid_js += [r''' - %s.prototype['__destroy__'] = ''' % name] + %s.prototype['__destroy__'] = %s.prototype.__destroy__ = ''' % (name, name)] render_function(name, '__destroy__', { 0: [] }, 'Void', None,