Skip to content

Commit 63810da

Browse files
authored
Add -s MINIMAL_RUNTIME=1 option. (#7923)
* Add -s MINIMAL_RUNTIME=1 option.
1 parent b089a08 commit 63810da

38 files changed

+1607
-134
lines changed

emcc.py

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ def uniquename(name):
663663

664664
specified_target = target
665665
target = specified_target if specified_target is not None else 'a.out.js' # specified_target is the user-specified one, target is what we will generate
666-
target_basename = unsuffixed_basename(target)
666+
shared.Settings.TARGET_BASENAME = target_basename = unsuffixed_basename(target)
667667

668668
final_suffix = suffix(target)
669669

@@ -1043,6 +1043,7 @@ def check(input_file):
10431043
assert not (not shared.Settings.DYNAMIC_EXECUTION and options.use_closure_compiler), 'cannot have both NO_DYNAMIC_EXECUTION and closure compiler enabled at the same time'
10441044

10451045
if options.emrun:
1046+
assert not shared.Settings.MINIMAL_RUNTIME, '--emrun is not compatible with -s MINIMAL_RUNTIME=1'
10461047
shared.Settings.EXPORTED_RUNTIME_METHODS.append('addOnExit')
10471048

10481049
if options.use_closure_compiler:
@@ -1122,7 +1123,7 @@ def check(input_file):
11221123
shared.Settings.EXPORTED_FUNCTIONS += ['___cxa_demangle']
11231124
forced_stdlibs += ['libc++abi']
11241125

1125-
if not shared.Settings.ONLY_MY_CODE:
1126+
if not shared.Settings.ONLY_MY_CODE and not shared.Settings.MINIMAL_RUNTIME:
11261127
# Always need malloc and free to be kept alive and exported, for internal use and other modules
11271128
shared.Settings.EXPORTED_FUNCTIONS += ['_malloc', '_free']
11281129
if shared.Settings.WASM_BACKEND:
@@ -1285,6 +1286,24 @@ def check(input_file):
12851286
if not shared.Settings.SEPARATE_ASM_MODULE_NAME:
12861287
shared.Settings.SEPARATE_ASM_MODULE_NAME = 'Module["asm"]'
12871288

1289+
if shared.Settings.MINIMAL_RUNTIME:
1290+
# Minimal runtime uses a different default shell file
1291+
if options.shell_path == shared.path_from_root('src', 'shell.html'):
1292+
options.shell_path = shared.path_from_root('src', 'shell_minimal_runtime.html')
1293+
1294+
# Remove the default exported functions 'memcpy', 'memset', 'malloc', 'free', etc. - those should only be linked in if used
1295+
shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = []
1296+
1297+
# Always build with STRICT mode enabled
1298+
shared.Settings.STRICT = 1
1299+
1300+
# Always use the new HTML5 API event target lookup rules (TODO: enable this when the other PR lands)
1301+
# shared.Settings.DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR = 1
1302+
1303+
# In asm.js always use memory init file to get the best code size, other modes are not currently supported.
1304+
if not shared.Settings.WASM:
1305+
options.memory_init_file = True
1306+
12881307
if shared.Settings.WASM:
12891308
if shared.Settings.SINGLE_FILE:
12901309
# placeholder strings for JS glue, to be replaced with subresource locations in do_binaryen
@@ -1317,18 +1336,20 @@ def check(input_file):
13171336
if any(s.startswith('MEM_INIT_METHOD=') for s in settings_changes):
13181337
exit_with_error('MEM_INIT_METHOD is not supported in wasm. Memory will be embedded in the wasm binary if threads are not used, and included in a separate file if threads are used.')
13191338
options.memory_init_file = True
1320-
if shared.Settings.BINARYEN_ASYNC_COMPILATION == 1:
1321-
# async compilation requires a swappable module - we swap it in when it's ready
1322-
shared.Settings.SWAPPABLE_ASM_MODULE = 1
1323-
else:
1324-
# if not wasm-only, we can't do async compilation as the build can run in other
1325-
# modes than wasm (like asm.js) which may not support an async step
1326-
shared.Settings.BINARYEN_ASYNC_COMPILATION = 0
1327-
warning = 'This will reduce performance and compatibility (some browsers limit synchronous compilation), see http://kripken.github.io/emscripten-site/docs/compiling/WebAssembly.html#codegen-effects'
1328-
if 'BINARYEN_ASYNC_COMPILATION=1' in settings_changes:
1329-
logger.warning('BINARYEN_ASYNC_COMPILATION requested, but disabled because of user options. ' + warning)
1330-
elif 'BINARYEN_ASYNC_COMPILATION=0' not in settings_changes:
1331-
logger.warning('BINARYEN_ASYNC_COMPILATION disabled due to user options. ' + warning)
1339+
1340+
if not shared.Settings.MINIMAL_RUNTIME: # BINARYEN_ASYNC_COMPILATION and SWAPPABLE_ASM_MODULE do not have a meaning in MINIMAL_RUNTIME (always async)
1341+
if shared.Settings.BINARYEN_ASYNC_COMPILATION == 1:
1342+
# async compilation requires a swappable module - we swap it in when it's ready
1343+
shared.Settings.SWAPPABLE_ASM_MODULE = 1
1344+
else:
1345+
# if not wasm-only, we can't do async compilation as the build can run in other
1346+
# modes than wasm (like asm.js) which may not support an async step
1347+
shared.Settings.BINARYEN_ASYNC_COMPILATION = 0
1348+
warning = 'This will reduce performance and compatibility (some browsers limit synchronous compilation), see http://kripken.github.io/emscripten-site/docs/compiling/WebAssembly.html#codegen-effects'
1349+
if 'BINARYEN_ASYNC_COMPILATION=1' in settings_changes:
1350+
logger.warning('BINARYEN_ASYNC_COMPILATION requested, but disabled because of user options. ' + warning)
1351+
elif 'BINARYEN_ASYNC_COMPILATION=0' not in settings_changes:
1352+
logger.warning('BINARYEN_ASYNC_COMPILATION disabled due to user options. ' + warning)
13321353

13331354
if not shared.Settings.DECLARE_ASM_MODULE_EXPORTS:
13341355
# Swappable wasm module/asynchronous wasm compilation requires an indirect stub
@@ -1337,10 +1358,6 @@ def check(input_file):
13371358
if shared.Settings.SWAPPABLE_ASM_MODULE == 1:
13381359
shared.Settings.DECLARE_ASM_MODULE_EXPORTS = 1
13391360
logger.warning('Enabling -s DECLARE_ASM_MODULE_EXPORTS=1 since -s SWAPPABLE_ASM_MODULE=1 is used')
1340-
# Wasm -O3 builds use Meta-DCE which is currently not compatible with -s DECLARE_ASM_MODULE_EXPORTS=0 option.
1341-
if will_metadce(options):
1342-
shared.Settings.DECLARE_ASM_MODULE_EXPORTS = 1
1343-
logger.warning('Enabling -s DECLARE_ASM_MODULE_EXPORTS=1 since -O3/-Os build with Wasm meta-DCE is used')
13441361

13451362
# we will include the mem init data in the wasm, when we don't need the
13461363
# mem init file to be loadable by itself
@@ -1405,6 +1422,16 @@ def check(input_file):
14051422
if not shared.Settings.WASM and (shared.Settings.MAIN_MODULE or shared.Settings.SIDE_MODULE):
14061423
assert not shared.Settings.ALLOW_MEMORY_GROWTH, 'memory growth is not supported with shared asm.js modules'
14071424

1425+
if shared.Settings.MINIMAL_RUNTIME:
1426+
if shared.Settings.ALLOW_MEMORY_GROWTH:
1427+
logging.warning('-s ALLOW_MEMORY_GROWTH=1 is not yet supported with -s MINIMAL_RUNTIME=1')
1428+
1429+
if shared.Settings.EMTERPRETIFY:
1430+
exit_with_error('-s EMTERPRETIFY=1 is not supported with -s MINIMAL_RUNTIME=1')
1431+
1432+
if shared.Settings.USE_PTHREADS:
1433+
exit_with_error('-s USE_PTHREADS=1 is not yet supported with -s MINIMAL_RUNTIME=1')
1434+
14081435
if shared.Settings.ALLOW_MEMORY_GROWTH and shared.Settings.ASM_JS == 1:
14091436
# this is an issue in asm.js, but not wasm
14101437
if not shared.Settings.WASM:
@@ -1437,6 +1464,10 @@ def check(input_file):
14371464
options.separate_asm = True
14381465
shared.Settings.FINALIZE_ASM_JS = False
14391466

1467+
# MINIMAL_RUNTIME always use separate .asm.js file for best performance and memory usage
1468+
if shared.Settings.MINIMAL_RUNTIME and not shared.Settings.WASM:
1469+
options.separate_asm = True
1470+
14401471
if shared.Settings.GLOBAL_BASE < 0:
14411472
shared.Settings.GLOBAL_BASE = 8 # default if nothing else sets it
14421473

@@ -1918,7 +1949,11 @@ def get_final():
19181949
with ToolchainProfiler.profile_block('memory initializer'):
19191950
memfile = None
19201951
if shared.Settings.MEM_INIT_METHOD > 0 or embed_memfile(options):
1921-
memfile = target + '.mem'
1952+
if shared.Settings.MINIMAL_RUNTIME:
1953+
# Independent of whether user is doing -o a.html or -o a.js, generate the mem init file as a.mem (and not as a.html.mem or a.js.mem)
1954+
memfile = target.replace('.html', '.mem').replace('.js', '.mem')
1955+
else:
1956+
memfile = target + '.mem'
19221957

19231958
if memfile and not shared.Settings.WASM_BACKEND:
19241959
# Strip the memory initializer out of the asmjs file
@@ -2109,6 +2144,15 @@ def get_eliminate():
21092144

21102145
module_export_name_substitution()
21112146

2147+
# Run a final regex pass to clean up items that were not possible to optimize by Closure, or unoptimalities that were left behind
2148+
# by processing steps that occurred after Closure.
2149+
if shared.Settings.MINIMAL_RUNTIME == 2 and shared.Settings.USE_CLOSURE_COMPILER and options.debug_level == 0:
2150+
# Process .js runtime file
2151+
shared.run_process([shared.PYTHON, shared.path_from_root('tools', 'hacky_postprocess_around_closure_limitations.py'), final])
2152+
# Process .asm.js file
2153+
if not shared.Settings.WASM:
2154+
shared.run_process([shared.PYTHON, shared.path_from_root('tools', 'hacky_postprocess_around_closure_limitations.py'), asm_target])
2155+
21122156
# The JS is now final. Move it to its final location
21132157
shutil.move(final, js_target)
21142158

@@ -2690,15 +2734,39 @@ def module_export_name_substitution():
26902734
logger.debug('Private module export name substitution with ' + shared.Settings.EXPORT_NAME)
26912735
src = open(final).read()
26922736
final = final + '.module_export_name_substitution.js'
2693-
replacement = "typeof %(EXPORT_NAME)s !== 'undefined' ? %(EXPORT_NAME)s : {}" % {"EXPORT_NAME": shared.Settings.EXPORT_NAME}
2737+
if shared.Settings.MINIMAL_RUNTIME:
2738+
# In MINIMAL_RUNTIME the Module object is always present to provide the .asm.js/.wasm content
2739+
replacement = shared.Settings.EXPORT_NAME
2740+
else:
2741+
replacement = "typeof %(EXPORT_NAME)s !== 'undefined' ? %(EXPORT_NAME)s : {}" % {"EXPORT_NAME": shared.Settings.EXPORT_NAME}
26942742
with open(final, 'w') as f:
2695-
f.write(src.replace(shared.JS.module_export_name_substitution_pattern, replacement))
2743+
src = src.replace(shared.JS.module_export_name_substitution_pattern, replacement)
2744+
# For Node.js, create an unminified Module object so that loading external .asm.js file that assigns to Module['asm'] works
2745+
# even when Closure is used.
2746+
if shared.Settings.MINIMAL_RUNTIME and shared.Settings.target_environment_may_be('node'):
2747+
src = 'if(typeof process!=="undefined"){var Module={};}' + src
2748+
f.write(src)
26962749
save_intermediate('module_export_name_substitution')
26972750

26982751

2752+
def generate_minimal_runtime_html(target, options, js_target, target_basename,
2753+
asm_target, wasm_binary_target,
2754+
memfile, optimizer):
2755+
logger.debug('generating HTML for minimal runtime')
2756+
shell = read_and_preprocess(options.shell_path)
2757+
html_contents = shell.replace('{{{ TARGET_BASENAME }}}', target_basename)
2758+
html_contents = tools.line_endings.convert_line_endings(html_contents, '\n', options.output_eol)
2759+
with open(target, 'wb') as f:
2760+
f.write(asbytes(html_contents))
2761+
2762+
26992763
def generate_html(target, options, js_target, target_basename,
27002764
asm_target, wasm_binary_target,
27012765
memfile, optimizer):
2766+
if shared.Settings.MINIMAL_RUNTIME:
2767+
return generate_minimal_runtime_html(target, options, js_target, target_basename, asm_target,
2768+
wasm_binary_target, memfile, optimizer)
2769+
27022770
script = ScriptSource()
27032771

27042772
logger.debug('generating HTML')

0 commit comments

Comments
 (0)