aboutsummaryrefslogtreecommitdiff
path: root/emscripten.py
diff options
context:
space:
mode:
Diffstat (limited to 'emscripten.py')
-rwxr-xr-xemscripten.py173
1 files changed, 162 insertions, 11 deletions
diff --git a/emscripten.py b/emscripten.py
index 15beb4ee..ea685718 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -128,10 +128,12 @@ def emscript(infile, settings, outfile, libraries=[]):
# Save settings to a file to work around v8 issue 1579
settings_file = temp_files.get('.txt').name
- settings_text = json.dumps(settings)
- s = open(settings_file, 'w')
- s.write(settings_text)
- s.close()
+ def save_settings():
+ settings_text = json.dumps(settings)
+ s = open(settings_file, 'w')
+ s.write(settings_text)
+ s.close()
+ save_settings()
# Phase 1 - pre
if DEBUG: t = time.time()
@@ -169,6 +171,9 @@ def emscript(infile, settings, outfile, libraries=[]):
if DEBUG: t = time.time()
forwarded_json = json.loads(forwarded_data)
indexed_functions = set()
+ if settings.get('ASM_JS'):
+ settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS']
+ save_settings()
chunks = shared.JCache.chunkify(funcs, chunk_size, 'emscript_files' if jcache else None)
@@ -222,16 +227,25 @@ def emscript(infile, settings, outfile, libraries=[]):
if DEBUG: print >> sys.stderr, ' emscript: phase 2 took %s seconds' % (time.time() - t)
if DEBUG: t = time.time()
- funcs_js = ''.join([output[0] for output in outputs])
-
+ # merge forwarded data
+ if settings.get('ASM_JS'):
+ all_exported_functions = set(settings['EXPORTED_FUNCTIONS']) # both asm.js and otherwise
+ exported_implemented_functions = set()
for func_js, curr_forwarded_data in outputs:
- # merge forwarded data
curr_forwarded_json = json.loads(curr_forwarded_data)
forwarded_json['Types']['preciseI64MathUsed'] = forwarded_json['Types']['preciseI64MathUsed'] or curr_forwarded_json['Types']['preciseI64MathUsed']
for key, value in curr_forwarded_json['Functions']['blockAddresses'].iteritems():
forwarded_json['Functions']['blockAddresses'][key] = value
for key in curr_forwarded_json['Functions']['indexedFunctions'].iterkeys():
indexed_functions.add(key)
+ if settings.get('ASM_JS'):
+ for key in curr_forwarded_json['Functions']['implementedFunctions'].iterkeys():
+ if key in all_exported_functions: exported_implemented_functions.add(key)
+ for key, value in curr_forwarded_json['Functions']['unimplementedFunctions'].iteritems():
+ forwarded_json['Functions']['unimplementedFunctions'][key] = value
+
+ funcs_js = ''.join([output[0] for output in outputs])
+
outputs = None
if DEBUG: print >> sys.stderr, ' emscript: phase 2b took %s seconds' % (time.time() - t)
if DEBUG: t = time.time()
@@ -240,6 +254,7 @@ def emscript(infile, settings, outfile, libraries=[]):
forwarded_json['Functions']['indexedFunctions'] = {}
i = 2
for indexed in indexed_functions:
+ #print >> sys.stderr, 'indaxx', indexed, i
forwarded_json['Functions']['indexedFunctions'][indexed] = i # make sure not to modify this python object later - we use it in indexize
i += 2
forwarded_json['Functions']['nextIndex'] = i
@@ -257,8 +272,6 @@ def emscript(infile, settings, outfile, libraries=[]):
pre = None
#if DEBUG: outfile.write('// funcs\n')
- outfile.write(blockaddrsize(indexize(funcs_js)))
- funcs_js = None
# forward
forwarded_data = json.dumps(forwarded_json)
@@ -271,8 +284,146 @@ def emscript(infile, settings, outfile, libraries=[]):
post_file = temp_files.get('.post.ll').name
open(post_file, 'w').write('\n') # no input, just processing of forwarded data
out = shared.run_js(compiler, shared.COMPILER_ENGINE, [settings_file, post_file, 'post', forwarded_file] + libraries, stdout=subprocess.PIPE, cwd=path_from_root('src'))
- #if DEBUG: outfile.write('// post\n')
- outfile.write(indexize(out))
+ post, last_forwarded_data = out.split('//FORWARDED_DATA:')
+ last_forwarded_json = json.loads(last_forwarded_data)
+
+ if settings.get('ASM_JS'):
+ class Counter:
+ i = 0
+ def make_table(sig, raw):
+ i = Counter.i
+ Counter.i += 1
+ bad = 'b' + str(i)
+ params = ','.join(['p%d' % p for p in range(len(sig)-1)])
+ coercions = ';'.join(['p%d = %sp%d%s' % (p, '+' if sig[p+1] == 'd' else '', p, '' if sig[p+1] == 'd' else '|0') for p in range(len(sig)-1)]) + ';'
+ ret = '' if sig[0] == 'v' else ('return %s0' % ('+' if sig[0] == 'd' else ''))
+ return 'function %s(%s) { %s abort(%d); %s };\n' % (bad, params, coercions, i, ret) + raw.replace('[0,', '[' + bad + ',').replace(',0,', ',' + bad + ',').replace(',0,', ',' + bad + ',').replace(',0]', ',' + bad + ']').replace(',0]', ',' + bad + ']')
+ function_tables_defs = '\n'.join([make_table(sig, raw) for sig, raw in last_forwarded_json['Functions']['tables'].iteritems()])
+
+ asm_setup = '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in ['Runtime.bitshift64', 'Math.floor', 'Math.min']])
+ fundamentals = ['buffer', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array']
+ basics = ['abort', 'assert', 'STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT', 'Runtime_bitshift64', 'Math_floor', 'Math_min']
+ if not settings['NAMED_GLOBALS']: basics += ['GLOBAL_BASE']
+ if forwarded_json['Types']['preciseI64MathUsed']:
+ basics += ['i64Math_' + op for op in ['add', 'subtract', 'multiply', 'divide', 'modulo']]
+ asm_setup += '''
+var i64Math_add = function(a, b, c, d) { i64Math.add(a, b, c, d) };
+var i64Math_subtract = function(a, b, c, d) { i64Math.subtract(a, b, c, d) };
+var i64Math_multiply = function(a, b, c, d) { i64Math.multiply(a, b, c, d) };
+var i64Math_divide = function(a, b, c, d, e) { i64Math.divide(a, b, c, d, e) };
+var i64Math_modulo = function(a, b, c, d, e) { i64Math.modulo(a, b, c, d, e) };
+'''
+ asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)]
+ # function tables
+ function_tables = ['dynCall_' + table for table in last_forwarded_json['Functions']['tables']]
+ function_tables_impls = []
+ for sig in last_forwarded_json['Functions']['tables'].iterkeys():
+ args = ','.join(['a' + str(i) for i in range(1, len(sig))])
+ arg_coercions = ' '.join(['a' + str(i) + '=' + ('+' if sig[i] == 'd' else '') + 'a' + str(i) + ('|0' if sig[i] == 'i' else '') + ';' for i in range(1, len(sig))])
+ function_tables_impls.append('''
+ function dynCall_%s(index%s%s) {
+ %s
+ %sFUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s);
+ }
+''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, 'return ' if sig[0] != 'v' else '', sig, sig, args))
+ # calculate exports
+ exported_implemented_functions = list(exported_implemented_functions)
+ exports = []
+ for export in exported_implemented_functions + asm_runtime_funcs + function_tables:
+ exports.append("'%s': %s" % (export, export))
+ exports = '{ ' + ', '.join(exports) + ' }'
+ # calculate globals
+ try:
+ del forwarded_json['Variables']['globals']['_llvm_global_ctors'] # not a true variable
+ except:
+ pass
+ global_vars = forwarded_json['Variables']['globals'].keys() if settings['NAMED_GLOBALS'] else []
+ global_funcs = ['_' + x for x in forwarded_json['Functions']['libraryFunctions'].keys()]
+ asm_globals = ''.join([' var ' + g + '=env.' + g + ';\n' for g in basics + global_funcs + global_vars])
+ # sent data
+ sending = '{ ' + ', '.join([s + ': ' + s for s in fundamentals + basics + global_funcs + global_vars]) + ' }'
+ # received
+ receiving = ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm.' + s for s in exported_implemented_functions + function_tables])
+ # finalize
+ funcs_js = '''
+%s
+var asmPre = (function(env, buffer) {
+ 'use asm';
+ var HEAP8 = new env.Int8Array(buffer);
+ var HEAP16 = new env.Int16Array(buffer);
+ var HEAP32 = new env.Int32Array(buffer);
+ var HEAPU8 = new env.Uint8Array(buffer);
+ var HEAPU16 = new env.Uint16Array(buffer);
+ var HEAPU32 = new env.Uint32Array(buffer);
+ var HEAPF32 = new env.Float32Array(buffer);
+ var HEAPF64 = new env.Float64Array(buffer);
+''' % (asm_setup,) + asm_globals + '''
+ var __THREW__ = 0;
+ var undef = 0;
+
+ function stackAlloc(size) {
+ var ret = STACKTOP;
+ STACKTOP = (STACKTOP + size)|0;
+ STACKTOP = ((STACKTOP + 3)>>2)<<2;
+ return ret|0;
+ }
+ function stackSave() {
+ return STACKTOP|0;
+ }
+ function stackRestore(top) {
+ top = top|0;
+ STACKTOP = top;
+ }
+ function setThrew(threw) {
+ threw = threw|0;
+ __THREW__ = threw;
+ }
+''' + ''.join(['''
+ var tempRet%d = 0;
+ function setTempRet%d(value) {
+ value = value|0;
+ tempRet%d = value;
+ }
+''' % (i, i, i) for i in range(10)]) + funcs_js.replace('\n', '\n ') + '''
+
+ %s
+
+ return %s;
+});
+if (asmPre.toSource) { // works in sm but not v8, so we get full coverage between those two
+ asmPre = asmPre.toSource();
+ asmPre = asmPre.substr(25, asmPre.length-28);
+ asmPre = new Function('env', 'buffer', asmPre);
+}
+var asm = asmPre(%s, buffer); // pass through Function to prevent seeing outside scope
+%s;
+Runtime.stackAlloc = function(size) { return asm.stackAlloc(size) };
+Runtime.stackSave = function() { return asm.stackSave() };
+Runtime.stackRestore = function(top) { asm.stackRestore(top) };
+''' % (function_tables_defs.replace('\n', '\n ') + '\n' + '\n'.join(function_tables_impls), exports, sending, receiving)
+
+ # Set function table masks
+ def function_table_maskize(js):
+ masks = {}
+ default = None
+ for sig, table in last_forwarded_json['Functions']['tables'].iteritems():
+ masks[sig] = str(table.count(','))
+ default = sig
+ def fix(m):
+ sig = m.groups(0)[0]
+ if not sig in masks:
+ print >> sys.stderr, 'warning: function table use without functions for it!', sig
+ return masks[default] # TODO: generate empty function tables for this case, even though it would fail at runtime if used
+ return masks[sig]
+ return re.sub(r'{{{ FTM_([\w\d_$]+) }}}', lambda m: fix(m), js) # masks[m.groups(0)[0]]
+ funcs_js = function_table_maskize(funcs_js)
+ else:
+ function_tables_defs = '\n'.join([table for table in last_forwarded_json['Functions']['tables'].itervalues()])
+ outfile.write(function_tables_defs)
+ outfile.write(blockaddrsize(indexize(funcs_js)))
+ funcs_js = None
+
+ outfile.write(indexize(post))
if DEBUG: print >> sys.stderr, ' emscript: phase 3 took %s seconds' % (time.time() - t)
outfile.close()