diff options
-rwxr-xr-x | emcc | 60 | ||||
-rw-r--r-- | emlink.py | 293 | ||||
-rwxr-xr-x | emscripten.py | 40 | ||||
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 69 | ||||
-rw-r--r-- | src/library.js | 4 | ||||
-rw-r--r-- | src/library_gl.js | 112 | ||||
-rw-r--r-- | src/modules.js | 17 | ||||
-rw-r--r-- | src/parseTools.js | 25 | ||||
-rw-r--r-- | src/preamble.js | 5 | ||||
-rw-r--r-- | src/settings.js | 6 | ||||
-rwxr-xr-x | tests/runner.py | 251 | ||||
-rw-r--r-- | tools/js-optimizer.js | 32 | ||||
-rw-r--r-- | tools/js_optimizer.py | 31 | ||||
-rw-r--r-- | tools/shared.py | 40 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-pre-output.js | 10 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-pre.js | 12 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-relocate-output.js | 8 | ||||
-rw-r--r-- | tools/test-js-optimizer-asm-relocate.js | 10 |
19 files changed, 849 insertions, 178 deletions
@@ -1066,6 +1066,17 @@ try: logging.warning('disabling LLVM optimizations, need typed arrays mode 2 for them') llvm_opts = 0 + if shared.Settings.MAIN_MODULE: + assert not shared.Settings.SIDE_MODULE + shared.Settings.INCLUDE_FULL_LIBRARY = 1 + elif shared.Settings.SIDE_MODULE: + assert not shared.Settings.MAIN_MODULE + + if shared.Settings.MAIN_MODULE or shared.Settings.SIDE_MODULE: + assert not memory_init_file, 'memory init file is not supported with module linking' + shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules + debug_level = max(debug_level, 2) + ## Compile source code to bitcode logging.debug('compiling to bitcode') @@ -1140,7 +1151,9 @@ try: extra_files_to_link = [] if not LEAVE_INPUTS_RAW and not AUTODEBUG and \ - not shared.Settings.BUILD_AS_SHARED_LIB == 2: # shared lib 2 use the library in the parent + not shared.Settings.BUILD_AS_SHARED_LIB == 2 and \ + not shared.Settings.SIDE_MODULE: # shared libraries/side modules link no C libraries, need them in parent + # Check if we need to include some libraries that we compile. (We implement libc ourselves in js, but # compile a malloc implementation and stdlibc++.) @@ -1352,7 +1365,9 @@ try: return 'SDL_Init' in all_needed and ('malloc' not in all_needed or 'free' not in all_needed) # Settings this in the environment will avoid checking dependencies and make building big projects a little faster + # 1 means include everything; otherwise it can be the name of a lib (libcxx, etc.) force = os.environ.get('EMCC_FORCE_STDLIBS') + force_all = force == '1' # Scan symbols all_needed = set() @@ -1370,7 +1385,8 @@ try: ('libcxxabi', create_libcxxabi, apply_libcxxabi, libcxxabi_symbols), ('sdl', create_sdl, apply_sdl, sdl_symbols), ('libc', create_libc, apply_libc, libc_symbols)]: - if not force: + force_this = force_all or force == name + if not force_this: need = set() has = set() for symbols in symbolses: @@ -1383,12 +1399,13 @@ try: if haz in need: need.remove(haz) if shared.Settings.VERBOSE: logging.debug('considering %s: we need %s and have %s' % (name, str(need), str(has))) - if (force or len(need) > 0) and apply_(need): - # We need to build and link the library in - logging.debug('including %s' % name) - libfile = shared.Cache.get(name, create) - extra_files_to_link.append(libfile) - force = True + if force_this or len(need) > 0: + force_all = True + if apply_(need): + # We need to build and link the library in + logging.debug('including %s' % name) + libfile = shared.Cache.get(name, create) + extra_files_to_link.append(libfile) # First, combine the bitcode files if there are several. We must also link if we have a singleton .a if len(input_files) + len(extra_files_to_link) > 1 or \ @@ -1396,7 +1413,7 @@ try: linker_inputs = temp_files + extra_files_to_link logging.debug('linking: ' + str(linker_inputs)) t0 = time.time() - shared.Building.link(linker_inputs, in_temp(target_basename + '.bc')) + shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), force_archive_contents = len(filter(lambda temp: not temp.endswith(STATICLIB_SUFFIXES), temp_files)) == 0) t1 = time.time() logging.debug(' linking took %.2f seconds' % (t1 - t0)) final = in_temp(target_basename + '.bc') @@ -1433,17 +1450,16 @@ try: shared.Building.llvm_opt(in_temp(target_basename + '.bc'), ['-O3']) if DEBUG: save_intermediate('opt', 'bc') - if shared.Building.can_build_standalone(): - # If we can LTO, do it before dce, since it opens up dce opportunities - if llvm_lto and llvm_lto != 2 and shared.Building.can_use_unsafe_opts(): - if not shared.Building.can_inline(): link_opts.append('-disable-inlining') - # do not internalize in std-link-opts - it ignores internalize-public-api-list - and add a manual internalize - link_opts += ['-disable-internalize'] + shared.Building.get_safe_internalize() + ['-std-link-opts'] - else: - # At minimum remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it) - link_opts += shared.Building.get_safe_internalize() + ['-globaldce'] - shared.Building.llvm_opt(in_temp(target_basename + '.bc'), link_opts) - if DEBUG: save_intermediate('linktime', 'bc') + # If we can LTO, do it before dce, since it opens up dce opportunities + if shared.Building.can_build_standalone() and llvm_lto and llvm_lto != 2 and shared.Building.can_use_unsafe_opts(): + if not shared.Building.can_inline(): link_opts.append('-disable-inlining') + # do not internalize in std-link-opts - it ignores internalize-public-api-list - and add a manual internalize + link_opts += ['-disable-internalize'] + shared.Building.get_safe_internalize() + ['-std-link-opts'] + else: + # At minimum remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it) + link_opts += shared.Building.get_safe_internalize() + ['-globaldce'] + shared.Building.llvm_opt(in_temp(target_basename + '.bc'), link_opts) + if DEBUG: save_intermediate('linktime', 'bc') if save_bc: shutil.copyfile(final, save_bc) @@ -1584,7 +1600,7 @@ try: if closure and shared.Settings.ASM_JS: js_optimizer_queue += ['closure'] - js_optimizer_queue += ['last'] + if not shared.Settings.SIDE_MODULE: js_optimizer_queue += ['last'] # side modules are not finalized until after relocation flush_js_optimizer_queue() @@ -1611,7 +1627,7 @@ try: if os.path.abspath(memfile) != os.path.abspath(memfile): shutil.copyfile(memfile, temp_memfile) return 'loadMemoryInitializer("%s");' % os.path.basename(memfile) - src = re.sub('/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, Runtime\.GLOBAL_BASE\)', repl, src, count=1) + src = re.sub(shared.JS.memory_initializer_pattern, repl, src, count=1) open(final + '.mem.js', 'w').write(src) final += '.mem.js' js_transform_tempfiles[-1] = final # simple text substitution preserves comment line number mappings diff --git a/emlink.py b/emlink.py new file mode 100644 index 00000000..0a209cc7 --- /dev/null +++ b/emlink.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python2 + +''' +Fast static linker for emscripten outputs. Specifically this links asm.js modules. + +See https://github.com/kripken/emscripten/wiki/Linking +''' + +import os, subprocess, sys, re +from tools import shared +from tools import js_optimizer + +try: + me, main, side, out = sys.argv[:4] +except: + print >> sys.stderr, 'usage: emlink.py [main module] [side module] [output name]' + sys.exit(1) + +print 'Main module:', main +print 'Side module:', side +print 'Output:', out + +shared.try_delete(out) + +class AsmModule(): + def __init__(self, filename): + self.filename = filename + self.js = open(filename).read() + + self.start_asm = self.js.find(js_optimizer.start_asm_marker) + self.start_funcs = self.js.find(js_optimizer.start_funcs_marker) + self.end_funcs = self.js.rfind(js_optimizer.end_funcs_marker) + self.end_asm = self.js.rfind(js_optimizer.end_asm_marker) + + # pre + self.pre_js = self.js[:self.start_asm] + + # heap initializer + self.staticbump = int(re.search(shared.JS.memory_staticbump_pattern, self.pre_js).group(1)) + if self.staticbump: + self.mem_init_js = re.search(shared.JS.memory_initializer_pattern, self.pre_js).group(0) + + # global initializers + global_inits = re.search(shared.JS.global_initializers_pattern, self.pre_js) + if global_inits: + self.global_inits_js = global_inits.group(0) + self.global_inits = map(lambda init: init.split('{')[2][1:].split('(')[0], global_inits.groups(0)[0].split(',')) + else: + self.global_inits_js = '' + self.global_inits = [] + + # imports (and global variables) + first_var = self.js.find('var ', self.js.find('var ', self.start_asm)+4) + self.pre_imports_js = self.js[self.start_asm:first_var] + self.imports_js = self.js[first_var:self.start_funcs] + self.imports = {} + for imp in js_optimizer.import_sig.finditer(self.imports_js): + key, value = imp.group(0).split('var ')[1][:-1].split('=', 1) + self.imports[key] = value + #print >> sys.stderr, 'imports', self.imports + + # funcs + self.funcs_js = self.js[self.start_funcs:self.end_funcs] + self.funcs = set([m.group(2) for m in js_optimizer.func_sig.finditer(self.funcs_js)]) + #print 'funcs', self.funcs + + # tables and exports + post_js = self.js[self.end_funcs:self.end_asm] + ret = post_js.find('return') + self.tables_js = post_js[:ret] + self.exports_js = post_js[ret:] + self.tables = self.parse_tables(self.tables_js) + self.exports = set([export.strip() for export in self.exports_js[self.exports_js.find('{')+1:self.exports_js.find('}')].split(',')]) + + # post + self.post_js = self.js[self.end_asm:] + self.sendings = {} + for sending in [sending.strip() for sending in self.post_js[self.post_js.find('}, { ')+5:self.post_js.find(' }, buffer);')].split(',')]: + colon = sending.find(':') + self.sendings[sending[:colon].replace('"', '')] = sending[colon+1:].strip() + self.module_defs = set(re.findall('var [\w\d_$]+ = Module\["[\w\d_$]+"\] = asm\["[\w\d_$]+"\];\n', self.post_js)) + + def relocate_into(self, main): + # heap initializer + if self.staticbump > 0: + new_mem_init = self.mem_init_js[:self.mem_init_js.rfind(', ')] + ', Runtime.GLOBAL_BASE+%d)' % main.staticbump + main.pre_js = re.sub(shared.JS.memory_staticbump_pattern, 'STATICTOP = STATIC_BASE + %d;\n' % (main.staticbump + side.staticbump) + new_mem_init, main.pre_js, count=1) + + # Find function name replacements TODO: do not rename duplicate names with duplicate contents, just merge them + replacements = {} + for func in self.funcs: + rep = func + while rep in main.funcs: + rep += '_' + replacements[func] = rep + #print >> sys.stderr, 'replacements:', replacements + + # sendings: add invokes for new tables + all_sendings = main.sendings + added_sending = False + for table in self.tables: + if table not in main.tables: + sig = table[table.rfind('_')+1:] + all_sendings['invoke_%s' % sig] = shared.JS.make_invoke(sig, named=False) + added_sending = True + + # imports + all_imports = main.imports + for key, value in self.imports.iteritems(): + if key in self.funcs or key in main.funcs: continue # external function in one module, implemented in the other + value_concrete = '.' not in value # env.key means it is an import, an external value, and not a concrete one + main_value = main.imports.get(key) + main_value_concrete = main_value and '.' not in main_value + if value_concrete and main_value_concrete: continue # standard global var + if not main_value or value_concrete: + if '+' in value: + # relocate + value = value.replace('(', '').replace(')', '').replace('| 0', '').replace('|0', '').replace(' ', '') + left, right = value.split('+') + assert left == 'H_BASE' + value = str(main.staticbump + int(right)) + all_imports[key] = value + if (value_concrete or main_value_concrete) and key in all_sendings: + del all_sendings[key] # import of external value no longer needed + main.imports_js = '\n'.join(['var %s = %s;' % (key, value) for key, value in all_imports.iteritems()]) + '\n' + + if added_sending: + sendings_js = ', '.join(['%s: %s' % (key, value) for key, value in all_sendings.iteritems()]) + sendings_start = main.post_js.find('}, { ')+5 + sendings_end = main.post_js.find(' }, buffer);') + main.post_js = main.post_js[:sendings_start] + sendings_js + main.post_js[sendings_end:] + + # check for undefined references to global variables + def check_import(key, value): + if value.startswith('+') or value.endswith('|0'): # ignore functions + if key not in all_sendings: + print >> sys.stderr, 'external variable %s is still not defined after linking' % key + for key, value in all_imports.iteritems(): check_import(key, value) + + # tables + f_bases = {} + f_sizes = {} + for table, data in self.tables.iteritems(): + main.tables[table] = self.merge_tables(table, main.tables.get(table), data, replacements, f_bases, f_sizes) + main.combine_tables() + #print >> sys.stderr, 'f bases', f_bases + + # relocate + temp = shared.Building.js_optimizer(self.filename, ['asm', 'relocate', 'last'], extra_info={ + 'replacements': replacements, + 'fBases': f_bases, + 'hBase': main.staticbump + }) + #print >> sys.stderr, 'relocated side into', temp + relocated_funcs = AsmModule(temp) + shared.try_delete(temp) + main.extra_funcs_js = relocated_funcs.funcs_js.replace(js_optimizer.start_funcs_marker, '\n') + + # update function table uses + ft_marker = 'FUNCTION_TABLE_' + + def update_fts(what): + updates = [] + i = 1 # avoid seeing marker in recursion + while 1: + i = what.find(ft_marker, i) + if i < 0: break; + start = i + end = what.find('[', start) + table = what[i:end] + if table not in f_sizes: + # table was not modified + i += len(ft_marker) + continue + nesting = 1 + while nesting > 0: + next = what.find(']', end+1) + nesting -= 1 + nesting += what.count('[', end+1, next) + end = next + assert end > 0 + mask = what.rfind('&', start, end) + assert mask > 0 and end - mask <= 13 + fixed = update_fts(what[start:mask+1] + str(f_sizes[table]-1) + ']') + updates.append((start, end, fixed)) + i = end # additional function table uses were done by recursion + # apply updates + if len(updates) == 0: return what + parts = [] + so_far = 0 + for i in range(len(updates)): + start, end, fixed = updates[i] + parts.append(what[so_far:start]) + parts.append(fixed) + so_far = end+1 + parts.append(what[so_far:]) + return ''.join(parts) + + main.funcs_js = update_fts(main.funcs_js) + main.extra_funcs_js = update_fts(main.extra_funcs_js) + + # global initializers + if self.global_inits: + my_global_inits = map(lambda init: replacements[init] if init in replacements else init, self.global_inits) + all_global_inits = map(lambda init: '{ func: function() { %s() } }' % init, main.global_inits + my_global_inits) + all_global_inits_js = '/* global initializers */ __ATINIT__.push(' + ','.join(all_global_inits) + ');' + if main.global_inits: + target = main.global_inits_js + else: + target = '// === Body ===\n' + all_global_inits_js = target + all_global_inits_js + main.pre_js = main.pre_js.replace(target, all_global_inits_js) + + # exports + def rep_exp(export): + key, value = export.split(':') + if key in replacements: + repped = replacements[key] + return repped + ': ' + repped + return export + my_exports = map(rep_exp, self.exports) + exports = main.exports.union(my_exports) + main.exports_js = 'return {' + ','.join(list(exports)) + '};\n})\n' + + # post + def rep_def(deff): + key = deff.split(' ')[1] + if key in replacements: + rep = replacements[key] + return 'var %s = Module["%s"] = asm["%s"];\n' % (rep, rep, rep) + return deff + my_module_defs = map(rep_def, self.module_defs) + new_module_defs = set(my_module_defs).difference(main.module_defs) + if len(new_module_defs) > 0: + position = main.post_js.find('Runtime.') # Runtime is the start of the hardcoded ones + main.post_js = main.post_js[:position] + ''.join(list(new_module_defs)) + '\n' + main.post_js[position:] + + def write(self, out): + f = open(out, 'w') + f.write(self.pre_js) + f.write(self.pre_imports_js) + f.write(self.imports_js) + f.write(self.funcs_js) + f.write(self.extra_funcs_js) + f.write(self.tables_js) + f.write(self.exports_js) + f.write(self.post_js) + f.close() + + # Utilities + + def parse_tables(self, js): + tables = {} + parts = js.split(';') + for part in parts: + if '=' not in part: continue + part = part.split('var ')[1] + name, data = part.split(' = ') + tables[name] = data + return tables + + def merge_tables(self, table, main, side, replacements, f_bases, f_sizes): + sig = table.split('_')[-1] + side = side[1:-1].split(',') + side = map(lambda f: replacements[f] if f in replacements else f, side) + if not main: + f_bases[sig] = 0 + f_sizes[table] = len(side) + return '[' + ','.join(side) + ']' + main = main[1:-1].split(',') + # TODO: handle non-aliasing case too + assert len(main) % 2 == 0 + f_bases[sig] = len(main) + ret = main + side + size = 2 + while size < len(ret): size *= 2 + aborter = ret[1] # we can assume odd indexes have an aborting function with the right signature + ret = ret + [aborter]*(size - len(ret)) + assert len(ret) == size + f_sizes[table] = size + return '[' + ','.join(ret) + ']' + + def combine_tables(self): + self.tables_js = '// EMSCRIPTEN_END_FUNCS\n' + for table, data in self.tables.iteritems(): + self.tables_js += 'var %s = %s;\n' % (table, data) + +main = AsmModule(main) +side = AsmModule(side) + +side.relocate_into(main) +main.write(out) + diff --git a/emscripten.py b/emscripten.py index 56f59273..df0587f9 100755 --- a/emscripten.py +++ b/emscripten.py @@ -11,6 +11,7 @@ headers, for the libc implementation in JS). import os, sys, json, optparse, subprocess, re, time, multiprocessing, functools +from tools import shared from tools import jsrun, cache as cache_module, tempfiles from tools.response_file import read_response_file @@ -25,7 +26,6 @@ def get_configuration(): if hasattr(get_configuration, 'configuration'): return get_configuration.configuration - from tools import shared configuration = shared.Configuration(environ=os.environ) get_configuration.configuration = configuration return configuration @@ -341,8 +341,18 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, ret = re.sub(r'"?{{{ BA_([\w\d_$]+)\|([\w\d_$]+) }}}"?,0,0,0', lambda m: split_32(blockaddrs[m.groups(0)[0]][m.groups(0)[1]]), js) return re.sub(r'"?{{{ BA_([\w\d_$]+)\|([\w\d_$]+) }}}"?', lambda m: str(blockaddrs[m.groups(0)[0]][m.groups(0)[1]]), ret) + pre = blockaddrsize(indexize(pre)) + + if settings.get('ASM_JS'): + # move postsets into the asm module + class PostSets: js = '' + def handle_post_sets(m): + PostSets.js = m.group(0) + return '\n' + pre = re.sub(r'function runPostSets[^}]+}', handle_post_sets, pre) + #if DEBUG: outfile.write('// pre\n') - outfile.write(blockaddrsize(indexize(pre))) + outfile.write(pre) pre = None #if DEBUG: outfile.write('// funcs\n') @@ -453,23 +463,12 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, } ''' % (sig, i, args, arg_coercions, jsret)) - 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' - asm_setup += ''' -function invoke_%s(%s) { - try { - %sModule["dynCall_%s"](%s); - } catch(e) { - if (typeof e !== 'number' && e !== 'longjmp') throw e; - asm["setThrew"](1, 0); - } -} -''' % (sig, args, 'return ' if sig[0] != 'v' else '', sig, args) + asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' basic_funcs.append('invoke_%s' % sig) # calculate exports exported_implemented_functions = list(exported_implemented_functions) + exported_implemented_functions.append('runPostSets') exports = [] if not simple: for export in exported_implemented_functions + asm_runtime_funcs + function_tables: @@ -491,6 +490,15 @@ function invoke_%s(%s) { ''.join([' var ' + g + '=env.' + math_fix(g) + ';\n' for g in basic_funcs + global_funcs]) asm_global_vars = ''.join([' var ' + g + '=env.' + g + '|0;\n' for g in basic_vars + global_vars]) + \ ''.join([' var ' + g + '=+env.' + g + ';\n' for g in basic_float_vars]) + # In linkable modules, we need to add some explicit globals for global variables that can be linked and used across modules + if settings.get('MAIN_MODULE') or settings.get('SIDE_MODULE'): + assert settings.get('TARGET_LE32'), 'TODO: support x86 target when linking modules (needs offset of 4 and not 8 here)' + for key, value in forwarded_json['Variables']['globals'].iteritems(): + if value.get('linkable'): + init = forwarded_json['Variables']['indexedGlobals'][key] + 8 # 8 is Runtime.GLOBAL_BASE / STATIC_BASE + if settings.get('SIDE_MODULE'): init = '(H_BASE+' + str(init) + ')|0' + asm_global_vars += ' var %s=%s;\n' % (key, str(init)) + # sent data the_global = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in fundamentals]) + ' }' sending = '{ ' + ', '.join(['"' + math_fix(s) + '": ' + s for s in basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }' @@ -578,7 +586,7 @@ var asm = (function(global, env, buffer) { value = value|0; tempRet%d = value; } -''' % (i, i) for i in range(10)])] + funcs_js + [''' +''' % (i, i) for i in range(10)])] + [PostSets.js + '\n'] + funcs_js + [''' %s return %s; diff --git a/src/intertyper.js b/src/intertyper.js index 6da30ae8..94d937e1 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -502,7 +502,7 @@ function intertyper(data, sidePass, baseLineNums) { } else { // variable var ident = item.tokens[0].text; - var private_ = findTokenText(item, 'private') >= 0; + var private_ = findTokenText(item, 'private') >= 0 || findTokenText(item, 'internal') >= 0; cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]); var external = false; if (item.tokens[2].text === 'external') { diff --git a/src/jsifier.js b/src/jsifier.js index 885fbc30..3f8c184c 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -264,9 +264,9 @@ function JSify(data, functionsOnly, givenFunctions) { assert(!item.lines); // FIXME remove this, after we are sure it isn't needed var ret = [item]; if (item.ident == '_llvm_global_ctors') { - item.JS = '\n__ATINIT__ = __ATINIT__.concat([\n' + - item.ctors.map(function(ctor) { return ' { func: function() { ' + ctor + '() } }' }).join(',\n') + - '\n]);\n'; + item.JS = '\n/* global initializers */ __ATINIT__.push(' + + item.ctors.map(function(ctor) { return '{ func: function() { ' + ctor + '() } }' }).join(',') + + ');\n'; return ret; } @@ -286,6 +286,11 @@ function JSify(data, functionsOnly, givenFunctions) { allocator = 'ALLOC_NONE'; } + if (ASM_JS && (MAIN_MODULE || SIDE_MODULE) && !item.private_ && !NAMED_GLOBALS && isIndexableGlobal(item.ident)) { + // We need this to be named (and it normally would not be), so that it can be linked to and used from other modules + Variables.globals[item.ident].linkable = 1; + } + if (isBSS(item)) { var length = calcAllocatedSize(item.type); length = Runtime.alignMemory(length); @@ -374,22 +379,11 @@ function JSify(data, functionsOnly, givenFunctions) { // Set the actual value in a postset, since it may be a global variable. We also order by dependencies there Variables.globals[item.ident].targetIdent = item.value.ident; var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value); - var fix = ''; - if (BUILD_AS_SHARED_LIB == 2 && !item.private_) { - var target = item.ident; - if (isFunctionType(item.type)) { - target = item.value.ident; // the other side does not know this is an alias/function table index. So make it the alias target. - var varData = Variables.globals[target]; - assert(!varData, 'multi-level aliasing does not work yet in shared lib 2 exports'); - } - fix = '\nif (globalScope) { assert(!globalScope["' + item.ident + '"]); globalScope["' + item.ident + '"] = ' + target + ' }' + if ((MAIN_MODULE || SIDE_MODULE) && isFunctionType(item.type)) { + var target = item.value.ident; + if (!Functions.aliases[target]) Functions.aliases[target] = []; + Functions.aliases[target].push(item.ident); } - ret.push({ - intertype: 'GlobalVariablePostSet', - ident: item.ident, - dependencies: set([value]), - JS: item.ident + ' = ' + value + ';' + fix - }); return ret; } }); @@ -847,6 +841,19 @@ function JSify(data, functionsOnly, givenFunctions) { } func.JS = func.JS.replace(/\n *;/g, '\n'); // remove unneeded lines + + if (MAIN_MODULE || SIDE_MODULE) { + // Clone the function for each of its aliases. We do not know which name it will be used by in another module, + // and we do not have a heavyweight metadata system to resolve aliases during linking + var aliases = Functions.aliases[func.ident]; + if (aliases) { + var body = func.JS.substr(func.JS.indexOf('(')); + aliases.forEach(function(alias) { + func.JS += '\n' + 'function ' + alias + body; + }); + } + } + return func; } }); @@ -1254,7 +1261,7 @@ function JSify(data, functionsOnly, givenFunctions) { case 'xchg': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, param2, type, null, null, null, null, ',') + ',tempValue)'; case 'cmpxchg': { var param3 = finalizeLLVMParameter(item.params[2]); - return '(tempValue=' + makeGetValue(param1, 0, type) + ',(' + makeGetValue(param1, 0, type) + '==(' + param2 + '|0) ? ' + makeSetValue(param1, 0, param3, type, null, null, null, null, ',') + ' : 0),tempValue)'; + return '(tempValue=' + makeGetValue(param1, 0, type) + ',(' + makeGetValue(param1, 0, type) + '==(' + param2 + '|0) ? ' + asmCoercion(makeSetValue(param1, 0, param3, type, null, null, null, null, ','), 'i32') + ' : 0),tempValue)'; } default: throw 'unhandled atomic op: ' + item.op; } @@ -1502,7 +1509,7 @@ function JSify(data, functionsOnly, givenFunctions) { // This is a call through an invoke_*, either a forced one, or a setjmp-required one // note: no need to update argsTypes at this point if (byPointerForced) Functions.unimplementedFunctions[callIdent] = sig; - args.unshift(byPointerForced ? Functions.getIndex(callIdent) : asmCoercion(callIdent, 'i32')); + args.unshift(byPointerForced ? Functions.getIndex(callIdent, undefined, sig) : asmCoercion(callIdent, 'i32')); callIdent = 'invoke_' + sig; } } else if (SAFE_DYNCALLS) { @@ -1582,6 +1589,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (phase == 'pre' && !Variables.generatedGlobalBase) { Variables.generatedGlobalBase = true; // Globals are done, here is the rest of static memory + assert((TARGET_LE32 && Runtime.GLOBAL_BASE == 8) || (TARGET_X86 && Runtime.GLOBAL_BASE == 4)); // this is assumed in e.g. relocations for linkable modules print('STATIC_BASE = ' + Runtime.GLOBAL_BASE + ';\n'); print('STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n'); } @@ -1615,11 +1623,11 @@ function JSify(data, functionsOnly, givenFunctions) { print('/* no memory initializer */'); // test purposes } - // Run postsets right before main, and after the memory initializer has been set up + // Define postsets. These will be run in ATINIT, right before global initializers (which might need the postsets). We cannot + // run them now because the memory initializer might not have been applied yet. print('function runPostSets() {\n'); print(itemsDict.GlobalVariablePostSet.map(function(item) { return item.JS }).join('\n')); print('}\n'); - print('if (!awaitingMemoryInitializer) runPostSets();\n'); // if we load the memory initializer, this is done later if (USE_TYPED_ARRAYS == 2) { print('var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n'); @@ -1729,8 +1737,8 @@ function JSify(data, functionsOnly, givenFunctions) { } } }); - print(read('fastLong.js')); } + print(read('fastLong.js')); print('// EMSCRIPTEN_END_FUNCS\n'); print(read('long.js')); } else { @@ -1792,6 +1800,21 @@ function JSify(data, functionsOnly, givenFunctions) { substrate.addItems(data.functionStubs, 'FunctionStub'); assert(data.functions.length == 0); } else { + if (phase == 'pre') { + // ensure there is a global ctors, for runPostSets + if ('_llvm_global_ctors' in data.globalVariables) { + data.globalVariables._llvm_global_ctors.ctors.unshift('runPostSets'); // run postsets right before global initializers + hasCtors = true; + } else { + substrate.addItems([{ + intertype: 'GlobalVariableStub', + ident: '_llvm_global_ctors', + type: '[1 x { i32, void ()* }]', + ctors: ["runPostSets"], + }], 'GlobalVariable'); + } + } + substrate.addItems(sortGlobals(data.globalVariables), 'GlobalVariable'); substrate.addItems(data.aliass, 'Alias'); substrate.addItems(data.functions, 'FunctionSplitter'); diff --git a/src/library.js b/src/library.js index 822f4319..bf969fd7 100644 --- a/src/library.js +++ b/src/library.js @@ -8271,4 +8271,8 @@ function autoAddDeps(object, name) { } } +// Add aborting stubs for various libc stuff needed by libc++ +['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'swprintf', 'pthread_join', 'pthread_detach', 'strcoll_l', 'strxfrm_l', 'wcscoll_l', 'toupper_l', 'tolower_l', 'iswspace_l', 'iswprint_l', 'iswcntrl_l', 'iswupper_l', 'iswlower_l', 'iswalpha_l', 'iswdigit_l', 'iswpunct_l', 'iswxdigit_l', 'iswblank_l', 'wcsxfrm_l', 'towupper_l', 'towlower_l'].forEach(function(aborter) { + LibraryManager.library[aborter] = function() { throw 'TODO: ' + aborter }; +}); diff --git a/src/library_gl.js b/src/library_gl.js index 1fa0cc9c..e59492cf 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -1260,6 +1260,8 @@ var LibraryGL = { return Module.ctx.isFramebuffer(fb); }, +#if DISABLE_GL_EMULATION == 0 + // GL emulation: provides misc. functionality not present in OpenGL ES 2.0 or WebGL $GLEmulation__postset: 'GLEmulation.init();', @@ -4139,6 +4141,44 @@ var LibraryGL = { }, glRotatef: 'glRotated', + glDrawBuffer: function() { throw 'glDrawBuffer: TODO' }, + glReadBuffer: function() { throw 'glReadBuffer: TODO' }, + + glLightfv: function() { throw 'glLightfv: TODO' }, + glLightModelfv: function() { throw 'glLightModelfv: TODO' }, + glMaterialfv: function() { throw 'glMaterialfv: TODO' }, + + glTexGeni: function() { throw 'glTexGeni: TODO' }, + glTexGenfv: function() { throw 'glTexGenfv: TODO' }, + glTexEnvi: function() { Runtime.warnOnce('glTexEnvi: TODO') }, + glTexEnvf: function() { Runtime.warnOnce('glTexEnvf: TODO') }, + glTexEnvfv: function() { Runtime.warnOnce('glTexEnvfv: TODO') }, + + glTexImage1D: function() { throw 'glTexImage1D: TODO' }, + glTexCoord3f: function() { throw 'glTexCoord3f: TODO' }, + glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' }, + + glShadeModel: function() { Runtime.warnOnce('TODO: glShadeModel') }, + + // Open GLES1.1 compatibility + + glGenFramebuffersOES : 'glGenFramebuffers', + glGenRenderbuffersOES : 'glGenRenderbuffers', + glBindFramebufferOES : 'glBindFramebuffer', + glBindRenderbufferOES : 'glBindRenderbuffer', + glGetRenderbufferParameterivOES : 'glGetRenderbufferParameteriv', + glFramebufferRenderbufferOES : 'glFramebufferRenderbuffer', + glRenderbufferStorageOES : 'glRenderbufferStorage', + glCheckFramebufferStatusOES : 'glCheckFramebufferStatus', + glDeleteFramebuffersOES : 'glDeleteFramebuffers', + glDeleteRenderbuffersOES : 'glDeleteRenderbuffers', + glGenVertexArraysOES: 'glGenVertexArrays', + glDeleteVertexArraysOES: 'glDeleteVertexArrays', + glBindVertexArrayOES: 'glBindVertexArray', + glFramebufferTexture2DOES: 'glFramebufferTexture2D', + +#endif // DISABLE_GL_EMULATION == 0 + // GLU gluPerspective: function(fov, aspect, near, far) { @@ -4205,25 +4245,6 @@ var LibraryGL = { _glOrtho(left, right, bottom, top, -1, 1); }, - glDrawBuffer: function() { throw 'glDrawBuffer: TODO' }, - glReadBuffer: function() { throw 'glReadBuffer: TODO' }, |