diff options
39 files changed, 3607 insertions, 293 deletions
@@ -48,6 +48,8 @@ a license to everyone to use it as detailed in LICENSE.) * Jasper St. Pierre <jstpierre@mecheye.net> * Manuel Schölling <manuel.schoelling@gmx.de> * Bruce Mitchener, Jr. <bruce.mitchener@gmail.com> - - +* Michael Bishop <mbtyke@gmail.com> +* Roger Braun <roger@rogerbraun.net> +* Vladimir Vukicevic <vladimir@pobox.com> (copyright owned by Mozilla Foundation) +* Lorant Pinter <lorant.pinter@prezi.com> @@ -11,6 +11,8 @@ import os, subprocess, sys from tools import shared DEBUG = os.environ.get('EMCC_DEBUG') +if DEBUG == "0": + DEBUG = None newargs = [shared.LLVM_AR] + sys.argv[1:] @@ -1,4 +1,5 @@ #!/usr/bin/env python2 +# -*- Mode: python -*- ''' emcc - compiler helper script @@ -90,7 +91,10 @@ LLVM_OPT_LEVEL = { 3: 3, } -DEBUG = int(os.environ.get('EMCC_DEBUG') or 0) +DEBUG = os.environ.get('EMCC_DEBUG') +if DEBUG == "0": + DEBUG = None + TEMP_DIR = os.environ.get('EMCC_TEMP_DIR') LEAVE_INPUTS_RAW = os.environ.get('EMCC_LEAVE_INPUTS_RAW') # Do not compile .ll files into .bc, just compile them with emscripten directly # Not recommended, this is mainly for the test runner, or if you have some other @@ -118,6 +122,28 @@ if len(sys.argv) == 1: print 'emcc: no input files' exit(1) +# read response files very early on +response_file = True +while response_file: + response_file = None + for index in range(1, len(sys.argv)): + if sys.argv[index][0] == '@': + # found one, loop again next time + print >>sys.stderr, 'emcc: using response file: %s' % response_file + response_file = sys.argv[index][1:] + if not os.path.exists(response_file): + print >>sys.stderr, 'emcc: error: Response file not found: %s' % response_file + exit(1) + + response_fd = open(response_file, 'r') + extra_args = shlex.split(response_fd.read()) + response_fd.close() + + # slice in extra_args in place of the response file arg + sys.argv[index:index+1] = extra_args + #if DEBUG: print >>sys.stderr, "Expanded response file: " + " | ".join(sys.argv) + break + if sys.argv[1] == '--version': revision = '(unknown revision)' here = os.getcwd() @@ -629,7 +655,8 @@ try: ignore_dynamic_linking = False shell_path = shared.path_from_root('src', 'shell.html') js_libraries = [] - keep_debug = False + keep_llvm_debug = False + keep_js_debug = False bind = False jcache = False if use_cxx: @@ -645,7 +672,8 @@ try: for i in range(len(newargs)): newargs[i] = newargs[i].strip() # On Windows Vista (and possibly others), excessive spaces in the command line leak into the items in this array, so trim e.g. 'foo.cpp ' -> 'foo.cpp' if newargs[i].startswith('-O'): - requested_level = newargs[i][2] + # Let -O default to -O2, which is what gcc does. + requested_level = newargs[i][2:] or '2' if requested_level == 's': print >> sys.stderr, 'emcc: warning: -Os is ignored (use -O0, -O1, -O2)' else: @@ -696,7 +724,8 @@ try: newargs[i] = '' newargs[i+1] = '' elif newargs[i] == '-g': - keep_debug = True + keep_llvm_debug = True + keep_js_debug = True elif newargs[i] == '--bind': bind = True newargs[i] = '' @@ -767,7 +796,8 @@ try: if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level] if llvm_lto is None: llvm_lto = llvm_opts > 0 - if opt_level <= 0: keep_debug = True # always keep debug in -O0 + if opt_level <= 0: keep_llvm_debug = keep_js_debug = True # always keep debug in -O0 + if opt_level > 0: keep_llvm_debug = False # JS optimizer wipes out llvm debug info from being visible if closure is None and opt_level == 3: closure = True if DEBUG: start_time = time.time() # done after parsing arguments, which might affect debug state @@ -908,9 +938,9 @@ try: shared.Settings.CORRECT_OVERFLOWS = 1 if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2: - keep_debug = True # must keep debug info to do line-by-line operations + keep_llvm_debug = True # must keep debug info to do line-by-line operations - if keep_debug and closure: + if (keep_llvm_debug or keep_js_debug) and closure: print >> sys.stderr, 'emcc: warning: disabling closure because debug info was requested' closure = False @@ -1017,7 +1047,7 @@ try: def create_libcxx(): if DEBUG: print >> sys.stderr, 'emcc: building libcxx for cache' os = [] - for src in ['algorithm.cpp', 'condition_variable.cpp', 'future.cpp', 'iostream.cpp', 'memory.cpp', 'random.cpp', 'stdexcept.cpp', 'system_error.cpp', 'utility.cpp', 'bind.cpp', 'debug.cpp', 'hash.cpp', 'mutex.cpp', 'string.cpp', 'thread.cpp', 'valarray.cpp', 'chrono.cpp', 'exception.cpp', 'ios.cpp', 'locale.cpp', 'regex.cpp', 'strstream.cpp', 'typeinfo.cpp']: + for src in ['algorithm.cpp', 'condition_variable.cpp', 'future.cpp', 'iostream.cpp', 'memory.cpp', 'random.cpp', 'stdexcept.cpp', 'system_error.cpp', 'utility.cpp', 'bind.cpp', 'debug.cpp', 'hash.cpp', 'mutex.cpp', 'string.cpp', 'thread.cpp', 'valarray.cpp', 'chrono.cpp', 'exception.cpp', 'ios.cpp', 'locale.cpp', 'regex.cpp', 'strstream.cpp']: o = in_temp(src + '.o') execute([shared.PYTHON, shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', src), '-o', o], stdout=stdout, stderr=stderr) os.append(o) @@ -1036,7 +1066,7 @@ try: def create_libcxxabi(): if DEBUG: print >> sys.stderr, 'emcc: building libcxxabi for cache' os = [] - for src in ['private_typeinfo.cpp']: + for src in ['private_typeinfo.cpp', 'typeinfo.cpp']: o = in_temp(src + '.o') execute([shared.PYTHON, shared.EMXX, shared.path_from_root('system', 'lib', 'libcxxabi', 'src', src), '-o', o], stdout=stdout, stderr=stderr) os.append(o) @@ -1050,29 +1080,32 @@ try: libcxxabi_symbols = filter(lambda symbol: symbol not in libc_symbols, libcxxabi_symbols) libcxxabi_symbols = set(libcxxabi_symbols) - force = False # If we have libcxx, we must force inclusion of libc, since libcxx uses new internally. Note: this is kind of hacky - + # If we have libcxx, we must force inclusion of libc, since libcxx uses new internally. Note: this is kind of hacky + # Settings this in the environment will avoid checking dependencies and make building big projects a little faster + force = os.environ.get('EMCC_FORCE_STDLIBS') + has = need = None for name, create, fix, library_symbols in [('libcxx', create_libcxx, fix_libcxx, libcxx_symbols), ('libcxxabi', create_libcxxabi, fix_libcxxabi, libcxxabi_symbols), ('libc', create_libc, fix_libc, libc_symbols)]: - need = set() - has = set() - for temp_file in temp_files: - symbols = shared.Building.llvm_nm(temp_file) - for library_symbol in library_symbols: - if library_symbol in symbols.undefs: - need.add(library_symbol) - if library_symbol in symbols.defs: - has.add(library_symbol) - for haz in has: # remove symbols that are supplied by another of the inputs - if haz in need: - need.remove(haz) - if DEBUG: print >> sys.stderr, 'emcc: considering including %s: we need %s and have %s' % (name, str(need), str(has)) + if not force: + need = set() + has = set() + for temp_file in temp_files: + symbols = shared.Building.llvm_nm(temp_file) + for library_symbol in library_symbols: + if library_symbol in symbols.undefs: + need.add(library_symbol) + if library_symbol in symbols.defs: + has.add(library_symbol) + for haz in has: # remove symbols that are supplied by another of the inputs + if haz in need: + need.remove(haz) + if DEBUG: print >> sys.stderr, 'emcc: considering including %s: we need %s and have %s' % (name, str(need), str(has)) if force or len(need) > 0: # We need to build and link the library in if DEBUG: print >> sys.stderr, 'emcc: including %s' % name libfile = shared.Cache.get(name, create) - if len(has) > 0: + if has and len(has) > 0: # remove the symbols we do not need fixed = in_temp(uniquename(libfile)) + '.bc' shutil.copyfile(libfile, fixed) @@ -1082,7 +1115,7 @@ try: libfile = fixed extra_files_to_link.append(libfile) force = True - if fix: + if fix and need: fix(need) # First, combine the bitcode files if there are several. We must also link if we have a singleton .a @@ -1090,7 +1123,10 @@ try: (not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_SUFFIXES or suffix(temp_files[0]) in DYNAMICLIB_SUFFIXES) and shared.Building.is_ar(temp_files[0])): linker_inputs = temp_files + extra_files_to_link if DEBUG: print >> sys.stderr, 'emcc: linking: ', linker_inputs + t0 = time.time() shared.Building.link(linker_inputs, in_temp(target_basename + '.bc')) + t1 = time.time() + if DEBUG: print >> sys.stderr, 'emcc: linking took %.2f seconds' % (t1 - t0) final = in_temp(target_basename + '.bc') else: if not LEAVE_INPUTS_RAW: @@ -1117,7 +1153,7 @@ try: # Optimize, if asked to if not LEAVE_INPUTS_RAW: - link_opts = [] if keep_debug and opt_level == 0 else ['-strip-debug'] # remove LLVM debug info in -O1+, since the optimizer removes it anyhow + link_opts = [] if keep_llvm_debug else ['-strip-debug'] # remove LLVM debug info in -O1+, since the optimizer removes it anyhow if llvm_opts > 0: shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts) if DEBUG: save_intermediate('opt', 'bc') @@ -1208,7 +1244,7 @@ try: def flush_js_optimizer_queue(): global final, js_optimizer_queue if len(js_optimizer_queue) > 0 and not(len(js_optimizer_queue) == 1 and js_optimizer_queue[0] == 'last'): - if DEBUG < 2: + if DEBUG != '2': if shared.Settings.ASM_JS: js_optimizer_queue = ['asm'] + js_optimizer_queue if DEBUG: print >> sys.stderr, 'emcc: applying js optimization passes:', js_optimizer_queue @@ -1227,7 +1263,7 @@ try: if opt_level >= 1: if DEBUG: print >> sys.stderr, 'emcc: running pre-closure post-opts' - if DEBUG >= 2: + if DEBUG == '2': # Clean up the syntax a bit final = shared.Building.js_optimizer(final, [], jcache) if DEBUG: save_intermediate('pretty') @@ -1249,7 +1285,7 @@ try: if DEBUG: print >> sys.stderr, 'emcc: running closure' final = shared.Building.closure_compiler(final) if DEBUG: save_intermediate('closure') - elif shared.Settings.RELOOP and not closure and not keep_debug: + elif shared.Settings.RELOOP and not closure and not keep_js_debug: # do this if closure is not enabled (it gives similar speedups), and we do not need to keep debug info around js_optimizer_queue += ['registerize'] diff --git a/emscripten.py b/emscripten.py index af762a21..704e2b25 100755 --- a/emscripten.py +++ b/emscripten.py @@ -21,6 +21,9 @@ WARNING: You should normally never use this! Use emcc instead. from tools import shared DEBUG = os.environ.get('EMCC_DEBUG') +if DEBUG == "0": + DEBUG = None +DEBUG_CACHE = DEBUG and "cache" in DEBUG __rootpath__ = os.path.abspath(os.path.dirname(__file__)) def path_from_root(*pathelems): @@ -43,7 +46,7 @@ def scan(ll, settings): if len(blockaddrs) > 0: settings['NECESSARY_BLOCKADDRS'] = blockaddrs -NUM_CHUNKS_PER_CORE = 5 +NUM_CHUNKS_PER_CORE = 1.25 MIN_CHUNK_SIZE = 1024*1024 MAX_CHUNK_SIZE = float(os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or 'inf') # configuring this is just for debugging purposes @@ -131,7 +134,7 @@ def emscript(infile, settings, outfile, libraries=[]): settings_file = temp_files.get('.txt').name def save_settings(): global settings_text - settings_text = json.dumps(settings) + settings_text = json.dumps(settings, sort_keys=True) s = open(settings_file, 'w') s.write(settings_text) s.close() @@ -145,7 +148,21 @@ def emscript(infile, settings, outfile, libraries=[]): if jcache: keys = [pre_input, settings_text, ','.join(libraries)] shortkey = shared.JCache.get_shortkey(keys) + if DEBUG_CACHE: print >>sys.stderr, 'shortkey', shortkey + out = shared.JCache.get(shortkey, keys) + + if DEBUG_CACHE and not out: + dfpath = os.path.join(shared.TEMP_DIR, "ems_" + shortkey) + dfp = open(dfpath, 'w') + dfp.write(pre_input); + dfp.write("\n\n========================== settings_text\n\n"); + dfp.write(settings_text); + dfp.write("\n\n========================== libraries\n\n"); + dfp.write("\n".join(libraries)) + dfp.close() + print >>sys.stderr, ' cache miss, key data dumped to %s' % dfpath + if out and DEBUG: print >> sys.stderr, ' loading pre from jcache' if not out: open(pre_file, 'w').write(pre_input) @@ -160,12 +177,12 @@ def emscript(infile, settings, outfile, libraries=[]): # Phase 2 - func - cores = multiprocessing.cpu_count() + cores = int(os.environ.get('EMCC_CORES') or multiprocessing.cpu_count()) assert cores >= 1 if cores > 1: - intended_num_chunks = cores * NUM_CHUNKS_PER_CORE + intended_num_chunks = int(round(cores * NUM_CHUNKS_PER_CORE)) chunk_size = max(MIN_CHUNK_SIZE, total_ll_size / intended_num_chunks) - chunk_size += 3*len(meta) # keep ratio of lots of function code to meta (expensive to process, and done in each parallel task) + chunk_size += 3*len(meta) + len(forwarded_data)/3 # keep ratio of lots of function code to meta (expensive to process, and done in each parallel task) and forwarded data (less expensive but potentially significant) chunk_size = min(MAX_CHUNK_SIZE, chunk_size) else: chunk_size = MAX_CHUNK_SIZE # if 1 core, just use the max chunk size @@ -317,15 +334,12 @@ def emscript(infile, settings, outfile, libraries=[]): params = ','.join(['p%d' % p for p in range(len(sig)-1)]) coercions = ';'.join(['p%d = %sp%d%s' % (p, '+' if sig[p+1] != 'i' else '', p, '' if sig[p+1] != 'i' else '|0') for p in range(len(sig)-1)]) + ';' ret = '' if sig[0] == 'v' else ('return %s0' % ('+' if sig[0] != 'i' else '')) - return ('function %s(%s) { %s abort(%d); %s };' % (bad, params, coercions, i, ret), raw.replace('[0,', '[' + bad + ',').replace(',0,', ',' + bad + ',').replace(',0,', ',' + bad + ',').replace(',0]', ',' + bad + ']').replace(',0]', ',' + bad + ']')) + return ('function %s(%s) { %s abort(%d); %s };' % (bad, params, coercions, i, ret), raw.replace('[0,', '[' + bad + ',').replace(',0,', ',' + bad + ',').replace(',0,', ',' + bad + ',').replace(',0]', ',' + bad + ']').replace(',0]', ',' + bad + ']').replace(',0\n', ',' + bad + '\n')) infos = [make_table(sig, raw) for sig, raw in last_forwarded_json['Functions']['tables'].iteritems()] function_tables_defs = '\n'.join([info[0] for info in infos] + [info[1] for info in infos]) asm_setup = '' - maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil']] - if settings['USE_MATH_IMUL']: - maths += ['Math.imul'] - asm_setup += 'if (!Math.imul) Math.imul = function(x, y) { return (x*y)|0 }; // # not a real polyfill since semantics not identical, but close and fairly fast\n' + maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil', 'imul']] fundamentals = ['Math', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array'] math_envs = ['Runtime.bitshift64', 'Math.min'] # TODO: move min to maths asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs]) diff --git a/src/analyzer.js b/src/analyzer.js index adc615fb..c930231f 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -684,9 +684,9 @@ function analyzer(data, sidePass) { params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null], type: 'i32', }; - if (j == 0 && isUnsignedOp(value.op) && sourceBits < 32) { + if (j == 0 && sourceBits < 32) { // zext sign correction - result.ident = makeSignOp(result.ident, 'i' + sourceBits, 'un', 1, 1); + result.ident = makeSignOp(result.ident, 'i' + sourceBits, isUnsignedOp(value.op) ? 'un' : 're', 1, 1); } if (fraction != 0) { var other = { diff --git a/src/corruptionCheck.js b/src/corruptionCheck.js index ae2a0bdf..315f5cf0 100644 --- a/src/corruptionCheck.js +++ b/src/corruptionCheck.js @@ -6,6 +6,7 @@ var CorruptionChecker = { ptrs: {}, checks: 0, + checkFrequency: 1, init: function() { this.realMalloc = _malloc; @@ -14,13 +15,18 @@ var CorruptionChecker = { this.realFree = _free; _free = Module['_free'] = this.free; + if (typeof _realloc != 'undefined') { + this.realRealloc = _realloc; + _realloc = Module['_realloc'] = this.realloc; + } + __ATEXIT__.push({ func: function() { Module.printErr('No corruption detected, ran ' + CorruptionChecker.checks + ' checks.'); } }); }, malloc: function(size) { + if (size <= 0) size = 1; // malloc(0) sometimes happens - just allocate a larger area, no harm CorruptionChecker.checkAll(); - assert(size > 0); // some mallocs accept zero - fix your code if you want to use this tool size = (size+7)&(~7); var allocation = CorruptionChecker.realMalloc(size*(1+2*CorruptionChecker.BUFFER_FACTOR)); var ptr = allocation + size*CorruptionChecker.BUFFER_FACTOR; @@ -28,29 +34,48 @@ var CorruptionChecker = { CorruptionChecker.ptrs[ptr] = size; CorruptionChecker.fillBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR); CorruptionChecker.fillBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR); + //Module.printErr('malloc ' + size + ' ==> ' + [ptr, allocation]); return ptr; }, free: function(ptr) { + if (!ptr) return; // ok to free(NULL), does nothing CorruptionChecker.checkAll(); var size = CorruptionChecker.ptrs[ptr]; + //Module.printErr('free ' + ptr + ' of size ' + size); assert(size); var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR; + //Module.printErr('free ' + ptr + ' of size ' + size + ' and allocation ' + allocation); delete CorruptionChecker.ptrs[ptr]; CorruptionChecker.realFree(allocation); }, + realloc: function(ptr, newSize) { + //Module.printErr('realloc ' + ptr + ' to size ' + newSize); + if (newSize <= 0) newSize = 1; // like in malloc + if (!ptr) return CorruptionChecker.malloc(newSize); // realloc(NULL, size) forwards to malloc according to the spec + var size = CorruptionChecker.ptrs[ptr]; + assert(size); + var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR; + var newPtr = CorruptionChecker.malloc(newSize); + //Module.printErr('realloc ' + ptr + ' to size ' + newSize + ' is now ' + newPtr); + var newAllocation = newPtr + newSize*CorruptionChecker.BUFFER_FACTOR; + HEAPU8.set(HEAPU8.subarray(ptr, ptr + Math.min(size, newSize)), newPtr); + CorruptionChecker.free(ptr); + return newPtr; + }, canary: function(x) { - return (x + (x << 3) + (x&75) - (x&47))&255; + return (x&127) + 10; }, - fillBuffer: function(allocation, size) { - for (var x = allocation; x < allocation + size; x++) { + fillBuffer: function(buffer, size) { + for (var x = buffer; x < buffer + size; x++) { {{{ makeSetValue('x', 0, 'CorruptionChecker.canary(x)', 'i8') }}}; } }, - checkBuffer: function(allocation, size) { - for (var x = allocation; x < allocation + size; x++) { - assert(({{{ makeGetValue('x', 0, 'i8') }}}&255) == CorruptionChecker.canary(x), 'Heap corruption detected!'); + checkBuffer: function(buffer, size) { + for (var x = buffer; x < buffer + size; x++) { + if (({{{ makeGetValue('x', 0, 'i8') }}}&255) != CorruptionChecker.canary(x)) { + assert(0, 'Heap corruption detected!' + [x, buffer, size, {{{ makeGetValue('x', 0, 'i8') }}}&255, CorruptionChecker.canary(x)]); + } } - CorruptionChecker.checks++; }, checkPtr: function(ptr) { var size = CorruptionChecker.ptrs[ptr]; @@ -59,7 +84,10 @@ var CorruptionChecker = { CorruptionChecker.checkBuffer(allocation, size*CorruptionChecker.BUFFER_FACTOR); CorruptionChecker.checkBuffer(allocation + size*(1+CorruptionChecker.BUFFER_FACTOR), size*CorruptionChecker.BUFFER_FACTOR); }, - checkAll: function() { + checkAll: function(force) { + CorruptionChecker.checks++; + if (!force && CorruptionChecker.checks % CorruptionChecker.checkFrequency != 0) return; + //Module.printErr('checking for corruption ' + (CorruptionChecker.checks/CorruptionChecker.checkFrequency)); for (var ptr in CorruptionChecker.ptrs) { CorruptionChecker.checkPtr(ptr, false); } diff --git a/src/jsifier.js b/src/jsifier.js index 21d34b67..cb234061 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -478,8 +478,7 @@ function JSify(data, functionsOnly, givenFunctions) { ident = '_' + ident; } var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); - // redirected idents just need a var, but no value assigned to them - it would be unused - var contentText = isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';'); + var contentText = isFunction ? snippet : ('var ' + ident + '=' + snippet + ';'); if (ASM_JS) { var sig = LibraryManager.library[ident.substr(1) + '__sig']; if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) { @@ -506,9 +505,9 @@ function JSify(data, functionsOnly, givenFunctions) { item.JS = ''; } else if (LibraryManager.library.hasOwnProperty(shortident)) { item.JS = addFromLibrary(shortident); - } else { + } else if (!LibraryManager.library.hasOwnProperty(shortident + '__inline')) { item.JS = 'var ' + item.ident + '; // stub for ' + item.ident; - if (WARN_ON_UNDEFINED_SYMBOLS) { + if (WARN_ON_UNDEFINED_SYMBOLS || ASM_JS) { // always warn on undefs in asm, since it breaks validation warn('Unresolved symbol: ' + item.ident); } } @@ -1402,6 +1401,9 @@ function JSify(data, functionsOnly, givenFunctions) { if (ASM_JS) { assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out) callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py + } else if (SAFE_DYNCALLS) { + assert(!ASM_JS, 'cannot emit safe dyncalls in asm'); + callIdent = '(tempInt=' + callIdent + ',tempInt < 0 || tempInt >= FUNCTION_TABLE.length-1 || !FUNCTION_TABLE[tempInt] ? abort("dyncall error: ' + sig + ' " + FUNCTION_TABLE_NAMES[tempInt]) : tempInt)'; } callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; } @@ -1512,7 +1514,7 @@ function JSify(data, functionsOnly, givenFunctions) { print('// ASM_LIBRARY FUNCTIONS'); function fix(f) { // fix indenting to not confuse js optimizer f = f.substr(f.indexOf('f')); // remove initial spaces before 'function' - f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last } + f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last } XXX assumes function has multiple lines return f + '}'; // add unindented } to match function } print(asmLibraryFunctions.map(fix).join('\n')); @@ -1568,9 +1570,11 @@ function JSify(data, functionsOnly, givenFunctions) { var shellParts = read(shellFile).split('{{BODY}}'); print(shellParts[1]); // Print out some useful metadata (for additional optimizations later, like the eliminator) - print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { - return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; - })) + '\n'); + if (EMIT_GENERATED_FUNCTIONS) { + print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(keys(Functions.implementedFunctions).filter(function(func) { + return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; + })) + '\n'); + } PassManager.serialize(); diff --git a/src/library.js b/src/library.js index 24f241c7..38399632 100644 --- a/src/library.js +++ b/src/library.js @@ -2491,6 +2491,17 @@ LibraryManager.library = { continue; } + // TODO: Support strings like "%5c" etc. + if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') { + var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; + argIndex += Runtime.getNativeFieldSize('void*'); + fields++; + next = get(); + {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}} + formatIndex += 2; + continue; + } + // remove whitespace while (1) { next = get(); @@ -4514,11 +4525,16 @@ LibraryManager.library = { return 0; }, + memcmp__asm: 'true', + memcmp__sig: 'iiii', memcmp: function(p1, p2, num) { - for (var i = 0; i < num; i++) { - var v1 = {{{ makeGetValue('p1', 'i', 'i8', 0, 1) }}}; - var v2 = {{{ makeGetValue('p2', 'i', 'i8', 0, 1) }}}; - if (v1 != v2) return v1 > v2 ? 1 : -1; + p1 = p1|0; p2 = p2|0; num = num|0; + var i = 0, v1 = 0, v2 = 0; + while ((i|0) < (num|0)) { + var v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}}; + var v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}}; + if ((v1|0) != (v2|0)) return ((v1|0) > (v2|0) ? 1 : -1)|0; + i = (i+1)|0; } return 0; }, @@ -4619,10 +4635,8 @@ LibraryManager.library = { __strtok_state: 0, strtok__deps: ['__strtok_state', 'strtok_r'], + strtok__postset: '___strtok_state = Runtime.staticAlloc(4);', strtok: function(s, delim) { - if (!___strtok_state) { - ___strtok_state = _malloc(4); - } return _strtok_r(s, delim, ___strtok_state); }, @@ -6729,11 +6743,12 @@ LibraryManager.library = { pthread_key_create: function(key, destructor) { if (!_pthread_key_create.keys) _pthread_key_create.keys = {}; - _pthread_key_create.keys[key] = null; + // values start at 0 + _pthread_key_create.keys[key] = 0; }, pthread_getspecific: function(key) { - return _pthread_key_create.keys[key]; + return _pthread_key_create.keys[key] || 0; }, pthread_setspecific: function(key, value) { diff --git a/src/library_browser.js b/src/library_browser.js index 6af2ce0b..5b19a360 100644 --- a/src/library_browser.js +++ b/ |