aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-04-19 20:30:03 -0700
committerAlon Zakai <alonzakai@gmail.com>2013-04-19 20:36:02 -0700
commit31872fbd01ca23962d954ca40232c7b8bfde58fe (patch)
treecf05b59fb1451f137e0ff96ef56bc9c184329099
parent9089dc78a6c7fd21be5fc115451996018779dd40 (diff)
support for Runtime.addFunction in asm.js
-rwxr-xr-xemscripten.py25
-rw-r--r--src/modules.js2
-rw-r--r--src/preamble.js9
-rw-r--r--src/runtime.js25
-rw-r--r--src/settings.js3
-rwxr-xr-xtests/runner.py18
6 files changed, 69 insertions, 13 deletions
diff --git a/emscripten.py b/emscripten.py
index 6d384a96..649b3c0b 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -303,6 +303,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
# calculations on merged forwarded data
forwarded_json['Functions']['indexedFunctions'] = {}
i = 2
+ if settings['ASM_JS']: i += 2*settings['RESERVED_FUNCTION_POINTERS']
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
@@ -379,7 +380,16 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
params = ','.join(['p%d' % p for p in range(len(sig)-1)])
coercions = ';'.join(['p%d = %sp%d%s' % (p, '+' if sig[p+1] != 'i' else '', p, '' if sig[p+1] != 'i' else '|0') for p in range(len(sig)-1)]) + ';'
ret = '' if sig[0] == 'v' else ('return %s0' % ('+' if sig[0] != 'i' else ''))
- return ('function %s(%s) { %s abort(%d); %s }' % (bad, params, coercions, i, ret), raw.replace('[0,', '[' + bad + ',').replace(',0,', ',' + bad + ',').replace(',0,', ',' + bad + ',').replace(',0]', ',' + bad + ']').replace(',0]', ',' + bad + ']').replace(',0\n', ',' + bad + '\n'))
+ start = raw.index('[')
+ end = raw.rindex(']')
+ body = raw[start+1:end].split(',')
+ for j in range(settings['RESERVED_FUNCTION_POINTERS']):
+ body[2 + 2*j] = 'jsCall_%s_%s' % (sig, j)
+ def fix_item(item):
+ newline = '\n' in item
+ return (bad if item.replace('\n', '') == '0' else item) + ('\n' if newline else '')
+ body = ','.join(map(fix_item, body))
+ return ('function %s(%s) { %s abort(%d); %s }' % (bad, params, coercions, i, ret), raw[:start+1] + body + raw[end:])
infos = [make_table(sig, raw) for sig, raw in last_forwarded_json['Functions']['tables'].iteritems()]
function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n// EMSCRIPTEN_END_FUNCS\n' + '\n'.join([info[1] for info in infos])
@@ -389,6 +399,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
math_envs = ['Math.min'] # TODO: move min to maths
asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs])
basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat', 'copyTempDouble', 'copyTempFloat'] + [m.replace('.', '_') for m in math_envs]
+ if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR']
if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
basic_vars = ['STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT']
@@ -404,7 +415,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
def asm_coerce(value, sig):
if sig == 'v': return value
return ('+' if sig != 'i' else '') + value + ('|0' if sig == 'i' else '')
-
+
function_tables = ['dynCall_' + table for table in last_forwarded_json['Functions']['tables']]
function_tables_impls = []
for sig in last_forwarded_json['Functions']['tables'].iterkeys():
@@ -419,6 +430,16 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
%s;
}
''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret))
+
+ for i in range(settings['RESERVED_FUNCTION_POINTERS']):
+ jsret = ('return ' if sig[0] != 'v' else '') + asm_coerce('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0])
+ function_tables_impls.append('''
+ function jsCall_%s_%s(%s) {
+ %s
+ %s;
+ }
+
+''' % (sig, i, args, arg_coercions, jsret))
args = ','.join(['a' + str(i) for i in range(1, len(sig))])
args = 'index' + (',' if args else '') + args
asm_setup += '''
diff --git a/src/modules.js b/src/modules.js
index 5b5f7947..7a769846 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -234,7 +234,7 @@ var Functions = {
unimplementedFunctions: {}, // library etc. functions that we need to index, maps id to signature
indexedFunctions: {},
- nextIndex: 2, // Start at a non-0 (even, see below) value
+ nextIndex: (ASM_JS ? 2*RESERVED_FUNCTION_POINTERS : 0) + 2, // Start at a non-0 (even, see below) value
blockAddresses: {}, // maps functions to a map of block labels to label ids
diff --git a/src/preamble.js b/src/preamble.js
index 17b74cd9..92305ca0 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -6,6 +6,15 @@
{{RUNTIME}}
+#if ASM_JS
+#if RESERVED_FUNCTION_POINTERS
+function jsCall() {
+ var args = Array.prototype.slice.call(arguments);
+ return Runtime.functionPointers[args[0]].apply(null, args.slice(1));
+}
+#endif
+#endif
+
#if BENCHMARK
Module.realPrint = Module.print;
Module.print = Module.printErr = function(){};
diff --git a/src/runtime.js b/src/runtime.js
index 2a26db28..5b9a8134 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -305,18 +305,35 @@ var Runtime = {
}
},
- addFunction: function(func, sig) {
- //assert(sig); // TODO: support asm
- var table = FUNCTION_TABLE; // TODO: support asm
+#if ASM_JS
+ functionPointers: new Array(RESERVED_FUNCTION_POINTERS),
+#endif
+
+ addFunction: function(func) {
+#if ASM_JS
+ for (var i = 0; i < Runtime.functionPointers.length; i++) {
+ if (!Runtime.functionPointers[i]) {
+ Runtime.functionPointers[i] = func;
+ return 2 + 2*i;
+ }
+ }
+ throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.';
+#else
+ var table = FUNCTION_TABLE;
var ret = table.length;
table.push(func);
table.push(0);
return ret;
+#endif
},
removeFunction: function(index) {
- var table = FUNCTION_TABLE; // TODO: support asm
+#if ASM_JS
+ Runtime.functionPointers[(index-2)/2] = null;
+#else
+ var table = FUNCTION_TABLE;
table[index] = null;
+#endif
},
warnOnce: function(text) {
diff --git a/src/settings.js b/src/settings.js
index db24cca4..7cd0c02e 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -131,6 +131,9 @@ var CHECK_HEAP_ALIGN = 0; // Check heap accesses for alignment, but don't do as
var SAFE_DYNCALLS = 0; // Show stack traces on missing function pointer/virtual method calls
+var RESERVED_FUNCTION_POINTERS = 0; // In asm.js mode, we cannot simply add function pointers to
+ // function tables, so we reserve some slots for them.
+
var ASM_HEAP_LOG = 0; // Simple heap logging, like SAFE_HEAP_LOG but cheaper, and in asm.js
var CORRUPTION_CHECK = 0; // When enabled, will emit a buffer area at the beginning and
diff --git a/tests/runner.py b/tests/runner.py
index c0e16632..99f24fc9 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -8106,9 +8106,9 @@ def process(filename):
def test_add_function(self):
if self.emcc_args is None: return self.skip('requires emcc')
- if Settings.ASM_JS: return self.skip('needs a singleton function table')
Settings.INVOKE_RUN = 0
+ Settings.RESERVED_FUNCTION_POINTERS = 1
src = r'''
#include <stdio.h>
@@ -8117,21 +8117,27 @@ def process(filename):
int main(int argc, char **argv) {
int fp = atoi(argv[1]);
printf("fp: %d\n", fp);
- void (*f)() = reinterpret_cast<void (*)()>(fp);
- f();
+ void (*f)(int) = reinterpret_cast<void (*)(int)>(fp);
+ f(7);
return 0;
}
'''
open(os.path.join(self.get_dir(), 'post.js'), 'w').write('''
- var newFuncPtr = Runtime.addFunction(function() {
- Module.print('Hello from JS!');
+ var newFuncPtr = Runtime.addFunction(function(num) {
+ Module.print('Hello ' + num + ' from JS!');
});
Module.callMain([newFuncPtr.toString()]);
''')
self.emcc_args += ['--post-js', 'post.js']
- self.do_run(src, '''Hello from JS!''')
+ self.do_run(src, '''Hello 7 from JS!''')
+
+ if Settings.ASM_JS:
+ Settings.RESERVED_FUNCTION_POINTERS = 0
+ self.do_run(src, '''Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.''')
+ generated = open('src.cpp.o.js').read()
+ assert 'jsCall' not in generated
def test_scriptaclass(self):
if self.emcc_args is None: return self.skip('requires emcc')