summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc23
-rw-r--r--tests/hello_world_error.c8
-rw-r--r--tests/hello_world_error.cpp8
-rw-r--r--tests/runner.py101
-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
8 files changed, 450 insertions, 91 deletions
diff --git a/emcc b/emcc
index e29946cf..b14810bb 100755
--- a/emcc
+++ b/emcc
@@ -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"]