diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/js-optimizer.js | 25 | ||||
-rw-r--r-- | tools/jsrun.py | 1 | ||||
-rw-r--r-- | tools/shared.py | 144 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-outline1-output.js | 133 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-outline1.js | 40 |
5 files changed, 288 insertions, 55 deletions
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index b42164f9..c029ab4e 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1564,6 +1564,7 @@ function normalizeAsm(func) { var data = { params: {}, // ident => ASM_* type vars: {}, // ident => ASM_* type + inlines: [], // list of inline assembly copies }; // process initial params var stats = func[3]; @@ -1621,6 +1622,10 @@ function normalizeAsm(func) { node[0] = 'name'; node[1] = 'Math_' + node[2]; } + } else if (type === 'call' && node[1][0] === 'function') { + assert(!node[1][1]); // anonymous functions only + data.inlines.push(node[1]); + node[1] = ['name', 'inlinejs']; // empty out body, leave arguments, so they are eliminated/minified properly } }); i++; @@ -1669,6 +1674,14 @@ function denormalizeAsm(func, data) { } else { stats[next] = emptyNode(); } + if (data.inlines.length > 0) { + var i = 0; + traverse(func, function(node, type) { + if (type === 'call' && node[1][0] === 'name' && node[1][1] === 'inlinejs') { + node[1] = data.inlines[i++]; // swap back in the body + } + }); + } //printErr('denormalized \n\n' + astToSrc(func) + '\n\n'); } @@ -2019,6 +2032,7 @@ function registerize(ast) { var finalAsmData = { params: {}, vars: {}, + inlines: asmData.inlines, }; for (var i = 1; i < nextReg; i++) { var reg = fullNames[i]; @@ -3018,6 +3032,9 @@ function outline(ast) { } var ignore = []; traverse(func, function(node) { + if (node[0] === 'while' && node[2][0] !== 'block') { + node[2] = ['block', [node[2]]]; // so we have a list of statements and can flatten while(1) switch + } var stats = getStatements(node); if (stats) { for (var i = 0; i < stats.length; i++) { @@ -3452,7 +3469,7 @@ function outline(ast) { }); // finalize var newFunc = ['defun', newIdent, ['sp'], code]; - var newAsmData = { params: { sp: ASM_INT }, vars: {} }; + var newAsmData = { params: { sp: ASM_INT }, vars: {}, inlines: asmData.inlines }; for (var v in codeInfo.reads) { if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData); } @@ -3694,8 +3711,10 @@ function outline(ast) { } } } - ret.push(func); - printErr('... resulting sizes of ' + func[1] + ' is ' + ret.map(measureSize) + '\n'); + if (ret) { + ret.push(func); + printErr('... resulting sizes of ' + func[1] + ' is ' + ret.map(measureSize) + '\n'); + } } denormalizeAsm(func, asmData); }); diff --git a/tools/jsrun.py b/tools/jsrun.py index 91038f6e..6f77ce51 100644 --- a/tools/jsrun.py +++ b/tools/jsrun.py @@ -10,6 +10,7 @@ def timeout_run(proc, timeout, note='unnamed process', full_output=False): proc.kill() # XXX bug: killing emscripten.py does not kill it's child process! raise Exception("Timed out: " + note) out = proc.communicate() + out = map(lambda o: '' if o is None else o, out) return '\n'.join(out) if full_output else out[0] def run_js(filename, engine=None, args=[], check_timeout=False, stdin=None, stdout=PIPE, stderr=None, cwd=None, full_output=False): diff --git a/tools/shared.py b/tools/shared.py index 3ee5db23..8031d99c 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -283,6 +283,20 @@ def check_node_version(): logging.warning('cannot check node version: %s', e) return False +# Finds the system temp directory without resorting to using the one configured in .emscripten +def find_temp_directory(): + if WINDOWS: + if os.getenv('TEMP') and os.path.isdir(os.getenv('TEMP')): + return os.getenv('TEMP') + elif os.getenv('TMP') and os.path.isdir(os.getenv('TMP')): + return os.getenv('TMP') + elif os.path.isdir('C:\\temp'): + return os.getenv('C:\\temp') + else: + return None # No luck! + else: + return '/tmp' + # Check that basic stuff we need (a JS engine to compile, Node.js, and Clang and LLVM) # exists. # The test runner always does this check (through |force|). emcc does this less frequently, @@ -429,7 +443,6 @@ EMCC = path_from_root('emcc') EMXX = path_from_root('em++') EMAR = path_from_root('emar') EMRANLIB = path_from_root('emranlib') -EMLIBTOOL = path_from_root('emlibtool') EMCONFIG = path_from_root('em-config') EMLINK = path_from_root('emlink.py') EMMAKEN = path_from_root('tools', 'emmaken.py') @@ -451,8 +464,13 @@ class Configuration: try: self.TEMP_DIR = TEMP_DIR except NameError: - logging.debug('TEMP_DIR not defined in ~/.emscripten, using /tmp') - self.TEMP_DIR = '/tmp' + self.TEMP_DIR = find_temp_directory() + if self.TEMP_DIR == None: + logging.critical('TEMP_DIR not defined in ' + os.path.expanduser('~\\.emscripten') + ", and could not detect a suitable directory! Please configure .emscripten to contain a variable TEMP_DIR='/path/to/temp/dir'.") + logging.debug('TEMP_DIR not defined in ~/.emscripten, using ' + self.TEMP_DIR) + + if not os.path.isdir(self.TEMP_DIR): + logging.critical("The temp directory TEMP_DIR='" + self.TEMP_DIR + "' doesn't seem to exist! Please make sure that the path is correct.") self.CANONICAL_TEMP_DIR = os.path.join(self.TEMP_DIR, 'emscripten_temp') @@ -470,12 +488,13 @@ class Configuration: save_debug_files=os.environ.get('EMCC_DEBUG_SAVE')) def apply_configuration(): - global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR + global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR, TEMP_DIR configuration = Configuration() DEBUG = configuration.DEBUG EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR DEBUG_CACHE = configuration.DEBUG_CACHE CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR + TEMP_DIR = configuration.TEMP_DIR apply_configuration() logging.basicConfig(format='%(levelname)-8s %(name)s: %(message)s') @@ -527,15 +546,13 @@ def get_llvm_target(): return os.environ.get('EMCC_LLVM_TARGET') or 'le32-unknown-nacl' # 'i386-pc-linux-gnu' LLVM_TARGET = get_llvm_target() +# COMPILER_OPTS: options passed to clang when generating bitcode for us try: COMPILER_OPTS # Can be set in EM_CONFIG, optionally except: COMPILER_OPTS = [] -# Force a simple, standard target as much as possible: target 32-bit linux, and disable various flags that hint at other platforms -COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__i386', '-Ui386', - '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__', - '-DEMSCRIPTEN', '-D__EMSCRIPTEN__', '-U__STRICT_ANSI__', - '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno', +COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-DEMSCRIPTEN', '-D__EMSCRIPTEN__', + '-fno-math-errno', #'-fno-threadsafe-statics', # disabled due to issue 1289 '-target', LLVM_TARGET] @@ -543,6 +560,11 @@ 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__'] + USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK') if USE_EMSDK: @@ -559,9 +581,8 @@ if USE_EMSDK: '-Xclang', '-isystem' + path_from_root('system', 'include', 'gfx'), '-Xclang', '-isystem' + path_from_root('system', 'include', 'net'), '-Xclang', '-isystem' + path_from_root('system', 'include', 'SDL'), - ] + [ - '-U__APPLE__', '-U__linux__' ] + EMSDK_OPTS += COMPILER_STANDARDIZATION_OPTS if LLVM_TARGET != 'le32-unknown-nacl': EMSDK_CXX_OPTS = ['-nostdinc++'] # le32 target does not need -nostdinc++ else: @@ -570,6 +591,7 @@ if USE_EMSDK: else: EMSDK_OPTS = [] EMSDK_CXX_OPTS = [] + COMPILER_OPTS += COMPILER_STANDARDIZATION_OPTS #print >> sys.stderr, 'SDK opts', ' '.join(EMSDK_OPTS) #print >> sys.stderr, 'Compiler opts', ' '.join(COMPILER_OPTS) @@ -717,6 +739,10 @@ class Settings2(type): return ret @classmethod + def copy(self, values): + self.attrs = values + + @classmethod def apply_opt_level(self, opt_level, noisy=False): if opt_level >= 1: self.attrs['ASM_JS'] = 1 @@ -782,7 +808,6 @@ class Building: env['LD'] = EMCC if not WINDOWS else 'python %r' % EMCC env['LDSHARED'] = EMCC if not WINDOWS else 'python %r' % EMCC env['RANLIB'] = EMRANLIB if not WINDOWS else 'python %r' % EMRANLIB - #env['LIBTOOL'] = EMLIBTOOL if not WINDOWS else 'python %r' % EMLIBTOOL env['EMMAKEN_COMPILER'] = Building.COMPILER env['EMSCRIPTEN_TOOLS'] = path_from_root('tools') env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(Building.COMPILER_TEST_OPTS) @@ -797,33 +822,12 @@ class Building: @staticmethod def handle_CMake_toolchain(args, env): - CMakeToolchain = ('''# the name of the target operating system -SET(CMAKE_SYSTEM_NAME Linux) - -# which C and C++ compiler to use -SET(CMAKE_C_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/emcc) -SET(CMAKE_CXX_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/em++) -SET(CMAKE_AR %(winfix)s$EMSCRIPTEN_ROOT/emar) -SET(CMAKE_RANLIB %(winfix)s$EMSCRIPTEN_ROOT/emranlib) -SET(CMAKE_C_FLAGS $CFLAGS) -SET(CMAKE_CXX_FLAGS $CXXFLAGS) - -# here is the target environment located -SET(CMAKE_FIND_ROOT_PATH $EMSCRIPTEN_ROOT/system/include ) - -# adjust the default behaviour of the FIND_XXX() commands: -# search headers and libraries in the target environment, search -# programs in the host environment -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) -set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS else 'python ' }) \ - .replace('$EMSCRIPTEN_ROOT', path_from_root('').replace('\\', '/')) \ - .replace('$CFLAGS', env['CFLAGS']) \ - .replace('$CXXFLAGS', env['CFLAGS']) - toolchainFile = mkstemp(suffix='.cmaketoolchain.txt', dir=configuration.TEMP_DIR)[1] - open(toolchainFile, 'w').write(CMakeToolchain) - args.append('-DCMAKE_TOOLCHAIN_FILE=%s' % os.path.abspath(toolchainFile)) + # Don't append a toolchain file if the user specified one already. + for arg in args: + if '-DCMAKE_TOOLCHAIN_FILE' in arg: + return args + + args.append('-DCMAKE_TOOLCHAIN_FILE=' + path_from_root('cmake', 'Platform', 'Emscripten.cmake')) return args @staticmethod @@ -843,6 +847,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e raise del env['EMMAKEN_JUST_CONFIGURE'] if process.returncode is not 0: + logging.error('Configure step failed with non-zero return code ' + str(process.returncode) + '! Command line: ' + str(args)) raise subprocess.CalledProcessError(cmd=args, returncode=process.returncode) @staticmethod @@ -893,12 +898,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e # except: # pass env = Building.get_building_env(native) + verbose_level = int(os.getenv('EM_BUILD_VERBOSE')) if os.getenv('EM_BUILD_VERBOSE') != None else 0 for k, v in env_init.iteritems(): env[k] = v if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |link| call) try: - Building.configure(configure + configure_args, stdout=open(os.path.join(project_dir, 'configure_'), 'w'), - stderr=open(os.path.join(project_dir, 'configure_err'), 'w'), env=env) + Building.configure(configure + configure_args, env=env, stdout=open(os.path.join(project_dir, 'configure_'), 'w') if verbose_level < 2 else None, + stderr=open(os.path.join(project_dir, 'configure_err'), 'w') if verbose_level < 1 else None) except subprocess.CalledProcessError, e: pass # Ignore exit code != 0 def open_make_out(i, mode='r'): @@ -906,13 +912,16 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e def open_make_err(i, mode='r'): return open(os.path.join(project_dir, 'make_err' + str(i)), mode) - + + if verbose_level >= 3: + make_args += ['VERBOSE=1'] + for i in range(2): # FIXME: Sad workaround for some build systems that need to be run twice to succeed (e.g. poppler) with open_make_out(i, 'w') as make_out: with open_make_err(i, 'w') as make_err: try: - Building.make(make + make_args, stdout=make_out, - stderr=make_err, env=env) + Building.make(make + make_args, stdout=make_out if verbose_level < 2 else None, + stderr=make_err if verbose_level < 1 else None, env=env) except subprocess.CalledProcessError, e: pass # Ignore exit code != 0 try: @@ -924,10 +933,11 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e break except Exception, e: if i > 0: - # Due to the ugly hack above our best guess is to output the first run - with open_make_err(0) as ferr: - for line in ferr: - sys.stderr.write(line) + if verbose_level == 0: + # Due to the ugly hack above our best guess is to output the first run + with open_make_err(0) as ferr: + for line in ferr: + sys.stderr.write(line) raise Exception('could not build library ' + name + ' due to exception ' + str(e)) if old_dir: os.chdir(old_dir) @@ -1176,8 +1186,6 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e def get_safe_internalize(): if not Building.can_build_standalone(): return [] # do not internalize anything exps = expand_response(Settings.EXPORTED_FUNCTIONS) - if '_malloc' not in exps: exps.append('_malloc') # needed internally, even if user did not add to EXPORTED_FUNCTIONS - if '_free' not in exps: exps.append('_free') exports = ','.join(map(lambda exp: exp[1:], exps)) # internalize carefully, llvm 3.2 will remove even main if not told not to return ['-internalize', '-internalize-public-api-list=' + exports] @@ -1426,7 +1434,7 @@ JCache = cache.JCache(Cache) chunkify = cache.chunkify class JS: - memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASE+]+)\)' + memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASEH+]+)\)' no_memory_initializer_pattern = '/\* no memory initializer \*/' memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);' @@ -1438,11 +1446,32 @@ class JS: return ident.replace('%', '$').replace('@', '_') @staticmethod + def make_extcall(sig, named=True): + args = ','.join(['a' + str(i) for i in range(1, len(sig))]) + args = 'index' + (',' if args else '') + args + # C++ exceptions are numbers, and longjmp is a string 'longjmp' + ret = '''function%s(%s) { + %sModule["dynCall_%s"](%s); +}''' % ((' extCall_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args) + + if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS: + # guard against cross-module stack leaks + ret = ret.replace(') {\n', ''') { + try { + var preStack = asm.stackSave(); +''').replace(';\n}', '''; + } finally { + assert(asm.stackSave() == preStack); + } +}''') + return ret + + @staticmethod def make_invoke(sig, named=True): args = ','.join(['a' + str(i) for i in range(1, len(sig))]) args = 'index' + (',' if args else '') + args # C++ exceptions are numbers, and longjmp is a string 'longjmp' - return '''function%s(%s) { + ret = '''function%s(%s) { try { %sModule["dynCall_%s"](%s); } catch(e) { @@ -1451,6 +1480,17 @@ class JS: } }''' % ((' invoke_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args) + if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS: + # guard against cross-module stack leaks + ret = ret.replace(' try {', ''' var preStack = asm.stackSave(); + try { +''').replace(' }\n}', ''' } finally { + assert(asm.stackSave() == preStack); + } +}''') + + return ret + @staticmethod def align(x, by): while x % by != 0: x += 1 diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js index 612da16a..27f93d8a 100644 --- a/tools/test-js-optimizer-asm-outline1-output.js +++ b/tools/test-js-optimizer-asm-outline1-output.js @@ -372,6 +372,57 @@ function switchh() { } STACKTOP = sp; } +function switchh2() { + var helper$0 = 0, helper$1 = 0, sp = 0; + sp = STACKTOP; + STACKTOP = STACKTOP + 280 | 0; + while (1) { + helper$0 = 1; + helper$1 = x; + if (helper$0) { + helper$0 = 0; + switch (helper$1 | 0) { + case 0: + f(0); + g(); + break; + default: + { + helper$0 = 1; + } + } + } + HEAP32[sp + 8 >> 2] = helper$0; + HEAP32[sp + 16 >> 2] = helper$1; + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; + switchh2$2(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; + tempValue = HEAP32[sp + 40 >> 2] | 0; + tempInt = HEAP32[sp + 44 >> 2] | 0; + tempDouble = +HEAPF32[sp + 44 >> 2]; + HEAP32[sp + 40 >> 2] = 0; + HEAP32[sp + 44 >> 2] = 0; + if ((tempValue | 0) == 5) { + STACKTOP = sp; + return; + } + HEAP32[sp + 8 >> 2] = helper$0; + HEAP32[sp + 16 >> 2] = helper$1; + HEAP32[sp + 32 >> 2] = 0; + HEAP32[sp + 36 >> 2] = 0; + switchh2$1(sp); + helper$0 = HEAP32[sp + 8 >> 2] | 0; + if (helper$0) { + helper$0 = 0; + HEAP32[sp + 16 >> 2] = helper$1; + HEAP32[sp + 24 >> 2] = 0; + HEAP32[sp + 28 >> 2] = 0; + switchh2$0(sp); + } + } + STACKTOP = sp; +} function lin$0(sp) { sp = sp | 0; c(14); @@ -793,4 +844,86 @@ function switchh$2(sp) { } while (0); HEAP32[sp + 8 >> 2] = helper$0; } +function switchh2$0(sp) { + sp = sp | 0; + var helper$1 = 0; + helper$1 = HEAP32[sp + 16 >> 2] | 0; + switch (helper$1 | 0) { + case 4: + f(4); + g(); + case 5: + f(5); + g(); + case 6: + f(6); + g(); + default: + print(9); + } +} +function switchh2$1(sp) { + sp = sp | 0; + var helper$0 = 0, helper$1 = 0; + helper$0 = HEAP32[sp + 8 >> 2] | 0; + helper$1 = HEAP32[sp + 16 >> 2] | 0; + if (helper$0) { + helper$0 = 0; + switch (helper$1 | 0) { + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 3: + f(3); + g(); + break; + default: + { + helper$0 = 1; + } + } + } + HEAP32[sp + 8 >> 2] = helper$0; +} +function switchh2$2(sp) { + sp = sp | 0; + var helper$0 = 0, helper$1 = 0; + helper$0 = HEAP32[sp + 8 >> 2] | 0; + helper$1 = HEAP32[sp + 16 >> 2] | 0; + OL : do { + if (helper$0) { + helper$0 = 0; + switch (helper$1 | 0) { + case 1: + f(1); + g(); + return; + default: + { + helper$0 = 1; + } + } + } + if (helper$0) { + helper$0 = 0; + switch (helper$1 | 0) { + case 2: + f(2); + g(); + break; + default: + { + helper$0 = 1; + } + } + } + } while (0); + HEAP32[sp + 8 >> 2] = helper$0; +} diff --git a/tools/test-js-optimizer-asm-outline1.js b/tools/test-js-optimizer-asm-outline1.js index 4282ec8e..b7ec9011 100644 --- a/tools/test-js-optimizer-asm-outline1.js +++ b/tools/test-js-optimizer-asm-outline1.js @@ -307,5 +307,45 @@ function switchh() { } } } +function switchh2() { + while (1) switch (x) { + case 0: + f(0); + g(); + break; + case 1: + f(1); + g(); + return; + case 2: + f(2); + g(); + break; + case 21: // gotta keem em unseparated + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 3: // these too + f(3); + g(); + break; + case 4: + f(4); + g(); + case 5: + f(5); + g(); + case 6: + f(6); + g(); + default: + print(9); + } +} // EMSCRIPTEN_GENERATED_FUNCTIONS // EXTRA_INFO: { "sizeToOutline": 30, "allowCostlyOutlines": 1 } |