diff options
-rwxr-xr-x | emscripten.py | 26 | ||||
-rw-r--r-- | src/jsifier.js | 31 | ||||
-rw-r--r-- | src/library.js | 41 | ||||
-rw-r--r-- | src/library_gl.js | 80 | ||||
-rw-r--r-- | src/modules.js | 48 | ||||
-rw-r--r-- | src/parseTools.js | 93 | ||||
-rw-r--r-- | src/preamble.js | 17 | ||||
-rw-r--r-- | src/runtime.js | 4 | ||||
-rw-r--r-- | src/settings.js | 8 | ||||
-rw-r--r-- | src/utility.js | 3 | ||||
-rw-r--r-- | system/include/emscripten/emscripten.h | 2 | ||||
-rw-r--r-- | tests/cases/storebigfloat.ll | 17 | ||||
-rw-r--r-- | tests/emscripten_get_now.cpp | 6 | ||||
-rw-r--r-- | tests/lua/Makefile | 3 | ||||
-rwxr-xr-x | tests/runner.py | 2 | ||||
-rw-r--r-- | tests/test_benchmark.py | 14 | ||||
-rw-r--r-- | tests/test_core.py | 46 | ||||
-rw-r--r-- | tools/eliminator/eliminator-test-output.js | 3 | ||||
-rw-r--r-- | tools/eliminator/eliminator-test.js | 10 | ||||
-rw-r--r-- | tools/js-optimizer.js | 142 | ||||
-rw-r--r-- | tools/shared.py | 22 |
21 files changed, 409 insertions, 209 deletions
diff --git a/emscripten.py b/emscripten.py index 5576baba..7c9cbdbe 100755 --- a/emscripten.py +++ b/emscripten.py @@ -11,6 +11,7 @@ headers, for the libc implementation in JS). import os, sys, json, optparse, subprocess, re, time, multiprocessing, string, logging +from tools import shared from tools import jsrun, cache as cache_module, tempfiles from tools.response_file import read_response_file @@ -25,7 +26,6 @@ def get_configuration(): if hasattr(get_configuration, 'configuration'): return get_configuration.configuration - from tools import shared configuration = shared.Configuration(environ=os.environ) get_configuration.configuration = configuration return configuration @@ -425,8 +425,8 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, 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] != '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 '')) + coercions = ';'.join(['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';' + ret = '' if sig[0] == 'v' else ('return %s' % shared.JS.make_initializer(sig[0], settings)) start = raw.index('[') end = raw.rindex(']') body = raw[start+1:end].split(',') @@ -451,7 +451,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, math_envs = ['Math.min'] # TODO: move min to maths asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs]) - if settings['TO_FLOAT32']: maths += ['Math.toFloat32'] + if settings['PRECISE_F32']: maths += ['Math.fround'] basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs] if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall') @@ -476,18 +476,14 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, 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) + '=' + 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]) + arg_coercions = ' '.join(['a' + str(i) + '=' + shared.JS.make_coercion('a' + str(i), sig[i], settings) + ';' for i in range(1, len(sig))]) + coerced_args = ','.join([shared.JS.make_coercion('a' + str(i), sig[i], settings) for i in range(1, len(sig))]) + ret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('FUNCTION_TABLE_%s[index&{{{ FTM_%s }}}](%s)' % (sig, sig, coerced_args), sig[0], settings) function_tables_impls.append(''' function dynCall_%s(index%s%s) { index = index|0; @@ -497,7 +493,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, ''' % (sig, ',' if len(sig) > 1 else '', args, arg_coercions, ret)) for i in range(settings['RESERVED_FUNCTION_POINTERS']): - jsret = ('return ' if sig[0] != 'v' else '') + asm_coerce('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0]) + jsret = ('return ' if sig[0] != 'v' else '') + shared.JS.make_coercion('jsCall(%d%s%s)' % (i, ',' if coerced_args else '', coerced_args), sig[0], settings) function_tables_impls.append(''' function jsCall_%s_%s(%s) { %s @@ -505,7 +501,6 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, } ''' % (sig, i, args, arg_coercions, jsret)) - from tools import shared shared.Settings.copy(settings) asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n' basic_funcs.append('invoke_%s' % sig) @@ -585,7 +580,7 @@ var asm = (function(global, env, buffer) { var undef = 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 + ''' + var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs] + [' var tempFloat = %s;\n' % ('Math_fround(0)' if settings.get('PRECISE_F32') else '0.0')] + [''' // EMSCRIPTEN_START_FUNCS function stackAlloc(size) { size = size|0; @@ -727,14 +722,12 @@ def main(args, compiler_engine, cache, jcache, relooper, temp_files, DEBUG, DEBU relooper = cache.get_path('relooper.js') settings.setdefault('RELOOPER', relooper) if not os.path.exists(relooper): - from tools import shared shared.Building.ensure_relooper(relooper) settings.setdefault('STRUCT_INFO', cache.get_path('struct_info.compiled.json')) struct_info = settings.get('STRUCT_INFO') if not os.path.exists(struct_info): - from tools import shared shared.Building.ensure_struct_info(struct_info) emscript(args.infile, settings, args.outfile, libraries, compiler_engine=compiler_engine, @@ -833,7 +826,6 @@ WARNING: You should normally never use this! Use emcc instead. temp_files = tempfiles.TempFiles(temp_dir) if keywords.compiler is None: - from tools import shared keywords.compiler = shared.COMPILER_ENGINE if keywords.verbose is None: diff --git a/src/jsifier.js b/src/jsifier.js index 0da48a8c..97317756 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -756,14 +756,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (func.setjmpTable && !ASM_JS) { ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }'; } - if (ASM_JS && func.returnType !== 'void') { - // Add a return - if (func.returnType in Runtime.FLOAT_TYPES) { - ret += ' return +0;\n'; - } else { - ret += ' return 0;\n'; - } - } + if (ASM_JS && func.returnType !== 'void') ret += ' return ' + asmInitializer(func.returnType) + ';\n'; // Add a return } else { ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0]); } @@ -833,11 +826,7 @@ function JSify(data, functionsOnly, givenFunctions) { var lastReturn = func.JS.lastIndexOf('return '); if ((lastCurly < 0 && lastReturn < 0) || // no control flow, no return (lastCurly >= 0 && lastReturn < lastCurly)) { // control flow, no return past last join - if (func.returnType in Runtime.FLOAT_TYPES) { - func.JS += ' return +0;\n'; - } else { - func.JS += ' return 0;\n'; - } + func.JS += ' return ' + asmInitializer(func.returnType) + ';\n'; } } func.JS += '}\n'; @@ -1337,7 +1326,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (isNumber(item.ident)) { // Direct read from a memory address; this may be an intentional segfault, if not, it is a bug in the source if (ASM_JS) { - return asmCoercion('abort(' + item.ident + ')', item.type); + return asmFFICoercion('abort(' + item.ident + ')', item.type); } else { item.assignTo = null; return 'throw "fault on read from ' + item.ident + '";'; @@ -1514,8 +1503,10 @@ function JSify(data, functionsOnly, givenFunctions) { args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) }); if (ASM_JS) { - if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable) { - args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) }); + var ffiCall = (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable) && + !(simpleIdent in JS_MATH_BUILTINS); + if (ffiCall) { + args = args.map(function(arg, i) { return asmCoercion(arg, ensureValidFFIType(argsTypes[i])) }); } else { args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) }); } @@ -1592,7 +1583,7 @@ function JSify(data, functionsOnly, givenFunctions) { returnType = getReturnType(type); if (callIdent in Functions.implementedFunctions) { // LLVM sometimes bitcasts for no reason. We must call using the exact same type as the actual function is generated as - var trueType = Functions.getSignatureReturnType(Functions.implementedFunctions[callIdent]); + var trueType = Functions.getSignatureType(Functions.implementedFunctions[callIdent][0]); if (trueType !== returnType && !isIdenticallyImplemented(trueType, returnType)) { if (VERBOSE) warnOnce('Fixing function call based on return type from signature, on ' + [callIdent, returnType, trueType]); returnType = trueType; @@ -1628,7 +1619,11 @@ function JSify(data, functionsOnly, givenFunctions) { var ret = callIdent + '(' + args.join(',') + ')'; if (ASM_JS) { // TODO: do only when needed (library functions and Math.*?) XXX && simpleIdent in Functions.libraryFunctions) { - ret = asmCoercion(ret, returnType); + if (ffiCall) { + ret = asmFFICoercion(ret, returnType); + } else { + ret = asmCoercion(ret, returnType); + } if (simpleIdent == 'abort' && funcData.returnType != 'void') { ret += '; return ' + asmCoercion('0', funcData.returnType); // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return } diff --git a/src/library.js b/src/library.js index 31f531e9..7a954662 100644 --- a/src/library.js +++ b/src/library.js @@ -847,10 +847,7 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.ERANGE); return 0; } else { - for (var i = 0; i < cwd.length; i++) { - {{{ makeSetValue('buf', 'i', 'cwd.charCodeAt(i)', 'i8') }}} - } - {{{ makeSetValue('buf', 'i', '0', 'i8') }}} + writeAsciiToMemory(cwd, buf); return buf; } }, @@ -1293,10 +1290,7 @@ LibraryManager.library = { if (namesize < ret.length + 1) { return ___setErrNo(ERRNO_CODES.ERANGE); } else { - for (var i = 0; i < ret.length; i++) { - {{{ makeSetValue('name', 'i', 'ret.charCodeAt(i)', 'i8') }}} - } - {{{ makeSetValue('name', 'i', '0', 'i8') }}} + writeAsciiToMemory(ret, name); return 0; } }, @@ -2699,10 +2693,7 @@ LibraryManager.library = { var result = dir + '/' + name; if (!_tmpnam.buffer) _tmpnam.buffer = _malloc(256); if (!s) s = _tmpnam.buffer; - for (var i = 0; i < result.length; i++) { - {{{ makeSetValue('s', 'i', 'result.charCodeAt(i)', 'i8') }}}; - } - {{{ makeSetValue('s', 'i', '0', 'i8') }}}; + writeAsciiToMemory(result, s); return s; }, tempnam__deps: ['tmpnam'], @@ -3343,10 +3334,7 @@ LibraryManager.library = { var ptrSize = {{{ Runtime.getNativeTypeSize('i8*') }}}; for (var i = 0; i < strings.length; i++) { var line = strings[i]; - for (var j = 0; j < line.length; j++) { - {{{ makeSetValue('poolPtr', 'j', 'line.charCodeAt(j)', 'i8') }}}; - } - {{{ makeSetValue('poolPtr', 'j', '0', 'i8') }}}; + writeAsciiToMemory(line, poolPtr); {{{ makeSetValue('envPtr', 'i * ptrSize', 'poolPtr', 'i8*') }}}; poolPtr += line.length + 1; } @@ -3976,10 +3964,7 @@ LibraryManager.library = { return ___setErrNo(ERRNO_CODES.ERANGE); } else { var msg = ERRNO_MESSAGES[errnum]; - for (var i = 0; i < msg.length; i++) { - {{{ makeSetValue('strerrbuf', 'i', 'msg.charCodeAt(i)', 'i8') }}} - } - {{{ makeSetValue('strerrbuf', 'i', 0, 'i8') }}} + writeAsciiToMemory(msg, strerrbuf); return 0; } } else { @@ -5067,10 +5052,7 @@ LibraryManager.library = { var layout = {{{ JSON.stringify(C_STRUCTS.utsname) }}}; function copyString(element, value) { var offset = layout[element]; - for (var i = 0; i < value.length; i++) { - {{{ makeSetValue('name', 'offset + i', 'value.charCodeAt(i)', 'i8') }}} - } - {{{ makeSetValue('name', 'offset + i', '0', 'i8') }}} + writeAsciiToMemory(value, name + offset); } if (name === 0) { return -1; @@ -6131,8 +6113,10 @@ LibraryManager.library = { // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); var seconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_sec, 'i32') }}}; var nanoseconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_nsec, 'i32') }}}; - {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}} - {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}} + if (rmtp !== 0) { + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}} + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}} + } return _usleep((seconds * 1e6) + (nanoseconds / 1000)); }, // TODO: Implement these for real. @@ -6572,10 +6556,7 @@ LibraryManager.library = { var me = _nl_langinfo; if (!me.ret) me.ret = _malloc(32); - for (var i = 0; i < result.length; i++) { - {{{ makeSetValue('me.ret', 'i', 'result.charCodeAt(i)', 'i8') }}} - } - {{{ makeSetValue('me.ret', 'i', '0', 'i8') }}} + writeAsciiToMemory(result, me.ret); return me.ret; }, diff --git a/src/library_gl.js b/src/library_gl.js index ecb72f0f..4b6ea579 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -530,7 +530,11 @@ var LibraryGL = { ret = allocate(intArrayFromString('OpenGL ES GLSL 1.00 (WebGL)'), 'i8', ALLOC_NORMAL); break; default: - throw 'Failure: Invalid glGetString value: ' + name_; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetString: Unknown parameter ' + name_ + '!'); +#endif + return 0; } GL.stringCache[name_] = ret; return ret; @@ -561,7 +565,11 @@ var LibraryGL = { {{{ makeSetValue('p', '0', 'result ? 1 : 0', 'i8') }}}; break; case "string": - throw 'Native code calling glGetIntegerv(' + name_ + ') on a name which returns a string!'; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') on a name which returns a string!'); +#endif + return; case "object": if (result === null) { {{{ makeSetValue('p', '0', '0', 'i32') }}}; @@ -583,13 +591,19 @@ var LibraryGL = { } else if (result instanceof WebGLTexture) { {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}}; } else { - throw 'Unknown object returned from WebGL getParameter'; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Unknown object returned from WebGL getParameter(' + name_ + ')!'); +#endif + return; } break; - case "undefined": - throw 'Native code calling glGetIntegerv(' + name_ + ') and it returns undefined'; default: - throw 'Why did we hit the default case?'; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); +#endif + return; } }, @@ -607,7 +621,11 @@ var LibraryGL = { {{{ makeSetValue('p', '0', '0', 'float') }}}; case "object": if (result === null) { - throw 'Native code calling glGetFloatv(' + name_ + ') and it returns null'; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns null!'); +#endif + return; } else if (result instanceof Float32Array || result instanceof Uint32Array || result instanceof Int32Array || @@ -626,13 +644,19 @@ var LibraryGL = { } else if (result instanceof WebGLTexture) { {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}}; } else { - throw 'Unknown object returned from WebGL getParameter'; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); +#endif + return; } break; - case "undefined": - throw 'Native code calling glGetFloatv(' + name_ + ') and it returns undefined'; default: - throw 'Why did we hit the default case?'; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); +#endif + return; } }, @@ -647,7 +671,11 @@ var LibraryGL = { {{{ makeSetValue('p', '0', 'result != 0', 'i8') }}}; break; case "string": - throw 'Native code calling glGetBooleanv(' + name_ + ') on a name which returns a string!'; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') on a name which returns a string!'); +#endif + return; case "object": if (result === null) { {{{ makeSetValue('p', '0', '0', 'i8') }}}; @@ -665,13 +693,19 @@ var LibraryGL = { result instanceof WebGLTexture) { {{{ makeSetValue('p', '0', '1', 'i8') }}}; // non-zero ID is always 1! } else { - throw 'Unknown object returned from WebGL getParameter'; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Unknown object returned from WebGL getParameter(' + name_ + ')!'); +#endif + return; } break; - case "undefined": - throw 'Unknown object returned from WebGL getParameter'; default: - throw 'Why did we hit the default case?'; + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); +#endif + return; } }, @@ -759,7 +793,12 @@ var LibraryGL = { case 0x1908 /* GL_RGBA */: sizePerPixel = 4; break; - default: throw 'unsupported glReadPixels format'; + default: + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glReadPixels: Unsupported format ' + format + '!'); +#endif + return; } var totalSize = width*height*sizePerPixel; Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels, pixels + totalSize)); @@ -2191,7 +2230,12 @@ var LibraryGL = { attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break; case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture]; break; - default: throw 'TODO: glGetPointerv for ' + name; + default: + GL.recordError(0x0500/*GL_INVALID_ENUM*/); +#if GL_ASSERTIONS + Module.printErr('GL_INVALID_ENUM in glGetPointerv: Unsupported name ' + name + '!'); +#endif + return; } {{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}}; }, diff --git a/src/modules.js b/src/modules.js index 854575e0..13cca977 100644 --- a/src/modules.js +++ b/src/modules.js @@ -18,7 +18,7 @@ var LLVM = { PHI_REACHERS: set('branch', 'switch', 'invoke', 'indirectbr'), EXTENDS: set('sext', 'zext'), COMPS: set('icmp', 'fcmp'), - CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui'), + CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui', 'fpext', 'fptrunc'), 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'])); @@ -253,13 +253,32 @@ var Functions = { aliases: {}, // in shared modules (MAIN_MODULE or SHARED_MODULE), a list of aliases for functions that have them + getSignatureLetter: function(type) { + switch(type) { + case 'float': return 'f'; + case 'double': return 'd'; + case 'void': return 'v'; + default: return 'i'; + } + }, + + getSignatureType: function(letter) { + switch(letter) { + case 'v': return 'void'; + case 'i': return 'i32'; + case 'f': return 'float'; + case 'd': return 'double'; + default: throw 'what is this sig? ' + sig; + } + }, + getSignature: function(returnType, argTypes, hasVarArgs) { - var sig = returnType == 'void' ? 'v' : (isIntImplemented(returnType) ? 'i' : 'f'); + var sig = Functions.getSignatureLetter(returnType); for (var i = 0; i < argTypes.length; i++) { var type = argTypes[i]; if (!type) break; // varargs if (type in Runtime.FLOAT_TYPES) { - sig += 'f'; + sig += Functions.getSignatureLetter(type); } else { var chunks = getNumIntChunks(type); for (var j = 0; j < chunks; j++) sig += 'i'; @@ -269,15 +288,6 @@ var Functions = { return sig; }, - getSignatureReturnType: function(sig) { - switch(sig[0]) { - case 'v': return 'void'; - case 'i': return 'i32'; - case 'f': return 'double'; - default: throw 'what is this sig? ' + sig; - } - }, - // Mark a function as needing indexing. Python will coordinate them all getIndex: function(ident, sig) { var ret; @@ -350,17 +360,15 @@ var Functions = { if (!wrapped[curr]) { var args = '', arg_coercions = '', call = short + '(', retPre = '', retPost = ''; if (t[0] != 'v') { - if (t[0] == 'i') { - retPre = 'return '; - retPost = '|0'; - } else { - retPre = 'return +'; - } + var temp = asmFFICoercion('X', Functions.getSignatureType(t[0])).split('X'); + retPre = 'return ' + temp[0]; + retPost = temp[1]; } 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'); + var type = Functions.getSignatureType(t[j]); + arg_coercions += 'a' + j + '=' + asmCoercion('a' + j, type) + ';'; + call += (j > 1 ? ',' : '') + asmCoercion('a' + j, type === 'float' ? 'double' : type); // ffi arguments must be doubles if they are floats } call += ')'; if (short == '_setjmp') printErr('WARNING: setjmp used via a function pointer. If this is for libc setjmp (not something of your own with the same name), it will break things'); diff --git a/src/parseTools.js b/src/parseTools.js index 52727903..3c2eeece 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -629,6 +629,8 @@ function cleanSegment(segment) { var MATHOPS = set(['add', 'sub', 'sdiv', 'udiv', 'mul', 'icmp', 'zext', 'urem', 'srem', 'fadd', 'fsub', 'fmul', 'fdiv', 'fcmp', 'frem', 'uitofp', 'sitofp', 'fpext', 'fptrunc', 'fptoui', 'fptosi', 'trunc', 'sext', 'select', 'shl', 'shr', 'ashl', 'ashr', 'lshr', 'lshl', 'xor', 'or', 'and', 'ptrtoint', 'inttoptr']); +var JS_MATH_BUILTINS = set(['Math_sin', 'Math_cos', 'Math_tan', 'Math_asin', 'Math_acos', 'Math_atan', 'Math_ceil', 'Math_floor', 'Math_exp', 'Math_log', 'Math_sqrt']); + var PARSABLE_LLVM_FUNCTIONS = set('getelementptr', 'bitcast'); mergeInto(PARSABLE_LLVM_FUNCTIONS, MATHOPS); @@ -788,8 +790,8 @@ function splitI64(value, floatConversion) { var high = makeInlineCalculation( asmCoercion('Math_abs(VALUE)', 'double') + ' >= ' + asmEnsureFloat('1', 'double') + ' ? ' + '(VALUE > ' + asmEnsureFloat('0', 'double') + ' ? ' + - asmCoercion('Math_min(' + asmCoercion('Math_floor((VALUE)/' + asmEnsureFloat(4294967296, 'float') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'float') + ')', 'i32') + '>>>0' + - ' : ' + asmFloatToInt(asmCoercion('Math_ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'float') + ')', 'double')) + '>>>0' + + asmCoercion('Math_min(' + asmCoercion('Math_floor((VALUE)/' + asmEnsureFloat(4294967296, 'double') + ')', 'double') + ', ' + asmEnsureFloat(4294967295, 'double') + ')', 'i32') + '>>>0' + + ' : ' + asmFloatToInt(asmCoercion('Math_ceil((VALUE - +((' + asmFloatToInt('VALUE') + ')>>>0))/' + asmEnsureFloat(4294967296, 'double') + ')', 'double')) + '>>>0' + ')' + ' : 0', value, @@ -1167,32 +1169,37 @@ function makeVarDef(js) { return js; } +function ensureDot(value) { + value = value.toString(); + // if already dotted, or Infinity or NaN, nothing to do here + // if smaller than 1 and running js opts, we always need to force a coercion (0.001 will turn into 1e-3, which has no .) + if ((value.indexOf('.') >= 0 || /[IN]/.test(value)) && (!RUNNING_JS_OPTS || Math.abs(value) >= 1)) return value; + if (RUNNING_JS_OPTS) return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later + var e = value.indexOf('e'); + if (e < 0) return value + '.0'; + return value.substr(0, e) + '.0' + value.substr(e); +} + 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; - // 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)) { - if (RUNNING_JS_OPTS) { - return '(+' + value + ')'; // JS optimizer will run, we must do +x, and it will be corrected later - } else { - // ensure a . - value = value.toString(); - if (value.indexOf('.') >= 0 || /[IN]/.test(value)) return value; // if already dotted, or Infinity or NaN, nothing to do here - var e = value.indexOf('e'); - if (e < 0) return value + '.0'; - return value.substr(0, e) + '.0' + value.substr(e); - } + if (!isNumber(value)) return value; + if (PRECISE_F32 && type === 'float') { + // normally ok to just emit Math_fround(0), but if the constant is large we may need a .0 (if it can't fit in an int) + if (value == 0) return 'Math_fround(0)'; + value = ensureDot(value); + return 'Math_fround(' + value + ')'; + } + if (type in Runtime.FLOAT_TYPES) { + return ensureDot(value); } else { return value; } } -function asmInitializer(type, impl) { +function asmInitializer(type) { if (type in Runtime.FLOAT_TYPES) { - if (RUNNING_JS_OPTS) { - return '+0'; - } else { - return '.0'; - } + if (PRECISE_F32 && type === 'float') return 'Math_fround(0)'; + return RUNNING_JS_OPTS ? '+0' : '.0'; } else { return '0'; } @@ -1213,7 +1220,11 @@ function asmCoercion(value, type, signedness) { value = '(' + value + ')|0'; } } - return '(+(' + value + '))'; + if (PRECISE_F32 && type === 'float') { + return 'Math_fround(' + value + ')'; + } else { + return '(+(' + value + '))'; + } } } else { return '((' + value + ')|0)'; @@ -2129,14 +2140,14 @@ function makeRounding(value, bits, signed, floatConversion) { } } -function makeIsNaN(value) { - if (ASM_JS) return makeInlineCalculation('((VALUE) != (VALUE))', value, 'tempDouble'); +function makeIsNaN(value, type) { + if (ASM_JS) return makeInlineCalculation('((VALUE) != (VALUE))', value, type === 'float' ? 'tempFloat' : 'tempDouble'); return 'isNaN(' + value + ')'; } function makeFloat(value, type) { - if (TO_FLOAT32 && type == 'float') { - return 'Math_toFloat32(' + value + ')'; + if (PRECISE_F32 && type == 'float') { + return 'Math_fround(' + value + ')'; } return value; } @@ -2253,8 +2264,8 @@ function processMathop(item) { case 'lshr': { throw 'shifts should have been legalized!'; } - case 'uitofp': case 'sitofp': return RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u'); - case 'fptoui': case 'fptosi': return finish(splitI64(idents[0], true)); + case 'uitofp': case 'sitofp': return makeFloat(RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u'), item.type); + case 'fptoui': case 'fptosi': return finish(splitI64(asmCoercion(idents[0], 'double'), true)); // coerce to double before conversion to i64 case 'icmp': { switch (variant) { case 'uge': return '((' + high1 + '>>>0) >= (' + high2 + '>>>0)) & ((((' + high1 + '>>>0) > (' + high2 + '>>>0)) | ' + @@ -2438,7 +2449,7 @@ function processMathop(item) { case 'fdiv': return makeFloat(getFastValue(idents[0], '/', idents[1], item.type), item.type); case 'fmul': return makeFloat(getFastValue(idents[0], '*', idents[1], item.type), item.type); case 'frem': return makeFloat(getFastValue(idents[0], '%', idents[1], item.type), item.type); - case 'uitofp': case 'sitofp': return asmCoercion(idents[0], 'double', op[0]); + case 'uitofp': case 'sitofp': return asmCoercion(idents[0], item.type, op[0]); case 'fptoui': case 'fptosi': return makeRounding(idents[0], bitsLeft, op === 'fptosi', true); // TODO: We sometimes generate false instead of 0, etc., in the *cmps. It seemed slightly faster before, but worth rechecking @@ -2470,8 +2481,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 '!' + makeIsNaN(idents[0]) + '&!' + makeIsNaN(idents[1]); - case 'uno': return makeIsNaN(idents[0]) + '|' + makeIsNaN(idents[1]); + case 'ord': return '!' + makeIsNaN(idents[0], paramTypes[0]) + '&!' + makeIsNaN(idents[1], paramTypes[0]); + case 'uno': return makeIsNaN(idents[0], paramTypes[0]) + '|' + makeIsNaN(idents[1], paramTypes[0]); case 'true': return '1'; default: throw 'Unknown fcmp variant: ' + variant; } @@ -2485,8 +2496,15 @@ function processMathop(item) { } // otherwise, fall through } - case 'fpext': case 'sext': return idents[0]; - case 'fptrunc': return idents[0]; + case 'sext': return idents[0]; + case 'fpext': { + if (PRECISE_F32) return '+(' + idents[0] + ')'; + return idents[0]; + } + case 'fptrunc': { + if (PRECISE_F32) return 'Math_fround(' + idents[0] + ')'; + return idents[0]; + } case 'select': return '(' + idents[0] + '?' + asmEnsureFloat(idents[1], item.type) + ':' + asmEnsureFloat(idents[2], item.type) + ')'; case 'ptrtoint': case 'inttoptr': { var ret = ''; @@ -2677,3 +2695,14 @@ function ensureVector(ident, base) { return ident == 0 ? base + '32x4.zero()' : ident; } +function ensureValidFFIType(type) { + return type === 'float' ? 'double' : type; // ffi does not tolerate float XXX +} + +// FFI return values must arrive as doubles, and we can force them to floats afterwards +function asmFFICoercion(value, type) { + value = asmCoercion(value, ensureValidFFIType(type)); + if (PRECISE_F32 && type === 'float') value = asmCoercion(value, 'float'); + return value; +} + diff --git a/src/preamble.js b/src/preamble.js index c88e4052..3e76e503 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -1074,11 +1074,16 @@ Math['imul'] = function imul(a, b) { #end |