summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc109
-rw-r--r--tests/cases/issue_39.ll (renamed from tests/issue_39.ll)0
-rw-r--r--tests/cases/issue_39.txt1
-rw-r--r--tests/hello_world_error.c8
-rw-r--r--tests/hello_world_error.cpp8
-rw-r--r--tests/runner.py877
-rw-r--r--tools/js-optimizer.js146
-rw-r--r--tools/shared.py79
-rw-r--r--tools/test-js-optimizer-output.js80
-rw-r--r--tools/test-js-optimizer.js96
10 files changed, 915 insertions, 489 deletions
diff --git a/emcc b/emcc
index e29946cf..6e218f4b 100755
--- a/emcc
+++ b/emcc
@@ -79,6 +79,11 @@ from tools import shared
DEBUG = os.environ.get('EMCC_DEBUG')
TEMP_DIR = os.environ.get('EMCC_TEMP_DIR')
+LEAVE_INPUTS_RAW = os.environ.get('EMCC_LEAVE_INPUTS_RAW') # Do not compile .ll files into .bc, just compile them with emscripten directly
+ # Not recommended, this is mainly for the test runner, or if you have some other
+ # specific need.
+ # One major limitation with this mode is that dlmalloc will not be added in.
+ # LLVM optimizations will also not be done.
if DEBUG: print >> sys.stderr, 'emcc: ', ' '.join(sys.argv)
@@ -338,18 +343,7 @@ try:
final_suffix = target.split('.')[-1]
# Apply optimization level settings
- if opt_level >= 1:
- shared.Settings.ASSERTIONS = 0
- shared.Settings.DISABLE_EXCEPTION_CATCHING = 1
- if opt_level >= 2:
- shared.Settings.RELOOP = 1
- if opt_level >= 3:
- shared.Settings.CORRECT_SIGNS = 0
- shared.Settings.CORRECT_OVERFLOWS = 0
- shared.Settings.CORRECT_ROUNDINGS = 0
- shared.Settings.I64_MODE = 0
- shared.Settings.DOUBLE_MODE = 0
- print >> sys.stderr, 'Warning: Applying some potentially unsafe optimizations! (Use -O2 if this fails.)'
+ shared.Settings.apply_opt_level(opt_level, noisy=True)
# Apply -s settings in newargs here (after optimization levels, so they can override them)
for change in settings_changes:
@@ -358,11 +352,12 @@ try:
## Compile source code to bitcode
- if DEBUG: print >> sys.stderr, 'emcc: compiling to bitcode (%s)' % str(sys.argv)
+ if DEBUG: print >> sys.stderr, 'emcc: compiling to bitcode'
# First, generate LLVM bitcode. For each input file, we get base.o with bitcode
for input_file in input_files:
if input_file.endswith(SOURCE_SUFFIXES):
+ if DEBUG: print >> sys.stderr, 'emcc: compiling source file: ', input_file
output_file = in_temp(unsuffixed_basename(input_file) + '.o')
args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file]
if DEBUG: print >> sys.stderr, "emcc running:", call, ' '.join(args)
@@ -372,12 +367,15 @@ try:
sys.exit(1)
else: # bitcode
if input_file.endswith(('.bc', '.o')):
+ if DEBUG: print >> sys.stderr, 'emcc: copying bitcode file: ', input_file
shutil.copyfile(input_file, in_temp(unsuffixed_basename(input_file) + '.o'))
else: #.ll
- shared.Building.llvm_as(input_file, in_temp(unsuffixed_basename(input_file) + '.o'))
+ if not LEAVE_INPUTS_RAW:
+ if DEBUG: print >> sys.stderr, 'emcc: assembling assembly file: ', input_file
+ shared.Building.llvm_as(input_file, in_temp(unsuffixed_basename(input_file) + '.o'))
# Optimize, if asked to
- if llvm_opt_level > 0:
+ if llvm_opt_level > 0 and not LEAVE_INPUTS_RAW:
if DEBUG: print >> sys.stderr, 'emcc: LLVM opts'
for input_file in input_files:
try:
@@ -411,53 +409,70 @@ try:
extra_files_to_link = []
- # Check if we need to include dlmalloc. Note that we assume a single symbol is enough to know if we have/do not have dlmalloc. If you
- # include just a few symbols but want the rest, this will not work.
- need_dlmalloc = False
- has_dlmalloc = False
- for input_file in input_files:
- symbols = shared.Building.llvm_nm(in_temp(unsuffixed_basename(input_file) + '.o'))
- for malloc_def in ['malloc', 'free', 'calloc', 'memalign', 'realloc', 'valloc', 'pvalloc', 'mallinfo', 'mallopt', 'malloc_trim', 'malloc_stats', 'malloc_usable_size', 'malloc_footprint', 'malloc_max_footprint', 'independent_calloc', 'independent_comalloc']:
- if malloc_def in symbols.undefs:
- need_dlmalloc = True
- if malloc_def in symbols.defs:
- has_dlmalloc = True
- if need_dlmalloc and not has_dlmalloc:
- # We need to build and link dlmalloc in
- if DEBUG: print >> sys.stderr, 'emcc: including dlmalloc'
- Popen([shared.EMCC, shared.path_from_root('src', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=PIPE, stderr=PIPE).communicate()
- if llvm_opt_level > 0:
- shared.Building.llvm_opt(in_temp('dlmalloc.o'), LLVM_INTERNAL_OPT_LEVEL, safe=llvm_opt_level < 2)
- extra_files_to_link.append(in_temp('dlmalloc.o'))
-
- # dlmalloc needs some sign correction. # If we are in mode 0, switch to 2. We will add our lines
- try:
- if shared.Settings.CORRECT_SIGNS == 0: raise Exception('we need to change to 2')
- except: # we fail if equal to 0 - so we need to switch to 2 - or if CORRECT_SIGNS is not even in Settings
- shared.Settings.CORRECT_SIGNS = 2
- if shared.Settings.CORRECT_SIGNS == 2:
- shared.Settings.CORRECT_SIGNS_LINES = [shared.path_from_root('src', 'dlmalloc.c') + ':' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]]
- # If we are in mode 1, we are correcting everything anyhow. If we are in mode 3, we will be corrected
- # so all is well anyhow too.
+ if not LEAVE_INPUTS_RAW:
+ # Check if we need to include dlmalloc. Note that we assume a single symbol is enough to know if we have/do not have dlmalloc. If you
+ # include just a few symbols but want the rest, this will not work.
+ need_dlmalloc = False
+ has_dlmalloc = False
+ for input_file in input_files:
+ symbols = shared.Building.llvm_nm(in_temp(unsuffixed_basename(input_file) + '.o'))
+ for malloc_def in ['malloc', 'free', 'calloc', 'memalign', 'realloc', 'valloc', 'pvalloc', 'mallinfo', 'mallopt', 'malloc_trim', 'malloc_stats', 'malloc_usable_size', 'malloc_footprint', 'malloc_max_footprint', 'independent_calloc', 'independent_comalloc']:
+ if malloc_def in symbols.undefs:
+ need_dlmalloc = True
+ if malloc_def in symbols.defs:
+ has_dlmalloc = True
+ if need_dlmalloc and not has_dlmalloc:
+ # We need to build and link dlmalloc in
+ if DEBUG: print >> sys.stderr, 'emcc: including dlmalloc'
+ Popen([shared.EMCC, shared.path_from_root('src', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=PIPE, stderr=PIPE).communicate()
+ if llvm_opt_level > 0:
+ shared.Building.llvm_opt(in_temp('dlmalloc.o'), LLVM_INTERNAL_OPT_LEVEL, safe=llvm_opt_level < 2)
+ extra_files_to_link.append(in_temp('dlmalloc.o'))
+
+ # dlmalloc needs some sign correction. # If we are in mode 0, switch to 2. We will add our lines
+ try:
+ if shared.Settings.CORRECT_SIGNS == 0: raise Exception('we need to change to 2')
+ except: # we fail if equal to 0 - so we need to switch to 2 - or if CORRECT_SIGNS is not even in Settings
+ shared.Settings.CORRECT_SIGNS = 2
+ if shared.Settings.CORRECT_SIGNS == 2:
+ shared.Settings.CORRECT_SIGNS_LINES = [shared.path_from_root('src', 'dlmalloc.c') + ':' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]]
+ # If we are in mode 1, we are correcting everything anyhow. If we are in mode 3, we will be corrected
+ # so all is well anyhow too.
# First, combine the bitcode files if there are several
if len(input_files) + len(extra_files_to_link) > 1:
- shared.Building.link(map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files) + extra_files_to_link,
+ linker_inputs = map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files) + extra_files_to_link
+ if DEBUG: print >> sys.stderr, 'emcc: linking: ', linker_inputs
+ shared.Building.link(linker_inputs,
in_temp(target_basename + '.bc'))
# TODO: LLVM link-time opts? here and/or elsewhere?
else:
- shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc'))
+ if not LEAVE_INPUTS_RAW:
+ shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc'))
# Emscripten
if opt_level >= 2:
print >> sys.stderr, 'Warning: The relooper optimization can be very slow.'
- final = shared.Building.emscripten(in_temp(target_basename + '.bc'), append_ext=False)
+ if not LEAVE_INPUTS_RAW:
+ emscripten_input = in_temp(target_basename + '.bc')
+ else:
+ assert len(input_files) == 1
+ emscripten_input = input_files[0]
+ final = shared.Building.emscripten(emscripten_input, append_ext=False)
+
+ # Apply a source code transformation, if requested
+ source_transform = os.environ.get('EMCC_JS_PROCESSOR')
+ if source_transform:
+ exec source_transform in locals()
+ shutil.copyfile(final, final + '.tr.js')
+ final += '.tr.js'
+ process(final)
if opt_level >= 1:
# js optimizer
if DEBUG: print >> sys.stderr, 'emcc: running pre-closure post-opts'
- final = shared.Building.js_optimizer(final, 'loopOptimizer')
+ final = shared.Building.js_optimizer(final, ['hoistMultiples', 'loopOptimizer'])
# eliminator
final = shared.Building.eliminator(final)
diff --git a/tests/issue_39.ll b/tests/cases/issue_39.ll
index 96460693..96460693 100644
--- a/tests/issue_39.ll
+++ b/tests/cases/issue_39.ll
diff --git a/tests/cases/issue_39.txt b/tests/cases/issue_39.txt
new file mode 100644
index 00000000..3b85d897
--- /dev/null
+++ b/tests/cases/issue_39.txt
@@ -0,0 +1 @@
+*yes*
diff --git a/tests/hello_world_error.c b/tests/hello_world_error.c
new file mode 100644
index 00000000..c76b5baf
--- /dev/null
+++ b/tests/hello_world_error.c
@@ -0,0 +1,8 @@
+#in clu de <st dio.h>
+
+int main() {
+ cheez burger
+ printf("hello, world!\n");
+ return 1;
+}
+
diff --git a/tests/hello_world_error.cpp b/tests/hello_world_error.cpp
new file mode 100644
index 00000000..c76b5baf
--- /dev/null
+++ b/tests/hello_world_error.cpp
@@ -0,0 +1,8 @@
+#in clu de <st dio.h>
+
+int main() {
+ cheez burger
+ printf("hello, world!\n");
+ return 1;
+}
+
diff --git a/tests/runner.py b/tests/runner.py
index 49339f51..ae72ec49 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -21,6 +21,8 @@ def path_from_root(*pathelems):
return os.path.join(__rootpath__, *pathelems)
exec(open(path_from_root('tools', 'shared.py'), 'r').read())
+sys.path += [path_from_root('')]
+
# Sanity check for config
try:
@@ -90,8 +92,29 @@ class RunnerCore(unittest.TestCase):
shutil.move(filename + '.o.ll', filename + '.o.ll.post') # for comparisons later
Building.llvm_dis(filename)
+ # Generate JS from ll, and optionally modify the generated JS with a post_build function. Note
+ # that post_build is called on unoptimized JS, so we send it to emcc (otherwise, if run after
+ # emcc, it would not apply on the optimized/minified JS)
+ def ll_to_js(self, filename, extra_emscripten_args, post_build):
+ if self.emcc_args is None:
+ Building.emscripten(filename, append_ext=True, extra_args=extra_emscripten_args)
+ if post_build is not None:
+ exec post_build in locals()
+ shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js')
+ process(filename + '.o.js')
+ else:
+ if post_build is not None:
+ os.environ['EMCC_JS_PROCESSOR'] = post_build
+ else:
+ try:
+ del os.environ['EMCC_JS_PROCESSOR']
+ except:
+ pass
+ Building.emcc(filename + '.o.ll', Settings.serialize() + self.emcc_args, filename + '.o.js')
+
# Build JavaScript code from source code
- def build(self, src, dirname, filename, output_processor=None, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, extra_emscripten_args=[]):
+ def build(self, src, dirname, filename, output_processor=None, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, extra_emscripten_args=[], post_build=None):
+
# Copy over necessary files for compiling the source
if main_file is None:
f = open(filename, 'w')
@@ -138,7 +161,11 @@ class RunnerCore(unittest.TestCase):
# Finalize
self.prep_ll_run(filename, filename + '.o', build_ll_hook=build_ll_hook)
- Building.emscripten(filename, output_processor, extra_args=extra_emscripten_args)
+ # BC => JS
+ self.ll_to_js(filename, extra_emscripten_args, post_build)
+
+ if output_processor is not None:
+ output_processor(open(filename + '.o.js').read())
def run_generated_code(self, engine, filename, args=[], check_timeout=True):
stdout = os.path.join(self.get_dir(), 'stdout') # use files, as PIPE can get too full and hang us
@@ -233,10 +260,7 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
filename = os.path.join(dirname, basename)
if not no_build:
self.build(src, dirname, filename, main_file=main_file, additional_files=additional_files, libraries=libraries, includes=includes,
- build_ll_hook=build_ll_hook, extra_emscripten_args=extra_emscripten_args)
-
- if post_build is not None:
- post_build(filename + '.o.js')
+ build_ll_hook=build_ll_hook, extra_emscripten_args=extra_emscripten_args, post_build=post_build)
# If not provided with expected output, then generate it right now, using lli
if expected_output is None:
@@ -261,18 +285,19 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
# No building - just process an existing .ll file (or .bc, which we turn into .ll)
def do_ll_run(self, ll_file, expected_output=None, args=[], js_engines=None, output_nicerizer=None, post_build=None, force_recompile=False, build_ll_hook=None, extra_emscripten_args=[]):
-
filename = os.path.join(self.get_dir(), 'src.cpp')
self.prep_ll_run(filename, ll_file, force_recompile, build_ll_hook)
- Building.emscripten(filename, extra_args=extra_emscripten_args)
+
+ self.ll_to_js(filename, extra_emscripten_args, post_build)
+
self.do_run(None,
expected_output,
args,
no_build=True,
js_engines=js_engines,
output_nicerizer=output_nicerizer,
- post_build=post_build)
+ post_build=None) # post_build was already done in ll_to_js, this do_run call is just to test the output
def test_hello_world(self):
src = '''
@@ -782,10 +807,6 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
'''
self.do_run(src, '*yes*')
- # Test for issue 39
- if not Building.LLVM_OPTS:
- self.do_ll_run(path_from_root('tests', 'issue_39.ll'), '*yes*')
-
def test_if_else(self):
src = '''
#include <stdio.h>
@@ -1624,9 +1645,11 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
}
'''
- def check(filename):
- src = open(filename, 'r').read()
- # TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src
+ check = '''
+def process(filename):
+ src = open(filename, 'r').read()
+ # TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src
+'''
self.do_run(src, 'hello world!\n*100*', post_build=check)
@@ -2406,14 +2429,16 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
}
'''
Settings.BUILD_AS_SHARED_LIB = 0
- def add_pre_run_and_checks(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);'''
- )
- open(filename, 'w').write(src)
+ add_pre_run_and_checks = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
+ )
+ open(filename, 'w').write(src)
+'''
self.do_run(src, 'Constructing main object.\nConstructing lib object.\n',
- post_build=add_pre_run_and_checks)
+ post_build=add_pre_run_and_checks)
def test_dlfcn_qsort(self):
if Settings.USE_TYPED_ARRAYS == 2:
@@ -2493,15 +2518,17 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
'''
Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
- def add_pre_run_and_checks(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);'''
- )
- open(filename, 'w').write(src)
+ add_pre_run_and_checks = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
+ )
+ open(filename, 'w').write(src)
+'''
self.do_run(src, 'Sort with main comparison: 5 4 3 2 1 *Sort with lib comparison: 1 2 3 4 5 *',
- output_nicerizer=lambda x: x.replace('\n', '*'),
- post_build=add_pre_run_and_checks)
+ output_nicerizer=lambda x: x.replace('\n', '*'),
+ post_build=add_pre_run_and_checks)
def test_dlfcn_data_and_fptr(self):
if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func')
@@ -2591,12 +2618,14 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
Settings.EXPORTED_GLOBALS = []
- def add_pre_run_and_checks(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);'''
- )
- open(filename, 'w').write(src)
+ add_pre_run_and_checks = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
+ )
+ open(filename, 'w').write(src)
+'''
self.do_run(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*',
output_nicerizer=lambda x: x.replace('\n', '*'),
post_build=add_pre_run_and_checks)
@@ -2639,16 +2668,18 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
Settings.BUILD_AS_SHARED_LIB = 0
Settings.INCLUDE_FULL_LIBRARY = 1
Settings.EXPORTED_FUNCTIONS = ['_main']
- def add_pre_run_and_checks(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);'''
- )
- open(filename, 'w').write(src)
+ add_pre_run_and_checks = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
+ )
+ open(filename, 'w').write(src)
+'''
self.do_run(src, 'Parent global: 123.*Parent global: 456.*',
- output_nicerizer=lambda x: x.replace('\n', '*'),
- post_build=add_pre_run_and_checks,
- extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h'])
+ output_nicerizer=lambda x: x.replace('\n', '*'),
+ post_build=add_pre_run_and_checks,
+ extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h'])
Settings.INCLUDE_FULL_LIBRARY = 0
def test_dlfcn_varargs(self):
@@ -2697,14 +2728,16 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
'''
Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
- def add_pre_run_and_checks(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);'''
- )
- open(filename, 'w').write(src)
+ add_pre_run_and_checks = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
+ )
+ open(filename, 'w').write(src)
+'''
self.do_run(src, '100\n200\n13\n42\n',
- post_build=add_pre_run_and_checks)
+ post_build=add_pre_run_and_checks)
def test_rand(self):
src = r'''
@@ -2975,23 +3008,27 @@ at function.:blag
self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/langinfo.h'])
def test_files(self):
- Settings.CORRECT_SIGNS = 1 # Just so our output is what we expect. Can flip them both.
- def post(filename):
- src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours
- '// {{PRE_RUN_ADDITIONS}}',
- '''
- FS.createDataFile('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory
- FS.createLazyFile('/', 'test.file', 'test.file', true, false);
- FS.root.write = true;
- var test_files_input = 'hi there!';
- var test_files_input_index = 0;
- FS.init(function() {
- return test_files_input.charCodeAt(test_files_input_index++) || null;
- });
- '''
- )
- open(filename, 'w').write(src)
+ if self.emcc_args is not None and '-O2' in self.emcc_args:
+ self.emcc_args += ['--closure', '1'] # Use closure here, to test we don't break FS stuff
+ Settings.CORRECT_SIGNS = 1 # Just so our output is what we expect. Can flip them both.
+ post = '''
+def process(filename):
+ src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours
+ '// {{PRE_RUN_ADDITIONS}}',
+ \'\'\'
+ FS.createDataFile('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory
+ FS.createLazyFile('/', 'test.file', 'test.file', true, false);
+ FS.root.write = true;
+ var test_files_input = 'hi there!';
+ var test_files_input_index = 0;
+ FS.init(function() {
+ return test_files_input.charCodeAt(test_files_input_index++) || null;
+ });
+ \'\'\'
+ )
+ open(filename, 'w').write(src)
+'''
other = open(os.path.join(self.get_dir(), 'test.file'), 'w')
other.write('some data');
other.close()
@@ -3001,20 +3038,21 @@ at function.:blag
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_folders(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''
- FS.createFolder('/', 'test', true, false);
- FS.createPath('/', 'test/hello/world/', true, false);
- FS.createPath('/test', 'goodbye/world/', true, false);
- FS.createPath('/test/goodbye', 'noentry', false, false);
- FS.createDataFile('/test', 'freeforall.ext', 'abc', true, true);
- FS.createDataFile('/test', 'restricted.ext', 'def', false, false);
- '''
- )
- open(filename, 'w').write(src)
-
+ add_pre_run = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ \'\'\'
+ FS.createFolder('/', 'test', true, false);
+ FS.createPath('/', 'test/hello/world/', true, false);
+ FS.createPath('/test', 'goodbye/world/', true, false);
+ FS.createPath('/test/goodbye', 'noentry', false, false);
+ FS.createDataFile('/test', 'freeforall.ext', 'abc', true, true);
+ FS.createDataFile('/test', 'restricted.ext', 'def', false, false);
+ \'\'\'
+ )
+ open(filename, 'w').write(src)
+'''
src = r'''
#include <stdio.h>
#include <dirent.h>
@@ -3079,69 +3117,79 @@ at function.:blag
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected), post_build=add_pre_run)
def test_stat(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''
- var f1 = FS.createFolder('/', 'test', true, true);
- var f2 = FS.createDataFile(f1, 'file', 'abcdef', true, true);
- var f3 = FS.createLink(f1, 'link', 'file', true, true);
- var f4 = FS.createDevice(f1, 'device', function(){}, function(){});
- f1.timestamp = f2.timestamp = f3.timestamp = f4.timestamp = new Date(1200000000000);
- '''
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ \'\'\'
+ var f1 = FS.createFolder('/', 'test', true, true);
+ var f2 = FS.createDataFile(f1, 'file', 'abcdef', true, true);
+ var f3 = FS.createLink(f1, 'link', 'file', true, true);
+ var f4 = FS.createDevice(f1, 'device', function(){}, function(){});
+ f1.timestamp = f2.timestamp = f3.timestamp = f4.timestamp = new Date(1200000000000);
+ \'\'\'
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'stat', 'src.c'), 'r').read()
expected = open(path_from_root('tests', 'stat', 'output.txt'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_fcntl(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createDataFile('/', 'test', 'abcdef', true, true);"
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createDataFile('/', 'test', 'abcdef', true, true);"
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'fcntl', 'src.c'), 'r').read()
expected = open(path_from_root('tests', 'fcntl', 'output.txt'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_fcntl_open(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''
- FS.createDataFile('/', 'test-file', 'abcdef', true, true);
- FS.createFolder('/', 'test-folder', true, true);
- FS.root.write = true;
- '''
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ \'\'\'
+ FS.createDataFile('/', 'test-file', 'abcdef', true, true);
+ FS.createFolder('/', 'test-folder', true, true);
+ FS.root.write = true;
+ \'\'\'
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'fcntl-open', 'src.c'), 'r').read()
expected = open(path_from_root('tests', 'fcntl-open', 'output.txt'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_fcntl_misc(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createDataFile('/', 'test', 'abcdef', true, true);"
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createDataFile('/', 'test', 'abcdef', true, true);"
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'fcntl-misc', 'src.c'), 'r').read()
expected = open(path_from_root('tests', 'fcntl-misc', 'output.txt'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_poll(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''
- FS.createDataFile('/', 'file', 'abcdef', true, true);
- FS.createDevice('/', 'device', function() {}, function() {});
- '''
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ \'\'\'
+ FS.createDataFile('/', 'file', 'abcdef', true, true);
+ FS.createDevice('/', 'device', function() {}, function() {});
+ \'\'\'
+ )
+ open(filename, 'w').write(src)
+'''
src = r'''
#include <stdio.h>
#include <errno.h>
@@ -3283,22 +3331,23 @@ at function.:blag
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
def test_utime(self):
- def add_pre_run_and_checks(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''
- var TEST_F1 = FS.createFolder('/', 'writeable', true, true);
- var TEST_F2 = FS.createFolder('/', 'unwriteable', true, false);
- '''
- ).replace(
- '// {{POST_RUN_ADDITIONS}}',
- '''
- print('first changed: ' + (TEST_F1.timestamp == 1200000000000));
- print('second changed: ' + (TEST_F2.timestamp == 1200000000000));
- '''
- )
- open(filename, 'w').write(src)
-
+ add_pre_run_and_checks = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ \'\'\'
+ var TEST_F1 = FS.createFolder('/', 'writeable', true, true);
+ var TEST_F2 = FS.createFolder('/', 'unwriteable', true, false);
+ \'\'\'
+ ).replace(
+ '// {{POST_RUN_ADDITIONS}}',
+ \'\'\'
+ print('first changed: ' + (TEST_F1.timestamp == 1200000000000));
+ print('second changed: ' + (TEST_F2.timestamp == 1200000000000));
+ \'\'\'
+ )
+ open(filename, 'w').write(src)
+'''
src = r'''
#include <stdio.h>
#include <errno.h>
@@ -3329,11 +3378,14 @@ at function.:blag
def test_fs_base(self):
Settings.INCLUDE_FULL_LIBRARY = 1
try:
- def addJS(filename):
- src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours
- '// {{PRE_RUN_ADDITIONS}}',
- open(path_from_root('tests', 'filesystem', 'src.js'), 'r').read())
- open(filename, 'w').write(src)
+ addJS = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace('FS.init();', '').replace( # Disable normal initialization, replace with ours
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'filesystem', 'src.js'), 'r').read())
+ open(filename, 'w').write(src)
+'''
src = 'int main() {return 0;}\n'
expected = open(path_from_root('tests', 'filesystem', 'output.txt'), 'r').read()
self.do_run(src, expected, post_build=addJS, extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h'])
@@ -3341,23 +3393,29 @@ at function.:blag
Settings.INCLUDE_FULL_LIBRARY = 0
def test_unistd_access(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(path_from_root('tests', 'unistd', 'access.js'), 'r').read()
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'access.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
def test_unistd_curdir(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(path_from_root('tests', 'unistd', 'curdir.js'), 'r').read()
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'curdir.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'unistd', 'curdir.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'curdir.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
@@ -3373,12 +3431,15 @@ at function.:blag
self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/unistd.h'])
def test_unistd_ttyname(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(path_from_root('tests', 'unistd', 'ttyname.js'), 'r').read()
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'ttyname.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'unistd', 'ttyname.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'ttyname.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
@@ -3394,12 +3455,15 @@ at function.:blag
self.do_run(src, expected)
def test_unistd_truncate(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(path_from_root('tests', 'unistd', 'truncate.js'), 'r').read()
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'truncate.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
@@ -3410,12 +3474,15 @@ at function.:blag
self.do_run(src, expected)
def test_unistd_isatty(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(path_from_root('tests', 'unistd', 'isatty.js'), 'r').read()
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'isatty.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'unistd', 'isatty.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'isatty.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
@@ -3431,23 +3498,29 @@ at function.:blag
self.do_run(src, expected)
def test_unistd_unlink(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(path_from_root('tests', 'unistd', 'unlink.js'), 'r').read()
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'unlink.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'unlink.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
def test_unistd_links(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(path_from_root('tests', 'unistd', 'links.js'), 'r').read()
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'links.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
@@ -3458,12 +3531,15 @@ at function.:blag
self.do_run(src, expected)
def test_unistd_io(self):
- def add_pre_run(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(path_from_root('tests', 'unistd', 'io.js'), 'r').read()
- )
- open(filename, 'w').write(src)
+ add_pre_run = '''
+def process(filename):
+ import tools.shared as shared
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ open(shared.path_from_root('tests', 'unistd', 'io.js'), 'r').read()
+ )
+ open(filename, 'w').write(src)
+'''
src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read()
self.do_run(src, expected, post_build=add_pre_run)
@@ -3625,7 +3701,7 @@ at function.:blag
self.do_run(src, '*1,0*', ['200', '1'], extra_emscripten_args=['-m'])
self.do_run(src, '*400,0*', ['400', '400'], extra_emscripten_args=['-m'], no_build=True)
- if self.use_defaults: # TODO: do this in other passes too, passing their opts into emcc
+ if self.emcc_args == []: # TODO: do this in other passes too, passing their opts into emcc
# emcc should build in dlmalloc automatically, and do all the sign correction etc. for it
try_delete(os.path.join(self.get_dir(), 'src.cpp.o.js'))
@@ -3683,20 +3759,25 @@ at function.:blag
# print opt, "FAIL"
def test_lua(self):
- if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work')
-
- # Overflows in luaS_newlstr hash loop
- Settings.SAFE_HEAP = 0 # Has various warnings, with copied HEAP_HISTORY values (fixed if we copy 'null' as the type)
- Settings.CORRECT_OVERFLOWS = 1
- Settings.CHECK_OVERFLOWS = 0
- Settings.CORRECT_SIGNS = 1 # Not sure why, but needed
- Settings.INIT_STACK = 1 # TODO: Investigate why this is necessary
-
- self.do_ll_run(path_from_root('tests', 'lua', 'lua.ll'),
- 'hello lua world!\n17\n1\n2\n3\n4\n7',
- args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''],
- output_nicerizer=lambda string: string.replace('\n\n', '\n').replace('\n\n', '\n'),
- extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h'])
+ try:
+ os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1'
+
+ if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work')
+
+ # Overflows in luaS_newlstr hash loop
+ Settings.SAFE_HEAP = 0 # Has various warnings, with copied HEAP_HISTORY values (fixed if we copy 'null' as the type)
+ Settings.CORRECT_OVERFLOWS = 1
+ Settings.CHECK_OVERFLOWS = 0
+ Settings.CORRECT_SIGNS = 1 # Not sure why, but needed
+ Settings.INIT_STACK = 1 # TODO: Investigate why this is necessary
+
+ self.do_ll_run(path_from_root('tests', 'lua', 'lua.ll'),
+ 'hello lua world!\n17\n1\n2\n3\n4\n7',
+ args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''],
+ output_nicerizer=lambda string: string.replace('\n\n', '\n').replace('\n\n', '\n'),
+ extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h'])
+ finally:
+ del os.environ['EMCC_LEAVE_INPUTS_RAW']
def get_build_dir(self):
return os.path.join(self.get_dir(), 'building')
@@ -3713,16 +3794,18 @@ at function.:blag
if Settings.CORRECT_SIGNS == 0: Settings.CORRECT_SIGNS = 1 # Not sure why, but needed
- def post(filename):
- # Embed the font into the document
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''FS.createDataFile('/', 'font.ttf', %s, true, false);''' % str(
- map(ord, open(path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), 'rb').read())
- )
- )
- open(filename, 'w').write(src)
-
+ post = '''
+def process(filename):
+ import tools.shared as shared
+ # Embed the font into the document
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createDataFile('/', 'font.ttf', %s, true, false);" % str(
+ map(ord, open(shared.path_from_root('tests', 'freetype', 'LiberationSansBold.ttf'), 'rb').read())
+ )
+ )
+ open(filename, 'w').write(src)
+'''
# Main
self.do_run(open(path_from_root('tests', 'freetype', 'main.c'), 'r').read(),
open(path_from_root('tests', 'freetype', 'ref.txt'), 'r').read(),
@@ -3739,7 +3822,7 @@ at function.:blag
pgo_data = read_pgo_data(path_from_root('tests', 'sqlite', 'sqlite-autooptimize.fails.txt'))
- Settings.CORRECT_SIGNS = 2
+ Settings.CORRECT_SIGNS = 1 # XXX: in default, we fail with 2 here, even though the pgo_data should be correct (and works in s_0_0). Investigate this.
Settings.CORRECT_SIGNS_LINES = pgo_data['signs_lines']
Settings.CORRECT_OVERFLOWS = 0
Settings.CORRECT_ROUNDINGS = 0
@@ -3750,18 +3833,20 @@ at function.:blag
Settings.INVOKE_RUN = 0 # We append code that does run() ourselves
- def post(filename):
- src = open(filename, 'a')
- src.write('''
- FS.init();
- FS.root.write = true;
- FS.ignorePermissions = true; // /dev is read-only
- FS.createPath('/', 'dev/shm/tmp', true, true);
- FS.ignorePermissions = false;
- FS.currentPath = '/dev/shm/tmp';
- run();
- ''')
- src.close()
+ post = '''
+def process(filename):
+ src = open(filename, 'a')
+ src.write(\'\'\'
+ FS.init();
+ FS.root.write = true;
+ FS.ignorePermissions = true; // /dev is read-only
+ FS.createPath('/', 'dev/shm/tmp', true, true);
+ FS.ignorePermissions = false;
+ FS.currentPath = '/dev/shm/tmp';
+ run();
+ \'\'\')
+ src.close()
+'''
self.do_run(r'''
#define SQLITE_DISABLE_LFS
@@ -3807,7 +3892,7 @@ at function.:blag
js_engines=[SPIDERMONKEY_ENGINE]) # V8 issue 1407
def test_poppler(self):
- if not self.use_defaults: return self.skip('very slow, we only do this in default')
+ if not self.emcc_args == []: return self.skip('very slow, we only do this in default')
Settings.SAFE_HEAP = 0 # Has variable object
@@ -3827,20 +3912,22 @@ at function.:blag
input_file.write(str(map(ord, open(path_from_root('tests', 'poppler', 'paper.pdf'), 'rb').read())))
input_file.close()
- def post(filename):
- # To avoid loading this large file to memory and altering it, we simply append to the end
- src = open(filename, 'a')
- src.write(
- '''
- FS.ignorePermissions = true;
- FS.createDataFile('/', 'paper.pdf', eval(read('paper.pdf.js')), true, false);
- FS.root.write = true;
- FS.ignorePermissions = false;
- run();
- print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) })));
- '''
- )
- src.close()
+ post = '''
+def process(filename):
+ # To avoid loading this large file to memory and altering it, we simply append to the end
+ src = open(filename, 'a')
+ src.write(
+ \'\'\'
+ FS.ignorePermissions = true;
+ FS.createDataFile('/', 'paper.pdf', eval(read('paper.pdf.js')), true, false);
+ FS.root.write = true;
+ FS.ignorePermissions = false;
+ run();
+ print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) })));
+ \'\'\'
+ )
+ src.close()
+'''
#fontconfig = self.get_library('fontconfig', [os.path.join('src', '.libs', 'libfontconfig.a')]) # Used in file, but not needed, mostly
@@ -3873,19 +3960,21 @@ at function.:blag
Settings.CORRECT_SIGNS = 2
Settings.CORRECT_SIGNS_LINES = ["mqc.c:566", "mqc.c:317"]
- original_j2k = path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.j2k')
-
- def post(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''FS.createDataFile('/', 'image.j2k', %s, true, false);FS.root.write = true;''' % line_splitter(str(
- map(ord, open(original_j2k, 'rb').read())
- ))
- ).replace(
- '// {{POST_RUN_ADDITIONS}}',
- '''print("Data: " + JSON.stringify(FS.root.contents['image.raw'].contents));'''
- )
- open(filename, 'w').write(src)
+ post = '''
+def process(filename):
+ import tools.shared as shared
+ original_j2k = shared.path_from_root('tests', 'openjpeg', 'syntensity_lobby_s.j2k')
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createDataFile('/', 'image.j2k', %s, true, false);FS.root.write = true;" % shared.line_splitter(str(
+ map(ord, open(original_j2k, 'rb').read())
+ ))
+ ).replace(
+ '// {{POST_RUN_ADDITIONS}}',
+ "print('Data: ' + JSON.stringify(FS.root.contents['image.raw'].contents));"
+ )
+ open(filename, 'w').write(src)
+'''
shutil.copy(path_from_root('tests', 'openjpeg', 'opj_config.h'), self.get_dir())
@@ -3967,31 +4056,37 @@ at function.:blag
# They are only valid enough for us to read for test purposes, not for llvm-as
# to process.
def test_cases(self):
- self.banned_js_engines = [NODE_JS] # node issue 1669, exception causes stdout not to be flushed
+ try:
+ self.banned_js_engines = [NODE_JS] # node issue 1669, exception causes stdout not to be flushed
- Settings.CHECK_OVERFLOWS = 0
- if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly")
+ os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1'
+
+ Settings.CHECK_OVERFLOWS = 0
+ if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly")
+
+ for name in glob.glob(path_from_root('tests', 'cases', '*.ll')):
+ shortname = name.replace('.ll', '')
+ if '' not in shortname: continue
+ print "Testing case '%s'..." % shortname
+ output_file = path_from_root('tests', 'cases', shortname + '.txt')
+ if Settings.QUANTUM_SIZE == 1:
+ q1_output_file = path_from_root('tests', 'cases', shortname + '_q1.txt')
+ if os.path.exists(q1_output_file):
+ output_file = q1_output_file
+ if os.path.exists(output_file):
+ output = open(output_file, 'r').read()
+ else:
+ output = 'hello, world!'
+ if output.rstrip() != 'skip':
+ self.do_ll_run(path_from_root('tests', 'cases', name), output)
+ # Optional source checking, a python script that gets a global generated with the source
+ src_checker = path_from_root('tests', 'cases', shortname + '.py')
+ if os.path.exists(src_checker):
+ generated = open('src.cpp.o.js').read()
+ exec(open(src_checker).read())
- for name in glob.glob(path_from_root('tests', 'cases', '*.ll')):
- shortname = name.replace('.ll', '')
- if '' not in shortname: continue
- print "Testing case '%s'..." % shortname
- output_file = path_from_root('tests', 'cases', shortname + '.txt')
- if Settings.QUANTUM_SIZE == 1:
- q1_output_file = path_from_root('tests', 'cases', shortname + '_q1.txt')
- if os.path.exists(q1_output_file):
- output_file = q1_output_file
- if os.path.exists(output_file):
- output = open(output_file, 'r').read()
- else:
- output = 'hello, world!'
- if output.rstrip() != 'skip':
- self.do_ll_run(path_from_root('tests', 'cases', name), output)
- # Optional source checking, a python script that gets a global generated with the source
- src_checker = path_from_root('tests', 'cases', shortname + '.py')
- if os.path.exists(src_checker):
- generated = open('src.cpp.o.js').read()
- exec(open(src_checker).read())
+ finally:
+ del os.environ['EMCC_LEAVE_INPUTS_RAW']
# Autodebug the code
def do_autodebug(self, filename):
@@ -4087,12 +4182,14 @@ at function.:blag
}
'''
- def post1(filename):
- src = open(filename, 'a')
- src.write('''
- Profiling.dump();
- ''')
- src.close()
+ post1 = '''
+def process(filename):
+ src = open(filename, 'a')
+ src.write(\'\'\'
+ Profiling.dump();
+ \'\'\')
+ src.close()
+'''
self.do_run(src, '''Profiling data:
Block 0: ''', post_build=post1)
@@ -4134,16 +4231,18 @@ Block 0: ''', post_build=post1)
}
'''
- def post(filename):
- src = open(filename, 'a')
- src.write('''
- startProfiling();
- run();
- stopProfiling();
- printProfiling();
- print('*ok*');
- ''')
- src.close()
+ post = '''
+def process(filename):
+ src = open(filename, 'a')
+ src.write(\'\'\'
+ startProfiling();
+ run();
+ stopProfiling();
+ printProfiling();
+ print('*ok*');
+ \'\'\')
+ src.close()
+'''
self.do_run(src, ': __Z6inner1i (5000)\n', post_build=post)
@@ -4182,15 +4281,17 @@ Block 0: ''', post_build=post1)
_free(sme);
print('*ok*');
'''
- def post(filename):
- Popen(['python', DEMANGLER, filename], stdout=open(filename + '.tmp', 'w')).communicate()
- Popen(['python', NAMESPACER, filename, filename + '.tmp'], stdout=open(filename + '.tmp2', 'w')).communicate()
- src = open(filename, 'r').read().replace(
- '// {{MODULE_ADDITIONS}',
- 'Module["_"] = ' + open(filename + '.tmp2', 'r').read().replace('var ModuleNames = ', '').rstrip() + ';\n\n' + script_src + '\n\n' +
- '// {{MODULE_ADDITIONS}'
- )
- open(filename, 'w').write(src)
+ post = '''
+def process(filename):
+ Popen(['python', DEMANGLER, filename], stdout=open(filename + '.tmp', 'w')).communicate()
+ Popen(['python', NAMESPACER, filename, filename + '.tmp'], stdout=open(filename + '.tmp2', 'w')).communicate()
+ src = open(filename, 'r').read().replace(
+ '// {{MODULE_ADDITIONS}',
+ 'Module["_"] = ' + open(filename + '.tmp2', 'r').read().replace('var ModuleNames = ', '').rstrip() + ';\n\n' + script_src + '\n\n' +
+ '// {{MODULE_ADDITIONS}'
+ )
+ open(filename, 'w').write(src)
+'''
# XXX disable due to possible v8 bug -- self.do_run(src, '*166*\n*ok*', post_build=post)
# Way 2: use CppHeaderParser
@@ -4247,7 +4348,9 @@ Block 0: ''', post_build=post1)
#include "bindingtest.cpp"
'''
- script_src_2 = '''
+ post2 = '''
+def process(filename):
+ script_src_2 = \'\'\'
var sme = new Parent(42);
sme.mulVal(2);
print('*')
@@ -4329,15 +4432,15 @@ Block 0: ''', post_build=post1)
c2.virtualFunc2();
print('*ok*');
- '''
+ \'\'\'
+ src = open(filename, 'r').read().replace(
+ '// {{MODULE_ADDITIONS}',
+ open('bindingtest.js').read() + '\\n\\n' + script_src_2 + '\\n\\n' +
+ '// {{MODULE_ADDITIONS}'
+ )
+ open(filename, 'w').write(src)
+'''
- def post2(filename):
- src = open(filename, 'r').read().replace(
- '// {{MODULE_ADDITIONS}',
- open(os.path.join(self.get_dir(), 'bindingtest.js')).read() + '\n\n' + script_src_2 + '\n\n' +
- '// {{MODULE_ADDITIONS}'
- )
- open(filename, 'w').write(src)
self.do_run(src, '''*
84
c1
@@ -4404,21 +4507,24 @@ Child2:9
}
'''
- def post(filename):
- src = open(filename, 'r').read().replace(
- '// {{POST_RUN_ADDITIONS}}',
- '''
- if (Runtime.typeInfo) {
- print('|' + Runtime.typeInfo.UserStruct.fields + '|' + Runtime.typeInfo.UserStruct.flatIndexes + '|');
- var t = Runtime.generateStructInfo(['x', { us: ['x', 'y', 'z'] }, 'y'], 'Encloser')
- print('|' + [t.x, t.us.x, t.us.y, t.us.z, t.y] + '|');
- print('|' + JSON.stringify(Runtime.generateStructInfo(null, 'UserStruct')) + '|');
- } else {
- print('No type info.');
- }
- '''
- )
- open(filename, 'w').write(src)
+ post = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{POST_RUN_ADDITIONS}}',
+ \'\'\'
+ if (Runtime.typeInfo) {
+ print('|' + Runtime.typeInfo.UserStruct.fields + '|' + Runtime.typeInfo.UserStruct.flatIndexes + '|');
+ var t = Runtime.generateStructInfo(['x', { us: ['x', 'y', 'z'] }, 'y'], 'Encloser')
+ print('|' + [t.x, t.us.x, t.us.y, t.us.z, t.y] + '|');
+ print('|' + JSON.stringify(Runtime.generateStructInfo(null, 'UserStruct')) + '|');
+ } else {
+ print('No type info.');
+ }
+ \'\'\'
+ )
+ open(filename, 'w').write(src)
+'''
+
self.do_run(src,
'*ok:5*\n|i32,i8,i16|0,4,6|\n|0,4,8,10,12|\n|{"__size__":8,"x":0,"y":4,"z":6}|',
post_build=post)
@@ -4429,47 +4535,6 @@ Child2:9
### Tests for tools
- def test_closure_compiler(self):
- src = '''
- #include<stdio.h>
- int main() {
- printf("*closured*\\n");
-
- FILE *file = fopen("somefile.binary", "rb");
- char buffer[1024];
- size_t read = fread(buffer, 1, 4, file);
- printf("data: %d", buffer[0]);
- for (int i = 1; i < 4; i++)
- printf(",%d", buffer[i]);
- printf("\\n");
-
- return 0;
- }
- '''
-
- def post(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- '''
- FS.createDataFile('/', 'somefile.binary', [100, 1, 50, 25, 10, 77, 123], true, false);
- '''
- )
- open(filename, 'w').write(src)
-
- Popen(['java', '-jar', CLOSURE_COMPILER,
- '--compilation_level', 'ADVANCED_OPTIMIZATIONS',
- '--formatting', 'PRETTY_PRINT',
- '--variable_map_output_file', filename + '.vars',
- '--js', filename, '--js_output_file', filename + '.cc.js'], stdout=PIPE, stderr=STDOUT).communicate()
- assert not re.search('function \w\(', open(filename, 'r').read()) # closure generates this kind of stuff - functions with single letters. Normal doesn't.
- src = open(filename + '.cc.js', 'r').read()
- assert re.search('function \w\(', src) # see before
- assert 'function _main()' not in src # closure should have wiped it out
- shutil.move(filename, filename + '.old.js')
- open(filename, 'w').write(src)
-
- self.do_run(src, '*closured*\ndata: 100,1,50,25\n', post_build=post)
-
def test_safe_heap(self):
if not Settings.SAFE_HEAP: return self.skip('We need SAFE_HEAP to test SAFE_HEAP')
if Settings.USE_TYPED_ARRAYS == 2: return self.skip('It is ok to violate the load-store assumption with TA2')
@@ -4615,13 +4680,15 @@ Child2:9
}
'''
try:
- def post(filename):
- lines = open(filename, 'r').readlines()
- lines = filter(lambda line: '___assert_fail(' in line or '___assert_func(' in line, lines)
- found_line_num = any(('//@line 7 "' in line) for line in lines)
- found_filename = any(('src.cpp"\n' in line) for line in lines)
- assert found_line_num, 'Must have debug info with the line number'
- assert found_filename, 'Must have debug info with the filename'
+ post = r'''
+def process(filename):
+ lines = open(filename, 'r').readlines()
+ lines = filter(lambda line: '___assert_fail(' in line or '___assert_func(' in line, lines)
+ found_line_num = any(('//@line 7 "' in line) for line in lines)
+ found_filename = any(('src.cpp"\n' in line) for line in lines)
+ assert found_line_num, 'Must have debug info with the line number'
+ assert found_filename, 'Must have debug info with the filename'
+'''
self.do_run(src, '*nothingatall*', post_build=post)
except Exception, e:
# This test *should* fail
@@ -4867,7 +4934,7 @@ Child2:9
# Generate tests for everything
- def make_run(name=-1, compiler=-1, llvm_opts=0, embetter=0, quantum_size=0, typed_arrays=0, defaults=False):
+ def make_run(fullname, name=-1, compiler=-1, llvm_opts=0, embetter=0, quantum_size=0, typed_arrays=0, emcc_args=None):
exec('''
class %s(T):
def tearDown(self):
@@ -4880,9 +4947,9 @@ class %s(T):
os.chdir(self.get_dir()) # Ensure the directory exists and go there
Building.COMPILER = %r
- self.use_defaults = %d
- if self.use_defaults:
- Settings.load_defaults()
+ self.emcc_args = %s
+ if self.emcc_args is not None:
+ Settings.load(self.emcc_args)
Building.LLVM_OPTS = 0
return
@@ -4892,7 +4959,8 @@ class %s(T):
# TODO: Move much of these to a init() function in shared.py, and reuse that
Settings.USE_TYPED_ARRAYS = %d
Settings.INVOKE_RUN = 1
- Settings.RELOOP = Settings.MICRO_OPTS = embetter
+ Settings.RELOOP = 0 # we only do them in the "o2" pass
+ Settings.MICRO_OPTS = embetter
Settings.QUANTUM_SIZE = quantum_size
Settings.ASSERTIONS = 1-embetter
Settings.SAFE_HEAP = 1-(embetter and llvm_opts)
@@ -4912,8 +4980,7 @@ class %s(T):
Settings.BUILD_AS_SHARED_LIB = 0
Settings.RUNTIME_LINKED_LIBS = []
Settings.CATCH_EXIT_CODE = 0
- Settings.TOTAL_MEMORY = Settings.FAST_MEMORY = None
- Settings.EMULATE_UNALIGNED_ACCESSES = Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2
+ Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2)
Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0
if Settings.USE_TYPED_ARRAYS == 2:
Settings.I64_MODE = 1
@@ -4921,18 +4988,20 @@ class %s(T):
else:
Settings.I64_MODE = 0
- if Settings.USE_TYPED_ARRAYS != 2 or Building.LLVM_OPTS == 2:
- Settings.RELOOP = 0 # XXX Would be better to use this, but it isn't really what we test in these cases, and is very slow
-
Building.pick_llvm_opts(3, safe=Building.LLVM_OPTS != 2)
TT = %s
-''' % (fullname, fullname, fullname, compiler, defaults, llvm_opts, embetter, quantum_size, typed_arrays, fullname))
+''' % (fullname, fullname, fullname, compiler, str(emcc_args), llvm_opts, embetter, quantum_size, typed_arrays, fullname))
return TT
# Make one run with the defaults
- fullname = 'default'
- exec(fullname + ' = make_run(compiler=CLANG, defaults=True)')
+ exec('default = make_run("default", compiler=CLANG, emcc_args=[])')
+
+ # Make one run with -O1, with safe heap
+ exec('o1 = make_run("o1", compiler=CLANG, emcc_args=["-O1", "-s", "SAFE_HEAP=1"])')
+
+ # Make one run with -O2, but without closure (we enable closure in specific tests, otherwise on everything it is too slow)
+ exec('o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "--closure", "0"])')
# Make custom runs with various options
for compiler, quantum, embetter, typed_arrays, llvm_opts in [
@@ -4942,14 +5011,11 @@ TT = %s
(CLANG, 4, 0, 0, 1),
(CLANG, 4, 1, 1, 0),
(CLANG, 4, 1, 1, 1),
- (CLANG, 4, 1, 2, 0),
- (CLANG, 4, 1, 2, 1),
- #(CLANG, 4, 1, 2, 2),
]:
fullname = 's_%d_%d%s%s' % (
llvm_opts, embetter, '' if quantum == 4 else '_q' + str(quantum), '' if typed_arrays in [0, 1] else '_t' + str(typed_arrays)
)
- exec('%s = make_run(%r,%r,%d,%d,%d,%d)' % (fullname, fullname, compiler, llvm_opts, embetter, quantum, typed_arrays))
+ exec('%s = make_run(fullname, %r,%r,%d,%d,%d,%d)' % (fullname, fullname, compiler, llvm_opts, embetter, quantum, typed_arrays))
del T # T is just a shape for the specific subclasses, we don't test it itself
@@ -5188,7 +5254,7 @@ Options that are modified or new in %s include:
def test_js_optimizer(self):
input = open(path_from_root('tools', 'test-js-optimizer.js')).read()
expected = open(path_from_root('tools', 'test-js-optimizer-output.js')).read()
- output = Popen([NODE_JS, JS_OPTIMIZER, 'unGlobalize', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost', 'loopOptimizer'],
+ output = Popen([NODE_JS, JS_OPTIMIZER, 'hoistMultiples', 'unGlobalize', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost', 'loopOptimizer'],
stdin=PIPE, stdout=PIPE).communicate(input)[0]
self.assertIdentical(expected, output.replace('\n\n', '\n'))
@@ -5237,7 +5303,7 @@ elif 'benchmark' in str(sys.argv):
Building.COMPILER_TEST_OPTS = []
TEST_REPS = 10
- TOTAL_TESTS = 7
+ TOTAL_TESTS = 8
tests_done = 0
total_times = map(lambda x: 0., range(TOTAL_TESTS))
@@ -5397,9 +5463,16 @@ elif 'benchmark' in str(sys.argv):
'''
self.do_benchmark(src, [], 'final: 826:14324.', emcc_args=['-s', 'CORRECT_SIGNS=1', '-s', 'CORRECT_OVERFLOWS=1', '-s', 'CORRECT_ROUNDINGS=1'])
- def test_fasta(self):
+ def fasta(self, emcc_args=[]):
src = open(path_from_root('tests', 'fasta.cpp'), 'r').read()
- self.do_benchmark(src, ['2100000'], '''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\nTCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\nAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\nGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\nCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\nGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\nGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\nTTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\nAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\nGCCTGGGCGA''')
+ self.do_benchmark(src, ['2100000'], '''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\nTCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\nAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\nGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\nCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\nGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\nGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\nTTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\nAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\nGCCTGGGCGA''',
+ emcc_args=emcc_args)
+
+ def test_fasta(self):
+ self.fasta()
+
+ def test_fasta_t2(self):
+ self.fasta(emcc_args=['-s', 'USE_TYPED_ARRAYS=2', '-s', 'QUANTUM_SIZE=4'])
def test_skinning(self):
src = open(path_from_root('tests', 'skinning_test_no_simd.cpp'), 'r').read()
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index bf971951..665ba0db 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -357,6 +357,140 @@ function simplifyExpressionsPost(ast) {
simplifyNotComps(ast);
}
+// Multiple blocks from the relooper are, in general, implemented by
+// if (__label__ == x) { } else if ..
+// and branching into them by
+// if (condition) { __label__ == x } else ..
+// We can hoist the multiple block into the condition, thus removing code and one 'if' check
+function hoistMultiples(ast) {
+ ast[1].forEach(function(node, i) {
+ var type = node[0];
+ if (type == 'defun') {
+ var statements = node[3];
+ for (var i = 0; i < statements.length-1; i++) {
+ var modified = false;
+ var pre = statements[i];
+ if (pre[0] != 'if') continue;
+ var post = statements[i+1];
+ // Look into some block types. shell() will then recreate the shell that we looked into
+ var postInner = post;
+ var shell = function(x) { return x };
+ while (true) {
+ /*if (postInner[0] == 'block') {
+ postInner = postInner[1][0];
+ } else */if (postInner[0] == 'label') {
+ shell = (function(oldShell, oldPostInner) {
+ return function(x) {
+ return oldShell(['label', oldPostInner[1], x]);
+ };
+ })(shell, postInner);
+ postInner = postInner[2];
+ } else if (postInner[0] == 'do') {
+ shell = (function(oldShell, oldPostInner) {
+ return function(x) {
+ return oldShell(['do', copy(oldPostInner[1]), ['block', [x]]]);
+ }
+ })(shell, postInner);;
+ postInner = postInner[2][1][0];
+ } else {
+ break; // give up
+ }
+ }
+ if (postInner[0] != 'if') continue;
+ // Look into this if, and its elseifs
+ while (postInner && postInner[0] == 'if') {
+ var cond = postInner[1];
+ if (cond[0] == 'binary' && cond[1] == '==' && cond[2][0] == 'name' && cond[2][1] == '__label__') {
+ assert(cond[3][0] == 'num');
+ // We have a valid Multiple check here. Try to hoist it, look for the source in |pre| and its else's
+ var labelNum = cond[3][1];
+ var labelBlock = postInner[2];
+ assert(labelBlock[0] == 'block');
+ var found = false;
+ traverse(pre, function(preNode, preType) {
+ if (!found && preType == 'assign' && preNode[2][0] == 'name' && preNode[2][1] == '__label__') {
+ assert(preNode[3][0] == 'num');
+ if (preNode[3][1] == labelNum) {
+ // That's it! Hoist away
+ found = true;
+ modified = true;
+ postInner[2] = ['block', []];
+ return ['block', [preNode].concat(labelBlock[1])];
+ }
+ }
+ });
+ }
+ postInner = postInner[3]; // Proceed to look in the else clause
+ }
+ if (modified) {
+ statements[i] = shell(pre);
+ }
+ }
+ }
+ });
+
+ // Clear out empty ifs and blocks, and redundant blocks/stats
+ var more = true;
+ while (more) {
+ more = false;
+ ast[1].forEach(function(node, i) {
+ var type = node[0];
+ if (type == 'defun') {
+ traverse(node, function(node, type) {
+ if (type == 'if' && node[2][0] == 'block' && node[2][1].length == 0) {
+ more = true;
+ if (node[2][2]) { // if there is an else, return that
+ return node[2][2];
+ } else {
+ return emptyNode();
+ }
+ } else if (type == 'block' && !node[1]) {
+ return emptyNode();
+ } else if (type == 'block' && (node[1].length == 0 || (node[1].length == 1 && jsonCompare(node[1][0], emptyNode())))) {
+ more = true;
+ return emptyNode();
+ } else if (type == 'block' && node[1].length == 1 && node[1][0][0] == 'block') {
+ more = true;
+ return node[1][0];
+ } else if (type == 'stat' && node[1][0] == 'block') {
+ more = true;
+ return node[1];
+ } else if (type == 'block') {
+ var pre = node[1].length;
+ node[1] = node[1].filter(function(blockItem) { return !jsonCompare(blockItem, emptyNode()) });
+ if (node[1].length < pre) {
+ more = true;
+ return node;
+ }
+ } else if (type == 'defun' && node[3].length == 1 && node[3][0][0] == 'block') {
+ more = true;
+ node[3] = node[3][0][1];
+ return node;
+ } else if (type == 'defun') {
+ var pre = node[3].length;
+ node[3] = node[3].filter(function(blockItem) { return !jsonCompare(blockItem, emptyNode()) });
+ if (node[3].length < pre) {
+ more = true;
+ return node;
+ }
+ } else if (type == 'do' && node[1][0] == 'num' && jsonCompare(node[2], emptyNode())) {
+ more = true;
+ return emptyNode();
+ } else if (type == 'label' && jsonCompare(node[2], emptyNode())) {
+ more = true;
+ return emptyNode();
+ } else if (type == 'if' && jsonCompare(node[3], emptyNode())) { // empty else clauses
+ node[3] = null;
+ return node;
+ }
+ });
+ }
+ });
+ }
+}
+
+// Simplifies loops
+// WARNING: This assumes all loops and breaks/continues are labelled
function loopOptimizer(ast) {
// Remove unneeded labels and one-time (do while(0)) loops. It is convenient to do these both at once.
function passTwo(ast) {
@@ -442,6 +576,7 @@ var passes = {
//removeUnneededLabelSettings: removeUnneededLabelSettings,
simplifyExpressionsPre: simplifyExpressionsPre,
simplifyExpressionsPost: simplifyExpressionsPost,
+ hoistMultiples: hoistMultiples,
loopOptimizer: loopOptimizer
};
@@ -457,16 +592,7 @@ if (metadata) setGeneratedFunctions(metadata);
arguments.forEach(function(arg) {
passes[arg](ast);
});
-/* TODO: run only on generated functions (but, some passes look at globals...)
-arguments.forEach(function(arg) {
- ast[1].forEach(function(node, i) {
- if (node[0] == 'defun' && isGenerated(node[1])) {
- passes[arg](node);
- }
- });
-});
-*/
-
+ast = srcToAst(astToSrc(ast)); // re-parse, to simplify a little
print(astToSrc(ast));
if (metadata) print(metadata + '\n');
diff --git a/tools/shared.py b/tools/shared.py
index 5b238aa4..adf3004e 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -269,18 +269,52 @@ def read_pgo_data(filename):
class Settings:
@classmethod
def reset(self):
- global Settings
class Settings2:
- reset = Settings.reset
- load_defaults = Settings.load_defaults
QUANTUM_SIZE = 4
- Settings = Settings2
+ reset = Settings.reset
- @classmethod
- def load_defaults(self):
- ''' Load the JS settings into Python '''
- settings = open(path_from_root('src', 'settings.js')).read().replace('var ', 'Settings.').replace('//', '#')
- exec(settings)
+ # Given some emcc-type args (-O3, -s X=Y, etc.), fill Settings with the right settings
+ @classmethod
+ def load(self, args):
+ # Load the JS defaults into python
+ settings = open(path_from_root('src', 'settings.js')).read().replace('var ', 'Settings.').replace('//', '#')
+ exec settings in globals()
+
+ # Apply additional settings. First -O, then -s
+ for i in range(len(args)):
+ if args[i].startswith('-O'):
+ level = eval(args[i][2])
+ Settings.apply_opt_level(level)
+ for i in range(len(args)):
+ if args[i] == '-s':
+ exec 'Settings.' + args[i+1] in globals() # execute the setting
+
+ # Transforms the Settings information into emcc-compatible args (-s X=Y, etc.). Basically
+ # the reverse of load_settings, except for -Ox which is relevant there but not here
+ @classmethod
+ def serialize(self):
+ ret = []
+ for key, value in Settings.__dict__.iteritems():
+ if key == key.upper(): # this is a hack. all of our settings are ALL_CAPS, python internals are not
+ ret += ['-s', key + '=' + json.dumps(value)]
+ return ret
+
+ @classmethod
+ def apply_opt_level(self, opt_level, noisy=False):
+ if opt_level >= 1:
+ Settings.ASSERTIONS = 0
+ Settings.DISABLE_EXCEPTION_CATCHING = 1
+ if opt_level >= 2:
+ Settings.RELOOP = 1
+ if opt_level >= 3:
+ Settings.CORRECT_SIGNS = 0
+ Settings.CORRECT_OVERFLOWS = 0
+ Settings.CORRECT_ROUNDINGS = 0
+ Settings.I64_MODE = 0
+ Settings.DOUBLE_MODE = 0
+ if noisy: print >> sys.stderr, 'Warning: Applying some potentially unsafe optimizations! (Use -O2 if this fails.)'
+ global Settings
+ Settings = Settings2
Settings.reset()
# Building
@@ -432,27 +466,21 @@ class Building:
return ret
@staticmethod
- def emcc(filename, args=[], stdout=None, stderr=None, env=None):
- try_delete(filename + '.o')
- Popen([EMCC, filename] + args + ['-o', filename + '.o'], stdout=stdout, stderr=stderr, env=env).communicate()[0]
- assert os.path.exists(filename + '.o'), 'Could not create bc file'
+ def emcc(filename, args=[], output_filename=None, stdout=None, stderr=None, env=None):
+ if output_filename is None:
+ output_filename = filename + '.o'
+ try_delete(output_filename)
+ Popen([EMCC, filename] + args + ['-o', output_filename], stdout=stdout, stderr=stderr, env=env).communicate()
+ assert os.path.exists(output_filename), 'emcc could not create output file'
@staticmethod
- def emscripten(filename, output_processor=None, append_ext=True, extra_args=[]):
+ def emscripten(filename, append_ext=True, extra_args=[]):
# Add some headers by default. TODO: remove manually adding these in each test
if '-H' not in extra_args:
extra_args += ['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']
# Run Emscripten
- exported_settings = {}
- for setting in ['QUANTUM_SIZE', 'RELOOP', 'MICRO_OPTS', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'PGO', 'EXPORTED_FUNCTIONS', 'EXPORTED_GLOBALS', 'BUILD_AS_SHARED_LIB', 'RUNTIME_LINKED_LIBS', 'INCLUDE_FULL_LIBRARY', 'RUNTIME_TYPE_INFO', 'DISABLE_EXCEPTION_CATCHING', 'TOTAL_MEMORY', 'FAST_MEMORY', 'EXCEPTION_DEBUG', 'PROFILE', 'I64_MODE', 'EMULATE_UNALIGNED_ACCESSES', 'CATCH_EXIT_CODE', 'USE_FHEAP']:
- try:
- value = eval('Settings.' + setting)
- if value is not None:
- exported_settings[setting] = value
- except:
- pass
- settings = ['-s %s=%s' % (k, json.dumps(v)) for k, v in exported_settings.items()]
+ settings = Settings.serialize()
compiler_output = timeout_run(Popen(['python', EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + settings + extra_args, stdout=PIPE), None, 'Compiling')
#print compiler_output
@@ -460,9 +488,6 @@ class Building:
if compiler_output is not None and 'Traceback' in compiler_output and 'in test_' in compiler_output: print compiler_output; assert 0
assert os.path.exists(filename + '.o.js') and len(open(filename + '.o.js', 'r').read()) > 0, 'Emscripten failed to generate .js: ' + str(compiler_output)
- if output_processor is not None:
- output_processor(open(filename + '.o.js').read())
-
return filename + '.o.js'
@staticmethod
@@ -564,7 +589,7 @@ class Building:
passes = [passes]
input = open(filename, 'r').read()
output, err = Popen([NODE_JS, JS_OPTIMIZER] + passes, stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(input)
- assert len(output) > 0, 'Error in js optimizer: ' + err + '\n\n' + output
+ assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + err + '\n\n' + output
filename += '.jo.js'
f = open(filename, 'w')
f.write(output)
diff --git a/tools/test-js-optimizer-output.js b/tools/test-js-optimizer-output.js
index d171c5b5..aa494a05 100644
--- a/tools/test-js-optimizer-output.js
+++ b/tools/test-js-optimizer-output.js
@@ -86,4 +86,82 @@ function maths() {
check(95);
__ZN6b2Vec2C1Ev($this1 + 76 | 0);
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths"]
+function hoisting() {
+ if ($i < $N) {
+ __label__ = 2;
+ callOther();
+ }
+ pause(1);
+ $for_body3$$for_end$5 : do {
+ if ($i < $N) {
+ __label__ = 2;
+ while (true) {
+ break $for_body3$$for_end$5;
+ }
+ callOther();
+ } else {
+ __label__ = 3;
+ }
+ } while (0);
+ pause(2);
+ do {
+ if ($i < $N) {
+ __label__ = 2;
+ if (callOther()) break;
+ } else {
+ __label__ = 3;
+ }
+ } while (0);
+ pause(3);
+ if ($i < $N) {
+ __label__ = 2;
+ callOther();
+ } else {
+ __label__ = 3;
+ }
+ pause(4);
+ if ($i < $N) {
+ __label__ = 2;
+ callOther();
+ } else {
+ __label__ = 3;
+ somethingElse();
+ }
+ pause(5);
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ somethingElse();
+ }
+ if (__label__ == 55) {
+ callOther();
+ }
+ pause(6);
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ somethingElse();
+ }
+}
+var FS = {
+ absolutePath: (function(relative, base) {
+ if (typeof relative !== "string") return null;
+ if (base === undefined) base = FS.currentPath;
+ if (relative && relative[0] == "/") base = "";
+ var full = base + "/" + relative;
+ var parts = full.split("/").reverse();
+ var absolute = [ "" ];
+ while (parts.length) {
+ var part = parts.pop();
+ if (part == "" || part == ".") {} else if (part == "..") {
+ if (absolute.length > 1) absolute.pop();
+ } else {
+ absolute.push(part);
+ }
+ }
+ return absolute.length == 1 ? "/" : absolute.join("/");
+ })
+};
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting"]
diff --git a/tools/test-js-optimizer.js b/tools/test-js-optimizer.js
index 0665462b..8d10d222 100644
--- a/tools/test-js-optimizer.js
+++ b/tools/test-js-optimizer.js
@@ -88,5 +88,97 @@ function maths() {
check(90+3+2);
__ZN6b2Vec2C1Ev(((((((($this1 + 20 | 0 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0) + 8 | 0);
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths"]
-
+function hoisting() {
+ if ($i < $N) {
+ __label__ = 2;
+ }
+ if (__label__ == 2) {
+ callOther();
+ }
+// ok /*
+ pause(1);
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ $for_body3$$for_end$5 : do {
+ if (__label__ == 2) {
+ while(true) { break $for_body3$$for_end$5 }
+ callOther();
+ }
+ } while (0);
+ pause(2);
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ cheez: do {
+ if (__label__ == 2) {
+ if (callOther()) break cheez;
+ }
+ } while (0);
+ pause(3);
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ if (__label__ == 2) {
+ callOther();
+ }
+ pause(4);
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ if (__label__ == 2) {
+ callOther();
+ } else if (__label__ == 3) {
+ somethingElse();
+ }
+ pause(5);
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ if (__label__ == 55) {
+ callOther();
+ } else if (__label__ == 3) {
+ somethingElse();
+ }
+ pause(6);
+ if ($i < $N) {
+ __label__ = 2;
+ } else {
+ __label__ = 3;
+ }
+ if (__label__ == 3) {
+ somethingElse();
+ }
+}
+var FS = {
+ absolutePath: function(relative, base) { // Don't touch this!
+ if (typeof relative !== 'string') return null;
+ if (base === undefined) base = FS.currentPath;
+ if (relative && relative[0] == '/') base = '';
+ var full = base + '/' + relative;
+ var parts = full.split('/').reverse();
+ var absolute = [''];
+ while (parts.length) {
+ var part = parts.pop();
+ if (part == '' || part == '.') {
+ // Nothing.
+ } else if (part == '..') {
+ if (absolute.length > 1) absolute.pop();
+ } else {
+ absolute.push(part);
+ }
+ }
+ return absolute.length == 1 ? '/' : absolute.join('/');
+ }
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting"]