diff options
Diffstat (limited to 'emcc')
-rwxr-xr-x | emcc | 196 |
1 files changed, 121 insertions, 75 deletions
@@ -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 |