aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemar16
-rwxr-xr-xemcc196
-rwxr-xr-xemconfigure11
-rwxr-xr-xemld28
-rwxr-xr-xemlibtool11
-rw-r--r--src/library.js4
-rw-r--r--src/library_sdl.js4
-rw-r--r--tests/runner.py67
-rwxr-xr-xtools/emconfiguren.py2
-rwxr-xr-xtools/emmaken.py2
-rw-r--r--tools/js-optimizer.js74
-rw-r--r--tools/shared.py16
-rw-r--r--tools/test-js-optimizer-output.js9
-rw-r--r--tools/test-js-optimizer.js7
14 files changed, 328 insertions, 119 deletions
diff --git a/emar b/emar
index 65e106f9..5a627993 100755
--- a/emar
+++ b/emar
@@ -1,13 +1,21 @@
#!/usr/bin/env python
'''
-emcc - ar helper script
+emar - ar helper script
=======================
This script acts as a frontend replacement for ar. See emcc.
'''
-if set(sys.argv[1]).issubset(set('-cruqs')): # ar
- sys.argv = sys.argv[:1] + sys.argv[3:] + ['-o='+sys.argv[2]]
- assert use_linker, 'Linker should be used in this case'
+import os, sys
+from tools import shared
+
+DEBUG = os.environ.get('EMCC_DEBUG')
+
+newargs = [shared.EMLD] + sys.argv[3:] + ['-o='+sys.argv[2]]
+
+if DEBUG:
+ print >> sys.stderr, 'emar:', sys.argv, ' ==> ', newargs
+
+os.execvp(shared.EMLD, newargs)
diff --git a/emcc b/emcc
index db3d58ac..e4def1cc 100755
--- a/emcc
+++ b/emcc
@@ -73,16 +73,15 @@ emcc can be influenced by a few environment variables:
EMMAKEN_COMPILER - The compiler to be used, if you don't want the default clang.
'''
-import os, sys, shutil
+import os, sys, shutil, tempfile
from subprocess import Popen, PIPE, STDOUT
from tools import shared
DEBUG = os.environ.get('EMCC_DEBUG')
-SAVE_FILES = os.environ.get('EMCC_SAVE_FILES') # saves some of the intermediate files
+TEMP_DIR = os.environ.get('EMCC_TEMP_DIR')
################### XXX
print >> sys.stderr, '\n***This is a WORK IN PROGRESS***'
-print >> sys.stderr, '***[%s]***\n' % str(sys.argv)
################### XXX
if DEBUG: print >> sys.stderr, 'emcc: ', ' '.join(sys.argv)
@@ -113,7 +112,11 @@ Options that are modified or new in %s include:
-O0 No optimizations (default)
-O1 Simple optimizations, including safe LLVM
optimizations, and no runtime assertions
- -O2 As -O1, plus code flow optimization (relooper)
+ or C++ exception catching (to re-enable
+ C++ exception catching, use
+ -s DISABLE_EXCEPTION_CATCHING=0 )
+ -O2 As -O1, plus the relooper (loop recreation),
+ plus closure compiler
Warning: Compiling with this takes a long time!
-O3 As -O2, plus dangerous optimizations that may
break the generated code! If that happens, try
@@ -124,11 +127,14 @@ Options that are modified or new in %s include:
--typed-arrays <mode> 0: No typed arrays
1: Parallel typed arrays
2: Shared (C-like) typed arrays (default)
- --llvm-opts <level> 0: No LLVM optimizations (default)
+ --llvm-opts <level> 0: No LLVM optimizations (default in -O0)
1: Safe/portable LLVM optimizations
+ (default in -O1 and above)
2: Full, unsafe/unportable LLVM optimizations;
this will almost certainly break the
generated code!
+ --closure <on> 0: No closure compiler (default in -O0, -O1)
+ 1: Run closure compiler (default in -O2, -O3)
The target file, if specified (-o <target>), defines what will
be generated:
@@ -150,7 +156,7 @@ CONFIGURE_CONFIG = os.environ.get('EMMAKEN_JUST_CONFIGURE')
CMAKE_CONFIG = 'CMakeFiles/cmTryCompileExec.dir' in ' '.join(sys.argv)# or 'CMakeCCompilerId' in ' '.join(sys.argv)
if CONFIGURE_CONFIG or CMAKE_CONFIG:
compiler = 'g++' if 'CXXCompiler' in ' '.join(sys.argv) or os.environ.get('EMMAKEN_CXX') else 'gcc'
- cmd = [compiler] + EMSDK_OPTS + sys.argv[1:]
+ cmd = [compiler] + shared.EMSDK_OPTS + sys.argv[1:]
if DEBUG: print >> sys.stderr, 'emcc, just configuring: ', cmd
exit(os.execvp(compiler, cmd))
@@ -172,6 +178,8 @@ if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += EMMAKEN_CFLAGS.split(' ')
# ---------------- Utilities ---------------
+SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc')
+
def unsuffixed(name):
return '.'.join(name.split('.')[:-1])
@@ -207,7 +215,23 @@ for i in range(len(sys.argv)-1):
sys.argv = sys.argv[:i] + sys.argv[i+2:]
break
-if not header:
+if header: # header or such
+ if DEBUG: print >> sys.stderr, 'Just copy.'
+ shutil.copy(sys.argv[-1], sys.argv[-2])
+ exit(0)
+
+if TEMP_DIR:
+ temp_dir = TEMP_DIR
+ if os.path.exists(temp_dir):
+ shutil.rmtree(temp_dir) # clear it
+ os.makedirs(temp_dir)
+else:
+ temp_dir = tempfile.mkdtemp()
+
+def in_temp(name):
+ return os.path.join(temp_dir, name)
+
+try:
call = CXX if use_cxx else CC
## Parse args
@@ -215,7 +239,11 @@ if not header:
newargs = sys.argv[1:]
opt_level = 0
- llvm_opt_level = 0
+ llvm_opt_level = None
+ closure = None
+
+ def check_bad_eq(arg):
+ assert '=' not in arg, 'Invalid parameter (do not use "=" with "--" options)'
for i in range(len(newargs)):
if newargs[i].startswith('-O'):
@@ -224,17 +252,23 @@ if not header:
assert 0 <= opt_level <= 3
except:
raise Exception('Invalid optimization level: ' + newargs[i])
- if opt_level >= 1:
- llvm_opt_level = 1
newargs[i] = ''
elif newargs[i].startswith('--llvm-opts'):
- assert '=' not in newargs[i], 'Invalid llvm opts parameter (do not use "=")'
+ check_bad_eq(newargs[i])
llvm_opt_level = eval(newargs[i+1])
assert 0 <= llvm_opt_level <= 1, 'Only two levels of LLVM optimizations are supported so far, 0 (none) and 1 (safe)'
newargs[i] = ''
newargs[i+1] = ''
+ elif newargs[i].startswith('--closure'):
+ check_bad_eq(newargs[i])
+ closure = int(newargs[i+1])
+ newargs[i] = ''
+ newargs[i+1] = ''
newargs = [ arg for arg in newargs if arg is not '' ]
+ if llvm_opt_level is None: llvm_opt_level = 1 if opt_level >= 1 else 0
+ if closure is None: closure = 1 if opt_level >= 2 else 0
+
settings_changes = []
for i in range(len(newargs)):
if newargs[i] == '-s':
@@ -248,12 +282,15 @@ if not header:
newargs = [ arg for arg in newargs if arg is not '' ]
input_files = []
+ has_source_inputs = False
for i in range(len(newargs)): # find input files XXX this a simple heuristic. we should really analyze based on a full understanding of gcc params,
# right now we just assume that what is left contains no more |-x OPT| things
arg = newargs[i]
- if arg.endswith(('.c', '.cpp', '.cxx', '.bc', '.o')): # we already removed -o <target>, so all these should be inputs
+ if arg.endswith(SOURCE_SUFFIXES + ('.bc', '.o')): # we already removed -o <target>, so all these should be inputs
input_files.append(arg)
newargs[i] = ''
+ if arg.endswith(SOURCE_SUFFIXES):
+ has_source_inputs = True
newargs = [ arg for arg in newargs if arg is not '' ]
assert len(input_files) > 0, 'emcc: no input files specified'
@@ -265,62 +302,74 @@ if not header:
target_basename = unsuffixed_basename(target)
- if '-c' in newargs: # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode
- target = target_basename + '.bc'
+ # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode
+ has_dash_c = '-c' in newargs
+ if has_dash_c:
+ assert has_source_inputs, 'Must have source code inputs to use -c'
+ target = target_basename + '.o'
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
- print >> sys.stderr, 'Warning: The relooper optimization can be very slow.'
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
- shared.Settings.DISABLE_EXCEPTION_CATCHING = 1
print >> sys.stderr, 'Warning: Applying some potentially unsafe optimizations! (Use -O2 if this fails.)'
## Compile source code to bitcode
+ if DEBUG: print >> sys.stderr, 'emcc: compiling to bitcode'
+
# First, generate LLVM bitcode. For each input file, we get base.o with bitcode
newargs = newargs + ['-emit-llvm', '-c']
for input_file in input_files:
- if input_file.endswith(('.c', '.cpp', '.cxx')):
- if DEBUG: print >> sys.stderr, "Running:", call, ' '.join(newargs)
- Popen([call] + newargs + [input_file]).communicate()
+ if input_file.endswith(SOURCE_SUFFIXES):
+ args = newargs + [input_file, '-o', in_temp(unsuffixed_basename(input_file) + '.o')]
+ if DEBUG: print >> sys.stderr, "emcc running:", call, ' '.join(args)
+ Popen([call] + args).communicate()
else:
- shutil.copyfile(input_file, unsuffixed_basename(input_file) + '.o')
+ shutil.copyfile(input_file, in_temp(unsuffixed_basename(input_file) + '.o'))
# Optimize, if asked to
if llvm_opt_level > 0:
+ if DEBUG: print >> sys.stderr, 'emcc: LLVM opts'
for input_file in input_files:
- shared.Building.llvm_opt(unsuffixed_basename(input_file) + '.o', 2, safe=llvm_opt_level < 2)
+ shared.Building.llvm_opt(in_temp(unsuffixed_basename(input_file) + '.o'), 2, safe=llvm_opt_level < 2)
# If we were just asked to generate bitcode, stop there
- if final_suffix in ['o', 'bc']:
- if final_suffix == 'bc':
+ if final_suffix not in ['js', 'html']:
+ if not specified_target:
for input_file in input_files:
- shutil.move(unsuffixed_basename(input_file) + '.o', unsuffixed_basename(input_file) + '.bc')
-
- if specified_target:
- assert len(input_files) == 1, 'fatal error: cannot specify -o with -c with multiple files'
- shutil.move(unsuffixed_basename(input_files[0]) + '.' + final_suffix, unsuffixed_basename(specified_target) + '.' + final_suffix)
+ shutil.move(in_temp(unsuffixed_basename(input_file) + '.o'), unsuffixed_basename(input_file) + '.' + final_suffix)
+ else:
+ if len(input_files) == 1:
+ shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), specified_target)
+ else:
+ assert not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files'
+ # We have a specified target (-o <target>), which is not JavaScript or HTML, and
+ # we have multiple files: Link them. TODO: Pass complex linker args along
+ shared.Building.link(map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files), specified_target)
exit(0)
## Continue on to create JavaScript
+ if DEBUG: print >> sys.stderr, 'emcc: generating JavaScript'
+
# First, combine the bitcode files if there are several
if len(input_files) > 1:
- shared.Building.link(map(lambda input_file: unsuffixed_basename(input_file) + '.o', input_files), target_basename + '.bc')
+ shared.Building.link(map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files), in_temp(target_basename + '.bc'))
else:
- shutil.move(unsuffixed_basename(input_files[0]) + '.o', target_basename + '.bc')
+ shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc'))
# Apply -s settings in newargs here (after -Ox, so they can override it)
@@ -328,51 +377,48 @@ if not header:
key, value = change.split('=')
exec('shared.Settings.' + key + ' = ' + value)
- temp_files = shared.TempFiles()
- temp_files.note(target_basename + '.bc')
- try:
- shared.Building.emscripten(target_basename + '.bc', append_ext=False)
- shutil.move(target_basename + '.bc.o.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.js')
-
- if opt_level >= 1:
- # js optimizer
- shared.Building.js_optimizer(target_basename + '.js', 'loopOptimizer')
- shutil.move(target_basename + '.js.jo.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.jo.js')
-
- # eliminator
- shared.Building.eliminator(target_basename + '.js')
- shutil.move(target_basename + '.js.el.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.jo.el.js')
-
- if opt_level >= 3:
- # closure
- shared.Building.closure_compiler(target_basename + '.js')
- shutil.move(target_basename + '.js.cc.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.jo.el.cc.js')
-
- if opt_level >= 1:
- # js optimizer
- shared.Building.js_optimizer(target_basename + '.js', 'simplifyExpressions')
- shutil.move(target_basename + '.js.jo.js', target_basename + '.js')
- if SAVE_FILES: shutil.copyfile(target_basename + '.js', 'save_' + target_basename + '.jo.el.cc.jo.js')
-
- # If we were asked to also generate HTML, do that
- if final_suffix == 'html':
- shell = open(shared.path_from_root('src', 'shell.html')).read()
- html = open(target_basename + '.html', 'w')
- html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(target_basename + '.js').read()))
- html.close()
- temp_files.note(target_basename + '.js')
-
- finally:
- temp_files.clean()
+ if opt_level >= 2:
+ print >> sys.stderr, 'Warning: The relooper optimization can be very slow.'
- exit(0)
+ final = shared.Building.emscripten(in_temp(target_basename + '.bc'), append_ext=False)
-else: # header or such
- if DEBUG: print >> sys.stderr, 'Just copy.'
- shutil.copy(sys.argv[-1], sys.argv[-2])
- exit(0)
+ if opt_level >= 1:
+ # js optimizer
+ if DEBUG: print >> sys.stderr, 'emcc: running pre-closure post-opts'
+ final = shared.Building.js_optimizer(final, 'loopOptimizer')
+
+ # eliminator
+ final = shared.Building.eliminator(final)
+
+ # js optimizer pre-pass
+ final = shared.Building.js_optimizer(final, 'simplifyExpressionsPre')
+
+ if closure:
+ if DEBUG: print >> sys.stderr, 'emcc: running closure'
+ final = shared.Building.closure_compiler(final)
+
+ if opt_level >= 1:
+ # js optimizer post-pass
+ if DEBUG: print >> sys.stderr, 'emcc: running post-closure post-opts'
+ final = shared.Building.js_optimizer(final, 'simplifyExpressionsPost')
+
+ # If we were asked to also generate HTML, do that
+ if final_suffix == 'html':
+ if DEBUG: print >> sys.stderr, 'emcc: generating HTML'
+ shell = open(shared.path_from_root('src', 'shell.html')).read()
+ html = open(target_basename + '.html', 'w')
+ html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(final).read()))
+ html.close()
+ else:
+ # copy final JS to output
+ shutil.move(final, target_basename + '.js')
+
+finally:
+ if not TEMP_DIR:
+ try:
+ shutil.rmtree(temp_dir)
+ except:
+ pass
+ else:
+ print >> sys.stderr, 'emcc saved files are in:', temp_dir
diff --git a/emconfigure b/emconfigure
new file mode 100755
index 00000000..b52d90c2
--- /dev/null
+++ b/emconfigure
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+'''
+This is a helper script. See emcc.
+'''
+
+import os, sys
+from tools import shared
+
+shared.Building.configure(sys.argv[1:])
+
diff --git a/emld b/emld
index c2056e68..954465c6 100755
--- a/emld
+++ b/emld
@@ -1,19 +1,37 @@
#!/usr/bin/env python
'''
-emcc - linker helper script
+emld - linker helper script
===========================
This script acts as a frontend replacement for the ld linker. See emcc.
+
+We could use the compiler code for this, but here we want to be careful to use all the linker flags we have been passed, sending them to ld.
'''
+import os, sys
+from tools import shared
+
+DEBUG = os.environ.get('EMCC_DEBUG')
+
+if DEBUG:
+ print >> sys.stderr, 'emld:', sys.argv
+
ALLOWED_LINK_ARGS = ['-f', '-help', '-o', '-print-after', '-print-after-all', '-print-before',
'-print-before-all', '-time-passes', '-v', '-verify-dom-info', '-version' ]
TWO_PART_DISALLOWED_LINK_ARGS = ['-L'] # Ignore thingsl like |-L .|
-#
+# Check for specified target
+target = None
+for i in range(len(sys.argv)-1):
+ if sys.argv[i].startswith('-o='):
+ raise Exception('Invalid syntax: do not use -o=X, use -o X')
+
+ if sys.argv[i] == '-o':
+ target = sys.argv[i+1]
+ sys.argv = sys.argv[:i] + sys.argv[i+2:]
+ break
-# We could use the compiler code for this, but here we want to be careful to use all the linker flags we have been passed, sending them to ld
call = shared.LLVM_LD
newargs = ['-disable-opt']
i = 0
@@ -37,6 +55,6 @@ if target:
actual_target = unsuffixed(target) + '.bc'
newargs.append('-o=' + actual_target)
-if DEBUG: print >> sys.stderr, "Running:", call, ' '.join(newargs)
-Popen([call] + newargs).communicate()
+if DEBUG: print >> sys.stderr, "emld running:", call, ' '.join(newargs)
+os.execvp(call, [call] + newargs)
diff --git a/emlibtool b/emlibtool
new file mode 100755
index 00000000..1220b93d
--- /dev/null
+++ b/emlibtool
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+'''
+This is a helper script. See emcc.
+'''
+
+import os, sys
+from tools import shared
+
+raise Exception('TODO: emlibtool')
+
diff --git a/src/library.js b/src/library.js
index e225591e..2a4a5e7d 100644
--- a/src/library.js
+++ b/src/library.js
@@ -2214,7 +2214,7 @@ LibraryManager.library = {
_formatString: function(format, varargs) {
var textIndex = format;
var argIndex = 0;
- var getNextArg = function(type) {
+ function getNextArg(type) {
// NOTE: Explicitly ignoring type safety. Otherwise this fails:
// int x = 4; printf("%c\n", (char)x);
var ret;
@@ -2231,7 +2231,7 @@ LibraryManager.library = {
}
argIndex += Runtime.getNativeFieldSize(type);
return Number(ret);
- };
+ }
var ret = [];
var curr, next, currArg;
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 9e305bec..5797ccec 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -83,7 +83,7 @@
// solution here is to have a singleIteration() function which is a single loop
// iteration, and from JS to do something like setInterval(_singleIteration, 1/30)
//
-// * SQL_Quit does nothing.
+// * SDL_Quit does nothing.
mergeInto(LibraryManager.library, {
$SDL__deps: ['$Browser'],
@@ -265,7 +265,7 @@ mergeInto(LibraryManager.library, {
},
SDL_Quit: function() {
- print('SQL_Quit called (and ignored)');
+ print('SDL_Quit called (and ignored)');
},
SDL_LockSurface: function(surf) {
diff --git a/tests/runner.py b/tests/runner.py
index a2ca7305..d5489dd6 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -4878,6 +4878,11 @@ TT = %s
del T # T is just a shape for the specific subclasses, we don't test it itself
class other(RunnerCore):
+ def test_reminder(self):
+ raise Exception('''Fix emmaken.py and emconfiguren.py, they should work but mention they are deprecated
+ Test emconfigure
+ configure in test_zlib looks broken''')
+
def test_emcc(self):
def clear():
for name in os.listdir(self.get_dir()):
@@ -4916,27 +4921,31 @@ Options that are modified or new in %s include:
# emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file
for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc']]:
- target = args[1] if len(args) == 2 else 'hello_world.bc'
+ target = args[1] if len(args) == 2 else 'hello_world.o'
clear()
output = Popen([compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate()
assert len(output[0]) == 0, output[0]
- assert os.path.exists(target), 'Expected %s to exist since args are %s : %s' % (target, str(args), output)
+ assert os.path.exists(target), 'Expected %s to exist since args are %s : %s' % (target, str(args), '\n'.join(output))
self.assertContained('hello, world!', self.run_llvm_interpreter([target]))
# Optimization: emcc src.cpp -o something.js [-Ox]. -O0 is the same as not specifying any optimization setting
- for params, opt_level, bc_params in [ # bc params are used after compiling to bitcode
- (['-o', 'something.js'], 0, None),
- (['-o', 'something.js', '-O0'], 0, None),
- (['-o', 'something.js', '-O1'], 1, None),
- (['-o', 'something.js', '-O2'], 2, None),
- (['-o', 'something.js', '-O3'], 3, None),
+ for params, opt_level, bc_params, closure in [ # bc params are used after compiling to bitcode
+ (['-o', 'something.js'], 0, None, 0),
+ (['-o', 'something.js', '-O0'], 0, None, 0),
+ (['-o', 'something.js', '-O1'], 1, None, 0),
+ (['-o', 'something.js', '-O1', '--closure', '1'], 1, None, 1),
+ (['-o', 'something.js', '-O2'], 2, None, 1),
+ (['-o', 'something.js', '-O2', '--closure', '0'], 2, None, 0),
+ (['-o', 'something.js', '-O3'], 3, None, 1),
+ (['-o', 'something.js', '-O3', '--closure', '0'], 3, None, 0),
# and, test compiling to bitcode first
- (['-o', 'something.bc'], 0, []),
- (['-o', 'something.bc'], 0, ['-O0']),
- (['-o', 'something.bc'], 1, ['-O1']),
- (['-o', 'something.bc'], 2, ['-O2']),
- (['-o', 'something.bc'], 3, ['-O3']),
+ (['-o', 'something.bc'], 0, [], 0),
+ (['-o', 'something.bc'], 0, ['-O0'], 0),
+ (['-o', 'something.bc'], 1, ['-O1'], 0),
+ (['-o', 'something.bc'], 2, ['-O2'], 1),
+ (['-o', 'something.bc'], 3, ['-O3'], 1),
]:
+ #print params, opt_level, bc_params, closure
clear()
output = Popen([compiler, path_from_root('tests', 'hello_world_loop.cpp')] + params,
stdout=PIPE, stderr=PIPE).communicate()
@@ -4952,16 +4961,17 @@ Options that are modified or new in %s include:
# Verify optimization level etc. in the generated code
# XXX these are quite sensitive, and will need updating when code generation changes
generated = open('something.js').read() # TODO: parse out the _main function itself, not support code, if the tests below need that some day
- assert ('(__label__)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2'
- assert ('assert(STACKTOP < STACK_MAX)' in generated) == (opt_level == 0), 'assertions should be in opt == 0'
- assert ('|0)/2)|0)' in generated or '| 0) / 2 | 0)' in generated) == (opt_level <= 2), 'corrections should be in opt <= 2'
assert 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 should be used by default'
assert 'SAFE_HEAP' not in generated, 'safe heap should not be used by default'
assert ': while(' not in generated, 'when relooping we also js-optimize, so there should be no labelled whiles'
- if opt_level >= 3:
+ if closure:
assert 'Module._main = ' in generated, 'closure compiler should have been run'
else:
# closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure
+ assert 'Module._main = ' not in generated, 'closure compiler should not have been run'
+ # XXX find a way to test this: assert ('& 255' in generated or '&255' in generated) == (opt_level <= 2), 'corrections should be in opt <= 2'
+ assert ('(__label__)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2'
+ assert ('assert(STACKTOP < STACK_MAX)' in generated) == (opt_level == 0), 'assertions should be in opt == 0'
assert 'var $i;' in generated, 'micro opts should always be on'
if opt_level >= 1: assert 'HEAP8[HEAP32[' in generated, 'eliminator should create compound expressions, and fewer one-time vars'
assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts'
@@ -5002,25 +5012,36 @@ Options that are modified or new in %s include:
assert 'fatal error' in output[1], output[1]
continue
- assert os.path.exists('twopart_main.bc'), '\n'.join(output)
- assert os.path.exists('twopart_side.bc'), '\n'.join(output)
+ assert os.path.exists('twopart_main.o'), '\n'.join(output)
+ assert os.path.exists('twopart_side.o'), '\n'.join(output)
assert not os.path.exists(target), 'We should only have created bitcode here: ' + '\n'.join(output)
# Compiling one of them alone is expected to fail
- output = Popen([compiler, 'twopart_main.bc'] + args, stdout=PIPE, stderr=PIPE).communicate()
+ output = Popen([compiler, 'twopart_main.o'] + args, stdout=PIPE, stderr=PIPE).communicate()
assert os.path.exists(target), '\n'.join(output)
#print '\n'.join(output)
self.assertContained('is not a function', run_js(target, stderr=STDOUT))
try_delete(target)
# Combining those bc files into js should work
- output = Popen([compiler, 'twopart_main.bc', 'twopart_side.bc'] + args, stdout=PIPE, stderr=PIPE).communicate()
+ output = Popen([compiler, 'twopart_main.o', 'twopart_side.o'] + args, stdout=PIPE, stderr=PIPE).communicate()
assert os.path.exists(target), '\n'.join(output)
self.assertContained('side got: hello from main, over', run_js(target))
+ # Combining bc files into another bc should also work
+ try_delete(target)
+ assert not os.path.exists(target)
+ output = Popen([compiler, 'twopart_main.o', 'twopart_side.o', '-o', 'combined.bc'] + args, stdout=PIPE, stderr=PIPE).communicate()
+ assert os.path.exists('combined.bc'), '\n'.join(output)
+ self.assertContained('side got: hello from main, over', self.run_llvm_interpreter(['combined.bc']))
+
+ # TODO: compile .ll inputs to emcc into .bc
+ # TODO: dlmalloc in emcc (just pass the arg to emscripten.py)
# TODO: test normal project linking, static and dynamic: get_library should not need to be told what to link!
# TODO: when ready, switch tools/shared building to use emcc over emmaken
# TODO: when this is done, more test runner to test these (i.e., test all -Ox thoroughly)
+ # TODO: emscripten tutorial with emcc
+ # TODO: deprecate llvm optimizations etc. in emscripten.py.
# Finally, do some web browser tests
def run_browser(html_file, message):
@@ -5069,7 +5090,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', 'simplifyExpressions', 'loopOptimizer'],
+ output = Popen([NODE_JS, JS_OPTIMIZER, 'unGlobalize', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost', 'loopOptimizer'],
stdin=PIPE, stdout=PIPE).communicate(input)[0]
self.assertIdentical(expected, output.replace('\n\n', '\n'))
@@ -5110,8 +5131,6 @@ else:
print 'Benchmarking JS engine:', JS_ENGINE
Building.COMPILER_TEST_OPTS = []
- # TODO: Use other js optimizer options, like remove assigns to undefined (seems to slow us down more than speed us up)
- POST_OPTIMIZATIONS = [['js-optimizer', 'loopOptimizer'], 'eliminator', 'closure', ['js-optimizer', 'simplifyExpressions']]
TEST_REPS = 10
TOTAL_TESTS = 7
diff --git a/tools/emconfiguren.py b/tools/emconfiguren.py
index abe41564..d549908b 100755
--- a/tools/emconfiguren.py
+++ b/tools/emconfiguren.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+raise Exception('emconfiguren is deprecated!')
+
'''
This is a helper script for emmaken.py. See docs in that file for more info.
'''
diff --git a/tools/emmaken.py b/tools/emmaken.py
index a509b940..89785bc5 100755
--- a/tools/emmaken.py
+++ b/tools/emmaken.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+raise Exception('emmaken is deprecated!')
+
'''
emmaken - the emscripten make proxy tool
========================================
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index d22de39c..bf971951 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -78,6 +78,7 @@ function traverse(node, pre, post, stack) {
if (stack) len = stack.length;
var result = pre(node, type, stack);
if (result == true) return true;
+ if (typeof result == 'object') node = result; // Continue processing on this node
if (stack && len == stack.length) stack.push(0);
}
for (var i = 0; i < node.length; i++) {
@@ -259,8 +260,74 @@ function removeUnneededLabelSettings(ast) {
});
}
-// Various expression simplifications
-function simplifyExpressions(ast) {
+// Various expression simplifications. Pre run before closure (where we still have metadata), Post run after.
+
+function simplifyExpressionsPre(ast) {
+ // When there is a bunch of math like (((8+5)|0)+12)|0, only the external |0 is needed, one correction is enough.
+ // At each node, ((X|0)+Y)|0 can be transformed into (X+Y): The inner corrections are not needed
+ // TODO: Is the same is true for 0xff, 0xffff?
+
+ function simplifyBitops(ast) {
+ var SAFE_BINARY_OPS = set('+', '-', '*', '/', '%', '|');
+ var ZERO = ['num', 0];
+ var rerun = true;
+ while (rerun) {
+ rerun = false;
+ traverseGenerated(ast, function(node, type, stack) {
+ if (type == 'binary' && node[1] == '|' && (jsonCompare(node[2], ZERO) || jsonCompare(node[3], ZERO))) {
+ stack.push(1); // From here on up, no need for this kind of correction, it's done at the top
+
+ // We might be able to remove this correction
+ for (var i = stack.length-2; i >= 0; i--) {
+ if (stack[i] == 1) {
+ // Great, we can eliminate
+ rerun = true;
+ return jsonCompare(node[2], ZERO) ? node[3] : node[2];
+ } else if (stack[i] == -1) {
+ break; // Too bad, we can't
+ }
+ }
+ } else if ((type == 'binary' && node[1] in SAFE_BINARY_OPS) || type == 'num' || type == 'name') {
+ stack.push(0); // This node is safe in that it does not interfere with this optimization
+ } else {
+ stack.push(-1); // This node is dangerous! Give up if you see this before you see '1'
+ }
+ }, null, []);
+ }
+ }
+
+ // The most common mathop is addition, e.g. in getelementptr done repeatedly. We can join all of those,
+ // by doing (num+num) ==> newnum, and (name+num)+num = name+newnum
+ function joinAdditions(ast) {
+ var rerun = true;
+ while (rerun) {
+ rerun = false;
+ traverseGenerated(ast, function(node, type) {
+ if (type == 'binary' && node[1] == '+') {
+ if (node[2][0] == 'num' && node[3][0] == 'num') {
+ rerun = true;
+ return ['num', node[2][1] + node[3][1]];
+ }
+ for (var i = 2; i <= 3; i++) {
+ var ii = 5-i;
+ for (var j = 2; j <= 3; j++) {
+ if (node[i][0] == 'num' && node[ii][0] == 'binary' && node[ii][1] == '+' && node[ii][j][0] == 'num') {
+ rerun = true;
+ node[ii][j][1] += node[i][1];
+ return node[ii];
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+
+ simplifyBitops(ast);
+ joinAdditions(ast);
+}
+
+function simplifyExpressionsPost(ast) {
// We often have branchings that are simplified so one end vanishes, and
// we then get
// if (!(x < 5))
@@ -373,7 +440,8 @@ var passes = {
unGlobalize: unGlobalize,
removeAssignsToUndefined: removeAssignsToUndefined,
//removeUnneededLabelSettings: removeUnneededLabelSettings,
- simplifyExpressions: simplifyExpressions,
+ simplifyExpressionsPre: simplifyExpressionsPre,
+ simplifyExpressionsPost: simplifyExpressionsPost,
loopOptimizer: loopOptimizer
};
diff --git a/tools/shared.py b/tools/shared.py
index b61552ba..cecfd4ac 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -28,6 +28,10 @@ DEMANGLER = path_from_root('third_party', 'demangler.py')
NAMESPACER = path_from_root('tools', 'namespacer.py')
EMCC = path_from_root('emcc')
EMXX = path_from_root('em++')
+EMAR = path_from_root('emar')
+EMLD = path_from_root('emld')
+EMRANLIB = path_from_root('emranlib')
+EMLIBTOOL = path_from_root('emlibtool')
EMMAKEN = path_from_root('tools', 'emmaken.py')
AUTODEBUGGER = path_from_root('tools', 'autodebugger.py')
DFE = path_from_root('tools', 'dead_function_eliminator.py')
@@ -217,7 +221,11 @@ class Building:
@staticmethod
def get_building_env():
env = os.environ.copy()
- env['RANLIB'] = env['AR'] = env['CXX'] = env['CC'] = env['LIBTOOL'] = EMMAKEN
+ env['CC'] = EMCC
+ env['CXX'] = EMXX
+ env['AR'] = EMAR
+ env['RANLIB'] = EMRANLIB
+ env['LIBTOOL'] = EMLIBTOOL
env['EMMAKEN_COMPILER'] = Building.COMPILER
env['EMSCRIPTEN_TOOLS'] = path_from_root('tools')
env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(COMPILER_OPTS + Building.COMPILER_TEST_OPTS) # Normal CFLAGS is ignored by some configure's.
@@ -369,6 +377,8 @@ class Building:
if output_processor is not None:
output_processor(open(filename + '.o.js').read())
+ return filename + '.o.js'
+
@staticmethod
def pick_llvm_opts(optimization_level, safe=True):
'''
@@ -472,6 +482,7 @@ class Building:
f = open(filename, 'w')
f.write(output)
f.close()
+ return filename
@staticmethod
def eliminator(filename):
@@ -486,6 +497,7 @@ class Building:
f = open(filename, 'w')
f.write(output)