diff options
Diffstat (limited to 'tools/shared.py')
-rw-r--r-- | tools/shared.py | 170 |
1 files changed, 139 insertions, 31 deletions
diff --git a/tools/shared.py b/tools/shared.py index 2ae37f91..31f0aad9 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -270,7 +270,7 @@ if EM_POPEN_WORKAROUND and os.name == 'nt': # Expectations -EXPECTED_LLVM_VERSION = (3,2) +EXPECTED_LLVM_VERSION = (3,3) actual_clang_version = None @@ -303,10 +303,31 @@ def check_fastcomp(): print >> sys.stderr, '===========================================================================' print >> sys.stderr, llc_version_info, print >> sys.stderr, '===========================================================================' + logging.critical('you can fall back to the older (pre-fastcomp) compiler core, although that is not recommended, see https://github.com/kripken/emscripten/wiki/LLVM-Backend') return False + + # look for a source tree under the llvm binary directory. if there is one, look for emscripten-version.txt files + seen = False + d = os.path.dirname(LLVM_COMPILER) + while d != os.path.dirname(d): + # look for version file in llvm repo, making sure not to mistake the emscripten repo for it + if os.path.exists(os.path.join(d, 'emscripten-version.txt')) and not os.path.abspath(d) == os.path.abspath(path_from_root()): + seen = True + llvm_version = open(os.path.join(d, 'emscripten-version.txt')).read().strip() + if os.path.exists(os.path.join(d, 'tools', 'clang', 'emscripten-version.txt')): + clang_version = open(os.path.join(d, 'tools', 'clang', 'emscripten-version.txt')).read().strip() + else: + clang_version = '?' + if EMSCRIPTEN_VERSION != llvm_version or EMSCRIPTEN_VERSION != clang_version: + logging.error('Emscripten, llvm and clang versions do not match, this is dangerous (%s, %s, %s)', EMSCRIPTEN_VERSION, llvm_version, clang_version) + logging.error('Make sure to use the same branch in each repo, and to be up-to-date on each. See https://github.com/kripken/emscripten/wiki/LLVM-Backend') + break + d = os.path.dirname(d) + if not seen: + logging.warning('did not see a source tree above LLVM_DIR, could not verify version numbers match') return True except Exception, e: - logging.warning('cound not check fastcomp: %s' % str(e)) + logging.warning('could not check fastcomp: %s' % str(e)) return True EXPECTED_NODE_VERSION = (0,8,0) @@ -345,7 +366,11 @@ def find_temp_directory(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.9.0' +try: + EMSCRIPTEN_VERSION = open(path_from_root('emscripten-version.txt')).read().strip() +except Exception, e: + logging.error('cannot find emscripten version ' + str(e)) + EMSCRIPTEN_VERSION = 'unknown' def generate_sanity(): return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT + '|' + get_clang_version() @@ -379,7 +404,7 @@ def check_sanity(force=False): # some warning, mostly not fatal checks - do them even if EM_IGNORE_SANITY is on check_llvm_version() check_node_version() - if os.environ.get('EMCC_FAST_COMPILER') == '1': + if os.environ.get('EMCC_FAST_COMPILER') != '0': fastcomp_ok = check_fastcomp() if os.environ.get('EM_IGNORE_SANITY'): @@ -402,7 +427,7 @@ def check_sanity(force=False): logging.critical('Cannot find %s, check the paths in %s' % (cmd, EM_CONFIG)) sys.exit(1) - if os.environ.get('EMCC_FAST_COMPILER') == '1': + if os.environ.get('EMCC_FAST_COMPILER') != '0': if not fastcomp_ok: logging.critical('failing sanity checks due to previous fastcomp failure') sys.exit(1) @@ -602,7 +627,11 @@ except: # Target choice. Must be synced with src/settings.js (TARGET_*) def get_llvm_target(): - return os.environ.get('EMCC_LLVM_TARGET') or 'le32-unknown-nacl' # 'i386-pc-linux-gnu' + if os.environ.get('EMCC_FAST_COMPILER') == '0': + if not os.environ.get('EMCC_LLVM_TARGET'): + os.environ['EMCC_LLVM_TARGET'] = 'le32-unknown-nacl' + return os.environ.get('EMCC_LLVM_TARGET') + return os.environ.get('EMCC_LLVM_TARGET') or 'asmjs-unknown-emscripten' LLVM_TARGET = get_llvm_target() # COMPILER_OPTS: options passed to clang when generating bitcode for us @@ -610,19 +639,28 @@ try: COMPILER_OPTS # Can be set in EM_CONFIG, optionally except: COMPILER_OPTS = [] -COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-DEMSCRIPTEN', '-D__EMSCRIPTEN__', - '-fno-math-errno', - #'-fno-threadsafe-statics', # disabled due to issue 1289 +COMPILER_OPTS = COMPILER_OPTS + [#'-fno-threadsafe-statics', # disabled due to issue 1289 '-target', LLVM_TARGET] -if LLVM_TARGET == 'le32-unknown-nacl': - COMPILER_OPTS = filter(lambda opt: opt != '-m32', COMPILER_OPTS) # le32 target is 32-bit anyhow, no need for -m32 - COMPILER_OPTS += ['-U__native_client__', '-U__pnacl__', '-U__ELF__'] # The nacl target is originally used for Google Native Client. Emscripten is not NaCl, so remove the platform #define, when using their triple. - -# Remove various platform specific defines, and set little endian -COMPILER_STANDARDIZATION_OPTS = ['-U__i386__', '-U__i386', '-Ui386', '-U__STRICT_ANSI__', '-D__IEEE_LITTLE_ENDIAN', - '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__', - '-U__APPLE__', '-U__linux__'] +# COMPILER_STANDARDIZATION_OPTS: Options to correct various predefined macro options. +COMPILER_STANDARDIZATION_OPTS = [] + +# When we're not using an appropriate target triple, use -m32 to get i386, which we +# can mostly make work. +if LLVM_TARGET != 'asmjs-unknown-emscripten' and LLVM_TARGET != 'le32-unknown-nacl': + COMPILER_OPTS += ['-m32'] + COMPILER_STANDARDIZATION_OPTS += ['-U__i386__', '-U__i386', '-Ui386', + '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__', + '-U__APPLE__', '-U__linux__'] + +# With the asmjs-unknown-emscripten target triple, clang sets up language modes +# and predefined macros properly. When using the other targets, we have to set things +# up manually. +if LLVM_TARGET != 'asmjs-unknown-emscripten': + COMPILER_OPTS += ['-fno-math-errno'] + COMPILER_STANDARDIZATION_OPTS += ['-D__IEEE_LITTLE_ENDIAN'] + COMPILER_OPTS += ['-DEMSCRIPTEN', '-D__EMSCRIPTEN__', '-fno-math-errno', + '-U__native_client__', '-U__pnacl__', '-U__ELF__'] USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK') @@ -642,8 +680,10 @@ if USE_EMSDK: '-Xclang', '-isystem' + path_from_root('system', 'include', 'SDL'), ] EMSDK_OPTS += COMPILER_STANDARDIZATION_OPTS - if LLVM_TARGET != 'le32-unknown-nacl': - EMSDK_CXX_OPTS = ['-nostdinc++'] # le32 target does not need -nostdinc++ + # For temporary compatibility, treat 'le32-unknown-nacl' as 'asmjs-unknown-emscripten'. + if LLVM_TARGET != 'asmjs-unknown-emscripten' and \ + LLVM_TARGET != 'le32-unknown-pnacl': + EMSDK_CXX_OPTS = ['-nostdinc++'] # asmjs-unknown-emscripten target does not need -nostdinc++ else: EMSDK_CXX_OPTS = [] COMPILER_OPTS += EMSDK_OPTS @@ -659,9 +699,12 @@ else: try: if 'gcparam' not in str(SPIDERMONKEY_ENGINE): + new_spidermonkey = SPIDERMONKEY_ENGINE if type(SPIDERMONKEY_ENGINE) is str: - SPIDERMONKEY_ENGINE = [SPIDERMONKEY_ENGINE] - SPIDERMONKEY_ENGINE += ['-e', "gcparam('maxBytes', 1024*1024*1024);"] # Our very large files need lots of gc heap + new_spidermonkey = [SPIDERMONKEY_ENGINE] + new_spidermonkey += ['-e', "gcparam('maxBytes', 1024*1024*1024);"] # Our very large files need lots of gc heap + JS_ENGINES = map(lambda x: x if x != SPIDERMONKEY_ENGINE else new_spidermonkey, JS_ENGINES) + SPIDERMONKEY_ENGINE = new_spidermonkey except NameError: pass @@ -803,18 +846,14 @@ class Settings2(type): @classmethod def apply_opt_level(self, opt_level, noisy=False): + if opt_level == 0 and os.environ.get('EMCC_FAST_COMPILER') == '0': + self.attrs['ASM_JS'] = 0 # non-fastcomp has asm off in -O1 if opt_level >= 1: self.attrs['ASM_JS'] = 1 self.attrs['ASSERTIONS'] = 0 self.attrs['DISABLE_EXCEPTION_CATCHING'] = 1 self.attrs['RELOOP'] = 1 self.attrs['ALIASING_FUNCTION_POINTERS'] = 1 - if opt_level >= 3: - # Aside from these, -O3 also runs closure compiler and llvm lto - self.attrs['FORCE_ALIGNED_MEMORY'] = 1 - self.attrs['DOUBLE_MODE'] = 0 - self.attrs['PRECISE_I64_MATH'] = 0 - if noisy: logging.warning('Applying some potentially unsafe optimizations! (Use -O2 if this fails.)') def __getattr__(self, attr): if attr in self.attrs: @@ -1173,7 +1212,7 @@ class Building: #opts += ['-debug-pass=Arguments'] if get_clang_version() == '3.4' and not Settings.SIMD: opts += ['-disable-loop-vectorization', '-disable-slp-vectorization'] # llvm 3.4 has these on by default - logging.debug('emcc: LLVM opts: ' + str(opts)) + logging.debug('emcc: LLVM opts: ' + ' '.join(opts)) target = out or (filename + '.opt.bc') output = Popen([LLVM_OPT, filename] + opts + ['-o', target], stdout=PIPE).communicate()[0] assert os.path.exists(target), 'Failed to run llvm optimizations: ' + output @@ -1473,7 +1512,7 @@ class Building: @staticmethod def ensure_relooper(relooper): if os.path.exists(relooper): return - if os.environ.get('EMCC_FAST_COMPILER') == '1': + if os.environ.get('EMCC_FAST_COMPILER') != '0': logging.debug('not building relooper to js, using it in c++ backend') return @@ -1548,7 +1587,7 @@ class Building: text = m.groups(0)[0] assert text.count('(') == 1 and text.count(')') == 1, 'must have simple expressions in emscripten_jcache_printf calls, no parens' assert text.count('"') == 2, 'must have simple expressions in emscripten_jcache_printf calls, no strings as varargs parameters' - if os.environ.get('EMCC_FAST_COMPILER') == '1': # fake it in fastcomp + if os.environ.get('EMCC_FAST_COMPILER') != '0': # fake it in fastcomp return text.replace('emscripten_jcache_printf', 'printf') start = text.index('(') end = text.rindex(')') @@ -1568,7 +1607,7 @@ JCache = cache.JCache(Cache) chunkify = cache.chunkify class JS: - memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASEH+]+)\)' + memory_initializer_pattern = '/\* memory initializer \*/ allocate\(\[([\d, ]+)\], "i8", ALLOC_NONE, ([\d+Runtime\.GLOBAL_BASEH]+)\);' no_memory_initializer_pattern = '/\* no memory initializer \*/' memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);' @@ -1652,6 +1691,75 @@ class JS: while x % by != 0: x += 1 return x + INITIALIZER_CHUNK_SIZE = 10240 + + @staticmethod + def collect_initializers(src): + ret = [] + max_offset = -1 + for init in re.finditer(JS.memory_initializer_pattern, src): + contents = init.group(1).split(',') + offset = sum([int(x) if x[0] != 'R' else 0 for x in init.group(2).split('+')]) + ret.append((offset, contents)) + assert offset > max_offset + max_offset = offset + return ret + + @staticmethod + def split_initializer(contents): + # given a memory initializer (see memory_initializer_pattern), split it up into multiple initializers to avoid long runs of zeros or a single overly-large allocator + ret = [] + l = len(contents) + maxx = JS.INITIALIZER_CHUNK_SIZE + i = 0 + start = 0 + while 1: + if i - start >= maxx or (i > start and i == l): + #print >> sys.stderr, 'new', start, i-start + ret.append((start, contents[start:i])) + start = i + if i == l: break + if contents[i] != '0': + i += 1 + else: + # look for a sequence of zeros + j = i + 1 + while j < l and contents[j] == '0': j += 1 + if j-i > maxx/10 or j-start >= maxx: + #print >> sys.stderr, 'skip', start, i-start, j-start + ret.append((start, contents[start:i])) # skip over the zeros starting at i and ending at j + start = j + i = j + return ret + + @staticmethod + def replace_initializers(src, inits): + class State: + first = True + def rep(m): + if not State.first: return '' + # write out all the new initializers in place of the first old one + State.first = False + def gen_init(init): + offset, contents = init + return '/* memory initializer */ allocate([%s], "i8", ALLOC_NONE, Runtime.GLOBAL_BASE%s);' % ( + ','.join(contents), + '' if offset == 0 else ('+%d' % offset) + ) + return '\n'.join(map(gen_init, inits)) + return re.sub(JS.memory_initializer_pattern, rep, src) + + @staticmethod + def optimize_initializer(src): + inits = JS.collect_initializers(src) + if len(inits) == 0: return None + assert len(inits) == 1 + init = inits[0] + offset, contents = init + assert offset == 0 # offset 0, singleton + if len(contents) <= JS.INITIALIZER_CHUNK_SIZE: return None + return JS.replace_initializers(src, JS.split_initializer(contents)) + # Compression of code and data for smaller downloads class Compression: on = False |