Skip to content

Commit

Permalink
Put all EM_JS/EM_ASM strings in a specific data section (#13443)
Browse files Browse the repository at this point in the history
This way, once binaryen has extracted that string data
it can potentially remove the segment or at least zero it
out.

Fixes #9366
  • Loading branch information
sbc100 authored Feb 17, 2021
1 parent 0ea8010 commit 2e38ed2
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 32 deletions.
27 changes: 14 additions & 13 deletions system/include/emscripten/em_asm.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,16 @@ void emscripten_asm_const_async_on_main_thread(
// then wrap the whole code block inside parentheses (). See tests/core/test_em_asm_2.cpp
// for example code snippets.

#define CODE_EXPR(code) ({ __attribute__((section("em_asm"), aligned(1))) static const char x[] = code; x; })

// Runs the given JavaScript code on the calling thread (synchronously), and returns no value back.
#define EM_ASM(code, ...) ((void)emscripten_asm_const_int(#code _EM_ASM_PREP_ARGS(__VA_ARGS__)))
#define EM_ASM(code, ...) ((void)emscripten_asm_const_int(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__)))

// Runs the given JavaScript code on the calling thread (synchronously), and returns an integer back.
#define EM_ASM_INT(code, ...) emscripten_asm_const_int(#code _EM_ASM_PREP_ARGS(__VA_ARGS__))
#define EM_ASM_INT(code, ...) emscripten_asm_const_int(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__))

// Runs the given JavaScript code on the calling thread (synchronously), and returns a double back.
#define EM_ASM_DOUBLE(code, ...) emscripten_asm_const_double(#code _EM_ASM_PREP_ARGS(__VA_ARGS__))
#define EM_ASM_DOUBLE(code, ...) emscripten_asm_const_double(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__))

// Runs the given JavaScript code synchronously on the main browser thread, and returns no value back.
// Call this function for example to access DOM elements in a pthread when building with -s USE_PTHREADS=1.
Expand All @@ -193,29 +195,28 @@ void emscripten_asm_const_async_on_main_thread(
// a return value back, consider using the function MAIN_THREAD_ASYNC_EM_ASM() instead, which will not block.
// In single-threaded builds (including proxy-to-worker), MAIN_THREAD_EM_ASM*()
// functions are direct aliases to the corresponding EM_ASM*() family of functions.
#define MAIN_THREAD_EM_ASM(code, ...) ((void)emscripten_asm_const_int_sync_on_main_thread(#code _EM_ASM_PREP_ARGS(__VA_ARGS__)))
#define MAIN_THREAD_EM_ASM(code, ...) ((void)emscripten_asm_const_int_sync_on_main_thread(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__)))

// Runs the given JavaScript code synchronously on the main browser thread, and returns an integer back.
// The same considerations apply as with MAIN_THREAD_EM_ASM().
#define MAIN_THREAD_EM_ASM_INT(code, ...) emscripten_asm_const_int_sync_on_main_thread(#code _EM_ASM_PREP_ARGS(__VA_ARGS__))
#define MAIN_THREAD_EM_ASM_INT(code, ...) emscripten_asm_const_int_sync_on_main_thread(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__))

// Runs the given JavaScript code synchronously on the main browser thread, and returns a double back.
// The same considerations apply as with MAIN_THREAD_EM_ASM().
#define MAIN_THREAD_EM_ASM_DOUBLE(code, ...) emscripten_asm_const_double_sync_on_main_thread(#code _EM_ASM_PREP_ARGS(__VA_ARGS__))
#define MAIN_THREAD_EM_ASM_DOUBLE(code, ...) emscripten_asm_const_double_sync_on_main_thread(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__))

// Asynchronously dispatches the given JavaScript code to be run on the main browser thread.
// If the calling thread is the main browser thread, then the specified JavaScript code is executed
// synchronously. Otherwise an event will be queued on the main browser thread to execute the call
// later (think postMessage()), and this call will immediately return without waiting. Be sure to
// guard any accesses to shared memory on the heap inside the JavaScript code with appropriate locking.
#define MAIN_THREAD_ASYNC_EM_ASM(code, ...) ((void)emscripten_asm_const_async_on_main_thread(#code _EM_ASM_PREP_ARGS(__VA_ARGS__)))
// later (think postMessage()), and this call will immediately return without waiting. Be sure to guard any accesses to shared memory on the heap inside the JavaScript code with appropriate locking.
#define MAIN_THREAD_ASYNC_EM_ASM(code, ...) ((void)emscripten_asm_const_async_on_main_thread(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__)))

// Old forms for compatibility, no need to use these.
// Replace EM_ASM_, EM_ASM_ARGS and EM_ASM_INT_V with EM_ASM_INT,
// and EM_ASM_DOUBLE_V with EM_ASM_DOUBLE.
#define EM_ASM_(code, ...) emscripten_asm_const_int(#code _EM_ASM_PREP_ARGS(__VA_ARGS__))
#define EM_ASM_ARGS(code, ...) emscripten_asm_const_int(#code _EM_ASM_PREP_ARGS(__VA_ARGS__))
#define EM_ASM_INT_V(code) EM_ASM_INT(#code)
#define EM_ASM_DOUBLE_V(code) EM_ASM_DOUBLE(#code)
#define EM_ASM_(code, ...) emscripten_asm_const_int(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__))
#define EM_ASM_ARGS(code, ...) emscripten_asm_const_int(CODE_EXPR(#code) _EM_ASM_PREP_ARGS(__VA_ARGS__))
#define EM_ASM_INT_V(code) EM_ASM_INT(code)
#define EM_ASM_DOUBLE_V(code) EM_ASM_DOUBLE(code)

#endif // !defined(__cplusplus) && defined(__STRICT_ANSI__)
24 changes: 12 additions & 12 deletions system/include/emscripten/em_js.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,20 @@
// We use <::> to separate the arguments from the function body because it isn't
// valid anywhere in a C function declaration.

// Generated __em_js__-prefixed functions are read by either the JSBackend (for
// asm.js) or by Binaryen, and the string data is extracted into the Emscripten
// metadata dictionary under the "emJsFuncs" key. emJsFuncs itself is a
// dictionary where the keys are function names (not prefixed with __em_js__),
// and the values are the <::>-including description strings.
// Generated __em_js__-prefixed functions are read by binaryen, and the string
// data is extracted into the Emscripten metadata dictionary under the
// "emJsFuncs" key. emJsFuncs itself is a dictionary where the keys are function
// names (not prefixed with __em_js__), and the values are the <::>-including
// description strings.

// emJsFuncs metadata is read in emscripten.py's create_em_js, which creates an
// array of JS function strings to be included in the JS output.

#define EM_JS(ret, name, params, ...) \
_EM_JS_CPP_BEGIN \
extern ret name params EM_IMPORT(name); \
__attribute__((used, visibility("default"))) \
const char* __em_js__##name() { \
return #params "<::>" #__VA_ARGS__; \
} \
#define EM_JS(ret, name, params, ...) \
_EM_JS_CPP_BEGIN \
extern ret name params EM_IMPORT(name); \
__attribute__((used, visibility("default"))) const char* __em_js__##name() { \
__attribute__((section("em_js"), aligned(1))) static const char s[] = #params "<::>" #__VA_ARGS__; \
return s; \
} \
_EM_JS_CPP_END
13 changes: 8 additions & 5 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1912,17 +1912,20 @@ def test_em_asm_arguments_side_effects(self):
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm_arguments_side_effects.cpp', force_c=True)

@parameterized({
'normal': ([],),
'linked': (['-s', 'MAIN_MODULE'],),
'': ([], False),
'c': ([], True),
'linked': (['-s', 'MAIN_MODULE'], False),
'linked_c': (['-s', 'MAIN_MODULE'], True),
})
def test_em_js(self, args):
def test_em_js(self, args, force_c):
if 'MAIN_MODULE' in args and not self.is_wasm():
self.skipTest('main module support for non-wasm')
if '-fsanitize=address' in self.emcc_args:
self.skipTest('no dynamic library support in asan yet')
self.emcc_args += args + ['-s', 'EXPORTED_FUNCTIONS=["_main","_malloc"]']
self.do_run_in_out_file_test('tests', 'core', 'test_em_js.cpp')
self.do_run_in_out_file_test('tests', 'core', 'test_em_js.cpp', force_c=True)

self.do_run_in_out_file_test('tests', 'core', 'test_em_js.cpp', force_c=force_c)
self.assertContained("no args returning int", open('test_em_js.js').read())

def test_runtime_stacksave(self):
self.do_runf(path_from_root('tests', 'core', 'test_runtime_stacksave.c'), 'success')
Expand Down
4 changes: 2 additions & 2 deletions tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -7018,7 +7018,7 @@ def test_export_aliasee(self):
self.run_process(cmd)

# build main module
args = ['-s', 'EXPORTED_FUNCTIONS=["_main", "_foo"]', '-s', 'MAIN_MODULE=2', '-s', 'EXIT_RUNTIME', '-lnodefs.js']
args = ['-g', '-s', 'EXPORTED_FUNCTIONS=["_main", "_foo"]', '-s', 'MAIN_MODULE=2', '-s', 'EXIT_RUNTIME', '-lnodefs.js']
cmd = [EMCC, path_from_root('tests', 'other', 'alias', 'main.c'), '-o', 'main.js'] + args
print(' '.join(cmd))
self.run_process(cmd)
Expand Down Expand Up @@ -7536,7 +7536,7 @@ def test_js_optimizer_parse_error(self):
var ASM_CONSTS = [function() { var x = !<->5.; }];
^
''', '''
1024: function() {var x = !<->5.;}
1025: function() {var x = !<->5.;}
^
'''), stderr)

Expand Down
4 changes: 4 additions & 0 deletions tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,11 @@ def lld_flags_for_executable(external_symbol_list):
cmd.append('--growable-table')

if not Settings.SIDE_MODULE:
# Export these two section start symbols so that we can extact the string
# data that they contain.
cmd += [
'--export', '__start_em_asm',
'--export', '__stop_em_asm',
'-z', 'stack-size=%s' % Settings.TOTAL_STACK,
'--initial-memory=%d' % Settings.INITIAL_MEMORY,
]
Expand Down

0 comments on commit 2e38ed2

Please sign in to comment.