aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc60
-rw-r--r--emlink.py293
-rwxr-xr-xemscripten.py40
-rw-r--r--src/intertyper.js2
-rw-r--r--src/jsifier.js69
-rw-r--r--src/library.js4
-rw-r--r--src/library_gl.js112
-rw-r--r--src/modules.js17
-rw-r--r--src/parseTools.js25
-rw-r--r--src/preamble.js5
-rw-r--r--src/settings.js6
-rwxr-xr-xtests/runner.py251
-rw-r--r--tools/js-optimizer.js32
-rw-r--r--tools/js_optimizer.py31
-rw-r--r--tools/shared.py40
-rw-r--r--tools/test-js-optimizer-asm-pre-output.js10
-rw-r--r--tools/test-js-optimizer-asm-pre.js12
-rw-r--r--tools/test-js-optimizer-asm-relocate-output.js8
-rw-r--r--tools/test-js-optimizer-asm-relocate.js10
19 files changed, 849 insertions, 178 deletions
diff --git a/emcc b/emcc
index 75750e1e..90a2ea71 100755
--- a/emcc
+++ b/emcc
@@ -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' },