summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-01-24 12:02:42 -0800
committerAlon Zakai <alonzakai@gmail.com>2013-01-24 12:02:42 -0800
commit64c779641a2a9587613cc65ad7251890f18e25c3 (patch)
tree87344a57db509e92b3196e5a4291a8c3bd8c2449
parent4e09482e006eda934527e1707036d74245d8dd91 (diff)
parent03e2e6c321d28e3df3b37a2c0bed3ba9d04e52b3 (diff)
Merge branch 'incoming'
-rw-r--r--AUTHORS3
-rw-r--r--README.markdown2
-rw-r--r--cmake/Platform/Emscripten.cmake17
-rwxr-xr-xemcc10
-rwxr-xr-xemscripten.py88
-rw-r--r--src/analyzer.js2
-rw-r--r--src/compiler.js1
-rw-r--r--src/determinstic.js16
-rw-r--r--src/experimental/functypeopt.diff113
-rw-r--r--src/intertyper.js8
-rw-r--r--src/jsifier.js112
-rw-r--r--src/library.js88
-rw-r--r--src/library_browser.js12
-rw-r--r--src/library_gl.js384
-rw-r--r--src/library_sdl.js21
-rw-r--r--src/modules.js49
-rw-r--r--src/parseTools.js187
-rw-r--r--src/preamble.js32
-rw-r--r--src/runtime.js7
-rw-r--r--src/settings.js8
-rw-r--r--src/shell.js2
-rw-r--r--src/utility.js6
-rw-r--r--system/include/libc/sys/_types.h6
-rw-r--r--system/include/libc/sys/types.h1
-rw-r--r--tests/cases/extendedprecision.ll2
-rw-r--r--tests/emscripten_api_browser.cpp2
-rw-r--r--tests/glbegin_points.c166
-rw-r--r--tests/glbegin_points.pngbin0 -> 1393 bytes
-rw-r--r--tests/glbook/CH13_ParticleSystem.pngbin11936 -> 5106 bytes
-rw-r--r--tests/openjpeg/codec/convert.h4
-rwxr-xr-xtests/runner.py240
-rw-r--r--tools/js-optimizer.js56
-rw-r--r--tools/js_optimizer.py1
-rw-r--r--tools/shared.py6
-rw-r--r--tools/test-js-optimizer-asm-last-output.js35
-rw-r--r--tools/test-js-optimizer-asm-last.js35
-rw-r--r--tools/test-js-optimizer-asm-pre-output.js44
-rw-r--r--tools/test-js-optimizer-asm-pre.js46
-rw-r--r--tools/test-js-optimizer-asm-regs-output.js7
-rw-r--r--tools/test-js-optimizer-asm-regs.js9
40 files changed, 1429 insertions, 399 deletions
diff --git a/AUTHORS b/AUTHORS
index d4e80f3f..fbdbd4ad 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -45,3 +45,6 @@ a license to everyone to use it as detailed in LICENSE.)
* Alan Kligman <alan.kligman@gmail.com> (copyright owned by Mozilla Foundation)
* Anthony Liot <wolfviking0@yahoo.com>
* Michael Riss <Michael.Riss@gmx.de>
+* Jasper St. Pierre <jstpierre@mecheye.net>
+* Manuel Schölling <manuel.schoelling@gmx.de>
+
diff --git a/README.markdown b/README.markdown
index dadbf3a4..81a95141 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,4 +1,6 @@
+![logo](http://dl.dropbox.com/u/80664946/emscripten_logo.jpg)
+
Emscripten
==========
diff --git a/cmake/Platform/Emscripten.cmake b/cmake/Platform/Emscripten.cmake
index 532e5d99..4b9c6572 100644
--- a/cmake/Platform/Emscripten.cmake
+++ b/cmake/Platform/Emscripten.cmake
@@ -42,3 +42,20 @@ set(CMAKE_C_ARCHIVE_CREATE "${CMAKE_C_COMPILER} -o <TARGET> -emit-llvm <LINK_FLA
# Set a global EMSCRIPTEN variable that can be used in client CMakeLists.txt to detect when building using Emscripten.
# There seems to be some kind of bug with CMake, so you might need to define this manually on the command line with "-DEMSCRIPTEN=1".
set(EMSCRIPTEN 1)
+
+set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG" CACHE STRING "Emscripten-overridden CMAKE_C_FLAGS_RELEASE")
+set(CMAKE_C_FLAGS_MINSIZEREL "-DNDEBUG" CACHE STRING "Emscripten-overridden CMAKE_C_FLAGS_MINSIZEREL")
+set(CMAKE_C_FLAGS_RELWITHDEBINFO "" CACHE STRING "Emscripten-overridden CMAKE_C_FLAGS_RELWITHDEBINFO")
+set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG" CACHE STRING "Emscripten-overridden CMAKE_CXX_FLAGS_RELEASE")
+set(CMAKE_CXX_FLAGS_MINSIZEREL "-DNDEBUG" CACHE STRING "Emscripten-overridden CMAKE_CXX_FLAGS_MINSIZEREL")
+set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "" CACHE STRING "Emscripten-overridden CMAKE_CXX_FLAGS_RELWITHDEBINFO")
+
+set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-O2" CACHE STRING "Emscripten-overridden CMAKE_EXE_LINKER_FLAGS_RELEASE")
+set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-O2" CACHE STRING "Emscripten-overridden CMAKE_EXE_LINKER_FLAGS_MINSIZEREL")
+set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "-O2" CACHE STRING "Emscripten-overridden CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO")
+set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "-O2" CACHE STRING "Emscripten-overridden CMAKE_SHARED_LINKER_FLAGS_RELEASE")
+set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "-O2" CACHE STRING "Emscripten-overridden CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL")
+set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "-O2" CACHE STRING "Emscripten-overridden CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO")
+set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "-O2" CACHE STRING "Emscripten-overridden CMAKE_MODULE_LINKER_FLAGS_RELEASE")
+set(CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL "-O2" CACHE STRING "Emscripten-overridden CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL")
+set(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO "-O2" CACHE STRING "Emscripten-overridden CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO")
diff --git a/emcc b/emcc
index 32ee0240..88f38bda 100755
--- a/emcc
+++ b/emcc
@@ -1186,15 +1186,7 @@ try:
if DEBUG: save_intermediate('transformed')
if shared.Settings.ASM_JS: # XXX temporary wrapping for testing purposes
- print >> sys.stderr, 'emcc: ASM_JS mode is highly experimental, and will not work on most codebases yet. It is NOT recommended that you try this yet.' # XXX TODO: 0.0 instead of +0 for local var defs
- unwrapped = open(final).read()
- final += '.asmwrap.js'
- open(final, 'w').write('''
-(function() { // prevent new Function from seeing the global scope
-%s
-}).apply(null, arguments);
-''' % unwrapped)
- if DEBUG: save_intermediate('asmwrap')
+ print >> sys.stderr, 'emcc: ASM_JS mode is highly experimental, and will not work on most codebases yet. It is NOT recommended that you try this yet.'
# It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing
js_optimizer_queue = []
diff --git a/emscripten.py b/emscripten.py
index 2662e40a..68fb4aee 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -248,6 +248,11 @@ def emscript(infile, settings, outfile, libraries=[]):
for key, value in curr_forwarded_json['Functions']['unimplementedFunctions'].iteritems():
forwarded_json['Functions']['unimplementedFunctions'][key] = value
+ if settings.get('ASM_JS'):
+ parts = pre.split('// ASM_LIBRARY FUNCTIONS\n')
+ if len(parts) > 1:
+ pre = parts[0]
+ outputs.append([parts[1]])
funcs_js = ''.join([output[0] for output in outputs])
outputs = None
@@ -265,7 +270,7 @@ def emscript(infile, settings, outfile, libraries=[]):
indexing = forwarded_json['Functions']['indexedFunctions']
def indexize(js):
- return re.sub(r'{{{ FI_([\w\d_$]+) }}}', lambda m: str(indexing[m.groups(0)[0]]), js)
+ return re.sub(r"'{{ FI_([\w\d_$]+) }}'", lambda m: str(indexing.get(m.groups(0)[0]) or 0), js)
blockaddrs = forwarded_json['Functions']['blockAddresses']
def blockaddrsize(js):
@@ -295,23 +300,34 @@ def emscript(infile, settings, outfile, libraries=[]):
simple = os.environ.get('EMCC_SIMPLE_ASM')
class Counter:
i = 0
+ pre_tables = last_forwarded_json['Functions']['tables']['pre']
+ del last_forwarded_json['Functions']['tables']['pre']
+
+ # Find function table calls without function tables generated for them
+ for use in set(re.findall(r'{{{ FTM_[\w\d_$]+ }}}', funcs_js)):
+ sig = use[8:len(use)-4]
+ if sig not in last_forwarded_json['Functions']['tables']:
+ if DEBUG: print >> sys.stderr, 'add empty function table', sig
+ last_forwarded_json['Functions']['tables'][sig] = 'var FUNCTION_TABLE_' + sig + ' = [0,0];\n'
+
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()])
-
- maths = ['Runtime.bitshift64', 'Math.floor', 'Math.min', 'Math.abs', 'Math.sqrt', 'Math.pow', 'Math.cos', 'Math.sin', 'Math.tan', 'Math.acos', 'Math.asin', 'Math.atan', 'Math.atan2', 'Math.exp', 'Math.log', 'Math.ceil']
+ 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 + ']'))
+ 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] + [info[1] for info in infos])
+ maths = ['Math.' + func for func in ['floor', 'abs', 'sqrt', 'pow', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan2', 'exp', 'log', 'ceil']]
if settings['USE_MATH_IMUL']:
maths += ['Math.imul']
- asm_setup = '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in maths])
- fundamentals = ['buffer', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array']
- basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in maths]
+ fundamentals = ['Math', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint16Array', 'Uint32Array', 'Float32Array', 'Float64Array']
+ math_envs = ['Runtime.bitshift64', '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'] + [m.replace('.', '_') for m in math_envs]
basic_vars = ['STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT']
basic_float_vars = ['NaN', 'Infinity']
if forwarded_json['Types']['preciseI64MathUsed']:
@@ -325,18 +341,25 @@ 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
+ 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():
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))])
+ arg_coercions = ' '.join(['a' + str(i) + '=' + asm_coerce('a' + str(i), sig[i]) + ';' for i in range(1, len(sig))])
+ coerced_args = ','.join([asm_coerce('a' + str(i), sig[i]) for i in range(1, len(sig))])
+ ret = ('return ' if sig[0] != 'v' else '') + asm_coerce('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0])
function_tables_impls.append('''
function dynCall_%s(index%s%s) {
index = index|0;
%s
- %sFUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s);
+ %s;
}
-''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, 'return ' if sig[0] != 'v' else '', sig, sig, args))
+''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret))
+
# calculate exports
exported_implemented_functions = list(exported_implemented_functions)
exports = []
@@ -356,11 +379,13 @@ var i64Math_modulo = function(a, b, c, d, e) { i64Math.modulo(a, b, c, d, e) };
global_funcs = ['_' + x for x in forwarded_json['Functions']['libraryFunctions'].keys()]
def math_fix(g):
return g if not g.startswith('Math_') else g.split('_')[1];
- asm_global_funcs = ''.join([' var ' + g + '=env.' + math_fix(g) + ';\n' for g in basic_funcs + global_funcs])
+ asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global.' + g + ';\n' for g in maths]) + \
+ ''.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])
# sent data
- sending = '{ ' + ', '.join([math_fix(s) + ': ' + s for s in fundamentals + basic_funcs + global_funcs + basic_vars + basic_float_vars + global_vars]) + ' }'
+ 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]) + ' }'
# received
if not simple:
receiving = ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm.' + s for s in exported_implemented_functions + function_tables])
@@ -375,20 +400,20 @@ function asmPrintInt(x) {
function asmPrintFloat(x) {
Module.print('float ' + x);// + ' ' + new Error().stack);
}
-var asmPre = (function(env, buffer) {
+var asm = (function(global, 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);
+ var HEAP8 = new global.Int8Array(buffer);
+ var HEAP16 = new global.Int16Array(buffer);
+ var HEAP32 = new global.Int32Array(buffer);
+ var HEAPU8 = new global.Uint8Array(buffer);
+ var HEAPU16 = new global.Uint16Array(buffer);
+ var HEAPU32 = new global.Uint32Array(buffer);
+ var HEAPF32 = new global.Float32Array(buffer);
+ var HEAPF64 = new global.Float64Array(buffer);
''' % (asm_setup,) + '\n' + asm_global_vars + '''
var __THREW__ = 0;
var undef = 0;
- var tempInt = 0, tempBigInt = 0, tempValue = 0;
+ var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0;
''' + ''.join(['''
var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs + '''
function stackAlloc(size) {
@@ -420,18 +445,12 @@ var asmPre = (function(env, buffer) {
%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, %s, buffer);
%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)
+''' % (pre_tables + '\n'.join(function_tables_impls) + '\n' + function_tables_defs.replace('\n', '\n '), exports, the_global, sending, receiving)
# Set function table masks
def function_table_maskize(js):
@@ -442,9 +461,6 @@ Runtime.stackRestore = function(top) { asm.stackRestore(top) };
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)
diff --git a/src/analyzer.js b/src/analyzer.js
index 229bda9f..60ef5ba8 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -635,7 +635,7 @@ function analyzer(data, sidePass) {
break;
}
case 'add': case 'sub': case 'sdiv': case 'udiv': case 'mul': case 'urem': case 'srem':
- case 'uitofp': case 'sitofp': {
+ case 'uitofp': case 'sitofp': case 'fptosi': case 'fptoui': {
// We cannot do these in parallel chunks of 32-bit operations. We will handle these in processMathop
i++;
continue;
diff --git a/src/compiler.js b/src/compiler.js
index 118ca83a..25c306cf 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -168,6 +168,7 @@ if (PGO) { // by default, correct everything during PGO
EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS);
EXPORTED_GLOBALS = set(EXPORTED_GLOBALS);
+EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST);
RUNTIME_DEBUG = LIBRARY_DEBUG || GL_DEBUG;
diff --git a/src/determinstic.js b/src/determinstic.js
new file mode 100644
index 00000000..7bf1143c
--- /dev/null
+++ b/src/determinstic.js
@@ -0,0 +1,16 @@
+
+var MAGIC = 0;
+Math.random = function() {
+ MAGIC = Math.pow(MAGIC + 1.8912, 3) % 1;
+ return MAGIC + 10;
+};
+var TIME = 0;
+Date.now = function() {
+ TIME += 0.05;
+ return TIME;
+};
+performance.now = function() {
+ TIME += 0.05;
+ return TIME;
+};
+
diff --git a/src/experimental/functypeopt.diff b/src/experimental/functypeopt.diff
new file mode 100644
index 00000000..6e2fa396
--- /dev/null
+++ b/src/experimental/functypeopt.diff
@@ -0,0 +1,113 @@
+diff --git a/src/parseTools.js b/src/parseTools.js
+index 9786460..fb4be9f 100644
+--- a/src/parseTools.js
++++ b/src/parseTools.js
+@@ -205,26 +205,57 @@ function isFunctionDef(token, out) {
+ function isPossiblyFunctionType(type) {
+ // A quick but unreliable way to see if something is a function type. Yes is just 'maybe', no is definite.
+ var len = type.length;
+- return type[len-2] == ')' && type[len-1] == '*';
++ return type[len-2] == ')' && type[len-1] == '*' && type.indexOf('(') > 0;
+ }
+
+ function isFunctionType(type, out) {
+ if (!isPossiblyFunctionType(type)) return false;
+ type = type.replace(/"[^"]+"/g, '".."');
+- var parts;
+ // hackish, but quick splitting of function def parts. this must be fast as it happens a lot
+- if (type[0] != '[') {
+- parts = type.split(' ');
+- } else {
+- var index = type.search(']');
+- index += type.substr(index).search(' ');
+- parts = [type.substr(0, index), type.substr(index+1)];
+- }
++ var parts = type.split(' ');
+ if (pointingLevels(type) !== 1) return false;
+ var text = removeAllPointing(parts.slice(1).join(' '));
+ if (!text) return false;
+- if (out) out.returnType = parts[0];
+- return isType(parts[0]) && isFunctionDef({ text: text, item: tokenize(text.substr(1, text.length-2), true) }, out);
++ if (!isType(parts[0])) return false;
++ var level = 0;
++ var chunks = [];
++ var currStart = 0;
++ for (var i = 0; i < type.length; i++) {
++ var curr = type[i];
++ if (curr == '(') {
++ level++;
++ if (level == 1) {
++ chunks.push(type.substring(currStart, i));
++ currStart = i+1;
++ }
++ } else if (curr == ')') {
++ level--;
++ if (level == 0) {
++ curr = type.substring(currStart, i);
++ if (curr == '') curr = '$'; // make sure inside of () stays valid
++ chunks.push(curr);
++ currStart = i+1;
++ }
++ }
++ }
++//printErr('pre chunks ' + JSON.stringify(chunks));
++ chunks = chunks.map(function(chunk) { return chunk.replace(/ /g, '') })
++ .filter(function(chunk) { return chunk.length > 0 })
++ .map(function(chunk) { return chunk.replace(/\$/g, ' ') });
++//printErr('post chunks ' + JSON.stringify(chunks));
++ switch (chunks.length) {
++ case 2: { // e.g. void (i32,i32)
++ if (out) out.returnType = chunks[0]; // TODO: add cache, with this as the value
++ return isFunctionDef({ text: '(' + chunks[1] + ')', item: tokenize(chunks[1], true) }, out);
++ }
++ case 4: { // e.g. void (i32)* (i32, i32) (i.e., returns void (i32)*!)
++ if (chunks[2] != '*') return false;
++ if (out) out.returnType = chunks[0] + ' ' + chunks[1];
++ return isFunctionDef({ text: '(' + chunks[1] + ')', item: tokenize(chunks[1], true) }) &&
++ isFunctionDef({ text: '(' + chunks[2] + ')', item: tokenize(chunks[2], true) }, out);
++ }
++ }
++ return false;
+ }
+
+ var isTypeCache = {}; // quite hot, optimize as much as possible
+diff --git a/tests/runner.py b/tests/runner.py
+index 842daca..312541f 100755
+--- a/tests/runner.py
++++ b/tests/runner.py
+@@ -2860,6 +2860,35 @@ Exiting setjmp function, level: 0, prev_jmp: -1
+ '''
+ self.do_run(src, 'fn2(-5) = 5, fn(10) = 3.16')
+
++ def test_funcptrfunc(self):
++ src = r'''
++ #include <stdio.h>
++
++/*
++define internal fastcc void ()* @sqlite3OsDlSym(%struct.sqlite3_vfs* %pVfs, i8* %pHdle, i8* %zSym) nounwind {
++ %1 = getelementptr inbounds %struct.sqlite3_vfs* %pVfs, i32 0, i32 12
++ %2 = load void ()* (%struct.sqlite3_vfs*, i8*, i8*)** %1, align 4
++ %3 = tail call void ()* (%struct.sqlite3_vfs*, i8*, i8*)* %2(%struct.sqlite3_vfs* %pVfs, i8* %pHdle, i8* %zSym) nounwind
++ ret void ()* %3
++}
++*/
++
++ typedef void (*funcptr)(int, int);
++ typedef funcptr (*funcptrfunc)(int);
++
++ funcptr __attribute__ ((noinline)) getIt(int x) {
++ return (funcptr)x;
++ }
++
++ int main(int argc, char **argv)
++ {
++ funcptrfunc fpf = argc < 100 ? getIt : NULL;
++ printf("*%p*\n", fpf(argc));
++ return 0;
++ }
++ '''
++ self.do_run(src, '*0x1*')
++
+ def test_emptyclass(self):
+ if self.emcc_args is None: return self.skip('requires emcc')
+ src = '''
diff --git a/src/intertyper.js b/src/intertyper.js
index 00d504f5..c1a98354 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -725,7 +725,7 @@ function intertyper(data, sidePass, baseLineNums) {
substrate.addActor('Invoke', {
processItem: function(item) {
var result = makeCall.call(this, item, 'invoke');
- if (DISABLE_EXCEPTION_CATCHING) {
+ if (DISABLE_EXCEPTION_CATCHING == 1) {
result.item.intertype = 'call';
result.ret.push({
intertype: 'branch',
@@ -834,15 +834,17 @@ function intertyper(data, sidePass, baseLineNums) {
item.params[i-1] = parseLLVMSegment(segments[i-1]);
}
}
+ var setParamTypes = true;
if (item.op === 'select') {
assert(item.params[1].type === item.params[2].type);
item.type = item.params[1].type;
- } else if (item.op === 'inttoptr' || item.op === 'ptrtoint') {
+ } else if (item.op in LLVM.CONVERSIONS) {
item.type = item.params[1].type;
+ setParamTypes = false;
} else {
item.type = item.params[0].type;
}
- if (item.op != 'ptrtoint') {
+ if (setParamTypes) {
for (var i = 0; i < 4; i++) {
if (item.params[i]) item.params[i].type = item.type; // All params have the same type, normally
}
diff --git a/src/jsifier.js b/src/jsifier.js
index 5fbea5ba..84a9b5f7 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -10,6 +10,7 @@ var UNDERSCORE_OPENPARENS = set('_', '(');
var RELOOP_IGNORED_LASTS = set('return', 'unreachable', 'resume');
var addedLibraryItems = {};
+var asmLibraryFunctions = [];
// JSifier
function JSify(data, functionsOnly, givenFunctions) {
@@ -76,7 +77,7 @@ function JSify(data, functionsOnly, givenFunctions) {
assert(!BUILD_AS_SHARED_LIB, 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB set.')
libFuncsToInclude = [];
for (var key in LibraryManager.library) {
- if (!key.match(/__(deps|postset|inline)$/)) {
+ if (!key.match(/__(deps|postset|inline|asm|sig)$/)) {
libFuncsToInclude.push(key);
}
}
@@ -292,7 +293,7 @@ function JSify(data, functionsOnly, givenFunctions) {
padding = makeEmptyStruct(item.type);
}
var padded = val.concat(padding.slice(val.length));
- var js = item.ident + '=' + makePointer(JSON.stringify(padded), null, allocator, item.type, index) + ';'
+ var js = item.ident + '=' + makePointer(padded, null, allocator, item.type, index) + ';'
if (LibraryManager.library[shortident + '__postset']) {
js += '\n' + LibraryManager.library[shortident + '__postset'];
}
@@ -332,7 +333,6 @@ function JSify(data, functionsOnly, givenFunctions) {
constant[i] = '0';
}
});
- constant = '[' + constant.join(', ') + ']';
}
// NOTE: This is the only place that could potentially create static
// allocations in a shared library.
@@ -346,7 +346,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (index !== null) {
index = getFastValue(index, '+', Runtime.alignMemory(calcAllocatedSize(Variables.globals[item.ident].type)));
}
- js += '\n' + makePointer('[0]', null, allocator, ['void*'], index) + ';';
+ js += '\n' + makePointer([0], null, allocator, ['void*'], index) + ';';
}
if (!ASM_JS && (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS))) {
js += '\nModule["' + item.ident + '"] = ' + item.ident + ';';
@@ -440,8 +440,8 @@ function JSify(data, functionsOnly, givenFunctions) {
// name the function; overwrite if it's already named
snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function _' + ident + '(');
if (LIBRARY_DEBUG) {
- snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.print("[library call:' + ident + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); ');
- snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.print(" [ return:" + Runtime.prettyPrint(ret)); return ret; }';
+ snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.printErr("[library call:' + ident + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); ');
+ snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.printErr(" [ return:" + Runtime.prettyPrint(ret)); return ret; \n}';
}
if (ASM_JS) Functions.libraryFunctions[ident] = 1;
}
@@ -477,13 +477,25 @@ function JSify(data, functionsOnly, givenFunctions) {
} else {
ident = '_' + ident;
}
- var text = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : '');
+ var depsText = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : '');
// redirected idents just need a var, but no value assigned to them - it would be unused
- text += isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';');
- if (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS)) {
- text += '\nModule["' + ident + '"] = ' + ident + ';';
+ var contentText = isFunction ? snippet : ('var ' + ident + (redirectedIdent ? '' : '=' + snippet) + ';');
+ if (ASM_JS) {
+ var sig = LibraryManager.library[ident.substr(1) + '__sig'];
+ if (isFunction && sig && LibraryManager.library[ident.substr(1) + '__asm']) {
+ // asm library function, add it as generated code alongside the generated code
+ Functions.implementedFunctions[ident] = sig;
+ asmLibraryFunctions.push(contentText);
+ contentText = ' ';
+ EXPORTED_FUNCTIONS[ident] = 1;
+ delete Functions.libraryFunctions[ident.substr(1)];
+ }
+ } else {
+ if (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS)) {
+ contentText += '\nModule["' + ident + '"] = ' + ident + ';';
+ }
}
- return text;
+ return depsText + contentText;
}
var ret = [item];
@@ -606,10 +618,11 @@ function JSify(data, functionsOnly, givenFunctions) {
}
for (i = 0; i < chunks.length; i++) {
func.JS += ' var ' + chunks[i].map(function(v) {
- if (!isIllegalType(v.type) || v.ident.indexOf('$', 1) > 0) { // not illegal, or a broken up illegal
- return v.ident + ' = ' + asmInitializer(v.type); //, func.variables[v.ident].impl);
+ var type = getImplementationType(v);
+ if (!isIllegalType(type) || v.ident.indexOf('$', 1) > 0) { // not illegal, or a broken up illegal
+ return v.ident + ' = ' + asmInitializer(type); //, func.variables[v.ident].impl);
} else {
- return range(Math.ceil(getBits(v.type)/32)).map(function(i) {
+ return range(Math.ceil(getBits(type)/32)).map(function(i) {
return v.ident + '$' + i + '= 0';
}).join(',');
}
@@ -722,12 +735,13 @@ function JSify(data, functionsOnly, givenFunctions) {
if (func.setjmpTable) {
ret += 'try { ';
}
- ret += 'switch(label) {\n';
+ ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n';
ret += block.labels.map(function(label) {
return indent + ' case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
+ getLabelLines(label, indent + ' ');
- }).join('\n');
- ret += '\n' + indent + ' default: assert(0, "bad label: " + label);\n' + indent + '}';
+ }).join('\n') + '\n';
+ if (ASSERTIONS) ret += indent + ' default: assert(0, "bad label: " + label);\n';
+ ret += indent + '}\n';
if (func.setjmpTable) {
ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
}
@@ -785,10 +799,10 @@ function JSify(data, functionsOnly, givenFunctions) {
func.JS += walkBlock(func.block, ' ');
// Finalize function
if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n";
- // Add an unneeded return, needed for strict mode to not throw warnings in some cases.
- // If we are not relooping, then switches make it unimportant to have this (and, we lack hasReturn anyhow)
- if (RELOOP && func.lines.length > 0 && func.labels.filter(function(label) { return label.hasReturn }).length > 0) {
- func.JS += ' return' + (func.returnType !== 'void' ? ' null' : '') + ';\n';
+ // Ensure a return in a function with a type that returns, even if it lacks a return (e.g., if it aborts())
+ if (RELOOP && func.lines.length > 0 && func.returnType != 'void') {
+ var returns = func.labels.filter(function(label) { return label.lines[label.lines.length-1].intertype == 'return' }).length;
+ if (returns == 0) func.JS += ' return ' + asmCoercion('0', func.returnType);
}
func.JS += '}\n';
@@ -1092,7 +1106,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var value;
if (useIfs) {
value = targetLabels[targetLabel].map(function(value) {
- return makeComparison(signedIdent, makeSignOp(value, item.type, 're'), item.type)
+ return makeComparison(signedIdent, '==', makeSignOp(value, item.type, 're'), item.type)
}).join(' | ');
ret += 'if (' + value + ') {\n';
} else {
@@ -1142,8 +1156,10 @@ function JSify(data, functionsOnly, givenFunctions) {
+ "INDENT = INDENT.substr(0, INDENT.length-2);\n";
}
ret += 'return';
- if (item.value) {
- ret += ' ' + asmCoercion(finalizeLLVMParameter(item.value), item.type);
+ var value = item.value ? finalizeLLVMParameter(item.value) : null;
+ if (!value && item.funcData.returnType != 'void') value = '0'; // no-value returns must become value returns if function returns
+ if (value) {
+ ret += ' ' + asmCoercion(value, item.type);
}
return ret + ';';
});
@@ -1152,20 +1168,29 @@ function JSify(data, functionsOnly, givenFunctions) {
var ptr = makeStructuralAccess(item.ident, 0);
return (EXCEPTION_DEBUG ? 'Module.print("Resuming exception");' : '') +
'if (' + makeGetValue('_llvm_eh_exception.buf', 0, 'void*') + ' == 0) { ' + makeSetValue('_llvm_eh_exception.buf', 0, ptr, 'void*') + ' } ' +
- makeThrow('ptr') + ';';
+ makeThrow(ptr) + ';';
});
makeFuncLineActor('invoke', function(item) {
// Wrapping in a function lets us easily return values if we are
// in an assignment
var phiSets = calcPhiSets(item);
var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type);
- var ret = '(function() { try { __THREW__ = 0; return '
- + call_ + ' '
- + '} catch(e) { '
- + 'if (typeof e != "number") throw e; '
- + 'if (ABORT) throw e; __THREW__ = 1; '
- + (EXCEPTION_DEBUG ? 'Module.print("Exception: " + e + ", currently at: " + (new Error().stack)); ' : '')
- + 'return null } })();';
+
+ var ret;
+
+ if (DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST)) {
+ ret = call_ + ';';
+ } else {
+ ret = '(function() { try { __THREW__ = 0; return '
+ + call_ + ' '
+ + '} catch(e) { '
+ + 'if (typeof e != "number") throw e; '
+ + 'if (ABORT) throw e; __THREW__ = 1; '
+ + (EXCEPTION_DEBUG ? 'Module.print("Exception: " + e + ", currently at: " + (new Error().stack)); ' : '')
+ + 'return null } })();';
+ }
+
+
if (item.assignTo) {
ret = 'var ' + item.assignTo + ' = ' + ret;
if (USE_TYPED_ARRAYS == 2 && isIllegalType(item.type)) {
@@ -1242,7 +1267,12 @@ function JSify(data, functionsOnly, givenFunctions) {
return ret + item.ident + '.f' + item.indexes[0][0].text + ' = ' + finalizeLLVMParameter(item.value) + ', ' + item.ident + ')';
});
makeFuncLineActor('indirectbr', function(item) {
- return makeBranch(finalizeLLVMParameter(item.value), item.currLabelId, true);
+ var phiSets = calcPhiSets(item);
+ var js = 'var ibr = ' + finalizeLLVMParameter(item.value) + ';\n';
+ for (var targetLabel in phiSets) {
+ js += 'if (' + makeComparison('ibr', '==', targetLabel, 'i32') + ') { ' + getPhiSetsForLabel(phiSets, targetLabel) + ' }\n';
+ }
+ return js + makeBranch('ibr', item.currLabelId, true);
});
makeFuncLineActor('alloca', function(item) {
if (typeof item.allocatedIndex === 'number') {
@@ -1365,10 +1395,12 @@ function JSify(data, functionsOnly, givenFunctions) {
}
var returnType;
- if (byPointer || ASM_JS) returnType = type.split(' ')[0];
+ if (byPointer || ASM_JS) {
+ returnType = getReturnType(type);
+ }
if (byPointer) {
- var sig = Functions.getSignature(returnType, argsTypes);
+ var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs);
if (ASM_JS) {
assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out)
callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py
@@ -1477,6 +1509,16 @@ function JSify(data, functionsOnly, givenFunctions) {
generated.forEach(function(item) { print(indentify(item.JS || '', 2)); });
legalizedI64s = legalizedI64sDefault;
+
+ if (asmLibraryFunctions.length > 0) {
+ print('// ASM_LIBRARY FUNCTIONS');
+ function fix(f) { // fix indenting to not confuse js optimizer
+ f = f.substr(f.indexOf('f')); // remove initial spaces before 'function'
+ f = f.substr(0, f.lastIndexOf('\n')+1); // remove spaces and last }
+ return f + '}'; // add unindented } to match function
+ }
+ print(asmLibraryFunctions.map(fix).join('\n'));
+ }
} else {
if (singlePhase) {
assert(data.unparsedGlobalss[0].lines.length == 0, dump([phase, data.unparsedGlobalss]));
diff --git a/src/library.js b/src/library.js
index b70aadbc..74ebdc07 100644
--- a/src/library.js
+++ b/src/library.js
@@ -52,7 +52,7 @@ LibraryManager.library = {
streams: [null],
#if ASSERTIONS
checkStreams: function() {
- for (var i in FS.streams) assert(i >= 0 && i < FS.streams.length); // no keys not in dense span
+ for (var i in FS.streams) if (FS.streams.hasOwnProperty(i)) assert(i >= 0 && i < FS.streams.length); // no keys not in dense span
for (var i = 0; i < FS.streams.length; i++) assert(typeof FS.streams[i] == 'object'); // no non-null holes in dense span
},
#endif
@@ -810,6 +810,8 @@ LibraryManager.library = {
return 0;
},
+ utimes: function() { throw 'utimes not implemented' },
+
// ==========================================================================
// libgen.h
// ==========================================================================
@@ -1038,6 +1040,8 @@ LibraryManager.library = {
return _chmod(allocate(pathArray, 'i8', ALLOC_STACK), mode);
}
},
+ lchmod: function() { throw 'TODO: lchmod' },
+
umask__deps: ['$FS'],
umask: function(newMask) {
// mode_t umask(mode_t cmask);
@@ -2515,7 +2519,7 @@ LibraryManager.library = {
var curr = 0;
var buffer = [];
// Read characters according to the format. floats are trickier, they may be in an unfloat state in the middle, then be a valid float later
- if (type == 'f') {
+ if (type == 'f' || type == 'e' || type == 'g' || type == 'E') {
var last = 0;
next = get();
while (next > 0) {
@@ -2569,6 +2573,10 @@ LibraryManager.library = {
{{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}}
break;
case 'f':
+ case 'e':
+ case 'g':
+ case 'E':
+ // fallthrough intended
if (long_) {
{{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'double') }}}
} else {
@@ -2607,6 +2615,7 @@ LibraryManager.library = {
// format: A pointer to the format string.
// varargs: A pointer to the start of the arguments list.
// Returns the resulting string string as a character array.
+ _formatString__deps: ['strlen'],
_formatString: function(format, varargs) {
var textIndex = format;
var argIndex = 0;
@@ -2933,7 +2942,7 @@ LibraryManager.library = {
} else if (next == 's'.charCodeAt(0)) {
// String.
var arg = getNextArg('i8*') || nullString;
- var argLength = String_len(arg);
+ var argLength = _strlen(arg);
if (precisionSet) argLength = Math.min(argLength, precision);
if (!flagLeftAlign) {
while (argLength < width--) {
@@ -3496,6 +3505,12 @@ LibraryManager.library = {
var result = __formatString(format, varargs);
var limit = (n === undefined) ? result.length
: Math.min(result.length, Math.max(n - 1, 0));
+ if (s < 0) {
+ s = -s;
+ var buf = _malloc(limit+1);
+ {{{ makeSetValue('s', '0', 'buf', 'i8*') }}};
+ s = buf;
+ }
for (var i = 0; i < limit; i++) {
{{{ makeSetValue('s', 'i', 'result[i]', 'i8') }}};
}
@@ -3525,10 +3540,15 @@ LibraryManager.library = {
// http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html
return _snprintf(s, undefined, format, varargs);
},
+ asprintf__deps: ['sprintf'],
+ asprintf: function(s, format, varargs) {
+ return _sprintf(-s, format, varargs);
+ },
vfprintf: 'fprintf',
vsnprintf: 'snprintf',
vprintf: 'printf',
vsprintf: 'sprintf',
+ vasprintf: 'asprintf',
vscanf: 'scanf',
vfscanf: 'fscanf',
vsscanf: 'sscanf',
@@ -3613,7 +3633,7 @@ LibraryManager.library = {
* implementation (replaced by dlmalloc normally) so
* not an issue.
*/
- ptr = Runtime.staticAlloc(bytes + 8);
+ var ptr = Runtime.staticAlloc(bytes + 8);
return (ptr+8) & 0xFFFFFFF8;
},
free: function(){},
@@ -4209,6 +4229,8 @@ LibraryManager.library = {
}
},
+ wmemcpy: function() { throw 'wmemcpy not implemented' },
+
llvm_memcpy_i32: 'memcpy',
llvm_memcpy_i64: 'memcpy',
llvm_memcpy_p0i8_p0i8_i32: 'memcpy',
@@ -4234,6 +4256,8 @@ LibraryManager.library = {
llvm_memmove_p0i8_p0i8_i32: 'memmove',
llvm_memmove_p0i8_p0i8_i64: 'memmove',
+ wmemmove: function() { throw 'wmemmove not implemented' },
+
memset__inline: function(ptr, value, num, align) {
return makeSetValues(ptr, 0, value, 'null', num, align);
},
@@ -4268,8 +4292,18 @@ LibraryManager.library = {
llvm_memset_p0i8_i32: 'memset',
llvm_memset_p0i8_i64: 'memset',
+ wmemset: function() { throw 'wmemset not implemented' },
+
+ strlen__sig: 'ii',
+ strlen__asm: true,
strlen: function(ptr) {
- return String_len(ptr);
+ ptr = ptr|0;
+ var curr = 0;
+ curr = ptr;
+ while ({{{ makeGetValueAsm('curr', '0', 'i8') }}}|0 != 0) {
+ curr = (curr + 1)|0;
+ }
+ return (curr - ptr)|0;
},
// TODO: Implement when we have real unicode support.
@@ -4277,6 +4311,13 @@ LibraryManager.library = {
return 1;
},
+ wcslen: function() { throw 'wcslen not implemented' },
+ mbrlen: function() { throw 'mbrlen not implemented' },
+ mbsrtowcs: function() { throw 'mbsrtowcs not implemented' },
+ wcsnrtombs: function() { throw 'wcsnrtombs not implemented' },
+ mbsnrtowcs: function() { throw 'mbsnrtowcs not implemented' },
+ mbrtowc: function() { throw 'mbrtowc not implemented' },
+
strspn: function(pstr, pset) {
var str = pstr, set, strcurr, setcurr;
while (1) {
@@ -4493,17 +4534,18 @@ LibraryManager.library = {
},
rindex: 'strrchr',
+ strdup__deps: ['strlen'],
strdup: function(ptr) {
- var len = String_len(ptr);
+ var len = _strlen(ptr);
var newStr = _malloc(len + 1);
{{{ makeCopyValues('newStr', 'ptr', 'len', 'null', null, 1) }}};
{{{ makeSetValue('newStr', 'len', '0', 'i8') }}};
return newStr;
},
- strndup__deps: ['strdup'],
+ strndup__deps: ['strdup', 'strlen'],
strndup: function(ptr, size) {
- var len = String_len(ptr);
+ var len = _strlen(ptr);
if (size >= len) {
return _strdup(ptr);
@@ -5069,6 +5111,8 @@ LibraryManager.library = {
_ZNSt9exceptionD2Ev: function(){}, // XXX a dependency of dlmalloc, but not actually needed if libcxx is not anyhow included
+ _ZNSt9type_infoD2Ev: function(){},
+
// RTTI hacks for exception handling, defining type_infos for common types.
// The values are dummies. We simply use the addresses of these statically
// allocated variables as unique identifiers.
@@ -5910,6 +5954,9 @@ LibraryManager.library = {
return 0;
},
+ setitimer: function() { throw 'setitimer not implemented yet' },
+ getitimer: function() { throw 'getitimer not implemented yet' },
+
// ==========================================================================
// sys/time.h
// ==========================================================================
@@ -6078,6 +6125,8 @@ LibraryManager.library = {
},
killpg: 'kill',
+ siginterrupt: function() { throw 'siginterrupt not implemented' },
+
// ==========================================================================
// sys/wait.h
// ==========================================================================
@@ -6125,6 +6174,8 @@ LibraryManager.library = {
return me.ret;
},
+ __locale_mb_cur_max: function() { throw '__locale_mb_cur_max not implemented' },
+
// ==========================================================================
// langinfo.h
// ==========================================================================
@@ -6305,6 +6356,10 @@ LibraryManager.library = {
return me.ret;
},
+ _Z7catopenPKci: function() { throw 'catopen not implemented' },
+ _Z7catgetsP8_nl_catdiiPKc: function() { throw 'catgets not implemented' },
+ _Z8catcloseP8_nl_catd: function() { throw 'catclose not implemented' },
+
// ==========================================================================
// errno.h
// ==========================================================================
@@ -6548,6 +6603,7 @@ LibraryManager.library = {
pthread_cond_init: function() {},
pthread_cond_destroy: function() {},
pthread_cond_broadcast: function() {},
+ pthread_cond_wait: function() {},
pthread_self: function() {
//FIXME: assumes only a single thread
return 0;
@@ -7108,6 +7164,22 @@ LibraryManager.library = {
return ret;
},
+ // pty.h
+
+ openpty: function() { throw 'openpty: TODO' },
+ forkpty: function() { throw 'forkpty: TODO' },
+
+ // grp.h
+
+ initgroups: function() { throw 'initgroups: TODO' },
+
+ // pwd.h
+
+ getpwnam: function() { throw 'getpwnam: TODO' },
+ setpwent: function() { throw 'setpwent: TODO' },
+ getpwent: function() { throw 'getpwent: TODO' },
+ endpwent: function() { throw 'endpwent: TODO' },
+
// ==========================================================================
// emscripten.h
// ==========================================================================
diff --git a/src/library_browser.js b/src/library_browser.js
index 13275702..0bc6d130 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -389,10 +389,10 @@ mergeInto(LibraryManager.library, {
Browser.asyncLoad(Pointer_stringify(url), function(byteArray) {
var buffer = _malloc(byteArray.length);
HEAPU8.set(byteArray, buffer);
- FUNCTION_TABLE[onload](arg, buffer, byteArray.length);
+ Runtime.dynCall('viii', onload, [arg, buffer, byteArray.length]);
_free(buffer);
}, function() {
- if (onerror) FUNCTION_TABLE[onerror](arg);
+ if (onerror) Runtime.dynCall('vi', onerror, [arg]);
}, true /* no need for run dependency, this is async but will not do any prepare etc. step */ );
},
@@ -411,21 +411,21 @@ mergeInto(LibraryManager.library, {
http.onload = function(e) {
if (http.status == 200) {
FS.createDataFile( _file.substr(0, index), _file.substr(index + 1), new Uint8Array(http.response), true, true);
- if (onload) FUNCTION_TABLE[onload](arg, file);
+ if (onload) Runtime.dynCall('vii', onload, [arg, file]);
} else {
- if (onerror) FUNCTION_TABLE[onerror](arg, http.status);
+ if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
}
};
// ERROR
http.onerror = function(e) {
- if (onerror) FUNCTION_TABLE[onerror](arg, http.status);
+ if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
};
// PROGRESS
http.onprogress = function(e) {
var percentComplete = (e.position / e.totalSize)*100;
- if (onprogress) FUNCTION_TABLE[onprogress](arg, percentComplete);
+ if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
};
// Useful because the browser can limit the number of redirection
diff --git a/src/library_gl.js b/src/library_gl.js
index 2a6ec92f..c153a181 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -364,6 +364,7 @@ var LibraryGL = {
}
},
+ glCompressedTexImage2D__sig: 'viiiiiiii',
glCompressedTexImage2D: function(target, level, internalFormat, width, height, border, imageSize, data) {
assert(GL.compressionExt);
if (data) {
@@ -374,6 +375,7 @@ var LibraryGL = {
Module.ctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, data);
},
+ glCompressedTexSubImage2D__sig: 'viiiiiiiii',
glCompressedTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, imageSize, data) {
assert(GL.compressionExt);
if (data) {
@@ -429,6 +431,7 @@ var LibraryGL = {
return Module.ctx.isTexture(fb);
},
+ glGenBuffers__sig: 'vii',
glGenBuffers: function(n, buffers) {
for (var i = 0; i < n; i++) {
var id = GL.getNewId(GL.buffers);
@@ -437,6 +440,7 @@ var LibraryGL = {
}
},
+ glDeleteBuffers__sig: 'vii',
glDeleteBuffers: function(n, buffers) {
for (var i = 0; i < n; i++) {
var id = {{{ makeGetValue('buffers', 'i*4', 'i32') }}};
@@ -449,10 +453,12 @@ var LibraryGL = {
{{{ makeSetValue('data', '0', 'Module.ctx.getBufferParameter(target, value)', 'i32') }}};
},
+ glBufferData__sig: 'viiii',
glBufferData: function(target, size, data, usage) {
Module.ctx.bufferData(target, HEAPU8.subarray(data, data+size), usage);
},
+ glBufferSubData__sig: 'viiii',
glBufferSubData: function(target, offset, size, data) {
Module.ctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size));
},
@@ -465,6 +471,7 @@ var LibraryGL = {
return Module.ctx.isBuffer(fb);
},
+ glGenRenderbuffers__sig: 'vii',
glGenRenderbuffers: function(n, renderbuffers) {
for (var i = 0; i < n; i++) {
var id = GL.getNewId(GL.renderbuffers);
@@ -473,6 +480,7 @@ var LibraryGL = {
}
},
+ glDeleteRenderbuffers__sig: 'vii',
glDeleteRenderbuffers: function(n, renderbuffers) {
for (var i = 0; i < n; i++) {
var id = {{{ makeGetValue('renderbuffers', 'i*4', 'i32') }}};
@@ -481,6 +489,7 @@ var LibraryGL = {
}
},
+ glBindRenderbuffer__sig: 'vii',
glBindRenderbuffer: function(target, renderbuffer) {
Module.ctx.bindRenderbuffer(target, renderbuffer ? GL.renderbuffers[renderbuffer] : null);
},
@@ -519,6 +528,7 @@ var LibraryGL = {
}
},
+ glGetUniformLocation__sig: 'iii',
glGetUniformLocation: function(program, name) {
name = Pointer_stringify(name);
var ptable = GL.uniformTable[program];
@@ -559,6 +569,7 @@ var LibraryGL = {
{{{ makeSetValue('pointer', '0', 'Module.ctx.getVertexAttribOffset(index, pname)', 'i32') }}};
},
+ glGetActiveUniform__sig: 'viiiiiii',
glGetActiveUniform: function(program, index, bufSize, length, size, type, name) {
program = GL.programs[program];
var info = Module.ctx.getActiveUniform(program, index);
@@ -577,52 +588,62 @@ var LibraryGL = {
}
},
+ glUniform1f__sig: 'vid',
glUniform1f: function(location, v0) {
location = GL.uniforms[location];
Module.ctx.uniform1f(location, v0);
},
+ glUniform2f__sig: 'vidd',
glUniform2f: function(location, v0, v1) {
location = GL.uniforms[location];
Module.ctx.uniform2f(location, v0, v1);
},
+ glUniform3f__sig: 'viddd',
glUniform3f: function(location, v0, v1, v2) {
location = GL.uniforms[location];
Module.ctx.uniform3f(location, v0, v1, v2);
},
+ glUniform4f__sig: 'vidddd',
glUniform4f: function(location, v0, v1, v2, v3) {
location = GL.uniforms[location];
Module.ctx.uniform4f(location, v0, v1, v2, v3);
},
+ glUniform1i__sig: 'vii',
glUniform1i: function(location, v0) {
location = GL.uniforms[location];
Module.ctx.uniform1i(location, v0);
},
+ glUniform2i__sig: 'viii',
glUniform2i: function(location, v0, v1) {
location = GL.uniforms[location];
Module.ctx.uniform2i(location, v0, v1);
},
+ glUniform3i__sig: 'viiii',
glUniform3i: function(location, v0, v1, v2) {
location = GL.uniforms[location];
Module.ctx.uniform3i(location, v0, v1, v2);
},
+ glUniform4i__sig: 'viiiii',
glUniform4i: function(location, v0, v1, v2, v3) {
location = GL.uniforms[location];
Module.ctx.uniform4i(location, v0, v1, v2, v3);
},
+ glUniform1iv__sig: 'viii',
glUniform1iv: function(location, count, value) {
location = GL.uniforms[location];
value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}};
Module.ctx.uniform1iv(location, value);
},
+ glUniform2iv__sig: 'viii',
glUniform2iv: function(location, count, value) {
location = GL.uniforms[location];
count *= 2;
@@ -630,6 +651,7 @@ var LibraryGL = {
Module.ctx.uniform2iv(location, value);
},
+ glUniform3iv__sig: 'viii',
glUniform3iv: function(location, count, value) {
location = GL.uniforms[location];
count *= 3;
@@ -637,6 +659,7 @@ var LibraryGL = {
Module.ctx.uniform3iv(location, value);
},
+ glUniform4iv__sig: 'viii',
glUniform4iv: function(location, count, value) {
location = GL.uniforms[location];
count *= 4;
@@ -644,12 +667,14 @@ var LibraryGL = {
Module.ctx.uniform4iv(location, value);
},
+ glUniform1fv__sig: 'viii',
glUniform1fv: function(location, count, value) {
location = GL.uniforms[location];
value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}};
Module.ctx.uniform1fv(location, value);
},
+ glUniform2fv__sig: 'viii',
glUniform2fv: function(location, count, value) {
location = GL.uniforms[location];
count *= 2;
@@ -657,6 +682,7 @@ var LibraryGL = {
Module.ctx.uniform2fv(location, value);
},
+ glUniform3fv__sig: 'viii',
glUniform3fv: function(location, count, value) {
location = GL.uniforms[location];
count *= 3;
@@ -664,6 +690,7 @@ var LibraryGL = {
Module.ctx.uniform3fv(location, value);
},
+ glUniform4fv__sig: 'viii',
glUniform4fv: function(location, count, value) {
location = GL.uniforms[location];
count *= 4;
@@ -692,6 +719,7 @@ var LibraryGL = {
Module.ctx.uniformMatrix4fv(location, transpose, value);
},
+ glBindBuffer__sig: 'vii',
glBindBuffer: function(target, buffer) {
Module.ctx.bindBuffer(target, buffer ? GL.buffers[buffer] : null);
},
@@ -740,6 +768,7 @@ var LibraryGL = {
}
},
+ glCreateShader__sig: 'ii',
glCreateShader: function(shaderType) {
var id = GL.getNewId(GL.shaders);
GL.shaders[id] = Module.ctx.createShader(shaderType);
@@ -768,6 +797,7 @@ var LibraryGL = {
}
},
+ glShaderSource__sig: 'viiii',
glShaderSource: function(shader, count, string, length) {
var source = GL.getSource(shader, count, string, length);
Module.ctx.shaderSource(GL.shaders[shader], source);
@@ -782,6 +812,7 @@ var LibraryGL = {
}
},
+ glCompileShader__sig: 'vi',
glCompileShader: function(shader) {
Module.ctx.compileShader(GL.shaders[shader]);
},
@@ -807,6 +838,7 @@ var LibraryGL = {
}
},
+ glGetProgramiv__sig: 'viii',
glGetProgramiv : function(program, pname, p) {
if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH
{{{ makeSetValue('p', '0', 'Module.ctx.getProgramInfoLog(GL.programs[program]).length + 1', 'i32') }}};
@@ -823,6 +855,7 @@ var LibraryGL = {
return Module.ctx.isShader(fb);
},
+ glCreateProgram__sig: 'i',
glCreateProgram: function() {
var id = GL.getNewId(GL.programs);
GL.programs[id] = Module.ctx.createProgram();
@@ -835,6 +868,7 @@ var LibraryGL = {
GL.uniformTable[program] = null;
},
+ glAttachShader__sig: 'vii',
glAttachShader: function(program, shader) {
Module.ctx.attachShader(GL.programs[program],
GL.shaders[shader]);
@@ -847,6 +881,7 @@ var LibraryGL = {
{{{ makeSetValue('precision', '0', 'result.precision', 'i32') }}};
},
+ glLinkProgram__sig: 'vi',
glLinkProgram: function(program) {
Module.ctx.linkProgram(GL.programs[program]);
GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking
@@ -865,6 +900,7 @@ var LibraryGL = {
}
},
+ glUseProgram__sig: 'vi',
glUseProgram: function(program) {
Module.ctx.useProgram(program ? GL.programs[program] : null);
},
@@ -881,15 +917,18 @@ var LibraryGL = {
return Module.ctx.isProgram(fb);
},
+ glBindAttribLocation__sig: 'viii',
glBindAttribLocation: function(program, index, name) {
name = Pointer_stringify(name);
Module.ctx.bindAttribLocation(GL.programs[program], index, name);
},
+ glBindFramebuffer__sig: 'vii',
glBindFramebuffer: function(target, framebuffer) {
Module.ctx.bindFramebuffer(target, framebuffer ? GL.framebuffers[framebuffer] : null);
},
+ glGenFramebuffers__sig: 'vii',
glGenFramebuffers: function(n, ids) {
for (var i = 0; i < n; ++i) {
var id = GL.getNewId(GL.framebuffers);
@@ -898,6 +937,7 @@ var LibraryGL = {
}
},
+ glDeleteFramebuffers__sig: 'vii',
glDeleteFramebuffers: function(n, framebuffers) {
for (var i = 0; i < n; ++i) {
var id = {{{ makeGetValue('framebuffers', 'i*4', 'i32') }}};
@@ -906,21 +946,25 @@ var LibraryGL = {
}
},
+ glFramebufferRenderbuffer__sig: 'viiii',
glFramebufferRenderbuffer: function(target, attachment, renderbuffertarget, renderbuffer) {
Module.ctx.framebufferRenderbuffer(target, attachment, renderbuffertarget,
GL.renderbuffers[renderbuffer]);
},
+ glFramebufferTexture2D__sig: 'viiiii',
glFramebufferTexture2D: function(target, attachment, textarget, texture, level) {
Module.ctx.framebufferTexture2D(target, attachment, textarget,
GL.textures[texture], level);
},
+ glGetFramebufferAttachmentParameteriv__sig: 'viiii',
glGetFramebufferAttachmentParameteriv: function(target, attachment, pname, params) {
var result = Module.ctx.getFramebufferAttachmentParameter(target, attachment, pname);
{{{ makeSetValue('params', '0', 'params', 'i32') }}};
},
+ glIsFramebuffer__sig: 'ii',
glIsFramebuffer: function(framebuffer) {
var fb = GL.framebuffers[framebuffer];
if (typeof(fb) == 'undefined') {
@@ -1275,114 +1319,119 @@ var LibraryGL = {
getProcAddress: function(name) {
name = name.replace('EXT', '').replace('ARB', '');
// Do the translation carefully because of closure
- var sig = '', func;
+ var ret = 0;
switch (name) {
- case 'glCreateShaderObject': case 'glCreateShader': func = _glCreateShader; sig = 'ii'; break;
- case 'glCreateProgramObject': case 'glCreateProgram': func = _glCreateProgram; sig = 'ii'; break;
- case 'glAttachObject': case 'glAttachShader': func = _glAttachShader; sig = 'vi'; break;
- case 'glUseProgramObject': case 'glUseProgram': func = _glUseProgram; sig = 'vi'; break;
- case 'glDeleteObject': func = function(id) {
- if (GL.programs[id]) {
- _glDeleteProgram(id);
- } else if (GL.shaders[id]) {
- _glDeleteShader(id);
- } else {
- Module.printErr('WARNING: deleteObject received invalid id: ' + id);
- }
- }; sig = 'vi'; break;
- case 'glGetObjectParameteriv': func = function(id, type, result) {
- if (GL.programs[id]) {
- if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
- {{{ makeSetValue('result', '0', 'Module.ctx.getProgramInfoLog(GL.programs[id]).length', 'i32') }}};
- return;
- }
- _glGetProgramiv(id, type, result);
- } else if (GL.shaders[id]) {
- if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
- {{{ makeSetValue('result', '0', 'Module.ctx.getShaderInfoLog(GL.shaders[id]).length', 'i32') }}};
- return;
- }
- _glGetShaderiv(id, type, result);
- } else {
- Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id);
- }
- }; sig = 'viii'; break;
- case 'glGetInfoLog': func = function(id, maxLength, length, infoLog) {
- if (GL.programs[id]) {
- _glGetProgramInfoLog(id, maxLength, length, infoLog);
- } else if (GL.shaders[id]) {
- _glGetShaderInfoLog(id, maxLength, length, infoLog);
- } else {
- Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id);
- }
- }; sig = 'viiii'; break;
- case 'glBindProgram': func = function(type, id) {
- assert(id == 0);
- }; sig = 'vii'; break;
- case 'glDrawRangeElements': func = _glDrawRangeElements; sig = 'viiiiii'; break;
- case 'glShaderSource': func = _glShaderSource; sig = 'viiii'; break;
- case 'glCompileShader': func = _glCompileShader; sig = 'vi'; break;
- case 'glLinkProgram': func = _glLinkProgram; sig = 'vi'; break;
- case 'glGetUniformLocation': func = _glGetUniformLocation; sig = 'iii'; break;
- case 'glUniform1f': func = _glUniform1f; sig = 'vid'; break;
- case 'glUniform2f': func = _glUniform2f; sig = 'vidd'; break;
- case 'glUniform3f': func = _glUniform3f; sig = 'viddd'; break;
- case 'glUniform4f': func = _glUniform4f; sig = 'vidddd'; break;
- case 'glUniform1fv': func = _glUniform1fv; sig = 'viii'; break;
- case 'glUniform2fv': func = _glUniform2fv; sig = 'viii'; break;
- case 'glUniform3fv': func = _glUniform3fv; sig = 'viii'; break;
- case 'glUniform4fv': func = _glUniform4fv; sig = 'viii'; break;
- case 'glUniform1i': func = _glUniform1i; sig = 'vii'; break;
- case 'glUniform2i': func = _glUniform2i; sig = 'viii'; break;
- case 'glUniform3i': func = _glUniform3i; sig = 'viiii'; break;
- case 'glUniform4i': func = _glUniform4i; sig = 'viiii'; break;
- case 'glUniform1iv': func = _glUniform1iv; sig = 'viii'; break;
- case 'glUniform2iv': func = _glUniform2iv; sig = 'viii'; break;
- case 'glUniform3iv': func = _glUniform3iv; sig = 'viii'; break;
- case 'glUniform4iv': func = _glUniform4iv; sig = 'viii'; break;
- case 'glBindAttribLocation': func = _glBindAttribLocation; sig = 'viii'; break;
- case 'glGetActiveUniform': func = _glGetActiveUniform; sig = 'viiiiiii'; break;
- case 'glGenBuffers': func = _glGenBuffers; sig = 'iii'; break;
- case 'glBindBuffer': func = _glBindBuffer; sig = 'vii'; break;
- case 'glBufferData': func = _glBufferData; sig = 'viiii'; break;
- case 'glBufferSubData': func = _glBufferSubData; sig = 'viiii'; break;
- case 'glDeleteBuffers': func = _glDeleteBuffers; sig = 'vii'; break;
- case 'glActiveTexture': func = _glActiveTexture; sig = 'vi'; break;
- case 'glClientActiveTexture': func = _glClientActiveTexture; sig = 'vi'; break;
- case 'glGetProgramiv': func = _glGetProgramiv; sig = 'viii'; break;
- case 'glEnableVertexAttribArray': func = _glEnableVertexAttribArray; sig = 'vi'; break;
- case 'glDisableVertexAttribArray': func = _glDisableVertexAttribArray; sig = 'vi'; break;
- case 'glVertexAttribPointer': func = _glVertexAttribPointer; sig = 'viiiiii'; break;
- case 'glBindRenderbuffer': func = _glBindRenderbuffer; sig = 'vii'; break;
- case 'glDeleteRenderbuffers': func = _glDeleteRenderbuffers; sig = 'vii'; break;
- case 'glGenRenderbuffers': func = _glGenRenderbuffers; sig = 'vii'; break;
- case 'glCompressedTexImage2D': func = _glCompressedTexImage2D; sig = 'viiiiiiii'; break;
- case 'glCompressedTexSubImage2D': func = _glCompressedTexSubImage2D; sig = 'viiiiiiiii'; break;
- case 'glBindFramebuffer': func = _glBindFramebuffer; sig = 'vii'; break;
- case 'glGenFramebuffers': func = _glGenFramebuffers; sig = 'vii'; break;
- case 'glDeleteFramebuffers': func = _glDeleteFramebuffers; sig = 'vii'; break;
- case 'glFramebufferRenderbuffer': func = _glFramebufferRenderbuffer; sig = 'viiii'; break;
- case 'glFramebufferTexture2D': func = _glFramebufferTexture2D; sig = 'viiiii'; break;
- case 'glGetFramebufferAttachmentParameteriv': func = _glGetFramebufferAttachmentParameteriv; sig = 'viiii'; break;
- case 'glIsFramebuffer': func = _glIsFramebuffer; sig = 'ii'; break;
- case 'glCheckFramebufferStatus': func = _glCheckFramebufferStatus; sig = 'ii'; break;
- case 'glRenderbufferStorage': func = _glRenderbufferStorage; sig = 'viiii'; break;
- default: {
- Module.printErr('WARNING: getProcAddress failed for ' + name);
- func = function() {
- Module.printErr('WARNING: empty replacement for ' + name + ' called, no-op');
- return 0;
- };
- sig = 'v';
- }
+ case 'glCreateShaderObject': case 'glCreateShader': ret = {{{ Functions.getIndex('_glCreateShader', true) }}}; break;
+ case 'glCreateProgramObject': case 'glCreateProgram': ret = {{{ Functions.getIndex('_glCreateProgram', true) }}}; break;
+ case 'glAttachObject': case 'glAttachShader': ret = {{{ Functions.getIndex('_glAttachShader', true) }}}; break;
+ case 'glUseProgramObject': case 'glUseProgram': ret = {{{ Functions.getIndex('_glUseProgram', true) }}}; break;
+ case 'glDeleteObject': ret = {{{ Functions.getIndex('_glDeleteObject', true) }}}; break;
+ case 'glGetObjectParameteriv': ret = {{{ Functions.getIndex('_glGetObjectParameteriv', true) }}}; break;
+ case 'glGetInfoLog': ret = {{{ Functions.getIndex('_glGetInfoLog', true) }}}; break;
+ case 'glBindProgram': ret = {{{ Functions.getIndex('_glBindProgram', true) }}}; break;
+ case 'glDrawRangeElements': ret = {{{ Functions.getIndex('_glDrawRangeElements', true) }}}; break;
+ case 'glShaderSource': ret = {{{ Functions.getIndex('_glShaderSource', true) }}}; break;
+ case 'glCompileShader': ret = {{{ Functions.getIndex('_glCompileShader', true) }}}; break;
+ case 'glLinkProgram': ret = {{{ Functions.getIndex('_glLinkProgram', true) }}}; break;
+ case 'glGetUniformLocation': ret = {{{ Functions.getIndex('_glGetUniformLocation', true) }}}; break;
+ case 'glUniform1f': ret = {{{ Functions.getIndex('_glUniform1f', true) }}}; break;
+ case 'glUniform2f': ret = {{{ Functions.getIndex('_glUniform2f', true) }}}; break;
+ case 'glUniform3f': ret = {{{ Functions.getIndex('_glUniform3f', true) }}}; break;
+ case 'glUniform4f': ret = {{{ Functions.getIndex('_glUniform4f', true) }}}; break;
+ case 'glUniform1fv': ret = {{{ Functions.getIndex('_glUniform1fv', true) }}}; break;
+ case 'glUniform2fv': ret = {{{ Functions.getIndex('_glUniform2fv', true) }}}; break;
+ case 'glUniform3fv': ret = {{{ Functions.getIndex('_glUniform3fv', true) }}}; break;
+ case 'glUniform4fv': ret = {{{ Functions.getIndex('_glUniform4fv', true) }}}; break;
+ case 'glUniform1i': ret = {{{ Functions.getIndex('_glUniform1i', true) }}}; break;
+ case 'glUniform2i': ret = {{{ Functions.getIndex('_glUniform2i', true) }}}; break;
+ case 'glUniform3i': ret = {{{ Functions.getIndex('_glUniform3i', true) }}}; break;
+ case 'glUniform4i': ret = {{{ Functions.getIndex('_glUniform4i', true) }}}; break;
+ case 'glUniform1iv': ret = {{{ Functions.getIndex('_glUniform1iv', true) }}}; break;
+ case 'glUniform2iv': ret = {{{ Functions.getIndex('_glUniform2iv', true) }}}; break;
+ case 'glUniform3iv': ret = {{{ Functions.getIndex('_glUniform3iv', true) }}}; break;
+ case 'glUniform4iv': ret = {{{ Functions.getIndex('_glUniform4iv', true) }}}; break;
+ case 'glBindAttribLocation': ret = {{{ Functions.getIndex('_glBindAttribLocation', true) }}}; break;
+ case 'glGetActiveUniform': ret = {{{ Functions.getIndex('_glGetActiveUniform', true) }}}; break;
+ case 'glGenBuffers': ret = {{{ Functions.getIndex('_glGenBuffers', true) }}}; break;
+ case 'glBindBuffer': ret = {{{ Functions.getIndex('_glBindBuffer', true) }}}; break;
+ case 'glBufferData': ret = {{{ Functions.getIndex('_glBufferData', true) }}}; break;
+ case 'glBufferSubData': ret = {{{ Functions.getIndex('_glBufferSubData', true) }}}; break;
+ case 'glDeleteBuffers': ret = {{{ Functions.getIndex('_glDeleteBuffers', true) }}}; break;
+ case 'glActiveTexture': ret = {{{ Functions.getIndex('_glActiveTexture', true) }}}; break;
+ case 'glClientActiveTexture': ret = {{{ Functions.getIndex('_glClientActiveTexture', true) }}}; break;
+ case 'glGetProgramiv': ret = {{{ Functions.getIndex('_glGetProgramiv', true) }}}; break;
+ case 'glEnableVertexAttribArray': ret = {{{ Functions.getIndex('_glEnableVertexAttribArray', true) }}}; break;
+ case 'glDisableVertexAttribArray': ret = {{{ Functions.getIndex('_glDisableVertexAttribArray', true) }}}; break;
+ case 'glVertexAttribPointer': ret = {{{ Functions.getIndex('_glVertexAttribPointer', true) }}}; break;
+ case 'glBindRenderbuffer': ret = {{{ Functions.getIndex('_glBindRenderbuffer', true) }}}; break;
+ case 'glDeleteRenderbuffers': ret = {{{ Functions.getIndex('_glDeleteRenderbuffers', true) }}}; break;
+ case 'glGenRenderbuffers': ret = {{{ Functions.getIndex('_glGenRenderbuffers', true) }}}; break;
+ case 'glCompressedTexImage2D': ret = {{{ Functions.getIndex('_glCompressedTexImage2D', true) }}}; break;
+ case 'glCompressedTexSubImage2D': ret = {{{ Functions.getIndex('_glCompressedTexSubImage2D', true) }}}; break;
+ case 'glBindFramebuffer': ret = {{{ Functions.getIndex('_glBindFramebuffer', true) }}}; break;
+ case 'glGenFramebuffers': ret = {{{ Functions.getIndex('_glGenFramebuffers', true) }}}; break;
+ case 'glDeleteFramebuffers': ret = {{{ Functions.getIndex('_glDeleteFramebuffers', true) }}}; break;
+ case 'glFramebufferRenderbuffer': ret = {{{ Functions.getIndex('_glFramebufferRenderbuffer', true) }}}; break;
+ case 'glFramebufferTexture2D': ret = {{{ Functions.getIndex('_glFramebufferTexture2D', true) }}}; break;
+ case 'glGetFramebufferAttachmentParameteriv': ret = {{{ Functions.getIndex('_glGetFramebufferAttachmentParameteriv', true) }}}; break;
+ case 'glIsFramebuffer': ret = {{{ Functions.getIndex('_glIsFramebuffer', true) }}}; break;
+ case 'glCheckFramebufferStatus': ret = {{{ Functions.getIndex('_glCheckFramebufferStatus', true) }}}; break;
+ case 'glRenderbufferStorage': ret = {{{ Functions.getIndex('_glRenderbufferStorage', true) }}}; break;
+ }
+ if (!ret) Module.printErr('WARNING: getProcAddress failed for ' + name);
+ return ret;
+ }
+ },
+
+ glDeleteObject__sig: 'vi',
+ glDeleteObject: function(id) {
+ if (GL.programs[id]) {
+ _glDeleteProgram(id);
+ } else if (GL.shaders[id]) {
+ _glDeleteShader(id);
+ } else {
+ Module.printErr('WARNING: deleteObject received invalid id: ' + id);
+ }
+ },
+
+ glGetObjectParameteriv__sig: 'viii',
+ glGetObjectParameteriv: function(id, type, result) {
+ if (GL.programs[id]) {
+ if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
+ {{{ makeSetValue('result', '0', 'Module.ctx.getProgramInfoLog(GL.programs[id]).length', 'i32') }}};
+ return;
+ }
+ _glGetProgramiv(id, type, result);
+ } else if (GL.shaders[id]) {
+ if (type == 0x8B84) { // GL_OBJECT_INFO_LOG_LENGTH_ARB
+ {{{ makeSetValue('result', '0', 'Module.ctx.getShaderInfoLog(GL.shaders[id]).length', 'i32') }}};
+ return;
}
- return Runtime.addFunction(func, sig);
+ _glGetShaderiv(id, type, result);
+ } else {
+ Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id);
}
},
+ glGetInfoLog__sig: 'viiii',
+ glGetInfoLog: function(id, maxLength, length, infoLog) {
+ if (GL.programs[id]) {
+ _glGetProgramInfoLog(id, maxLength, length, infoLog);
+ } else if (GL.shaders[id]) {
+ _glGetShaderInfoLog(id, maxLength, length, infoLog);
+ } else {
+ Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id);
+ }
+ },
+
+ glBindProgram__sig: 'vii',
+ glBindProgram: function(type, id) {
+ assert(id == 0);
+ },
+
// GL Immediate mode
- $GLImmediate__postset: 'Browser.moduleContextCreatedCallbacks.push(function() { GL.immediate.init() });',
+ $GLImmediate__postset: 'GL.immediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GL.immediate.init() });',
$GLImmediate__deps: ['$Browser', '$GL'],
$GLImmediate: {
MAX_TEXTURES: 7,
@@ -1393,7 +1442,7 @@ var LibraryGL = {
tempData: null,
indexData: null,
vertexCounter: 0,
- mode: 0,
+ mode: -1,
rendererCache: null,
rendererCacheItemTemplate: [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null], // 16 nulls
@@ -1843,6 +1892,46 @@ var LibraryGL = {
return ret;
},
+ setupFuncs: function() {
+ // Replace some functions with immediate-mode aware versions. If there are no client
+ // attributes enabled, and we use webgl-friendly modes (no GL_QUADS), then no need
+ // for emulation
+ _glDrawArrays = function(mode, first, count) {
+ if (GL.immediate.totalEnabledClientAttributes == 0 && mode <= 6) {
+ Module.ctx.drawArrays(mode, first, count);
+ return;
+ }
+ GL.immediate.prepareClientAttributes(count, false);
+ GL.immediate.mode = mode;
+ if (!GL.currArrayBuffer) {
+ GL.immediate.vertexData = {{{ makeHEAPView('F32', 'GL.immediate.vertexPointer', 'GL.immediate.vertexPointer + (first+count)*GL.immediate.stride') }}}; // XXX assuming float
+ GL.immediate.firstVertex = first;
+ GL.immediate.lastVertex = first + count;
+ }
+ GL.immediate.flush(null, first);
+ GL.immediate.mode = -1;
+ };
+
+ _glDrawElements = function(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements
+ if (GL.immediate.totalEnabledClientAttributes == 0 && mode <= 6 && GL.currElementArrayBuffer) {
+ Module.ctx.drawElements(mode, count, type, indices);
+ return;
+ }
+ if (!GL.currElementArrayBuffer) {
+ assert(type == Module.ctx.UNSIGNED_SHORT); // We can only emulate buffers of this kind, for now
+ }
+ GL.immediate.prepareClientAttributes(count, false);
+ GL.immediate.mode = mode;
+ if (!GL.currArrayBuffer) {
+ GL.immediate.firstVertex = end ? start : TOTAL_MEMORY; // if we don't know the start, set an invalid value and we will calculate it later from the indices
+ GL.immediate.lastVertex = end ? end+1 : 0;
+ GL.immediate.vertexData = {{{ makeHEAPView('F32', 'GL.immediate.vertexPointer', '(end ? GL.immediate.vertexPointer + (end+1)*GL.immediate.stride : TOTAL_MEMORY)') }}}; // XXX assuming float
+ }
+ GL.immediate.flush(count, 0, indices);
+ GL.immediate.mode = -1;
+ };
+ },
+
// Main functions
initted: false,
init: function() {
@@ -1879,44 +1968,6 @@ var LibraryGL = {
this.generateTempBuffers();
this.clientColor = new Float32Array([1, 1, 1, 1]);
-
- // Replace some functions with immediate-mode aware versions. If there are no client
- // attributes enabled, and we use webgl-friendly modes (no GL_QUADS), then no need
- // for emulation
- _glDrawArrays = function(mode, first, count) {
- if (GL.immediate.totalEnabledClientAttributes == 0 && mode <= 6) {
- Module.ctx.drawArrays(mode, first, count);
- return;
- }
- GL.immediate.prepareClientAttributes(count, false);
- GL.immediate.mode = mode;
- if (!GL.currArrayBuffer) {
- GL.immediate.vertexData = {{{ makeHEAPView('F32', 'GL.immediate.vertexPointer', 'GL.immediate.vertexPointer + (first+count)*GL.immediate.stride') }}}; // XXX assuming float
- GL.immediate.firstVertex = first;
- GL.immediate.lastVertex = first + count;
- }
- GL.immediate.flush(null, first);
- GL.immediate.mode = 0;
- };
-
- _glDrawElements = function(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements
- if (GL.immediate.totalEnabledClientAttributes == 0 && mode <= 6 && GL.currElementArrayBuffer) {
- Module.ctx.drawElements(mode, count, type, indices);
- return;
- }
- if (!GL.currElementArrayBuffer) {
- assert(type == Module.ctx.UNSIGNED_SHORT); // We can only emulate buffers of this kind, for now
- }
- GL.immediate.prepareClientAttributes(count, false);
- GL.immediate.mode = mode;
- if (!GL.currArrayBuffer) {
- GL.immediate.firstVertex = end ? start : TOTAL_MEMORY; // if we don't know the start, set an invalid value and we will calculate it later from the indices
- GL.immediate.lastVertex = end ? end+1 : 0;
- GL.immediate.vertexData = {{{ makeHEAPView('F32', 'GL.immediate.vertexPointer', '(end ? GL.immediate.vertexPointer + (end+1)*GL.immediate.stride : TOTAL_MEMORY)') }}}; // XXX assuming float
- }
- GL.immediate.flush(count, 0, indices);
- GL.immediate.mode = 0;
- };
},
// Prepares and analyzes client attributes.
@@ -2060,12 +2111,12 @@ var LibraryGL = {
GL.immediate.lastVertex = GL.immediate.vertexCounter / (GL.immediate.stride >> 2);
GL.immediate.flush();
GL.immediate.disableBeginEndClientAttributes();
- GL.immediate.mode = 0;
+ GL.immediate.mode = -1;
},
glVertex3f: function(x, y, z) {
#if ASSERTIONS
- assert(GL.immediate.mode); // must be in begin/end
+ assert(GL.immediate.mode >= 0); // must be in begin/end
#endif
GL.immediate.vertexData[GL.immediate.vertexCounter++] = x;
GL.immediate.vertexData[GL.immediate.vertexCounter++] = y;
@@ -2086,9 +2137,11 @@ var LibraryGL = {
_glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, 0);
},
+ glVertex2i: function() { throw 'glVertex2i: TODO' },
+
glTexCoord2i: function(u, v) {
#if ASSERTIONS
- assert(GL.immediate.mode); // must be in begin/end
+ assert(GL.immediate.mode >= 0); // must be in begin/end
#endif
GL.immediate.vertexData[GL.immediate.vertexCounter++] = u;
GL.immediate.vertexData[GL.immediate.vertexCounter++] = v;
@@ -2101,6 +2154,8 @@ var LibraryGL = {
_glTexCoord2i({{{ makeGetValue('v', '0', 'float') }}}, {{{ makeGetValue('v', '4', 'float') }}});
},
+ glTexCoord4f: function() { throw 'glTexCoord4f: TODO' },
+
glColor4f: function(r, g, b, a) {
r = Math.max(Math.min(r, 1), 0);
g = Math.max(Math.min(g, 1), 0);
@@ -2108,7 +2163,7 @@ var LibraryGL = {
a = Math.max(Math.min(a, 1), 0);
// TODO: make ub the default, not f, save a few mathops
- if (GL.immediate.mode) {
+ if (GL.immediate.mode >= 0) {
var start = GL.immediate.vertexCounter << 2;
GL.immediate.vertexDataU8[start + 0] = r * 255;
GL.immediate.vertexDataU8[start + 1] = g * 255;
@@ -2175,6 +2230,8 @@ var LibraryGL = {
_glColor4f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}}, {{{ makeGetValue('p', '12', 'float') }}});
},
+ glColor4ubv: function() { throw 'glColor4ubv not implemented' },
+
glFogf: function(pname, param) { // partial support, TODO
switch(pname) {
case 0x0B63: // GL_FOG_START
@@ -2236,6 +2293,7 @@ var LibraryGL = {
// Additional non-GLES rendering calls
+ glDrawRangeElements__sig: 'viiiiii',
glDrawRangeElements: function(mode, start, end, count, type, indices) {
_glDrawElements(mode, count, type, indices, start, end);
},
@@ -2283,6 +2341,7 @@ var LibraryGL = {
GL.immediate.setClientAttribute(GL.immediate.COLOR, size, type, stride, pointer);
},
+ glClientActiveTexture__sig: 'vi',
glClientActiveTexture: function(texture) {
GL.immediate.clientActiveTexture = texture - 0x84C0; // GL_TEXTURE0
},
@@ -2466,7 +2525,31 @@ var LibraryGL = {
gluOrtho2D: function(left, right, bottom, top) {
_glOrtho(left, right, bottom, top, -1, 1);
- }
+ },
+
+ 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() { throw 'glTexEnvi: TODO' },
+ glTexEnvfv: function() { throw 'glTexEnvfv: TODO' },
+
+ glTexImage1D: function() { throw 'glTexImage1D: TODO' },
+ glTexCoord3f: function() { throw 'glTexCoord3f: TODO' },
+ glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' },
+
+ // signatures of simple pass-through functions, see later
+ glActiveTexture__sig: 'vi',
+ glEnableVertexAttribArray__sig: 'vi',
+ glDisableVertexAttribArray__sig: 'vi',
+ glVertexAttribPointer__sig: 'viiiiii',
+ glCheckFramebufferStatus__sig: 'ii',
+ glRenderbufferStorage__sig: 'viiii',
};
// Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name
@@ -2503,12 +2586,17 @@ var LibraryGL = {
autoAddDeps(LibraryGL, '$GL');
// Emulation requires everything else, potentially
-LibraryGL.$GLEmulation__deps = LibraryGL.$GLEmulation__deps.slice(0);
+LibraryGL.$GLEmulation__deps = LibraryGL.$GLEmulation__deps.slice(0); // the __deps object is shared
+var glFuncs = [];
for (var item in LibraryGL) {
- if (item != '$GLEmulation' && item.substr(-6) != '__deps' && item.substr(-9) != '__postset' && item.substr(0, 2) == 'gl') {
- LibraryGL.$GLEmulation__deps.push(item);
+ if (item != '$GLEmulation' && item.substr(-6) != '__deps' && item.substr(-9) != '__postset' && item.substr(-5) != '__sig' && item.substr(0, 2) == 'gl') {
+ glFuncs.push(item);
}
}
+LibraryGL.$GLEmulation__deps = LibraryGL.$GLEmulation__deps.concat(glFuncs);
+LibraryGL.$GLEmulation__deps.push(function() {
+ for (var func in Functions.getIndex.tentative) Functions.getIndex(func);
+});
mergeInto(LibraryManager.library, LibraryGL);
diff --git a/src/library_sdl.js b/src/library_sdl.js
index b2ca338b..712ec290 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1371,6 +1371,7 @@ var LibrarySDL = {
},
Mix_LoadMUS: 'Mix_LoadWAV_RW',
+ Mix_LoadMUS_RW: 'Mix_LoadWAV_RW',
Mix_FreeMusic: 'Mix_FreeChunk',
@@ -1575,7 +1576,25 @@ var LibrarySDL = {
SDL_CreateThread: function() {
throw 'SDL threads cannot be supported in the web platform because they assume shared state. See emscripten_create_worker etc. for a message-passing concurrency model that does let you run code in another thread.'
- }
+ },
+
+ SDL_WaitThread: function() { throw 'SDL_WaitThread' },
+ SDL_GetThreadID: function() { throw 'SDL_GetThreadID' },
+ SDL_ThreadID: function() { throw 'SDL_ThreadID' },
+ SDL_AllocRW: function() { throw 'SDL_AllocRW: TODO' },
+ SDL_FreeRW: function() { throw 'SDL_FreeRW: TODO' },
+ SDL_CondBroadcast: function() { throw 'SDL_CondBroadcast: TODO' },
+ SDL_CondWaitTimeout: function() { throw 'SDL_CondWaitTimeout: TODO' },
+ SDL_WM_ToggleFullScreen: function() { throw 'SDL_WM_ToggleFullScreen: TODO' },
+
+ Mix_SetPostMix: function() { throw 'Mix_SetPostMix: TODO' },
+ Mix_QuerySpec: function() { throw 'Mix_QuerySpec: TODO' },
+ Mix_FadeInChannelTimed: function() { throw 'Mix_FadeInChannelTimed' },
+ Mix_FadeOutChannel: function() { throw 'Mix_FadeOutChannel' },
+
+ Mix_Linked_Version: function() { throw 'Mix_Linked_Version: TODO' },
+ SDL_CreateRGBSurfaceFrom: function() { throw 'SDL_CreateRGBSurfaceFrom: TODO' },
+ SDL_SaveBMP_RW: function() { throw 'SDL_SaveBMP_RW: TODO' },
};
autoAddDeps(LibrarySDL, '$SDL');
diff --git a/src/modules.js b/src/modules.js
index 2e4b206d..f33f302b 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -13,9 +13,10 @@ var LLVM = {
ACCESS_OPTIONS: set('volatile', 'atomic'),
INVOKE_MODIFIERS: set('alignstack', 'alwaysinline', 'inlinehint', 'naked', 'noimplicitfloat', 'noinline', 'alwaysinline attribute.', 'noredzone', 'noreturn', 'nounwind', 'optsize', 'readnone', 'readonly', 'ssp', 'sspreq'),
SHIFTS: set('ashr', 'lshr', 'shl'),
- PHI_REACHERS: set('branch', 'switch', 'invoke'),
+ PHI_REACHERS: set('branch', 'switch', 'invoke', 'indirectbr'),
EXTENDS: set('sext', 'zext'),
COMPS: set('icmp', 'fcmp'),
+ CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui'),
INTRINSICS_32: set('_llvm_memcpy_p0i8_p0i8_i64', '_llvm_memmove_p0i8_p0i8_i64', '_llvm_memset_p0i8_i64'), // intrinsics that need args converted to i32 in USE_TYPED_ARRAYS == 2
};
LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden']));
@@ -227,24 +228,30 @@ var Functions = {
blockAddresses: {}, // maps functions to a map of block labels to label ids
- getSignature: function(returnType, argTypes) {
+ getSignature: function(returnType, argTypes, hasVarArgs) {
var sig = returnType == 'void' ? 'v' : (isIntImplemented(returnType) ? 'i' : 'f');
for (var i = 0; i < argTypes.length; i++) {
var type = argTypes[i];
if (!type) break; // varargs
sig += isIntImplemented(type) ? (getBits(type) == 64 ? 'ii' : 'i') : 'f'; // legalized i64s will be i32s
}
+ if (hasVarArgs) sig += 'i';
return sig;
},
// Mark a function as needing indexing. Python will coordinate them all
- getIndex: function(ident) {
+ getIndex: function(ident, doNotCreate) {
+ if (doNotCreate && !(ident in this.indexedFunctions)) {
+ if (!Functions.getIndex.tentative) Functions.getIndex.tentative = {}; // only used by GL emulation; TODO: generalize when needed
+ Functions.getIndex.tentative[ident] = 0;
+ }
if (phase != 'post' && singlePhase) {
- this.indexedFunctions[ident] = 0; // tell python we need this indexized
- return '{{{ FI_' + ident + ' }}}'; // something python will replace later
+ if (!doNotCreate) this.indexedFunctions[ident] = 0; // tell python we need this indexized
+ return "'{{ FI_" + ident + " }}'"; // something python will replace later
} else {
var ret = this.indexedFunctions[ident];
if (!ret) {
+ if (doNotCreate) return '0';
ret = this.nextIndex;
this.nextIndex += 2; // Need to have indexes be even numbers, see |polymorph| test
this.indexedFunctions[ident] = ret;
@@ -264,20 +271,22 @@ var Functions = {
function emptyTable(sig) {
return zeros(total);
}
- var tables = {};
+ var tables = { pre: '' };
if (ASM_JS) {
['v', 'vi', 'ii', 'iii'].forEach(function(sig) { // add some default signatures that are used in the library
tables[sig] = emptyTable(sig); // TODO: make them compact
});
}
for (var ident in this.indexedFunctions) {
- var sig = ASM_JS ? Functions.implementedFunctions[ident] || Functions.unimplementedFunctions[ident] : 'x';
+ var sig = ASM_JS ? Functions.implementedFunctions[ident] || Functions.unimplementedFunctions[ident] || LibraryManager.library[ident.substr(1) + '__sig'] : 'x';
assert(sig, ident);
if (!tables[sig]) tables[sig] = emptyTable(sig); // TODO: make them compact
tables[sig][this.indexedFunctions[ident]] = ident;
}
var generated = false;
+ var wrapped = {};
for (var t in tables) {
+ if (t == 'pre') continue;
generated = true;
var table = tables[t];
for (var i = 0; i < table.length; i++) {
@@ -294,6 +303,32 @@ var Functions = {
table[i] = (libName.indexOf('.') < 0 ? '_' : '') + libName;
}
}
+ if (ASM_JS) {
+ var curr = table[i];
+ if (curr && !Functions.implementedFunctions[curr]) {
+ // This is a library function, we can't just put it in the function table, need a wrapper
+ if (!wrapped[curr]) {
+ var args = '', arg_coercions = '', call = curr + '(', retPre = '', retPost = '';
+ if (t[0] != 'v') {
+ if (t[0] == 'i') {
+ retPre = 'return ';
+ retPost = '|0';
+ } else {
+ retPre = 'return +';
+ }
+ }
+ for (var j = 1; j < t.length; j++) {
+ args += (j > 1 ? ',' : '') + 'a' + j;
+ arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32') + ';';
+ call += (j > 1 ? ',' : '') + asmCoercion('a' + j, t[j] != 'i' ? 'float' : 'i32');
+ }
+ call += ')';
+ tables.pre += 'function ' + curr + '__wrapper(' + args + ') { ' + arg_coercions + ' ; ' + retPre + call + retPost + ' }\n';
+ wrapped[curr] = 1;
+ }
+ table[i] = curr + '__wrapper';
+ }
+ }
}
var indices = table.toString().replace('"', '');
if (BUILD_AS_SHARED_LIB) {
diff --git a/src/parseTools.js b/src/parseTools.js
index 3ff5e710..3410c4c9 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -210,21 +210,29 @@ function isPossiblyFunctionType(type) {
function isFunctionType(type, out) {
if (!isPossiblyFunctionType(type)) return false;
+ type = type.substr(0, type.length-1); // remove final '*'
+ var firstOpen = type.indexOf('(');
+ if (firstOpen <= 0) return false;
type = type.replace(/"[^"]+"/g, '".."');
- var parts;
- // hackish, but quick splitting of function def parts. this must be fast as it happens a lot
- if (type[0] != '[') {
- parts = type.split(' ');
+ var lastOpen = type.lastIndexOf('(');
+ var returnType;
+ if (firstOpen == lastOpen) {
+ returnType = getReturnType(type);
+ if (!isType(returnType)) return false;
} else {
- var index = type.search(']');
- index += type.substr(index).search(' ');
- parts = [type.substr(0, index), type.substr(index+1)];
+ returnType = 'i8*'; // some pointer type, no point in analyzing further
}
- if (pointingLevels(type) !== 1) return false;
- var text = removeAllPointing(parts.slice(1).join(' '));
- if (!text) return false;
- if (out) out.returnType = parts[0];
- return isType(parts[0]) && isFunctionDef({ text: text, item: tokenize(text.substr(1, text.length-2), true) }, out);
+ if (out) out.returnType = returnType;
+ var argText = type.substr(lastOpen);
+ return isFunctionDef({ text: argText, item: tokenize(argText.substr(1, argText.length-2), true) }, out);
+}
+
+function getReturnType(type) {
+ var lastOpen = type.lastIndexOf('(');
+ if (lastOpen > 0) {
+ return type.substr(0, lastOpen-1);
+ }
+ return type;
}
var isTypeCache = {}; // quite hot, optimize as much as possible
@@ -404,8 +412,7 @@ function isIndexableGlobal(ident) {
return false;
}
var data = Variables.globals[ident];
- // in asm.js, externals are just globals
- return !data.alias && (ASM_JS || !data.external);
+ return !data.alias && !data.external;
}
function makeGlobalDef(ident) {
@@ -642,14 +649,16 @@ function makeI64(low, high) {
// Splits a number (an integer in a double, possibly > 32 bits) into an USE_TYPED_ARRAYS == 2 i64 value.
// Will suffer from rounding. mergeI64 does the opposite.
-function splitI64(value) {
+function splitI64(value, floatConversion) {
// We need to min here, since our input might be a double, and large values are rounded, so they can
// be slightly higher than expected. And if we get 4294967296, that will turn into a 0 if put into a
// HEAP32 or |0'd, etc.
+ var lowInput = legalizedI64s ? value : 'VALUE';
+ if (floatConversion && ASM_JS) lowInput = asmFloatToInt(lowInput);
if (legalizedI64s) {
- return [value + '>>>0', 'Math.min(Math.floor((' + value + ')/4294967296), 4294967295)'];
+ return [lowInput + '>>>0', 'Math.min(Math.floor((' + value + ')/' + asmEnsureFloat(4294967296, 'float') + '), ' + asmEnsureFloat(4294967295, 'float') + ')>>>0'];
} else {
- return makeInlineCalculation(makeI64('VALUE>>>0', 'Math.min(Math.floor(VALUE/4294967296), 4294967295)'), value, 'tempBigIntP');
+ return makeInlineCalculation(makeI64(lowInput + '>>>0', 'Math.min(Math.floor(VALUE/' + asmEnsureFloat(4294967296, 'float') + '), ' + asmEnsureFloat(4294967295, 'float') + ')>>>0'), value, 'tempBigIntP');
}
}
function mergeI64(value, unsigned) {
@@ -974,7 +983,7 @@ if (ASM_JS) {
var memoryMask = hexMemoryMask.length <= decMemoryMask.length ? hexMemoryMask : decMemoryMask;
}
-function getHeapOffset(offset, type) {
+function getHeapOffset(offset, type, forceAsm) {
if (USE_TYPED_ARRAYS !== 2) {
return offset;
} else {
@@ -983,7 +992,7 @@ function getHeapOffset(offset, type) {
}
var shifts = Math.log(Runtime.getNativeTypeSize(type))/Math.LN2;
offset = '(' + offset + ')';
- if (ASM_JS && phase == 'funcs') offset = '(' + offset + '&' + memoryMask + ')';
+ if (ASM_JS && (phase == 'funcs' || forceAsm)) offset = '(' + offset + '&' + memoryMask + ')';
if (shifts != 0) {
return '(' + offset + '>>' + shifts + ')';
} else {
@@ -999,7 +1008,8 @@ function makeVarDef(js) {
function asmEnsureFloat(value, type) { // ensures that a float type has either 5.5 (clearly a float) or +5 (float due to asm coercion)
if (!ASM_JS) return value;
- if (type in Runtime.FLOAT_TYPES && isNumber(value) && value.toString().indexOf('.') < 0) {
+ // coerce if missing a '.', or if smaller than 1, so could be 1e-5 which has no .
+ if (type in Runtime.FLOAT_TYPES && isNumber(value) && (value.toString().indexOf('.') < 0 || Math.abs(value) < 1)) {
return '(+(' + value + '))';
} else {
return value;
@@ -1045,6 +1055,10 @@ function asmMultiplyI32(a, b) {
return '(~~(+((' + a + ')|0) * +((' + b + ')|0)))';
}
+function asmFloatToInt(x) {
+ return '(~~(' + x + '))';
+}
+
function makeGetTempDouble(i, type, forSet) { // get an aliased part of the tempDouble temporary storage
// Cannot use makeGetValue because it uses us
// this is a unique case where we *can* use HEAPF64
@@ -1067,7 +1081,7 @@ function makeSetTempDouble(i, type, value) {
}
// See makeSetValue
-function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSafe) {
+function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSafe, forceAsm) {
if (UNALIGNED_MEMORY) align = 1;
if (isStructType(type)) {
var typeData = Types.types[type];
@@ -1092,7 +1106,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
if (isIntImplemented(type)) {
if (bytes == 4 && align == 2) {
// Special case that we can optimize
- ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore) + '+' +
+ ret += makeGetValue(ptr, pos, 'i16', noNeedFirst, 2, ignore) + '|' +
'(' + makeGetValue(ptr, getFastValue(pos, '+', 2), 'i16', noNeedFirst, 2, ignore) + '<<16)';
} else { // XXX we cannot truly handle > 4...
ret = '';
@@ -1120,7 +1134,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
if (type[0] === '#') type = type.substr(1);
return 'SAFE_HEAP_LOAD(' + offset + ', ' + type + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
} else {
- var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type) + ']';
+ var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']';
if (ASM_JS && phase == 'funcs') {
ret = asmCoercion(ret, type);
}
@@ -1128,6 +1142,10 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
}
}
+function makeGetValueAsm(ptr, pos, type) {
+ return makeGetValue(ptr, pos, type, null, null, null, null, null, true);
+}
+
function indexizeFunctions(value, type) {
assert((type && type !== '?') || (typeof value === 'string' && value.substr(0, 6) === 'CHECK_'), 'No type given for function indexizing');
assert(value !== type, 'Type set to value');
@@ -1246,7 +1264,7 @@ function makeSetValues(ptr, pos, value, type, num, align) {
// If we don't know how to handle this at compile-time, or handling it is best done in a large amount of code, call memset
// TODO: optimize the case of numeric num but non-numeric value
if (!isNumber(num) || !isNumber(value) || (align < 4 && parseInt(num) >= SEEK_OPTIMAL_ALIGN_MIN)) {
- return '_memset(' + getFastValue(ptr, '+', pos) + ', ' + value + ', ' + num + ', ' + align + ')';
+ return '_memset(' + asmCoercion(getFastValue(ptr, '+', pos), 'i32') + ', ' + asmCoercion(value, 'i32') + ', ' + asmCoercion(num, 'i32') + ', ' + align + ')';
}
num = parseInt(num);
value = parseInt(value);
@@ -1436,8 +1454,18 @@ var IHEAP_FHEAP = set('IHEAP', 'IHEAPU', 'FHEAP');
function makePointer(slab, pos, allocator, type, ptr) {
assert(type, 'makePointer requires type info');
- if (slab.substr(0, 4) === 'HEAP' || (USE_TYPED_ARRAYS == 1 && slab in IHEAP_FHEAP)) return pos;
+ if (typeof slab == 'string' && (slab.substr(0, 4) === 'HEAP' || (USE_TYPED_ARRAYS == 1 && slab in IHEAP_FHEAP))) return pos;
var types = generateStructTypes(type);
+ if (typeof slab == 'object') {
+ for (var i = 0; i < slab.length; i++) {
+ var curr = slab[i];
+ if (isNumber(curr)) {
+ slab[i] = parseFloat(curr); // fix "5" to 5 etc.
+ } else if (curr == 'undef') {
+ slab[i] = 0;
+ }
+ }
+ }
// compress type info and data if possible
var de;
try {
@@ -1445,25 +1473,68 @@ function makePointer(slab, pos, allocator, type, ptr) {
// note that we cannot always eval the slab, e.g., if it contains ident,0,0 etc. In that case, no compression TODO: ensure we get arrays here, not str
var evaled = typeof slab === 'string' ? eval(slab) : slab;
de = dedup(evaled);
- if (de.length === 1 && de[0] === 0) {
+ if (de.length === 1 && de[0] == 0) {
slab = types.length;
- if (USE_TYPED_ARRAYS == 2) {
- types = ['i8']; // if data is zeros, we don't need type info
- }
}
// TODO: if not all zeros, at least filter out items with type === 0. requires cleverness to know how to skip at runtime though. also
// be careful of structure padding
} catch(e){}
- de = dedup(types);
- if (de.length === 1) {
- types = de[0];
- } else if (de.length === 2 && typeof slab === 'number') {
- // If slab is all zeros, we can compress types even if we have i32,0,0,0,i32,0,0,0 etc. - we do not need the zeros
- de = de.filter(function(x) { return x !== 0 });
+ if (USE_TYPED_ARRAYS != 2) {
+ de = dedup(types);
if (de.length === 1) {
types = de[0];
+ } else if (de.length === 2 && typeof slab === 'number') {
+ // If slab is all zeros, we can compress types even if we have i32,0,0,0,i32,0,0,0 etc. - we do not need the zeros
+ de = de.filter(function(x) { return x !== 0 });
+ if (de.length === 1) {
+ types = de[0];
+ }
}
+ } else { // USE_TYPED_ARRAYS == 2
+ var fail = false;
+ if (typeof slab === 'object') {
+ // flatten out into i8 values, so we can just to typed array .set()
+ for (var i = 0; i < slab.length; i++) {
+ if (!isNumber(slab[i])) { fail = true; break }
+ }
+ if (!fail) {
+ // XXX This heavily assumes the target endianness is the same as our current endianness! XXX
+ var i = 0;
+ var temp64f = new Float64Array(1);
+ var temp32f = new Float32Array(temp64f.buffer);
+ var temp32 = new Uint32Array(temp64f.buffer);
+ var temp16 = new Uint16Array(temp64f.buffer);
+ var temp8 = new Uint8Array(temp64f.buffer);
+ while (i < slab.length) {
+ var currType = types[i];
+ if (!currType) { i++; continue }
+ var currSize = 0, currValue = slab[i];
+ switch (currType) {
+ case 'i8': i++; continue;
+ case 'i16': temp16[0] = currValue; currSize = 2; break;
+ case 'i64': // fall through, i64 is two i32 chunks
+ case 'i32': temp32[0] = currValue; currSize = 4; break;
+ case 'float': temp32f[0] = currValue; currSize = 4; break;
+ case 'double': temp64f[0] = currValue; currSize = 8; break;
+ default: {
+ if (currType[currType.length-1] == '*') {
+ temp32[0] = currValue;
+ currSize = 4;
+ } else {
+ throw 'what? ' + types[i];
+ }
+ }
+ }
+ for (var j = 0; j < currSize; j++) {
+ slab[i+j] = temp8[j];
+ }
+ i += currSize;
+ }
+ }
+ }
+ if (!fail) types = 'i8';
}
+ if (typeof slab == 'object') slab = '[' + slab.join(',') + ']';
// JS engines sometimes say array initializers are too large. Work around that by chunking and calling concat to combine at runtime
var chunkSize = 10240;
function chunkify(array) {
@@ -1476,7 +1547,7 @@ function makePointer(slab, pos, allocator, type, ptr) {
}
return ret;
}
- if (typeof slab == 'string' && evaled && evaled.length > chunkSize) {
+ if (typeof slab == 'string' && evaled && evaled.length > chunkSize && slab.length > chunkSize) {
slab = chunkify(evaled);
}
if (typeof types != 'string' && types.length > chunkSize) {
@@ -1677,7 +1748,7 @@ function makeStructuralAccess(ident, i) {
}
function makeThrow(what) {
- return 'throw ' + what + (DISABLE_EXCEPTION_CATCHING ? ' + " - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 to catch."' : '') + ';';
+ return 'throw ' + what + (DISABLE_EXCEPTION_CATCHING == 1 ? ' + " - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch."' : '') + ';';
}
// From parseLLVMSegment
@@ -1721,12 +1792,14 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) {
return ret;
}
-function makeComparison(a, b, type) {
+function makeComparison(a, op, b, type) {
+ assert(type);
if (!isIllegalType(type)) {
- return a + ' == ' + b;
+ return asmCoercion(a, type) + op + asmCoercion(b, type);
} else {
assert(type == 'i64');
- return a + '$0 == ' + b + '$0 && ' + a + '$1 == ' + b + '$1';
+ return asmCoercion(a + '$0', 'i32') + op + asmCoercion(b + '$0', 'i32') + ' & ' +
+ asmCoercion(a + '$1', 'i32') + op + asmCoercion(b + '$1', 'i32');
}
}
@@ -1798,7 +1871,7 @@ function makeRounding(value, bits, signed, floatConversion) {
// Note that if converting a float, we may have the wrong sign at this point! But, we have
// been rounded properly regardless, and we will be sign-corrected later when actually used, if
// necessary.
- return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR');
+ return makeInlineCalculation(makeComparison('VALUE', '>=', '0', 'float') + ' ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR');
} else {
// asm.js mode, cleaner refactoring of this function as well. TODO: use in non-asm case, most of this
if (floatConversion && bits <= 32) {
@@ -1815,10 +1888,15 @@ function makeRounding(value, bits, signed, floatConversion) {
// Math.floor is reasonably fast if we don't care about corrections (and even correct if unsigned)
if (!correctRoundings() || !signed) return 'Math.floor(' + value + ')';
// We are left with >32 bits
- return makeInlineCalculation('VALUE >= 0 ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR');
+ return makeInlineCalculation(makeComparison('VALUE', '>=', '0', 'float') + ' ? Math.floor(VALUE) : Math.ceil(VALUE)', value, 'tempBigIntR');
}
}
+function makeIsNaN(value) {
+ if (ASM_JS) return makeInlineCalculation('((VALUE) != (VALUE))', value, 'tempDouble');
+ return 'isNaN(' + value + ')';
+}
+
// fptoui and fptosi are not in these, because we need to be careful about what we do there. We can't
// just sign/unsign the input first.
var UNSIGNED_OP = set('udiv', 'urem', 'uitofp', 'zext', 'lshr');
@@ -1946,7 +2024,7 @@ function processMathop(item) {
}
}
case 'uitofp': case 'sitofp': return RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u');
- case 'fptoui': case 'fptosi': return finish(splitI64(idents[0]));
+ case 'fptoui': case 'fptosi': return finish(splitI64(idents[0], true));
case 'icmp': {
switch (variant) {
case 'uge': return '((' + high1 + '>>>0) >= (' + high2 + '>>>0)) & ((((' + high1 + '>>>0) > (' + high2 + '>>>0)) | ' +
@@ -1984,7 +2062,7 @@ function processMathop(item) {
return i64PreciseOp('add');
} else {
warnI64_1();
- return finish(splitI64(mergeI64(idents[0]) + '+' + mergeI64(idents[1])));
+ return finish(splitI64(mergeI64(idents[0]) + '+' + mergeI64(idents[1]), true));
}
}
case 'sub': {
@@ -1992,7 +2070,7 @@ function processMathop(item) {
return i64PreciseOp('subtract');
} else {
warnI64_1();
- return finish(splitI64(mergeI64(idents[0]) + '-' + mergeI64(idents[1])));
+ return finish(splitI64(mergeI64(idents[0]) + '-' + mergeI64(idents[1]), true));
}
}
case 'sdiv': case 'udiv': {
@@ -2000,7 +2078,7 @@ function processMathop(item) {
return i64PreciseOp('divide', op[0] === 'u');
} else {
warnI64_1();
- return finish(splitI64(makeRounding(mergeI64(idents[0], op[0] === 'u') + '/' + mergeI64(idents[1], op[0] === 'u'), bits, op[0] === 's')));
+ return finish(splitI64(makeRounding(mergeI64(idents[0], op[0] === 'u') + '/' + mergeI64(idents[1], op[0] === 'u'), bits, op[0] === 's'), true));
}
}
case 'mul': {
@@ -2008,7 +2086,7 @@ function processMathop(item) {
return i64PreciseOp('multiply');
} else {
warnI64_1();
- return finish(splitI64(mergeI64(idents[0], op[0] === 'u') + '*' + mergeI64(idents[1], op[0] === 'u')));
+ return finish(splitI64(mergeI64(idents[0], op[0] === 'u') + '*' + mergeI64(idents[1], op[0] === 'u'), true));
}
}
case 'urem': case 'srem': {
@@ -2016,7 +2094,7 @@ function processMathop(item) {
return i64PreciseOp('modulo', op[0] === 'u');
} else {
warnI64_1();
- return finish(splitI64(mergeI64(idents[0], op[0] === 'u') + '%' + mergeI64(idents[1], op[0] === 'u')));
+ return finish(splitI64(mergeI64(idents[0], op[0] === 'u') + '%' + mergeI64(idents[1], op[0] === 'u'), true));
}
}
case 'bitcast': {
@@ -2054,7 +2132,7 @@ function processMathop(item) {
Types.preciseI64MathUsed = true;
return '(i64Math' + (ASM_JS ? '_' : '.') + 'multiply(' + asmCoercion(idents[0], 'i32') + ',0,' + asmCoercion(idents[1], 'i32') + ',0),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')';
} else {
- return handleOverflow(getFastValue(idents[0], '*', idents[1], item.type), bits);
+ return '((' +getFastValue(idents[0], '*', idents[1], item.type) + ')&-1)'; // force a non-eliminatable coercion here, to prevent a double result from leaking
}
}
case 'urem': case 'srem': return getFastValue(idents[0], '%', idents[1], item.type);
@@ -2135,8 +2213,8 @@ function processMathop(item) {
case 'ult': case 'olt': return idents[0] + ' < ' + idents[1];
case 'une': case 'one': return idents[0] + ' != ' + idents[1];
case 'ueq': case 'oeq': return idents[0] + ' == ' + idents[1];
- case 'ord': return '!isNaN(' + idents[0] + ') && !isNaN(' + idents[1] + ')';
- case 'uno': return 'isNaN(' + idents[0] + ') || isNaN(' + idents[1] + ')';
+ case 'ord': return '!' + makeIsNaN(idents[0]) + ' & !' + makeIsNaN(idents[1]);
+ case 'uno': return makeIsNaN(idents[0]) + ' | ' + makeIsNaN(idents[1]);
case 'true': return '1';
default: throw 'Unknown fcmp variant: ' + variant;
}
@@ -2266,3 +2344,10 @@ function stripCorrections(param) {
return param;
}
+function getImplementationType(varInfo) {
+ if (varInfo.impl == 'nativized') {
+ return removePointing(varInfo.type);
+ }
+ return varInfo.type;
+}
+
diff --git a/src/preamble.js b/src/preamble.js
index cb01994f..52e6a7ca 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -506,10 +506,17 @@ function allocate(slab, types, allocator, ptr) {
}
if (zeroinit) {
- _memset(ret, 0, size);
- return ret;
+ _memset(ret, 0, size);
+ return ret;
+ }
+
+#if USE_TYPED_ARRAYS == 2
+ if (singleType === 'i8') {
+ HEAPU8.set(new Uint8Array(slab), ret);
+ return ret;
}
-
+#endif
+
var i = 0, type;
while (i < size) {
var curr = slab[i];
@@ -591,8 +598,12 @@ var STATICTOP;
#if USE_TYPED_ARRAYS
function enlargeMemory() {
#if ALLOW_MEMORY_GROWTH == 0
+#if ASM_JS == 0
abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.');
#else
+ abort('Cannot enlarge memory arrays in asm.js. Compile with -s TOTAL_MEMORY=X with X higher than the current value.');
+#endif
+#else
// TOTAL_MEMORY is the current size of the actual array, and STATICTOP is the new top.
#if ASSERTIONS
Module.printErr('Warning: Enlarging memory arrays, this is not fast, and ALLOW_MEMORY_GROWTH is not fully tested with all optimizations on! ' + [STATICTOP, TOTAL_MEMORY]); // We perform safe elimination instead of elimination in this mode, but if you see this error, try to disable it and other optimizations entirely
@@ -631,7 +642,11 @@ function enlargeMemory() {
#endif
var TOTAL_STACK = Module['TOTAL_STACK'] || {{{ TOTAL_STACK }}};
+#if ASM_JS == 0
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}};
+#else
+var TOTAL_MEMORY = {{{ TOTAL_MEMORY }}}; // in asm, we hardcode the mask, so cannot adjust memory at runtime
+#endif
var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}};
// Initialize the runtime's memory
@@ -750,17 +765,6 @@ function exitRuntime() {
CorrectionsMonitor.print();
}
-function String_len(ptr) {
- var i = ptr;
- while ({{{ makeGetValue('i++', '0', 'i8') }}}) { // Note: should be |!= 0|, technically. But this helps catch bugs with undefineds
-#if ASSERTIONS
- assert(i < TOTAL_MEMORY);
-#endif
- }
- return i - ptr - 1;
-}
-Module['String_len'] = String_len;
-
// Tools
// This processes a JS string into a C-line array of numbers, 0-terminated.
diff --git a/src/runtime.js b/src/runtime.js
index d1475bd4..43bd7de1 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -46,7 +46,7 @@ var RuntimeGenerator = {
ret += '; assert(STACKTOP < STACK_MAX)';
}
if (INIT_STACK) {
- ret += '; _memset(__stackBase__, 0, ' + initial + ')';
+ ret += '; _memset(' + asmCoercion('__stackBase__', 'i32') + ', 0, ' + initial + ')';
}
return ret;
},
@@ -79,8 +79,8 @@ var RuntimeGenerator = {
// Rounding is inevitable if the number is large. This is a particular problem for small negative numbers
// (-1 will be rounded!), so handle negatives separately and carefully
makeBigInt: function(low, high, unsigned) {
- var unsignedRet = '(' + makeSignOp(low, 'i32', 'un', 1, 1) + '+(' + makeSignOp(high, 'i32', 'un', 1, 1) + '*4294967296))';
- var signedRet = '(' + makeSignOp(low, 'i32', 'un', 1, 1) + '+(' + makeSignOp(high, 'i32', 're', 1, 1) + '*4294967296))';
+ var unsignedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'float') + '+(' + asmCoercion(makeSignOp(high, 'i32', 'un', 1, 1), 'float') + '*' + asmEnsureFloat(4294967296, 'float') + '))';
+ var signedRet = '(' + asmCoercion(makeSignOp(low, 'i32', 'un', 1, 1), 'float') + '+(' + asmCoercion(makeSignOp(high, 'i32', 're', 1, 1), 'float') + '*' + asmEnsureFloat(4294967296, 'float') + '))';
if (typeof unsigned === 'string') return '(' + unsigned + ' ? ' + unsignedRet + ' : ' + signedRet + ')';
return unsigned ? unsignedRet : signedRet;
}
@@ -334,6 +334,7 @@ var Runtime = {
assert(args.length == sig.length-1);
#endif
#if ASM_JS
+ if (!args.splice) args = Array.prototype.slice.call(args);
args.splice(0, 0, ptr);
return Module['dynCall_' + sig].apply(null, args);
#else
diff --git a/src/settings.js b/src/settings.js
index 8ae287f9..ccf2a25b 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -158,8 +158,16 @@ var DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catc
// then this can make it much faster. If an exception actually happens,
// it will not be caught and the program will halt (so this will not
// introduce silent failures, which is good).
+ // DISABLE_EXCEPTION_CATCHING = 0 - generate code to actually catch exceptions
+ // DISABLE_EXCEPTION_CATCHING = 1 - disable exception catching at all
+ // DISABLE_EXCEPTION_CATCHING = 2 - disable exception catching, but enables
+ // catching in whitelist
// TODO: Make this also remove cxa_begin_catch etc., optimize relooper
// for it, etc. (perhaps do all of this as preprocessing on .ll?)
+
+var EXCEPTION_CATCHING_WHITELIST = []; // Enables catching exception in listed functions if
+ // DISABLE_EXCEPTION_CATCHING = 2 set
+
var EXECUTION_TIMEOUT = -1; // Throw an exception after X seconds - useful to debug infinite loops
var CHECK_OVERFLOWS = 0; // Add code that checks for overflows in integer math operations.
// There is currently not much to do to handle overflows if they occur.
diff --git a/src/shell.js b/src/shell.js
index 5571b8ac..8c37bf5b 100644
--- a/src/shell.js
+++ b/src/shell.js
@@ -1,5 +1,3 @@
-// TODO: " u s e s t r i c t ";
-
try {
this['Module'] = Module;
} catch(e) {
diff --git a/src/utility.js b/src/utility.js
index 63582ae8..8db37c61 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -10,6 +10,10 @@ function safeQuote(x) {
function dump(item) {
try {
+ if (typeof item == 'object' && item !== null && item.funcData) {
+ var funcData = item.funcData;
+ item.funcData = null;
+ }
return '// ' + JSON.stringify(item, null, ' ').replace(/\n/g, '\n// ');
} catch(e) {
var ret = [];
@@ -22,6 +26,8 @@ function dump(item) {
}
}
return ret.join(',\n');
+ } finally {
+ if (funcData) item.funcData = funcData;
}
}
diff --git a/system/include/libc/sys/_types.h b/system/include/libc/sys/_types.h
index c56fbd58..0511602c 100644
--- a/system/include/libc/sys/_types.h
+++ b/system/include/libc/sys/_types.h
@@ -27,9 +27,15 @@ typedef unsigned __dev_t; /* XXX Emscripten */
#ifndef __uid_t_defined
typedef unsigned __uid_t; /* XXX Emscripten */
+#define __uid_t_defined 1
#endif
#ifndef __gid_t_defined
typedef unsigned __gid_t; /* XXX Emscripten */
+#define __gid_t_defined 1
+#endif
+#ifndef __id_t_defined
+typedef unsigned __id_t; /* can hold a gid_t, pid_t, or uid_t XXX EMSCRIPTEN specific*/
+#define __id_t_defined 1
#endif
#ifndef __off64_t_defined
diff --git a/system/include/libc/sys/types.h b/system/include/libc/sys/types.h
index 4bf41a34..e90a74ac 100644
--- a/system/include/libc/sys/types.h
+++ b/system/include/libc/sys/types.h
@@ -162,6 +162,7 @@ typedef _off_t off_t;
typedef __dev_t dev_t;
typedef __uid_t uid_t;
typedef __gid_t gid_t;
+typedef __id_t id_t ; /* can hold a uid_t or pid_t */
#endif
#if defined(__XMK__)
diff --git a/tests/cases/extendedprecision.ll b/tests/cases/extendedprecision.ll
index 2ab74d58..6f1b2626 100644
--- a/tests/cases/extendedprecision.ll
+++ b/tests/cases/extendedprecision.ll
@@ -5,7 +5,7 @@ target triple = "i386-pc-linux-gnu"
@.str = private constant [14 x i8] c"hello, world!\00", align 1 ; [#uses=1]
; [#uses=2]
-define void @"\01_Z5hellov"() {
+define void @"\01_Z5hellov"(x86_fp80 %waka) {
entry:
%0 = call i32 bitcast (i32 (i8*)* @puts to i32 (i32*)*)(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ; [#uses=0]
br label %return
diff --git a/tests/emscripten_api_browser.cpp b/tests/emscripten_api_browser.cpp
index b35fbac2..18046ca3 100644
--- a/tests/emscripten_api_browser.cpp
+++ b/tests/emscripten_api_browser.cpp
@@ -68,7 +68,7 @@ void second(void *arg) {
printf("sacond! %d\n", now);
assert(fabs(now - last - 500) < 250);
last = now;
- emscripten_async_run_script("_third()", 1000);
+ emscripten_async_run_script("Module._third()", 1000);
}
}
diff --git a/tests/glbegin_points.c b/tests/glbegin_points.c
new file mode 100644
index 00000000..b28cca4e
--- /dev/null
+++ b/tests/glbegin_points.c
@@ -0,0 +1,166 @@
+/*******************************************************************
+ * *
+ * Using SDL With OpenGL *
+ * *
+ * Tutorial by Kyle Foley (sdw) *
+ * *
+ * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL *
+ * *
+ *******************************************************************/
+
+/*
+THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION
+AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN.
+
+THE ORIGINAL AUTHOR IS KYLE FOLEY.
+
+THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
+OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
+MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
+ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
+RESULTING FROM THE USE, MODIFICATION, OR
+REDISTRIBUTION OF THIS SOFTWARE.
+*/
+
+#include "SDL/SDL.h"
+#include "SDL/SDL_image.h"
+#include "SDL/SDL_opengl.h"
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+
+ // Slightly different SDL initialization
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*
+
+ screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed*
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ // Set the OpenGL state after creating the context with SDL_SetVideoMode
+
+ glClearColor( 0, 0, 0, 0 );
+
+#if !EMSCRIPTEN
+ glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL
+#endif
+
+ glViewport( 0, 0, 640, 480 );
+
+ glMatrixMode( GL_PROJECTION );
+ GLfloat matrixData[] = { 2.0/640, 0, 0, 0,
+ 0, -2.0/480, 0, 0,
+ 0, 0, -1, 0,
+ -1, 1, 0, 1 };
+ glLoadMatrixf(matrixData); // test loadmatrix
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+
+ // Load the OpenGL texture
+
+ GLuint texture; // Texture object handle
+ SDL_Surface *surface; // Gives us the information to make the texture
+
+ if ( (surface = IMG_Load("screenshot.png")) ) {
+
+ // Check that the image's width is a power of 2
+ if ( (surface->w & (surface->w - 1)) != 0 ) {
+ printf("warning: image.bmp's width is not a power of 2\n");
+ }
+
+ // Also check if the height is a power of 2
+ if ( (surface->h & (surface->h - 1)) != 0 ) {
+ printf("warning: image.bmp's height is not a power of 2\n");
+ }
+
+ // Have OpenGL generate a texture object handle for us
+ glGenTextures( 1, &texture );
+
+ // Bind the texture object
+ glBindTexture( GL_TEXTURE_2D, texture );
+
+ // Set the texture's stretching properties
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+
+ //SDL_LockSurface(surface);
+
+ // Add some greyness
+ memset(surface->pixels, 0x66, surface->w*surface->h);
+
+ // Edit the texture object's image data using the information SDL_Surface gives us
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels );
+
+ //SDL_UnlockSurface(surface);
+ }
+ else {
+ printf("SDL could not load image.bmp: %s\n", SDL_GetError());
+ SDL_Quit();
+ return 1;
+ }
+
+ // Free the SDL_Surface only if it was successfully created
+ if ( surface ) {
+ SDL_FreeSurface( surface );
+ }
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ // Bind the texture to which subsequent calls refer to
+ glBindTexture( GL_TEXTURE_2D, texture );
+
+ // Use clientside vertex pointers to render two items
+ GLfloat vertexData[] = { 0, 0, 10, 10, // texture2, position2
+ 1, 0, 300, 10,
+ 1, 1, 300, 128,
+ 0, 1, 10, 128,
+ 0, 0.5, 410, 10,
+ 1, 0.5, 600, 10,
+ 1, 1, 630, 200,
+ 0.5, 1, 310, 250 };
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_FLOAT, 4*4, &vertexData[0]);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 4*4, &vertexData[2]);
+
+ glDrawArrays(GL_POINTS, 0, 8);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ // Render the last item using oldschool glBegin etc
+ glBegin( GL_POINTS );
+ glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 0 );
+ glTexCoord2i( 1, 0 ); glVertex2f( 300, 300 );
+ glTexCoord2i( 1, 1 ); { float vals[3] = { 300, 400, 0 }; glVertex3fv(vals); }
+ glTexCoord2i( 0, 1 ); { float vals[2] = { 500, 410 }; glVertex2fv(vals); }
+ glEnd();
+
+ SDL_GL_SwapBuffers();
+
+#if !EMSCRIPTEN
+ // Wait for 3 seconds to give us a chance to see the image
+ SDL_Delay(3000);
+#endif
+
+ // Now we can delete the OpenGL texture and close down SDL
+ glDeleteTextures( 1, &texture );
+
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/tests/glbegin_points.png b/tests/glbegin_points.png
new file mode 100644
index 00000000..57e15d29
--- /dev/null
+++ b/tests/glbegin_points.png
Binary files differ
diff --git a/tests/glbook/CH13_ParticleSystem.png b/tests/glbook/CH13_ParticleSystem.png
index 39b9af0a..ff9c3496 100644
--- a/tests/glbook/CH13_ParticleSystem.png
+++ b/tests/glbook/CH13_ParticleSystem.png
Binary files differ
diff --git a/tests/openjpeg/codec/convert.h b/tests/openjpeg/codec/convert.h
index 1dc58d72..73ad6fb7 100644
--- a/tests/openjpeg/codec/convert.h
+++ b/tests/openjpeg/codec/convert.h
@@ -57,7 +57,7 @@ int imagetobmp(opj_image_t *image, const char *outfile);
/* TIFF conversion*/
opj_image_t* tiftoimage(const char *filename, opj_cparameters_t *parameters);
-int imagetotif(opj_image_t *image, const char *outfile);
+static int imagetotif(opj_image_t *image, const char *outfile) { return 0; } // XXX EMSCRIPTEN
/**
Load a single image component encoded in PGX file format
@param filename Name of the PGX file to load
@@ -75,7 +75,7 @@ int imagetoraw(opj_image_t * image, const char *outfile);
opj_image_t* rawtoimage(const char *filename, opj_cparameters_t *parameters, raw_cparameters_t *raw_cp);
/* PNG conversion*/
-extern int imagetopng(opj_image_t *image, const char *write_idf);
+static int imagetopng(opj_image_t *image, const char *write_idf) { return 0; } // XXX EMSCRIPTEN
extern opj_image_t* pngtoimage(const char *filename, opj_cparameters_t *parameters);
#endif /* __J2K_CONVERT_H */
diff --git a/tests/runner.py b/tests/runner.py
index 6e76d061..4e79f7e9 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -114,10 +114,10 @@ class RunnerCore(unittest.TestCase):
shutil.rmtree(self.get_dir())
# Make sure we don't leave stuff around
- if not self.has_prev_ll:
- for temp_file in os.listdir(TEMP_DIR):
- assert not temp_file.endswith('.ll'), temp_file
- # TODO assert not temp_file.startswith('emscripten_'), temp_file
+ #if not self.has_prev_ll:
+ # for temp_file in os.listdir(TEMP_DIR):
+ # assert not temp_file.endswith('.ll'), temp_file
+ # # TODO assert not temp_file.startswith('emscripten_'), temp_file
def skip(self, why):
print >> sys.stderr, '<skipping: %s> ' % why,
@@ -279,8 +279,8 @@ process(sys.argv[1])
if engine == SPIDERMONKEY_ENGINE and Settings.ASM_JS:
if 'Successfully compiled asm.js code' in err and 'asm.js link error' not in err:
print >> sys.stderr, "[was asm.js'ified]"
- else:
- print >> sys.stderr, "[did NOT asm.js'ify]"
+ elif 'asm.js' in err: # if no asm.js error, then not an odin build
+ raise Exception("did NOT asm.js'ify")
if output_nicerizer:
ret = output_nicerizer(out, err)
else:
@@ -495,6 +495,8 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'brows
self.do_run(src, 'hello, world!')
def test_intvars(self):
+ if self.emcc_args == None: return self.skip('needs ta2')
+
src = '''
#include <stdio.h>
int global = 20;
@@ -1138,6 +1140,47 @@ m_divisor is 1091269979
'''
self.do_run(src, '3217489085')
+ def test_i32_mul_semiprecise(self):
+ src = r'''
+ #include <stdio.h>
+
+ typedef unsigned int uint;
+
+ // from cube2, zlib licensed
+
+ #define N (624)
+ #define M (397)
+ #define K (0x9908B0DFU)
+
+ static uint state[N];
+ static int next = N;
+
+ void seedMT(uint seed)
+ {
+ state[0] = seed;
+ for(uint i = 1; i < N; i++) // if we do not do this precisely, at least we should coerce to int immediately, not wait
+ state[i] = seed = 1812433253U * (seed ^ (seed >> 30)) + i;
+ next = 0;
+ }
+
+ int main() {
+ seedMT(5497);
+ for (int i = 0; i < 10; i++) printf("%d: %u\n", i, state[i]);
+ return 0;
+ }
+ '''
+ self.do_run(src, '''0: 5497
+1: 2916432318
+2: 2502517762
+3: 3151524867
+4: 2323729668
+5: 2053478917
+6: 2409490438
+7: 848473607
+8: 691103752
+9: 3915535113
+''')
+
def test_i16_emcc_intrinsic(self):
Settings.CORRECT_SIGNS = 1 # Relevant to this test
@@ -1166,6 +1209,8 @@ m_divisor is 1091269979
def test_llvm_intrinsics(self):
if self.emcc_args == None: return self.skip('needs ta2')
+ Settings.PRECISE_I64_MATH = 2 # for bswap64
+
src = r'''
#include <stdio.h>
#include <sys/types.h>
@@ -1193,6 +1238,10 @@ m_divisor is 1091269979
printf("%d\n", llvm_expect_i32(x % 27, 3));
+ int64_t a = 1;
+ a = __builtin_bswap64(a);
+ printf("%lld\n", a);
+
return 0;
}
'''
@@ -1202,6 +1251,7 @@ c8,ef
c5,de,15,8a
23,21
13
+72057594037927936
''')
def test_bswap64(self):
@@ -1495,7 +1545,7 @@ Succeeded!
src = '''
#include <stdio.h>
#include <math.h>
- int main()
+ int main(int argc, char **argv)
{
float x = 1.234, y = 3.5, q = 0.00000001;
y *= 3;
@@ -1504,6 +1554,8 @@ Succeeded!
printf("%.2f, %.2f, %.2f, %.2f\\n", fmin(0.5, 3.3), fmin(NAN, 3.3), fmax(0.5, 3.3), fmax(NAN, 3.3));
+ printf("small: %.10f\\n", argc * 0.000001);
+
/*
// Rounding behavior
float fs[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 };
@@ -1515,7 +1567,36 @@ Succeeded!
return 0;
}
'''
- self.do_run(src, '*1,10,10.5,1,1.2340,0.00*\n0.50, 3.30, 3.30, 3.30\n')
+ self.do_run(src, '*1,10,10.5,1,1.2340,0.00*\n0.50, 3.30, 3.30, 3.30\nsmall: 0.0000010000\n')
+
+ def test_isnan(self):
+ src = r'''
+ #include <stdio.h>
+
+ int IsNaN(double x){
+ int rc; /* The value return */
+ volatile double y = x;
+ volatile double z = y;
+ rc = (y!=z);
+ return rc;
+ }
+
+ int main() {
+ double tests[] = { 1.0, 3.333, 1.0/0.0, 0.0/0.0, -1.0/0.0, -0, 0, -123123123, 12.0E200 };
+ for (int i = 0; i < sizeof(tests)/sizeof(double); i++)
+ printf("%d - %f - %d\n", i, tests[i], IsNaN(tests[i]));
+ }
+ '''
+ self.do_run(src, '''0 - 1.000000 - 0
+1 - 3.333000 - 0
+2 - inf - 0
+3 - nan - 1
+4 - -inf - 0
+5 - 0.000000 - 0
+6 - 0.000000 - 0
+7 - -123123123.000000 - 0
+8 - 1.2e+201 - 0
+''')
def test_globaldoubles(self):
src = r'''
@@ -2282,6 +2363,7 @@ Exception execution path of first function! 1
''')
def test_exceptions(self):
+ if Settings.ASM_JS: return self.skip('no exceptions support in asm')
if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1")
Settings.EXCEPTION_DEBUG = 1
@@ -2321,7 +2403,7 @@ Exception execution path of first function! 1
self.do_run(src, '*throw...caught!infunc...done!*')
Settings.DISABLE_EXCEPTION_CATCHING = 1
- self.do_run(src, 'Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 to catch.')
+ self.do_run(src, 'Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0')
src = '''
#include <iostream>
@@ -2372,7 +2454,41 @@ Exception execution path of first function! 1
Settings.DISABLE_EXCEPTION_CATCHING = 0
self.do_run(src, 'Throw...Construct...Catched...Destruct...Throw...Construct...Copy...Catched...Destruct...Destruct...')
+ def test_white_list_exception(self):
+ if Settings.ASM_JS: return self.skip('no exceptions support in asm')
+ Settings.DISABLE_EXCEPTION_CATCHING = 2
+ Settings.EXCEPTION_CATCHING_WHITELIST = ["__Z12somefunctionv"]
+
+ src = '''
+ #include <stdio.h>
+
+ void thrower() {
+ printf("infunc...");
+ throw(99);
+ printf("FAIL");
+ }
+
+ void somefunction() {
+ try {
+ thrower();
+ } catch(...) {
+ printf("done!*\\n");
+ }
+ }
+
+ int main() {
+ somefunction();
+ return 0;
+ }
+ '''
+ self.do_run(src, 'infunc...done!*')
+
+ Settings.DISABLE_EXCEPTION_CATCHING = 0
+ Settings.EXCEPTION_CATCHING_WHITELIST = []
+
+
def test_uncaught_exception(self):
+ if Settings.ASM_JS: return self.skip('no exceptions support in asm')
if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc')
if '-O2' in self.emcc_args:
self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage
@@ -2414,6 +2530,7 @@ Exception execution path of first function! 1
self.do_run(src, 'success')
def test_typed_exceptions(self):
+ if Settings.ASM_JS: return self.skip('no exceptions support in asm')
Settings.DISABLE_EXCEPTION_CATCHING = 0
Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access.
src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read()
@@ -2421,6 +2538,7 @@ Exception execution path of first function! 1
self.do_run(src, expected)
def test_multiexception(self):
+ if Settings.ASM_JS: return self.skip('no exceptions support in asm')
Settings.DISABLE_EXCEPTION_CATCHING = 0
src = r'''
#include <stdio.h>
@@ -2775,6 +2893,26 @@ Exiting setjmp function, level: 0, prev_jmp: -1
'''
self.do_run(src, 'fn2(-5) = 5, fn(10) = 3.16')
+ def test_funcptrfunc(self):
+ src = r'''
+ #include <stdio.h>
+
+ typedef void (*funcptr)(int, int);
+ typedef funcptr (*funcptrfunc)(int);
+
+ funcptr __attribute__ ((noinline)) getIt(int x) {
+ return (funcptr)x;
+ }
+
+ int main(int argc, char **argv)
+ {
+ funcptrfunc fpf = argc < 100 ? getIt : NULL;
+ printf("*%p*\n", fpf(argc));
+ return 0;
+ }
+ '''
+ self.do_run(src, '*0x1*')
+
def test_emptyclass(self):
if self.emcc_args is None: return self.skip('requires emcc')
src = '''
@@ -3200,6 +3338,7 @@ def process(filename):
self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check)
def test_inlinejs(self):
+ if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm')
src = r'''
#include <stdio.h>
@@ -3421,7 +3560,6 @@ def process(filename):
def test_varargs(self):
if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this')
- if Settings.ASM_JS: return self.skip('varargs by function pointer not yet supported')
src = '''
#include <stdio.h>
@@ -3472,7 +3610,7 @@ def process(filename):
GETMAX(i, int);
GETMAX(D, double);
- int main() {
+ int main(int argc, char **argv) {
vary("*cheez: %d+%d*", 0, 24); // Also tests that '0' is not special as an array ender
vary("*albeit*"); // Should not fail with no var args in vararg function
vary2('Q', "%d*", 85);
@@ -3483,7 +3621,7 @@ def process(filename):
printf("maxxD:%.2f*\\n", (float)maxxD);
// And, as a function pointer
- void (*vfp)(const char *s, ...) = vary;
+ void (*vfp)(const char *s, ...) = argc == 1211 ? NULL : vary;
vfp("*vfp:%d,%d*", 22, 199);
return 0;
@@ -3789,6 +3927,7 @@ The current type of b is: 9
self.do_run(src, '*0\n')
def test_intentional_fault(self):
+ if Settings.ASM_JS: return self.skip('no throw support in asm')
# Some programs intentionally segfault themselves, we should compile that into a throw
src = r'''
int main () {
@@ -4728,7 +4867,7 @@ at function.:blag
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
- def test_snprintf0(self):
+ def test_printf_more(self):
src = r'''
#include <stdio.h>
int main() {
@@ -4736,10 +4875,13 @@ at function.:blag
char buf[size];
snprintf(buf, size, "%s %d %.2f\n", "me and myself", 25, 1.345);
printf("%d : %s\n", size, buf);
+ char *buff = NULL;
+ asprintf(&buff, "%d waka %d\n", 21, 95);
+ puts(buff);
return 0;
}
'''
- self.do_run(src, '22 : me and myself 25 1.34\n')
+ self.do_run(src, '22 : me and myself 25 1.34\n21 waka 95\n')
def test_atoX(self):
if self.emcc_args is None: return self.skip('requires ta2')
@@ -4871,13 +5013,19 @@ at function.:blag
printf("%f, %f\n", atof("1.234567"), atof("cheez"));
- float n = -1;
- sscanf(" 2.8208", "%f", &n);
- printf("%.4f\n", n);
+ char float_formats[] = "fegE";
+ char format[] = "%_";
+ for(int i = 0; i < 4; ++i) {
+ format[1] = float_formats[i];
+
+ float n = -1;
+ sscanf(" 2.8208", format, &n);
+ printf("%.4f\n", n);
- float a = -1;
- sscanf("-3.03", "%f", &a);
- printf("%.4f\n", a);
+ float a = -1;
+ sscanf("-3.03", format, &a);
+ printf("%.4f\n", a);
+ }
char buffy[100];
sscanf("cheez some thing moar 123\nyet more\n", "cheez %s", buffy);
@@ -4910,7 +5058,7 @@ at function.:blag
return 0;
}
'''
- self.do_run(src, 'en-us : 2\nen-r : 99\nen : 3\n1.234567, 0.000000\n2.8208\n-3.0300\n|some|\n|something|\n|somethingmoar|\n' +
+ self.do_run(src, 'en-us : 2\nen-r : 99\nen : 3\n1.234567, 0.000000\n2.8208\n-3.0300\n2.8208\n-3.0300\n2.8208\n-3.0300\n2.8208\n-3.0300\n|some|\n|something|\n|somethingmoar|\n' +
'1\n1499\n' +
'5\n87,0.481565,0.059481,0,1\n' +
'3\n-123,4294966531,-34\n' +
@@ -5514,7 +5662,7 @@ def process(filename):
int main() {
char *c = "μ†ℱ ╋ℯ╳╋";
printf("%d %d %d %d %s\n", c[0]&0xff, c[1]&0xff, c[2]&0xff, c[3]&0xff, c);
- emscripten_run_script("cheez = Module._malloc(100);"
+ emscripten_run_script("cheez = _malloc(100);"
"Module.writeStringToMemory(\"μ†ℱ ╋ℯ╳╋\", cheez);"
"Module.print([Pointer_stringify(cheez), Module.getValue(cheez, 'i8')&0xff, Module.getValue(cheez+1, 'i8')&0xff, Module.getValue(cheez+2, 'i8')&0xff, Module.getValue(cheez+3, 'i8')&0xff, ]);");
}
@@ -6463,7 +6611,6 @@ void*:16
Settings.CORRECT_OVERFLOWS = 1
Settings.CHECK_OVERFLOWS = 0
Settings.CORRECT_SIGNS = 1 # Not sure why, but needed
- Settings.INIT_STACK = 1 # TODO: Investigate why this is necessary
self.do_ll_run(path_from_root('tests', 'lua', 'lua.ll'),
'hello lua world!\n17\n1\n2\n3\n4\n7',
@@ -7427,30 +7574,6 @@ def process(filename):
# This test *should* fail, by throwing this exception
assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e)
- def test_check_overflow(self):
- if Settings.ASM_JS: return self.skip('asm always corrects, and cannot check')
-
- Settings.CHECK_OVERFLOWS = 1
- Settings.CORRECT_OVERFLOWS = 0
-
- src = '''
- #include<stdio.h>
- int main() {
- int t = 77;
- for (int i = 0; i < 30; i++) {
- //t = (t << 2) + t + 1; // This would have worked, since << forces into 32-bit int...
- t = t*5 + 1; // Python lookdict_string has ~the above line, which turns into this one with optimizations...
- printf("%d,%d\\n", t, t & 127);
- }
- return 0;
- }
- '''
- try:
- self.do_run(src, '*nothingatall*')
- except Exception, e:
- # This test *should* fail, by throwing this exception
- assert 'Too many corrections' in str(e), str(e)
-
def test_debug(self):
if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g')
@@ -7539,7 +7662,7 @@ def process(filename):
int main() {
int t = 77;
for (int i = 0; i < 30; i++) {
- t = t*5 + 1;
+ t = t + t + t + t + t + 1;
}
printf("*%d,%d*\\n", t, t & 127);
return 0;
@@ -7653,7 +7776,7 @@ def process(filename):
int main() {
int t = 77;
for (int i = 0; i < 30; i++) {
- t = t*5 + 1;
+ t = t + t + t + t + t + 1;
}
printf("*%d,%d*\\n", t, t & 127);
@@ -7670,7 +7793,7 @@ def process(filename):
def check(output, err):
# TODO: check the line #
if self.emcc_args is None or self.emcc_args == []: # LLVM full opts optimize out some corrections
- assert re.search('^Overflow\|.*src.cpp:6 : 60 hits, %20 failures$', output, re.M), 'no indication of Overflow corrections: ' + output
+ assert re.search('^Overflow\|.*src.cpp:6 : 150 hits, %21 failures$', output, re.M), 'no indication of Overflow corrections: ' + output
assert re.search('^UnSign\|.*src.cpp:13 : 6 hits, %17 failures$', output, re.M), 'no indication of Sign corrections: ' + output
return output
@@ -8999,6 +9122,8 @@ f.close()
['asm', 'registerize']),
(path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(),
['asm', 'simplifyExpressionsPre']),
+ (path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(),
+ ['asm', 'last']),
]:
output = Popen([NODE_JS, path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0]
self.assertIdentical(expected, output.replace('\r\n', '\n').replace('\n\n', '\n'))
@@ -9794,7 +9919,7 @@ elif 'browser' in str(sys.argv):
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
Module.postRun = function() {
function doOne() {
- _one();
+ Module._one();
setTimeout(doOne, 1000/60);
}
setTimeout(doOne, 1000/60);
@@ -9815,7 +9940,7 @@ elif 'browser' in str(sys.argv):
''')
open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read()))
- Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js']).communicate()
+ Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''']).communicate()
self.run_browser('page.html', '', '/report_result?510510')
def test_sdl_mouse(self):
@@ -10138,7 +10263,7 @@ elif 'browser' in str(sys.argv):
self.run_browser('test.html', '.', ['/report_result?' + e for e in expected])
def test_emscripten_api(self):
- self.btest('emscripten_api_browser.cpp', '1')
+ self.btest('emscripten_api_browser.cpp', '1', args=['-s', '''EXPORTED_FUNCTIONS=['_main', '_third']'''])
def test_emscripten_api_infloop(self):
self.btest('emscripten_api_browser_infloop.cpp', '7')
@@ -10238,6 +10363,10 @@ elif 'browser' in str(sys.argv):
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_canvas_palette_2.c'), '-o', 'page.html', '--pre-js', 'pre.js']).communicate()
self.run_browser('page.html', '')
+ def test_glbegin_points(self):
+ shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png'))
+ self.btest('glbegin_points.c', reference='glbegin_points.png', args=['--preload-file', 'screenshot.png'])
+
def test_s3tc(self):
shutil.copyfile(path_from_root('tests', 'screenshot.dds'), os.path.join(self.get_dir(), 'screenshot.dds'))
self.btest('s3tc.c', reference='s3tc.png', args=['--preload-file', 'screenshot.dds'])
@@ -10499,7 +10628,7 @@ elif 'benchmark' in str(sys.argv):
JS_ENGINE = eval(arg)
sys.argv[i] = None
sys.argv = filter(lambda arg: arg is not None, sys.argv)
- print 'Benchmarking JS engine:', JS_ENGINE
+ print 'Benchmarking JS engine:', ' '.join(JS_ENGINE)
Building.COMPILER_TEST_OPTS = []
@@ -10553,7 +10682,8 @@ elif 'benchmark' in str(sys.argv):
try_delete(final_filename)
output = Popen([PYTHON, EMCC, filename, #'-O3',
- '-O2', '-s', 'INLINING_LIMIT=0', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0',# '-s', 'ASM_JS=1',
+ '-O2', '-s', 'INLINING_LIMIT=0', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0',
+ #'-s', 'ASM_JS=1', '-s', 'USE_MATH_IMUL=1',
'-s', 'TOTAL_MEMORY=128*1024*1024', '-s', 'FAST_MEMORY=10*1024*1024',
'-o', final_filename] + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate()
assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0]
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index d26d902f..12754bb2 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -428,12 +428,13 @@ function simplifyExpressionsPre(ast) {
if (stack[i] == 1) {
// we will replace ourselves with the non-zero side. Recursively process that node.
var result = jsonCompare(node[2], ZERO) ? node[3] : node[2], other;
- // Great, we can eliminate
- rerun = true;
- while (other = process(result, result[0], stack)) {
- result = other;
+ // replace node in-place
+ node.length = result.length;
+ for (var j = 0; j < result.length; j++) {
+ node[j] = result[j];
}
- return result;
+ rerun = true;
+ return process(result, result[0], stack);
} else if (stack[i] == -1) {
break; // Too bad, we can't
} else if (asm) {
@@ -1415,7 +1416,7 @@ function registerize(ast) {
// We also mark local variables - i.e., having a var definition
var localVars = {};
var hasSwitch = false; // we cannot optimize variables if there is a switch
- var hasReturnValue = false;
+ var returnType = null; // for asm
traverse(fun, function(node, type) {
if (type == 'var') {
node[1].forEach(function(defined) { localVars[defined[0]] = 1 });
@@ -1428,7 +1429,7 @@ function registerize(ast) {
} else if (type == 'switch') {
hasSwitch = true;
} else if (asm && type == 'return' && node[1]) {
- hasReturnValue = true;
+ returnType = detectAsmCoercion(node[1]);
}
});
vacuum(fun);
@@ -1607,11 +1608,13 @@ function registerize(ast) {
denormalizeAsm(fun, finalAsmData);
// Add a final return if one is missing. This is not strictly a register operation, but
// this pass traverses the entire AST anyhow so adding it here is efficient.
- if (hasReturnValue) {
+ if (returnType !== null) {
var stats = getStatements(fun);
var last = stats[stats.length-1];
if (last[0] != 'return') {
- stats.push(['return', ['num', 0]]);
+ var returnValue = ['num', 0];
+ if (returnType == ASM_DOUBLE) returnValue = ['unary-prefix', '+', returnValue];
+ stats.push(['return', returnValue]);
}
}
}
@@ -2148,6 +2151,32 @@ function eliminateMemSafe(ast) {
eliminate(ast, true);
}
+// Change +5 to DOT$ZERO(5). We then textually change 5 to 5.0 (uglify's ast cannot differentiate between 5 and 5.0 directly)
+function prepDotZero(ast) {
+ traverse(ast, function(node, type) {
+ if (type == 'unary-prefix' && node[1] == '+') {
+ if (node[2][0] == 'num') {
+ return ['call', ['name', 'DOT$ZERO'], [node[2]]];
+ } else if (node[2][0] == 'unary-prefix' && node[2][1] == '-' && node[2][2][0] == 'num') {
+ node[2][2][1] = -node[2][2][1];
+ return ['call', ['name', 'DOT$ZERO'], [node[2][2]]];
+ }
+ }
+ });
+}
+function fixDotZero(js) {
+ return js.replace(/DOT\$ZERO\(((0x)?[-+]?[0-9a-f]*\.?[0-9]+([eE][-+]?[0-9]+)?)\)/g, function(m, num) {
+ if (num.substr(0, 2) == '0x') {
+ if (num[2] == '-') num = '-0x' + num.substr(3); // uglify generates 0x-8000 for some reason
+ return eval(num) + '.0';
+ }
+ if (num.indexOf('.') >= 0) return num;
+ var e = num.indexOf('e');
+ if (e < 0) return num + '.0';
+ return num.substr(0, e) + '.0' + num.substr(e);
+ });
+}
+
// Passes table
var compress = false, printMetadata = true, asm = false, last = false;
@@ -2185,15 +2214,18 @@ if (metadata) setGeneratedFunctions(metadata);
arguments_.slice(1).forEach(function(arg) {
passes[arg](ast);
});
+if (asm && last) {
+ prepDotZero(ast);
+}
var js = astToSrc(ast, compress), old;
+if (asm && last) {
+ js = fixDotZero(js);
+}
// remove unneeded newlines+spaces, and print
do {
old = js;
js = js.replace(/\n *\n/g, '\n');
- if (asm && last) {
- js = js.replace(/ = \+0([,;])/g, function(m, end) { return ' = 0.0' + end }); // asm requires 0.0 in var definitions, not +0
- }
} while (js != old);
print(js);
print('\n');
diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py
index b72a2084..f2e610d0 100644
--- a/tools/js_optimizer.py
+++ b/tools/js_optimizer.py
@@ -18,6 +18,7 @@ DEBUG = os.environ.get('EMCC_DEBUG')
def run_on_chunk(command):
filename = command[2] # XXX hackish
+ #print >> sys.stderr, 'running js optimizer command', ' '.join(command), '""""', open(filename).read()
output = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0]
assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + output
filename = temp_files.get(os.path.basename(filename) + '.jo.js').name
diff --git a/tools/shared.py b/tools/shared.py
index a78db8e0..f94bb263 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -344,10 +344,12 @@ try:
except:
COMPILER_OPTS = []
# Force a simple, standard target as much as possible: target 32-bit linux, and disable various flags that hint at other platforms
+# -fno-ms-compatibility is passed, since on Windows, Clang enables a 'MS compatibility mode' by default, that disables char16_t and char32_t
+# to be MSVC header -compatible. This would cause build errors in libcxx file __config.
COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__x86_64__', '-U__i386', '-U__x86_64', '-Ui386', '-Ux86_64', '-U__SSE__', '-U__SSE2__', '-U__MMX__',
'-UX87_DOUBLE_ROUNDING', '-UHAVE_GCC_ASM_FOR_X87', '-DEMSCRIPTEN', '-U__STRICT_ANSI__', '-U__CYGWIN__',
- '-D__STDC__', '-Xclang', '-triple=i386-pc-linux-gnu', '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno']
-
+ '-D__STDC__', '-Xclang', '-triple=i386-pc-linux-gnu', '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno',
+ '-fno-ms-compatibility']
USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK')
diff --git a/tools/test-js-optimizer-asm-last-output.js b/tools/test-js-optimizer-asm-last-output.js
new file mode 100644
index 00000000..c10cc6b0
--- /dev/null
+++ b/tools/test-js-optimizer-asm-last-output.js
@@ -0,0 +1,35 @@
+function finall(x) {
+ x = +x;
+ var a = 5.0;
+ a = +x;
+ a = 17;
+ a = 44.0;
+ a = 44.0;
+ a = 44.9;
+ a = 1278.0e3;
+ a = 12.0e10;
+ a = -x;
+ a = -17;
+ a = -44;
+ a = -44;
+ a = -44.9;
+ a = -1278e3;
+ a = -12e10;
+ a = +-x;
+ a = -17.0;
+ a = -44.0;
+ a = -44.0;
+ a = -44.9;
+ a = -1278.0e3;
+ a = -12.0e10;
+ a = 9223372036854776000.0;
+ a = -9223372036854776000.0;
+ a = -9223372036854776000.0;
+ a = -0x8000000000000000;
+ a = 999999984306749400.0;
+ a = -999999984306749400.0;
+ a = -999999984306749400.0;
+ a = -0xde0b6b000000000;
+ return 12.0e10;
+}
+
diff --git a/tools/test-js-optimizer-asm-last.js b/tools/test-js-optimizer-asm-last.js
new file mode 100644
index 00000000..6e97b687
--- /dev/null
+++ b/tools/test-js-optimizer-asm-last.js
@@ -0,0 +1,35 @@
+function finall(x) {
+ x = +x;
+ var a = +5;
+ a = +x;
+ a = 17;
+ a = +44;
+ a = +44.0;
+ a = +44.9;
+ a = +12.78e5;
+ a = +12e10;
+ a = -x;
+ a = -17;
+ a = -44;
+ a = -44.0;
+ a = -44.9;
+ a = -12.78e5;
+ a = -12e10;
+ a = +-x;
+ a = +-17;
+ a = +-44;
+ a = +-44.0;
+ a = +-44.9;
+ a = +-12.78e5;
+ a = +-12e10;
+ a = +0x8000000000000000;
+ a = +-0x8000000000000000;
+ a = -+0x8000000000000000;
+ a = -0x8000000000000000;
+ a = +0xde0b6b000000000;
+ a = +-0xde0b6b000000000;
+ a = -+0xde0b6b000000000;
+ a = -0xde0b6b000000000;
+ return +12e10;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["finall"]
diff --git a/tools/test-js-optimizer-asm-pre-output.js b/tools/test-js-optimizer-asm-pre-output.js
index afd43893..84c95360 100644
--- a/tools/test-js-optimizer-asm-pre-output.js
+++ b/tools/test-js-optimizer-asm-pre-output.js
@@ -5,4 +5,48 @@ function a() {
f(351);
f(8);
}
+function b($this, $__n) {
+ $this = $this | 0;
+ $__n = $__n | 0;
+ var $4 = 0, $5 = 0, $10 = 0, $13 = 0, $14 = 0, $15 = 0, $23 = 0, $30 = 0, $38 = 0, $40 = 0;
+ if (($__n | 0) == 0) {
+ return;
+ }
+ $4 = $this;
+ $5 = HEAP8[$4 & 16777215] | 0;
+ if (($5 & 1) << 24 >> 24 == 0) {
+ $14 = 10;
+ $13 = $5;
+ } else {
+ $10 = HEAP32[($this & 16777215) >> 2] | 0;
+ $14 = ($10 & -2) - 1 | 0;
+ $13 = $10 & 255;
+ }
+ $15 = $13 & 255;
+ if (($15 & 1 | 0) == 0) {
+ $23 = $15 >>> 1;
+ } else {
+ $23 = HEAP32[($this + 4 & 16777215) >> 2] | 0;
+ }
+ if (($14 - $23 | 0) >>> 0 < $__n >>> 0) {
+ __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEjjjjjj($this, $14, ($__n - $14 | 0) + $23 | 0, $23, $23);
+ $30 = HEAP8[$4 & 16777215] | 0;
+ } else {
+ $30 = $13;
+ }
+ if (($30 & 1) << 24 >> 24 == 0) {
+ $38 = $this + 1 | 0;
+ } else {
+ $38 = HEAP32[($this + 8 & 16777215) >> 2] | 0;
+ }
+ _memset($38 + $23 | 0, 0, $__n | 0, 1, 1213141516);
+ $40 = $23 + $__n | 0;
+ if ((HEAP8[$4 & 16777215] & 1) << 24 >> 24 == 0) {
+ HEAP8[$4 & 16777215] = $40 << 1 & 255;
+ } else {
+ HEAP32[($this + 4 & 16777215) >> 2] = $40;
+ }
+ HEAP8[$38 + $40 & 16777215] = 0;
+ return;
+}
diff --git a/tools/test-js-optimizer-asm-pre.js b/tools/test-js-optimizer-asm-pre.js
index 6c9e64c1..3042ef66 100644
--- a/tools/test-js-optimizer-asm-pre.js
+++ b/tools/test-js-optimizer-asm-pre.js
@@ -5,4 +5,48 @@ function a() {
f(347 | 12);
f(347 & 12);
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a"]
+function b($this, $__n) {
+ $this = $this | 0;
+ $__n = $__n | 0;
+ var $4 = 0, $5 = 0, $10 = 0, $13 = 0, $14 = 0, $15 = 0, $23 = 0, $30 = 0, $38 = 0, $40 = 0;
+ if (($__n | 0) == 0) {
+ return;
+ }
+ $4 = $this;
+ $5 = HEAP8[$4 & 16777215] | 0;
+ if (($5 & 1) << 24 >> 24 == 0) {
+ $14 = 10;
+ $13 = $5;
+ } else {
+ $10 = HEAP32[(($this | 0) & 16777215) >> 2] | 0;
+ $14 = ($10 & -2) - 1 | 0;
+ $13 = $10 & 255;
+ }
+ $15 = $13 & 255;
+ if (($15 & 1 | 0) == 0) {
+ $23 = $15 >>> 1;
+ } else {
+ $23 = HEAP32[(($this + 4 | 0) & 16777215) >> 2] | 0;
+ }
+ if (($14 - $23 | 0) >>> 0 < $__n >>> 0) {
+ __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEjjjjjj($this, $14, ($__n - $14 | 0) + $23 | 0, $23, $23);
+ $30 = HEAP8[$4 & 16777215] | 0;
+ } else {
+ $30 = $13;
+ }
+ if (($30 & 1) << 24 >> 24 == 0) {
+ $38 = $this + 1 | 0;
+ } else {
+ $38 = HEAP32[(($this + 8 | 0) & 16777215) >> 2] | 0;
+ }
+ _memset($38 + $23 | 0 | 0 | 0, 0 | 0 | 0, $__n | 0 | 0, 1 | 0 | 0, 1213141516);
+ $40 = $23 + $__n | 0;
+ if (((HEAP8[$4 & 16777215] | 0) & 1) << 24 >> 24 == 0) {
+ HEAP8[$4 & 16777215] = $40 << 1 & 255;
+ } else {
+ HEAP32[(($this + 4 | 0) & 16777215) >> 2] = $40;
+ }
+ HEAP8[($38 + $40 | 0) & 16777215] = 0;
+ return;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b"]
diff --git a/tools/test-js-optimizer-asm-regs-output.js b/tools/test-js-optimizer-asm-regs-output.js
index 26d1d134..99bccd2e 100644
--- a/tools/test-js-optimizer-asm-regs-output.js
+++ b/tools/test-js-optimizer-asm-regs-output.js
@@ -31,4 +31,11 @@ function ret2t() {
return;
}
}
+function retf() {
+ if (f()) {
+ g();
+ return +h();
+ }
+ return +0;
+}
diff --git a/tools/test-js-optimizer-asm-regs.js b/tools/test-js-optimizer-asm-regs.js
index 9192f32e..0afced29 100644
--- a/tools/test-js-optimizer-asm-regs.js
+++ b/tools/test-js-optimizer-asm-regs.js
@@ -34,5 +34,12 @@ function ret2t() {
}
// missing final return, but no need
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "_doit", "rett", "ret2t"]
+function retf() {
+ if (f()) {
+ g();
+ return +h();
+ }
+ // missing final return, need it as a float
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "_doit", "rett", "ret2t", "retf"]