aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rwxr-xr-xemcc25
-rw-r--r--emlink.py268
-rw-r--r--src/library.js28
-rw-r--r--src/postamble.js26
-rw-r--r--src/preamble.js67
-rw-r--r--tests/qsort/benchmark.cpp43
-rwxr-xr-xtests/runner.py39
-rw-r--r--tools/asm_module.py271
-rw-r--r--tools/file_packager.py2
-rwxr-xr-xtools/merge_asm.py26
-rw-r--r--tools/shared.py10
-rwxr-xr-xtools/split_asm.py30
13 files changed, 513 insertions, 323 deletions
diff --git a/AUTHORS b/AUTHORS
index 60089f6a..d3cdd156 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -91,4 +91,5 @@ a license to everyone to use it as detailed in LICENSE.)
* Ryan Kelly (ryan@rfk.id.au)
* Michael Lelli <toadking@toadking.com>
* Yu Kobayashi <yukoba@accelart.jp>
+* Pin Zhang <zhangpin04@gmail.com>
diff --git a/emcc b/emcc
index 0b982fbe..8f71883d 100755
--- a/emcc
+++ b/emcc
@@ -53,6 +53,15 @@ from tools import shared, jsrun
from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename
from tools.response_file import read_response_file
+CXX_SUFFIXES = ('.cpp', '.cxx', '.cc')
+SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc', '.m', '.mm')
+BITCODE_SUFFIXES = ('.bc', '.o', '.obj')
+DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll')
+STATICLIB_SUFFIXES = ('.a',)
+ASSEMBLY_SUFFIXES = ('.ll',)
+LIB_PREFIXES = ('', 'lib')
+JS_CONTAINING_SUFFIXES = ('js', 'html')
+
# Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt
# levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get
# llvm opt level 3, and speed-wise emcc level 2 is already the slowest/most optimizing
@@ -532,7 +541,7 @@ if CONFIGURE_CONFIG or CMAKE_CONFIG:
if debug_configure: open(tempout, 'a').write('============= ' + arg + '\n' + src + '\n=============\n\n')
except:
pass
- if arg.endswith('.s'):
+ elif arg.endswith('.s'):
if debug_configure: open(tempout, 'a').write('(compiling .s assembly, must use clang\n')
use_js = 0
@@ -615,15 +624,6 @@ if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += shlex.split(EMMAKEN_CFLAGS)
# ---------------- Utilities ---------------
-SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc', '.m', '.mm')
-BITCODE_SUFFIXES = ('.bc', '.o', '.obj')
-DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll')
-STATICLIB_SUFFIXES = ('.a',)
-ASSEMBLY_SUFFIXES = ('.ll',)
-LIB_PREFIXES = ('', 'lib')
-
-JS_CONTAINING_SUFFIXES = ('js', 'html')
-
seen_names = {}
def uniquename(name):
if name not in seen_names:
@@ -787,6 +787,7 @@ try:
newargs[i+1] = ''
elif newargs[i].startswith('--minify'):
check_bad_eq(newargs[i])
+ assert newargs[i+1] == '0', '0 is the only supported option for --minify; 1 has been deprecated'
debug_level = max(1, debug_level)
newargs[i] = ''
newargs[i+1] = ''
@@ -1010,7 +1011,7 @@ try:
logging.error('no input files\nnote that input files without a known suffix are ignored, make sure your input files end with one of: ' + str(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + STATICLIB_SUFFIXES + ASSEMBLY_SUFFIXES))
exit(0)
- newargs += CC_ADDITIONAL_ARGS
+ newargs = CC_ADDITIONAL_ARGS + newargs
assert not (Compression.on and final_suffix != 'html'), 'Compression only works when generating HTML'
@@ -1105,6 +1106,8 @@ try:
output_file = in_temp(unsuffixed(uniquename(input_file)) + '.o')
temp_files.append(output_file)
args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file]
+ if input_file.endswith(CXX_SUFFIXES):
+ args += shared.EMSDK_CXX_OPTS
logging.debug("running:" + call + ' ' + ' '.join(args))
execute([call] + args) # let compiler frontend print directly, so colors are saved (PIPE kills that)
if not os.path.exists(output_file):
diff --git a/emlink.py b/emlink.py
index 387b7de8..7311f84a 100644
--- a/emlink.py
+++ b/emlink.py
@@ -6,9 +6,9 @@ Fast static linker for emscripten outputs. Specifically this links asm.js module
See https://github.com/kripken/emscripten/wiki/Linking
'''
-import os, subprocess, sys, re
+import sys
from tools import shared
-from tools import js_optimizer
+from tools.asm_module import AsmModule
try:
me, main, side, out = sys.argv[:4]
@@ -22,270 +22,6 @@ 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.strip()] = value.strip()
- #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'
-
- # 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, 'warning: external variable %s is still not defined after linking' % key
- all_sendings[key] = '0'
- for key, value in all_imports.iteritems(): check_import(key, value)
-
- 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:]
-
- # 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)
diff --git a/src/library.js b/src/library.js
index 7bb4ca77..143d7c28 100644
--- a/src/library.js
+++ b/src/library.js
@@ -3945,7 +3945,11 @@ LibraryManager.library = {
bsearch: function(key, base, num, size, compar) {
var cmp = function(x, y) {
- return Runtime.dynCall('iii', compar, [x, y])
+#if ASM_JS
+ return Module['dynCall_iii'](compar, x, y);
+#else
+ return FUNCTION_TABLE[compar](x, y);
+#endif
};
var left = 0;
var right = num;
@@ -3955,7 +3959,6 @@ LibraryManager.library = {
mid = (left + right) >>> 1;
addr = base + (mid * size);
test = cmp(key, addr);
-
if (test < 0) {
right = mid;
} else if (test > 0) {
@@ -4175,13 +4178,14 @@ LibraryManager.library = {
if (num == 0 || size == 0) return;
// forward calls to the JavaScript sort method
// first, sort the items logically
- var comparator = function(x, y) {
- return Runtime.dynCall('iii', cmp, [x, y]);
- }
var keys = [];
for (var i = 0; i < num; i++) keys.push(i);
keys.sort(function(a, b) {
- return comparator(base+a*size, base+b*size);
+#if ASM_JS
+ return Module['dynCall_iii'](cmp, base+a*size, base+b*size);
+#else
+ return FUNCTION_TABLE[cmp](base+a*size, base+b*size);
+#endif
});
// apply the sort
var temp = _malloc(num*size);
@@ -4954,7 +4958,17 @@ LibraryManager.library = {
(chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}});
},
isspace: function(chr) {
- return chr in { 32: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0 };
+ switch(chr) {
+ case 32:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ return true;
+ default:
+ return false;
+ };
},
isblank: function(chr) {
return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}};
diff --git a/src/postamble.js b/src/postamble.js
index 8fe3b65d..25a50bfc 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -6,7 +6,7 @@ var inMain;
Module['callMain'] = Module.callMain = function callMain(args) {
assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on __ATMAIN__)');
- assert(!Module['preRun'] || Module['preRun'].length == 0, 'cannot call main when preRun functions remain to be called');
+ assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called');
args = args || [];
@@ -74,17 +74,11 @@ function run(args) {
return;
}
- if (Module['preRun']) {
- if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
- var toRun = Module['preRun'];
- Module['preRun'] = [];
- for (var i = toRun.length-1; i >= 0; i--) {
- toRun[i]();
- }
- if (runDependencies > 0) {
- // a preRun added a dependency, run will be called later
- return;
- }
+ preRun();
+
+ if (runDependencies > 0) {
+ // a preRun added a dependency, run will be called later
+ return;
}
function doRun() {
@@ -96,12 +90,8 @@ function run(args) {
if (Module['_main'] && shouldRunNow) {
Module['callMain'](args);
}
- if (Module['postRun']) {
- if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];
- while (Module['postRun'].length > 0) {
- Module['postRun'].pop()();
- }
- }
+
+ postRun();
}
if (Module['setStatus']) {
diff --git a/src/preamble.js b/src/preamble.js
index 57376ad3..2955c885 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -705,37 +705,74 @@ function callRuntimeCallbacks(callbacks) {
}
}
-var __ATINIT__ = []; // functions called during startup
-var __ATMAIN__ = []; // functions called when main() is to be run
-var __ATEXIT__ = []; // functions called during shutdown
+var __ATPRERUN__ = []; // functions called before the runtime is initialized
+var __ATINIT__ = []; // functions called during startup
+var __ATMAIN__ = []; // functions called when main() is to be run
+var __ATEXIT__ = []; // functions called during shutdown
+var __ATPOSTRUN__ = []; // functions called after the runtime has exited
var runtimeInitialized = false;
+function preRun() {
+ // compatibility - merge in anything from Module['preRun'] at this time
+ if (Module['preRun']) {
+ if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
+ while (Module['preRun'].length) {
+ addOnPreRun(Module['preRun'].shift());
+ }
+ }
+ callRuntimeCallbacks(__ATPRERUN__);
+}
+
function ensureInitRuntime() {
if (runtimeInitialized) return;
runtimeInitialized = true;
callRuntimeCallbacks(__ATINIT__);
}
+
function preMain() {
callRuntimeCallbacks(__ATMAIN__);
}
+
function exitRuntime() {
callRuntimeCallbacks(__ATEXIT__);
}
-Module['addOnInit'] = Module.addOnInit = function addOnInit(cb) {
+function postRun() {
+ // compatibility - merge in anything from Module['postRun'] at this time
+ if (Module['postRun']) {
+ if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];
+ while (Module['postRun'].length) {
+ addOnPostRun(Module['postRun'].shift());
+ }
+ }
+ callRuntimeCallbacks(__ATPOSTRUN__);
+}
+
+function addOnPreRun(cb) {
+ __ATPRERUN__.unshift(cb);
+}
+Module['addOnPreRun'] = Module.addOnPreRun = addOnPreRun;
+
+function addOnInit(cb) {
__ATINIT__.unshift(cb);
-};
+}
+Module['addOnInit'] = Module.addOnInit = addOnInit;
-Module['addOnPreMain'] = Module.addOnPreMain = function addOnPreMain(cb) {
+function addOnPreMain(cb) {
__ATMAIN__.unshift(cb);
-};
+}
+Module['addOnPreMain'] = Module.addOnPreMain = addOnPreMain;
-Module['addOnExit'] = Module.addOnExit = function addOnExit(cb) {
+function addOnExit(cb) {
__ATEXIT__.unshift(cb);
-};
+}
+Module['addOnExit'] = Module.addOnExit = addOnExit;
-// TODO add onprerun, onpostrun
+function addOnPostRun(cb) {
+ __ATPOSTRUN__.unshift(cb);
+}
+Module['addOnPostRun'] = Module.addOnPostRun = addOnPostRun;
// Tools
@@ -873,12 +910,6 @@ Module['removeRunDependency'] = removeRunDependency;
Module["preloadedImages"] = {}; // maps url to image data
Module["preloadedAudios"] = {}; // maps url to audio data
-function addPreRun(func) {
- if (!Module['preRun']) Module['preRun'] = [];
- else if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
- Module['preRun'].push(func);
-}
-
#if PGO
var PGOMonitor = {
called: {},
@@ -893,7 +924,7 @@ var PGOMonitor = {
};
Module['PGOMonitor'] = PGOMonitor;
__ATEXIT__.push({ func: function() { PGOMonitor.dump() } });
-addPreRun(function() { addRunDependency('pgo') });
+addOnPreRun(function() { addRunDependency('pgo') });
#endif
function loadMemoryInitializer(filename) {
@@ -906,7 +937,7 @@ function loadMemoryInitializer(filename) {
}
// always do this asynchronously, to keep shell and web as similar as possible
- addPreRun(function() {
+ addOnPreRun(function() {
if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) {
applyData(Module['readBinary'](filename));
} else {
diff --git a/tests/qsort/benchmark.cpp b/tests/qsort/benchmark.cpp
new file mode 100644
index 00000000..950ca437
--- /dev/null
+++ b/tests/qsort/benchmark.cpp
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+typedef unsigned int uint32;
+
+int cmp_uint(const void *i1, const void *i2) {
+ if (*static_cast<const uint32*>(i1) >
+ *static_cast<const uint32*>(i2))
+ return 1;
+
+ if (*static_cast<const uint32*>(i1) <
+ *static_cast<const uint32*>(i2))
+ return -1;
+
+ return 0;
+}
+
+int main() {
+ clock_t start = clock();
+ const size_t TIMES = 10000;
+ for (size_t i = 0; i < TIMES; i++) {
+ const size_t num = 100;
+ uint32 rnd[num] = { \
+ 407, 236, 765, 529, 24, 13, 577, 900, 242, 245, \
+ 782, 972, 514, 100, 596, 470, 680, 65, 370, 788, \
+ 44, 330, 579, 314, 914, 399, 100, 945, 992, 412, \
+ 308, 102, 895, 529, 216, 422, 851, 778, 28, 804, \
+ 325, 975, 961, 623, 922, 667, 141, 755, 416, 575, \
+ 712, 503, 174, 675, 14, 647, 544, 881, 858, 621, \
+ 26, 283, 460, 252, 146, 16, 571, 570, 14, 143, \
+ 674, 985, 477, 386, 932, 490, 611, 127, 702, 619, \
+ 104, 892, 58, 635, 663, 424, 714, 740, 229, 538, \
+ 167, 181, 193, 193, 657, 778, 217, 573, 764, 745};
+
+ qsort(rnd, num, sizeof(uint32), cmp_uint);
+ }
+ clock_t end = clock();
+
+ float diff = (((float)end - (float)start) / CLOCKS_PER_SEC ) * 1000;
+ printf("cost %fms\n", diff);
+}
+
diff --git a/tests/runner.py b/tests/runner.py
index 55d666e9..9f037164 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -10733,6 +10733,27 @@ f.close()
os.chdir(path_from_root('tests')) # Move away from the directory we are about to remove.
shutil.rmtree(tempdirname)
+ def test_nostdincxx(self):
+ try:
+ old = os.environ.get('EMCC_LLVM_TARGET') or ''
+ for compiler in [EMCC, EMXX]:
+ for target in ['i386-pc-linux-gnu', 'le32-unknown-nacl']:
+ print compiler, target
+ os.environ['EMCC_LLVM_TARGET'] = target
+ out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-v'], stdout=PIPE, stderr=PIPE).communicate()
+ out2, err2 = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-v', '-nostdinc++'], stdout=PIPE, stderr=PIPE).communicate()
+ assert out == out2
+ def focus(e):
+ assert 'search starts here:' in e, e
+ assert e.count('End of search list.') == 1, e
+ return e[e.index('search starts here:'):e.index('End of search list.')+20]
+ err = focus(err)
+ err2 = focus(err2)
+ assert err == err2, err + '\n\n\n\n' + err2
+ finally:
+ if old:
+ os.environ['EMCC_LLVM_TARGET'] = old
+
def test_failure_error_code(self):
for compiler in [EMCC, EMXX]:
# Test that if one file is missing from the build, then emcc shouldn't succeed, and shouldn't try to produce an output file.
@@ -14906,12 +14927,30 @@ fi
finally:
del os.environ['EMCC_DEBUG']
+ restore()
+
+ def ensure_cache():
+ self.do([EMCC, '-O2', path_from_root('tests', 'hello_world.c')])
+
# Manual cache clearing
+ ensure_cache()
assert os.path.exists(EMCC_CACHE)
output = self.do([EMCC, '--clear-cache'])
assert ERASING_MESSAGE in output
assert not os.path.exists(EMCC_CACHE)
+ # Changing LLVM_ROOT, even without altering .emscripten, clears the cache
+ ensure_cache()
+ old = os.environ.get('LLVM')
+ try:
+ os.environ['LLVM'] = 'waka'
+ assert os.path.exists(EMCC_CACHE)
+ output = self.do([EMCC])
+ assert ERASING_MESSAGE in output
+ assert not os.path.exists(EMCC_CACHE)
+ finally:
+ if old: os.environ['LLVM'] = old
+
try_delete(CANONICAL_TEMP_DIR)
def test_relooper(self):
diff --git a/tools/asm_module.py b/tools/asm_module.py
new file mode 100644
index 00000000..e54cfc21
--- /dev/null
+++ b/tools/asm_module.py
@@ -0,0 +1,271 @@
+
+import sys, re
+
+import shared, js_optimizer
+
+
+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 and asm
+ self.pre_js = self.js[:self.start_asm]
+ self.asm_js = self.js[self.start_asm:self.end_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.strip()] = value.strip()
+ #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 + self.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'
+
+ # 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, 'warning: external variable %s is still not defined after linking' % key
+ all_sendings[key] = '0'
+ for key, value in all_imports.iteritems(): check_import(key, value)
+
+ 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:]
+
+ # 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.appe