Skip to content
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

Wasm async compilation #4867

Merged
merged 4 commits into from
Jan 26, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,20 @@ def check(input_file):
if shared.Building.is_wasm_only() and shared.Settings.EVAL_CTORS:
logging.debug('disabling EVAL_CTORS, as in wasm-only mode it hurts more than it helps. TODO: a wasm version of it')
shared.Settings.EVAL_CTORS = 0
# enable async compilation if optimizing and not turned off manually
if opt_level > 0:
if 'BINARYEN_ASYNC_COMPILATION=0' not in settings_changes:
shared.Settings.BINARYEN_ASYNC_COMPILATION = 1
if shared.Settings.BINARYEN_ASYNC_COMPILATION == 1:
if shared.Building.is_wasm_only():
# async compilation requires a swappable module - we swap it in when it's ready
shared.Settings.SWAPPABLE_ASM_MODULE = 1
Copy link
Member

Choose a reason for hiding this comment

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

it's not clear from this limited slice of context, but is there something somewhere explaining in more detail what SWAPPABLE_ASM_MODULE means?

Copy link
Member Author

Choose a reason for hiding this comment

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

There is a little detail in src/settings.js where the setting is defined. I improved it now.

else:
# if not wasm-only, we can't do async compilation as the build can run in other
# modes than wasm (like asm.js) which may not support an async step
shared.Settings.BINARYEN_ASYNC_COMPILATION = 0
if 'BINARYEN_ASYNC_COMPILATION=1' in settings_changes:
logging.warning('BINARYEN_ASYNC_COMPILATION requested, but disabled since not in wasm-only mode')

# wasm outputs are only possible with a side wasm
if target.endswith(WASM_ENDINGS):
Expand Down
23 changes: 18 additions & 5 deletions src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -2200,6 +2200,23 @@ function integrateWasmJS(Module) {
};
info['global.Math'] = global.Math;
info['env'] = env;
// handle a generated wasm instance, receiving its exports and
// performing other necessary setup
function receiveInstance(instance) {
exports = instance.exports;
if (exports.memory) mergeMemory(exports.memory);
Module["usingWasm"] = true;
}
#if BINARYEN_ASYNC_COMPILATION
Module['printErr']('asynchronously preparing wasm');
addRunDependency('wasm-instantiate'); // we can't run yet
WebAssembly.instantiate(getBinary(), info).then(function(output) {
receiveInstance(output.instance);
Module['asm'] = exports; // swap in the exports so they can be called
removeRunDependency('wasm-instantiate');
});
return {}; // no exports yet; we'll fill them in later
#endif
var instance;
try {
instance = new WebAssembly.Instance(new WebAssembly.Module(getBinary()), info)
Expand All @@ -2210,11 +2227,7 @@ function integrateWasmJS(Module) {
}
return false;
}
exports = instance.exports;
if (exports.memory) mergeMemory(exports.memory);

Module["usingWasm"] = true;

receiveInstance(instance);
return exports;
}

Expand Down
3 changes: 3 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,9 @@ var BINARYEN_PASSES = ""; // A comma-separated list of passes to run in the bina
var BINARYEN_MEM_MAX = -1; // Set the maximum size of memory in the wasm module (in bytes).
// Without this, TOTAL_MEMORY is used (as it is used for the initial value),
// or if memory growth is enabled, no limit is set. This overrides both of those.
var BINARYEN_ASYNC_COMPILATION = 0; // Whether to compile the wasm asynchronously, which is more
// efficient. This is off by default in unoptimized builds and
// on by default in optimized ones.
var BINARYEN_ROOT = ""; // Directory where we can find Binaryen. Will be automatically set for you,
// but you can set it to override if you are a Binaryen developer.

Expand Down
13 changes: 13 additions & 0 deletions tests/binaryen_async.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <stdio.h>

#include <emscripten.h>

int main() {
printf("hello, world!\n");
int result = EM_ASM_INT_V({
return Module.sawAsyncCompilation | 0;
});
REPORT_RESULT();
return 0;
}

38 changes: 38 additions & 0 deletions tests/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3274,6 +3274,44 @@ def test_binaryen(self):
self.btest('browser_test_hello_world.c', expected='0', args=['-s', 'BINARYEN=1', '-s', 'BINARYEN_METHOD="interpret-binary"'])
self.btest('browser_test_hello_world.c', expected='0', args=['-s', 'BINARYEN=1', '-s', 'BINARYEN_METHOD="interpret-binary"', '-O2'])

def test_binaryen_native(self):
for opts in [[], ['-O1'], ['-O2'], ['-O3']]:
print opts
self.btest('browser_test_hello_world.c', expected='0', args=['-s', 'BINARYEN=1'] + opts)

def test_binaryen_async(self):
# notice when we use async compilation
open('shell.html', 'w').write(open(path_from_root('src', 'shell.html')).read().replace(
'''{{{ SCRIPT }}}''',
'''
<script>
// note if we do async compilation
var real_wasm_instantiate = WebAssembly.instantiate;
WebAssembly.instantiate = function(a, b) {
Module.sawAsyncCompilation = true;
return real_wasm_instantiate(a, b);
};
// show stderr for the viewer's fun
Module.printErr = function(x) {
Module.print('<<< ' + x + ' >>>');
console.log(x);
};
</script>
{{{ SCRIPT }}}
''',
))
for opts, expect in [
([], 0),
(['-O1'], 1),
(['-O2'], 1),
(['-O3'], 1),
(['-s', 'BINARYEN_ASYNC_COMPILATION=1'], 1), # force it on
(['-O1', '-s', 'BINARYEN_ASYNC_COMPILATION=0'], 0), # force it off
(['-s', 'BINARYEN_ASYNC_COMPILATION=1', '-s', 'BINARYEN_METHOD="native-wasm,asmjs"'], 0), # try to force it on, but have it disabled
]:
print opts, expect
self.btest('binaryen_async.c', expected=str(expect), args=['-s', 'BINARYEN=1', '--shell-file', 'shell.html'] + opts)

def test_utf8_textdecoder(self):
self.btest('benchmark_utf8.cpp', expected='0', args=['--embed-file', path_from_root('tests/utf8_corpus.txt') + '@/utf8_corpus.txt'])

Expand Down