-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
STANDALONE_WASM option #9461
STANDALONE_WASM option #9461
Changes from all commits
e11288c
479e297
8dd1aaa
b25ee39
fab6ceb
664e17b
40e2f58
90b1eaa
78db28a
2e0770a
1848451
6e5db08
f781f39
5994a4c
d129daf
5a0b65c
f89fa67
998aa4a
61c75eb
521686d
c347034
5e894da
8136e2b
67007b8
0f6e911
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* Copyright 2019 The Emscripten Authors. All rights reserved. | ||
* Emscripten is available under two separate licenses, the MIT license and the | ||
* University of Illinois/NCSA Open Source License. Both these licenses can be | ||
* found in the LICENSE file. | ||
*/ | ||
|
||
mergeInto(LibraryManager.library, { | ||
proc_exit__deps: ['exit'], | ||
proc_exit: function(code) { | ||
return _exit(code); | ||
}, | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -152,6 +152,10 @@ var LibraryManager = { | |
libraries.push('library_glemu.js'); | ||
} | ||
|
||
if (STANDALONE_WASM) { | ||
libraries.push('library_wasi.js'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why make this conditional? Aren't all the library functions only included on demand anyway? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it's all on demand, but we also include some libraries only based on their flags, like the GL stuff right above this. I think it's nice as it reflects the fact that nothing should be used from those libraries without the flag, the error is clearer that way. |
||
} | ||
|
||
libraries = libraries.concat(additionalLibraries); | ||
|
||
if (BOOTSTRAPPING_STRUCT_INFO) libraries = ['library_bootstrap_structInfo.js', 'library_formatString.js']; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -989,6 +989,12 @@ function createWasm() { | |
exports = Asyncify.instrumentWasmExports(exports); | ||
#endif | ||
Module['asm'] = exports; | ||
#if STANDALONE_WASM | ||
// In pure wasm mode the memory is created in the wasm (not imported), and | ||
// then exported. | ||
// TODO: do not create a Memory earlier in JS | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like a TODO we certainly want to fix. |
||
updateGlobalBufferAndViews(exports['memory'].buffer); | ||
#endif | ||
#if USE_PTHREADS | ||
// Keep a reference to the compiled module so we can post it to the workers. | ||
wasmModule = module; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* Copyright 2019 The Emscripten Authors. All rights reserved. | ||
* Emscripten is available under two separate licenses, the MIT license and the | ||
* University of Illinois/NCSA Open Source License. Both these licenses can be | ||
* found in the LICENSE file. | ||
*/ | ||
|
||
#include <emscripten.h> | ||
#include <errno.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
|
||
#include <wasi/wasi.h> | ||
|
||
/* | ||
* WASI support code. These are compiled with the program, and call out | ||
* using wasi APIs, which can be provided either by a wasi VM or by our | ||
* emitted JS. | ||
*/ | ||
|
||
// libc | ||
|
||
void exit(int status) { | ||
__wasi_proc_exit(status); | ||
__builtin_unreachable(); | ||
} | ||
|
||
void abort() { | ||
exit(1); | ||
} | ||
|
||
// Musl lock internals. As we assume wasi is single-threaded for now, these | ||
// are no-ops. | ||
|
||
void __lock(void* ptr) {} | ||
void __unlock(void* ptr) {} | ||
|
||
// Emscripten additions | ||
|
||
void *emscripten_memcpy_big(void *restrict dest, const void *restrict src, size_t n) { | ||
// This normally calls out into JS which can do a single fast operation, | ||
// but with wasi we can't do that. As this is called when n >= 8192, we | ||
// can just split into smaller calls. | ||
// TODO optimize, maybe build our memcpy with a wasi variant, maybe have | ||
// a SIMD variant, etc. | ||
const int CHUNK = 8192; | ||
unsigned char* d = (unsigned char*)dest; | ||
unsigned char* s = (unsigned char*)src; | ||
while (n > 0) { | ||
size_t curr_n = n; | ||
if (curr_n > CHUNK) curr_n = CHUNK; | ||
memcpy(d, s, curr_n); | ||
d += CHUNK; | ||
s += CHUNK; | ||
n -= curr_n; | ||
} | ||
return dest; | ||
} | ||
|
||
static const int WASM_PAGE_SIZE = 65536; | ||
|
||
// Note that this does not support memory growth in JS because we don't update the JS | ||
// heaps. Wasm and wasi lack a good API for that. | ||
int emscripten_resize_heap(size_t size) { | ||
size_t result = __builtin_wasm_memory_grow(0, (size + WASM_PAGE_SIZE - 1) / WASM_PAGE_SIZE); | ||
return result != (size_t)-1; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ | |
raise Exception('do not run this file directly; do something like: tests/runner.py other') | ||
|
||
from tools.shared import Building, PIPE, run_js, run_process, STDOUT, try_delete, listify | ||
from tools.shared import EMCC, EMXX, EMAR, EMRANLIB, PYTHON, FILE_PACKAGER, WINDOWS, MACOS, LLVM_ROOT, EMCONFIG, EM_BUILD_VERBOSE | ||
from tools.shared import EMCC, EMXX, EMAR, EMRANLIB, PYTHON, FILE_PACKAGER, WINDOWS, MACOS, LINUX, LLVM_ROOT, EMCONFIG, EM_BUILD_VERBOSE | ||
from tools.shared import CLANG, CLANG_CC, CLANG_CPP, LLVM_AR | ||
from tools.shared import COMPILER_ENGINE, NODE_JS, SPIDERMONKEY_ENGINE, JS_ENGINES, V8_ENGINE | ||
from tools.shared import WebAssembly | ||
|
@@ -8287,16 +8287,22 @@ def run(args, expected): | |
run(['-s', 'TOTAL_MEMORY=32MB', '-s', 'ALLOW_MEMORY_GROWTH=1', '-s', 'BINARYEN=1'], (2 * 1024 * 1024 * 1024 - 65536) // 16384) | ||
run(['-s', 'TOTAL_MEMORY=32MB', '-s', 'ALLOW_MEMORY_GROWTH=1', '-s', 'BINARYEN=1', '-s', 'WASM_MEM_MAX=128MB'], 2048 * 4) | ||
|
||
def test_wasm_targets(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it ok that we seems to have lost of test coverage for fastcomp here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, good point, I missed something here. I thought it was fine to remove this, but actually it hid a possible regression: building in fastcomp without SIDE_MODULE but with That never worked very well anyhow, it was a half-hearted attempt at standalone wasm files. So I am leaning towards showing a clear error in that case that the user should use the upstream wasm backend for standalone wasm? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like there is a reasonable fix actually, pushed now. |
||
def test_wasm_target_and_STANDALONE_WASM(self): | ||
# STANDALONE_WASM means we never minify imports and exports. | ||
for opts, potentially_expect_minified_exports_and_imports in ( | ||
([], False), | ||
(['-O2'], False), | ||
(['-O3'], True), | ||
(['-Os'], True), | ||
([], False), | ||
(['-O2'], False), | ||
(['-O3'], True), | ||
(['-O3', '-s', 'STANDALONE_WASM'], False), | ||
(['-Os'], True), | ||
): | ||
if 'STANDALONE_WASM' in opts and not self.is_wasm_backend(): | ||
continue | ||
# targeting .wasm (without .js) means we enable STANDALONE_WASM automatically, and don't minify imports/exports | ||
for target in ('out.js', 'out.wasm'): | ||
expect_minified_exports_and_imports = potentially_expect_minified_exports_and_imports and target.endswith('.js') | ||
print(opts, potentially_expect_minified_exports_and_imports, target, ' => ', expect_minified_exports_and_imports) | ||
standalone = target.endswith('.wasm') or 'STANDALONE_WASM' in opts | ||
print(opts, potentially_expect_minified_exports_and_imports, target, ' => ', expect_minified_exports_and_imports, standalone) | ||
|
||
self.clear() | ||
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-o', target] + opts) | ||
|
@@ -8308,13 +8314,33 @@ def test_wasm_targets(self): | |
exports = [line.strip().split(' ')[1].replace('"', '') for line in wast_lines if "(export " in line] | ||
imports = [line.strip().split(' ')[2].replace('"', '') for line in wast_lines if "(import " in line] | ||
exports_and_imports = exports + imports | ||
print(exports) | ||
print(imports) | ||
print(' exports', exports) | ||
print(' imports', imports) | ||
if expect_minified_exports_and_imports: | ||
assert 'a' in exports_and_imports | ||
else: | ||
assert 'a' not in exports_and_imports | ||
assert 'memory' in exports_and_imports, 'some things are not minified anyhow' | ||
assert 'memory' in exports_and_imports or 'fd_write' in exports_and_imports, 'some things are not minified anyhow' | ||
# verify the wasm runs with the JS | ||
if target.endswith('.js'): | ||
self.assertContained('hello, world!', run_js('out.js')) | ||
# verify the wasm runs in a wasm VM, without the JS | ||
# TODO: more platforms than linux | ||
if LINUX and standalone and self.is_wasm_backend(): | ||
WASMER = os.path.expanduser(os.path.join('~', '.wasmer', 'bin', 'wasmer')) | ||
if os.path.isfile(WASMER): | ||
print(' running in wasmer') | ||
out = run_process([WASMER, 'run', 'out.wasm'], stdout=PIPE).stdout | ||
self.assertContained('hello, world!', out) | ||
else: | ||
print('[WARNING - no wasmer]') | ||
WASMTIME = os.path.expanduser(os.path.join('~', 'wasmtime')) | ||
if os.path.isfile(WASMTIME): | ||
print(' running in wasmtime') | ||
out = run_process([WASMTIME, 'out.wasm'], stdout=PIPE).stdout | ||
self.assertContained('hello, world!', out) | ||
else: | ||
print('[WARNING - no wasmtime]') | ||
|
||
def test_wasm_targets_side_module(self): | ||
# side modules do allow a wasm target | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this. I really hope we can move fd_read in there and link it by default in the future.