Skip to content
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
34 changes: 27 additions & 7 deletions emscripten.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,27 +642,44 @@ def get_exported_implemented_functions(all_exported_functions, all_implemented,
def get_implemented_functions(metadata):
return set(metadata['implementedFunctions'])

def proxy_debug_print(call_type, settings):
if shared.Settings.PTHREADS_DEBUG:
if call_type == 'sync_on_main_thread_': return 'Runtime.warnOnce("sync proxying function " + code);';
elif call_type == 'async_on_main_thread_': return 'Runtime.warnOnce("async proxying function " + code);';
return ''

def include_asm_consts(pre, forwarded_json, metadata, settings):
if settings['BINARYEN'] and settings['SIDE_MODULE']:
assert len(metadata['asmConsts']) == 0, 'EM_ASM is not yet supported in shared wasm module (it cannot be stored in the wasm itself, need some solution)'

asm_consts, all_sigs = all_asm_consts(metadata)
asm_consts, all_sigs, call_types = all_asm_consts(metadata)
asm_const_funcs = []
for sig in set(all_sigs):
forwarded_json['Functions']['libraryFunctions']['_emscripten_asm_const_' + sig] = 1
for s in range(len(all_sigs)):
sig = all_sigs[s]
call_type = call_types[s] if s < len(call_types) else ''
forwarded_json['Functions']['libraryFunctions']['_emscripten_asm_const_' + call_type + sig] = 1
args = ['a%d' % i for i in range(len(sig)-1)]
all_args = ['code'] + args
proxy_function = ''
if shared.Settings.USE_PTHREADS:
if call_type == 'sync_on_main_thread_': proxy_function = '_emscripten_sync_run_in_browser_thread_' + sig
elif call_type == 'async_on_main_thread_': proxy_function = '_emscripten_async_run_in_browser_thread_' + sig

# In proxied function calls, positive integers 1, 2, 3, ... denote pointers to regular C compiled functions. Negative integers -1, -2, -3, ... denote indices to EM_ASM() blocks, so remap the EM_ASM() indices from 0, 1, 2, ... over to the negative integers starting at -1.
proxy_args = '-1 - ' + ','.join(all_args)

if proxy_function: proxy_to_main_thread = ' if (ENVIRONMENT_IS_PTHREAD) { ' + proxy_debug_print(call_type, settings) + 'return ' + proxy_function + '(' + proxy_args + '); } \n'
else: proxy_to_main_thread = ''
asm_const_funcs.append(r'''
function _emscripten_asm_const_%s(%s) {
return ASM_CONSTS[code](%s);
}''' % (sig.encode('utf-8'), ', '.join(all_args), ', '.join(args)))
%s return ASM_CONSTS[code](%s);
}''' % (call_type + sig.encode('utf-8'), ', '.join(all_args), proxy_to_main_thread, ', '.join(args)))

asm_consts_text = '\nvar ASM_CONSTS = [' + ',\n '.join(asm_consts) + '];\n'
asm_funcs_text = '\n'.join(asm_const_funcs) + '\n'

body_marker = '// === Body ==='
return pre.replace(body_marker, body_marker + '\n' + asm_consts_text + asm_funcs_text)
return pre.replace(body_marker, body_marker + '\n' + asm_consts_text + asm_funcs_text.encode('utf-8'))

# Test if the parentheses at body[openIdx] and body[closeIdx] are a match to each other.
def parentheses_match(body, openIdx, closeIdx):
Expand Down Expand Up @@ -690,9 +707,11 @@ def trim_asm_const_body(body):
def all_asm_consts(metadata):
asm_consts = [0]*len(metadata['asmConsts'])
all_sigs = []
all_call_types = []
for k, v in metadata['asmConsts'].items():
const = v[0].encode('utf-8')
sigs = v[1]
call_types = v[2] if len(v) >= 3 else None
const = trim_asm_const_body(const)
const = '{ ' + const + ' }'
args = []
Expand All @@ -702,7 +721,8 @@ def all_asm_consts(metadata):
const = 'function(' + ', '.join(args) + ') ' + const
asm_consts[int(k)] = const
all_sigs += sigs
return asm_consts, all_sigs
if call_types: all_call_types += call_types
return asm_consts, all_sigs, all_call_types


def unfloat(s):
Expand Down
6 changes: 3 additions & 3 deletions site/source/docs/api_reference/Filesystem-API.rst
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ File system API
#include <emscripten.h>

int main() {
EM_ASM(
MAIN_THREAD_EM_ASM(
FS.writeFile('file', 'foobar');
FS.symlink('file', 'link');
console.log(FS.readlink('link'));
Expand Down Expand Up @@ -341,7 +341,7 @@ File system API
#include <emscripten.h>

int main() {
EM_ASM(
MAIN_THREAD_EM_ASM(
FS.writeFile('file', 'foobar');
console.log(FS.stat('file'));
);
Expand Down Expand Up @@ -459,7 +459,7 @@ File system API
#include <emscripten.h>

int main() {
EM_ASM(
MAIN_THREAD_EM_ASM(
FS.writeFile('file', 'foobar');
FS.truncate('file', 3);
console.log(FS.readFile('file', { encoding: 'utf8' }));
Expand Down
3 changes: 3 additions & 0 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -4372,6 +4372,9 @@ LibraryManager.library = {
emscripten_asm_const: true,
emscripten_asm_const_int: true,
emscripten_asm_const_double: true,
emscripten_asm_const_int_sync_on_main_thread: true,
emscripten_asm_const_double_sync_on_main_thread: true,
emscripten_asm_const_async_on_main_thread: true,

// ======== compiled code from system/lib/compiler-rt , see readme therein
__muldsi3__asm: true,
Expand Down
2 changes: 2 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,8 @@ var PTHREAD_HINT_NUM_CORES = 4;

var PTHREADS_PROFILING = 0; // True when building with --threadprofiler

var PTHREADS_DEBUG = 0; // If true, add in debug traces for diagnosing pthreads related issues.

var MAX_GLOBAL_ALIGN = -1; // received from the backend

// Duplicate function elimination. This coalesces function bodies that are
Expand Down
36 changes: 33 additions & 3 deletions system/include/emscripten/em_asm.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ extern "C" {
void emscripten_asm_const(const char* code);
int emscripten_asm_const_int(const char* code, ...);
double emscripten_asm_const_double(const char* code, ...);

int emscripten_asm_const_int_sync_on_main_thread(const char* code, ...);
double emscripten_asm_const_double_sync_on_main_thread(const char* code, ...);

void emscripten_asm_const_async_on_main_thread(const char* code, ...);

#ifdef __cplusplus
}
#endif
Expand All @@ -43,15 +49,39 @@ void emscripten_asm_const(const char* code);
// then wrap the whole code block inside parentheses (). See tests/core/test_em_asm_2.cpp
// for example code snippets.

// Runs the given JavaScript code, and returns nothing back.
// 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, ##__VA_ARGS__))

// Runs the given JavaScript code, and returns an integer back.
// 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, ##__VA_ARGS__)

// Runs the given JavaScript code, and returns a double back.
// 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, ##__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/web worker. Avoid calling this
// function in performance sensitive code, because this will effectively sleep the calling thread until the
// main browser thread is able to service the proxied function call. If you have multiple MAIN_THREAD_EM_ASM()
// code blocks to call in succession, it will likely be much faster to coalesce all the calls to a single
// MAIN_THREAD_EM_ASM() block. If you do not need synchronization nor a return value back, consider using
// the function MAIN_THREAD_ASYNC_EM_ASM() instead, which will not block.
#define MAIN_THREAD_EM_ASM(code, ...) ((void)emscripten_asm_const_int_sync_on_main_thread(#code, ##__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, ##__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, ##__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, ##__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.
Expand Down
11 changes: 11 additions & 0 deletions tests/core/test_main_thread_async_em_asm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <emscripten.h>
#include <stdio.h>

int main()
{
// Test that on main browser thread, MAIN_THREAD_ASYNC_EM_ASM() will get
// synchronously executed.
printf("Before MAIN_THREAD_ASYNC_EM_ASM\n");
MAIN_THREAD_ASYNC_EM_ASM(Module.print('Inside MAIN_THREAD_ASYNC_EM_ASM: ' + $0 + ' ' + $1), 42, 3.5);
printf("After MAIN_THREAD_ASYNC_EM_ASM\n");
}
3 changes: 3 additions & 0 deletions tests/core/test_main_thread_async_em_asm.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Before MAIN_THREAD_ASYNC_EM_ASM
Inside MAIN_THREAD_ASYNC_EM_ASM: 42 3.5
After MAIN_THREAD_ASYNC_EM_ASM
22 changes: 22 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1603,10 +1603,32 @@ def test_em_asm(self):
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm')
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm', force_c=True)

# Tests various different ways to invoke the EM_ASM(), EM_ASM_INT() and EM_ASM_DOUBLE() macros.
def test_em_asm_2(self):
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm_2')
self.do_run_in_out_file_test('tests', 'core', 'test_em_asm_2', force_c=True)

# Tests various different ways to invoke the MAIN_THREAD_EM_ASM(), MAIN_THREAD_EM_ASM_INT() and MAIN_THREAD_EM_ASM_DOUBLE() macros.
# This test is identical to test_em_asm_2, just search-replaces EM_ASM to MAIN_THREAD_EM_ASM on the test file. That way if new
# test cases are added to test_em_asm_2.cpp for EM_ASM, they will also get tested in MAIN_THREAD_EM_ASM form.
@@no_wasm_backend
def test_main_thread_em_asm(self):
src = open(path_from_root('tests', 'core', 'test_em_asm_2.cpp'), 'r').read()
test_file = 'src.cpp'
open(test_file, 'w').write(src.replace('EM_ASM', 'MAIN_THREAD_EM_ASM'))

expected_result = open(path_from_root('tests', 'core', 'test_em_asm_2.out'), 'r').read()
expected_result_file = 'result.out'
open(expected_result_file, 'w').write(expected_result.replace('EM_ASM', 'MAIN_THREAD_EM_ASM'))

self.do_run_from_file(test_file, expected_result_file)
self.do_run_from_file(test_file, expected_result_file, force_c=True)

@no_wasm_backend
def test_main_thread_async_em_asm(self):
self.do_run_in_out_file_test('tests', 'core', 'test_main_thread_async_em_asm')
self.do_run_in_out_file_test('tests', 'core', 'test_main_thread_async_em_asm', force_c=True)

def test_em_asm_unicode(self):
self.do_run(r'''
#include <emscripten.h>
Expand Down