diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-01-24 12:02:42 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-01-24 12:02:42 -0800 |
commit | 64c779641a2a9587613cc65ad7251890f18e25c3 (patch) | |
tree | 87344a57db509e92b3196e5a4291a8c3bd8c2449 | |
parent | 4e09482e006eda934527e1707036d74245d8dd91 (diff) | |
parent | 03e2e6c321d28e3df3b37a2c0bed3ba9d04e52b3 (diff) |
Merge branch 'incoming'
40 files changed, 1429 insertions, 399 deletions
@@ -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 @@ + + 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") @@ -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 Binary files differnew file mode 100644 index 00000000..57e15d29 --- /dev/null +++ b/tests/glbegin_points.png diff --git a/tests/glbook/CH13_ParticleSystem.png b/tests/glbook/CH13_ParticleSystem.png Binary files differindex 39b9af0a..ff9c3496 100644 --- a/tests/glbook/CH13_ParticleSystem.png +++ b/tests/glbook/CH13_ParticleSystem.png 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"] |