diff --git a/emcc.py b/emcc.py index f2dc5821aba76..55f308f6f43aa 100755 --- a/emcc.py +++ b/emcc.py @@ -607,6 +607,8 @@ def get_binaryen_passes(): passes += ['--signext-lowering'] if optimizing: passes += ['--post-emscripten'] + if settings.SIDE_MODULE: + passes += ['--pass-arg=post-emscripten-side-module'] if optimizing: passes += [building.opt_level_to_str(settings.OPT_LEVEL, settings.SHRINK_LEVEL)] # when optimizing, use the fact that low memory is never used (1024 is a diff --git a/emscripten.py b/emscripten.py index ec62146e6e101..76dcf4859b0aa 100644 --- a/emscripten.py +++ b/emscripten.py @@ -337,8 +337,6 @@ def emscript(in_wasm, out_wasm, outfile_js, memfile): diagnostics.warning('em-js-i64', 'using 64-bit arguments in EM_JS function without WASM_BIGINT is not yet fully supported: `%s` (%s, %s)', em_js_func, c_sig, signature.params) if settings.SIDE_MODULE: - if metadata.asmConsts: - exit_with_error('EM_ASM is not supported in side modules') if metadata.emJsFuncs: exit_with_error('EM_JS is not supported in side modules') logger.debug('emscript: skipping remaining js glue generation') diff --git a/site/source/docs/compiling/Dynamic-Linking.rst b/site/source/docs/compiling/Dynamic-Linking.rst index 7a0d51fa287e9..497b877a60966 100644 --- a/site/source/docs/compiling/Dynamic-Linking.rst +++ b/site/source/docs/compiling/Dynamic-Linking.rst @@ -212,6 +212,10 @@ Limitations startup `[doc] `__ `[discuss] `__. +- ``EM_ASM`` code defined within side modules depends on ``eval`` support are + is therefore incompatible with ``-sDYNAMIC_EXECUTION=0``. +- ``EM_JS`` functions defined in side modules are not yet supported. + Pthreads support ---------------- diff --git a/src/library_dylink.js b/src/library_dylink.js index 8be5bb47e2195..06085ff8fc9db 100644 --- a/src/library_dylink.js +++ b/src/library_dylink.js @@ -89,6 +89,8 @@ var LibraryDylink = { '_emscripten_tls_init', '__wasm_init_tls', '__wasm_call_ctors', + '__start_em_asm', + '__stop_em_asm', ].includes(symName) #if SPLIT_MODULE // Exports synthesized by wasm-split should be prefixed with '%' @@ -657,6 +659,37 @@ var LibraryDylink = { } #endif +#if MAIN_MODULE + function addEmAsm(addr, body) { + var args = []; + var arity = 0; + for (; arity < 16; arity++) { + if (body.indexOf('$' + arity) != -1) { + args.push('$' + arity); + } else { + break; + } + } + args = args.join(','); + var func = '(' + args +' ) => { ' + body + '};' +#if DYLINK_DEBUG + dbg('adding new EM_ASM constant at: ' + ptrToString(start)); +#endif + {{{ makeEval('ASM_CONSTS[start] = eval(func)') }}}; + } + + // Add any EM_ASM function that exist in the side module + if ('__start_em_asm' in moduleExports) { + var start = {{{ from64("moduleExports['__start_em_asm']") }}}; + var stop = {{{ from64("moduleExports['__stop_em_asm']") }}}; + while (start < stop) { + var jsString = UTF8ToString(start); + addEmAsm(start, jsString); + start = HEAPU8.indexOf(0, start) + 1; + } + } +#endif + // initialize the module #if USE_PTHREADS // Only one thread (currently The main thread) should call diff --git a/src/settings.js b/src/settings.js index cbcc9f1579b97..b63c29c8ee654 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1251,12 +1251,13 @@ var BENCHMARK = false; // [link] var EXPORT_NAME = 'Module'; -// When set to 0, we do not emit eval() and new Function(), which disables some functionality -// (causing runtime errors if attempted to be used), but allows the emitted code to be -// acceptable in places that disallow dynamic code execution (chrome packaged app, -// privileged firefox app, etc.). Pass this flag when developing an Emscripten application -// that is targeting a privileged or a certified execution environment, see -// Firefox Content Security Policy (CSP) webpage for details: +// When set to 0, we do not emit eval() and new Function(), which disables some +// functionality (causing runtime errors if attempted to be used), but allows +// the emitted code to be acceptable in places that disallow dynamic code +// execution (chrome packaged app, privileged firefox app, etc.). Pass this flag +// when developing an Emscripten application that is targeting a privileged or a +// certified execution environment, see Firefox Content Security Policy (CSP) +// webpage for details: // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src // in particular the 'unsafe-eval' and 'wasm-unsafe-eval' policies. // @@ -1271,11 +1272,12 @@ var EXPORT_NAME = 'Module'; // - emscripten_run_script_int(), // - emscripten_run_script_string(), // - dlopen(), -// - the functions ccall() and cwrap() are still available, but they are restricted to only -// being able to call functions that have been exported in the Module object in advance. +// - the functions ccall() and cwrap() are still available, but they are +// restricted to only being able to call functions that have been exported in +// the Module object in advance. // -// When set to -sDYNAMIC_EXECUTION=2 flag is set, attempts to call to eval() are demoted -// to warnings instead of throwing an exception. +// When -sDYNAMIC_EXECUTION=2 is set, attempts to call to eval() are demoted to +// warnings instead of throwing an exception. // [link] var DYNAMIC_EXECUTION = 1; diff --git a/test/core/test_em_asm_main.c b/test/core/test_em_asm_main.c new file mode 100644 index 0000000000000..daef865a5b874 --- /dev/null +++ b/test/core/test_em_asm_main.c @@ -0,0 +1,10 @@ +#include +#include + +int test_side(); + +int main() { + printf("in main\n"); + EM_ASM(out("em_asm in main")); + return test_side(); +} diff --git a/test/core/test_em_asm_main.out b/test/core/test_em_asm_main.out new file mode 100644 index 0000000000000..b9c2b2751899a --- /dev/null +++ b/test/core/test_em_asm_main.out @@ -0,0 +1,17 @@ +in main +em_asm in main +no args works +int types: + char : 1 + signed char : 2 +unsigned char : 3 + short: 4 + signed short: 5 +unsigned short: 6 + int : 7 + signed int : 8 +unsigned int : 9 + long : 10 + signed long : 11 +unsigned long : 12 + terminator: 42 diff --git a/test/core/test_em_asm_side.c b/test/core/test_em_asm_side.c new file mode 100644 index 0000000000000..30f1f4cf6f073 --- /dev/null +++ b/test/core/test_em_asm_side.c @@ -0,0 +1,28 @@ +#include +#include + +int test_side() { + EM_ASM(out("no args works")); + + EM_ASM({ + console.log("int types:"); + out(" char : " + $0); + out(" signed char : " + $1); + out("unsigned char : " + $2); + out(" short: " + $3); + out(" signed short: " + $4); + out("unsigned short: " + $5); + out(" int : " + $6); + out(" signed int : " + $7); + out("unsigned int : " + $8); + out(" long : " + $9); + out(" signed long : " + $10); + out("unsigned long : " + $11); + out(" terminator: " + $12); + }, (char)1, (signed char)2, (unsigned char)3, + (short)4, (signed short)5, (unsigned short)6, + (int)7, (signed int)8, (unsigned int)9, + (long)10, (signed long)11, (unsigned long)12, 42); + + return 0; +} diff --git a/test/test_core.py b/test/test_core.py index e8bab5cd5c694..5d5b331fb224b 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -2257,6 +2257,11 @@ def test_em_asm_arguments_side_effects(self): def test_em_asm_direct(self): self.do_core_test('test_em_asm_direct.c') + @needs_dylink + def test_em_asm_side_module(self): + self.build(test_file('core/test_em_asm_side.c'), js_outfile=False, emcc_args=['-sSIDE_MODULE'], output_basename='side') + self.do_core_test('test_em_asm_main.c', emcc_args=['-sMAIN_MODULE=2', 'side.wasm']) + @parameterized({ '': ([], False), 'pthreads': (['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'], False), diff --git a/test/test_other.py b/test/test_other.py index b31ffed4fa2b8..942355f52a3ee 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -11645,10 +11645,6 @@ def test_runtime_keepalive(self): self.set_setting('EXIT_RUNTIME') self.do_other_test('test_runtime_keepalive.cpp') - def test_em_asm_side_module(self): - err = self.expect_fail([EMCC, '-sSIDE_MODULE', test_file('hello_world_em_asm.c')]) - self.assertContained('EM_ASM is not supported in side modules', err) - def test_em_js_side_module(self): err = self.expect_fail([EMXX, '-sSIDE_MODULE', test_file('core/test_em_js.cpp')]) self.assertContained('EM_JS is not supported in side modules', err)