Skip to content

Enable EM_ASM in side modules #18228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
4 changes: 4 additions & 0 deletions site/source/docs/compiling/Dynamic-Linking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ Limitations
startup
`[doc] <https://emscripten.org/docs/porting/files/packaging_files.html#preloading-files>`__
`[discuss] <https://groups.google.com/forum/#!topic/emscripten-discuss/cE3hUV3fDSw>`__.
- ``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
----------------
Expand Down
35 changes: 35 additions & 0 deletions src/library_dylink.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 '%'
Expand Down Expand Up @@ -657,6 +659,39 @@ 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 = moduleExports['__start_em_asm'];
var stop = moduleExports['__stop_em_asm'];
{{{ from64('start') }}}
{{{ from64('stop') }}}
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
Expand Down
22 changes: 12 additions & 10 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand All @@ -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;

Expand Down
10 changes: 10 additions & 0 deletions test/core/test_em_asm_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <stdio.h>
#include <emscripten.h>

int test_side();

int main() {
printf("in main\n");
EM_ASM(out("em_asm in main"));
return test_side();
}
17 changes: 17 additions & 0 deletions test/core/test_em_asm_main.out
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions test/core/test_em_asm_side.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <emscripten.h>
#include <stdio.h>

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;
}
5 changes: 5 additions & 0 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this now a core test because it's likely to be affected by optlevel? or just because we don't need to run it on multiple platforms?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make sure it passes at all opt levels (and in other configurations too).


@parameterized({
'': ([], False),
'pthreads': (['-sUSE_PTHREADS', '-sPROXY_TO_PTHREAD', '-sEXIT_RUNTIME'], False),
Expand Down
4 changes: 0 additions & 4 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down