diff options
-rwxr-xr-x | emcc | 23 | ||||
-rw-r--r-- | tests/hello_world_error.c | 8 | ||||
-rw-r--r-- | tests/hello_world_error.cpp | 8 | ||||
-rw-r--r-- | tests/runner.py | 101 | ||||
-rw-r--r-- | tools/js-optimizer.js | 146 | ||||
-rw-r--r-- | tools/shared.py | 79 | ||||
-rw-r--r-- | tools/test-js-optimizer-output.js | 80 | ||||
-rw-r--r-- | tools/test-js-optimizer.js | 96 |
8 files changed, 450 insertions, 91 deletions
@@ -338,18 +338,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: @@ -454,10 +443,18 @@ try: final = shared.Building.emscripten(in_temp(target_basename + '.bc'), 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/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 e9886ab8..bb10ca81 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -91,7 +91,7 @@ class RunnerCore(unittest.TestCase): Building.llvm_dis(filename) # 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 +138,25 @@ 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 + 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') + + 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 +251,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: @@ -1624,9 +1639,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) @@ -2341,12 +2358,14 @@ 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) @@ -3560,7 +3579,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')) @@ -3742,7 +3761,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 @@ -4802,7 +4821,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): @@ -4815,9 +4834,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 @@ -4827,7 +4846,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) @@ -4847,8 +4867,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 @@ -4856,18 +4875,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 [ @@ -4877,14 +4898,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 @@ -5123,7 +5141,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')) @@ -5172,7 +5190,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)) @@ -5332,9 +5350,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"] |