From 9483f8b998df9c49a4122d163e0e2ef92bf7a1c1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 12 Mar 2013 17:14:07 -0700 Subject: [PATCH] infrastructure for implementing i64 math in asm.js, and implement i64Add and llvm_uadd_with_overflow_i64 that way --- emscripten.py | 4 ++++ src/jsifier.js | 44 ++++++++++++++++++++++++++++---------------- src/library.js | 42 +++++++++++++++++++++++++++++++++++------- src/long.js | 7 ------- src/parseTools.js | 17 +++++++++++------ 5 files changed, 78 insertions(+), 36 deletions(-) diff --git a/emscripten.py b/emscripten.py index bbc4f76dd..d7a24fbce 100755 --- a/emscripten.py +++ b/emscripten.py @@ -313,6 +313,10 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, last_forwarded_json = json.loads(last_forwarded_data) if settings.get('ASM_JS'): + post_funcs, post_rest = post.split('// EMSCRIPTEN_END_FUNCS\n') + post = post_rest + funcs_js += '\n' + post_funcs + '// EMSCRIPTEN_END_FUNCS\n' + simple = os.environ.get('EMCC_SIMPLE_ASM') class Counter: i = 0 diff --git a/src/jsifier.js b/src/jsifier.js index 3cb5c23ec..e7e4706ba 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -397,6 +397,20 @@ function JSify(data, functionsOnly, givenFunctions) { } }); + function processLibraryFunction(snippet, ident) { + snippet = snippet.toString(); + assert(snippet.indexOf('XXX missing C define') == -1, + 'Trying to include a library function with missing C defines: ' + ident + ' | ' + snippet); + + // 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.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}'; + } + return snippet; + } + // functionStub substrate.addActor('FunctionStub', { processItem: function(item) { @@ -434,16 +448,7 @@ function JSify(data, functionsOnly, givenFunctions) { snippet = stringifyWithFunctions(snippet); } else if (typeof snippet === 'function') { isFunction = true; - snippet = snippet.toString(); - assert(snippet.indexOf('XXX missing C define') == -1, - 'Trying to include a library function with missing C defines: ' + ident + ' | ' + snippet); - - // 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.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}'; - } + snippet = processLibraryFunction(snippet, ident); if (ASM_JS) Functions.libraryFunctions[ident] = 1; } @@ -1563,16 +1568,23 @@ function JSify(data, functionsOnly, givenFunctions) { // This is the main 'post' pass. Print out the generated code that we have here, together with the // rest of the output that we started to print out earlier (see comment on the // "Final shape that will be created"). + if (PRECISE_I64_MATH && Types.preciseI64MathUsed) { + ['i64Add'].forEach(function(func) { + print(processLibraryFunction(LibraryManager.library[func], func)); // must be first to be close to generated code + Functions.implementedFunctions['_' + func] = LibraryManager.library[func + '__sig']; + }); + print('// EMSCRIPTEN_END_FUNCS\n'); + print(read('long.js')); + } else { + print('// EMSCRIPTEN_END_FUNCS\n'); + print('// Warning: printing of i64 values may be slightly rounded! No deep i64 math used, so precise i64 code not included'); + print('var i64Math = null;'); + } + if (CORRUPTION_CHECK) { assert(!ASM_JS); // cannot monkeypatch asm! print(processMacros(read('corruptionCheck.js'))); } - if (PRECISE_I64_MATH && Types.preciseI64MathUsed) { - print(read('long.js')); - } else { - print('// Warning: printing of i64 values may be slightly rounded! No deep i64 math used, so precise i64 code not included'); - print('var i64Math = null;'); - } if (HEADLESS) { print('if (!ENVIRONMENT_IS_WEB) {'); print(read('headless.js').replace("'%s'", "'http://emscripten.org'").replace("'?%s'", "''").replace('%s,', 'null,').replace('%d', '0')); diff --git a/src/library.js b/src/library.js index 12e550ff4..0f8b0af52 100644 --- a/src/library.js +++ b/src/library.js @@ -5206,13 +5206,6 @@ LibraryManager.library = { {{{ makeStructuralReturn(['(x*y)>>>0', 'x*y > 4294967295']) }}}; }, - llvm_uadd_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }], - llvm_uadd_with_overflow_i64: function(xl, xh, yl, yh) { - i64Math.add(xl, xh, yl, yh); - {{{ makeStructuralReturn([makeGetTempDouble(0, 'i32'), makeGetTempDouble(1, 'i32'), '0']) }}}; - // XXX Need to hack support for second param in long.js - }, - llvm_umul_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }], llvm_umul_with_overflow_i64: function(xl, xh, yl, yh) { i64Math.multiply(xl, xh, yl, yh); @@ -7352,6 +7345,41 @@ LibraryManager.library = { Module.print(intArrayToString(__formatString(_emscripten_jcache_printf_.buffer, varargs + i*4)).replace('\\n', '')); Runtime.stackAlloc(-4*i); // free up the stack space we know is ok to free }, + + //============================ + // i64 math + //============================ + + i64Add__asm: 'true', + i64Add__sig: 'iiiii', + i64Add: function(a, b, c, d) { + /* + x = a + b*2^32 + y = c + d*2^32 + result = l + h*2^32 + */ + a = a|0; b = b|0; c = c|0; d = d|0; + var l = 0, h = 0; + l = (a + c)>>>0; + h = (b + d)>>>0; + if ((l>>>0) < (a>>>0)) { // iff we overflowed + h = (h+1)>>>0; + } + {{{ makeStructuralReturn(['l|0', 'h'], true) }}}; + }, + llvm_uadd_with_overflow_i64__asm: 'true', + llvm_uadd_with_overflow_i64__sig: 'iiiii', + llvm_uadd_with_overflow_i64: function(a, b, c, d) { + a = a|0; b = b|0; c = c|0; d = d|0; + var l = 0, h = 0, overflow = 0; + l = (a + c)>>>0; + h = (b + d)>>>0; + if ((l>>>0) < (a>>>0)) { // iff we overflowed + h = (h+1)>>>0; + overflow = 1; + } + {{{ makeStructuralReturn(['l|0', 'h', 'overflow'], true) }}}; + }, }; function autoAddDeps(object, name) { diff --git a/src/long.js b/src/long.js index c3651bd9c..6d0e873d9 100644 --- a/src/long.js +++ b/src/long.js @@ -1530,13 +1530,6 @@ var i64Math = (function() { // Emscripten wrapper // Emscripten wrapper var Wrapper = { - add: function(xl, xh, yl, yh) { - var x = new goog.math.Long(xl, xh); - var y = new goog.math.Long(yl, yh); - var ret = x.add(y); - HEAP32[tempDoublePtr>>2] = ret.low_; - HEAP32[tempDoublePtr+4>>2] = ret.high_; - }, subtract: function(xl, xh, yl, yh) { var x = new goog.math.Long(xl, xh); var y = new goog.math.Long(yl, yh); diff --git a/src/parseTools.js b/src/parseTools.js index 48274cd55..9a91070cf 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1724,13 +1724,14 @@ function makeLLVMStruct(values) { } } -function makeStructuralReturn(values) { +function makeStructuralReturn(values, inAsm) { if (USE_TYPED_ARRAYS == 2) { - var i = 0; - return 'return (' + values.slice(1).map(function(value) { - return ASM_JS ? 'asm.setTempRet' + (i++) + '(' + value + ')' + var i = -1; + return 'return ' + asmCoercion(values.slice(1).map(function(value) { + i++; + return ASM_JS ? (inAsm ? 'tempRet' + i + ' = ' + value : 'asm.setTempRet' + i + '(' + value + ')') : 'tempRet' + (i++) + ' = ' + value; - }).concat([values[0]]).join(',') + ')'; + }).concat([values[0]]).join(','), 'i32'); } else { var i = 0; return 'return { ' + values.map(function(value) { @@ -1971,6 +1972,10 @@ function processMathop(item) { return finish(['(i64Math' + (ASM_JS ? '_' : '.') + type + '(' + asmCoercion(low1, 'i32') + ',' + asmCoercion(high1, 'i32') + ',' + asmCoercion(low2, 'i32') + ',' + asmCoercion(high2, 'i32') + (lastArg ? ',' + asmCoercion(+lastArg, 'i32') : '') + '),' + makeGetValue('tempDoublePtr', 0, 'i32') + ')', makeGetValue('tempDoublePtr', Runtime.getNativeTypeSize('i32'), 'i32')]); } + function i64PreciseLib(type) { + Types.preciseI64MathUsed = true; + return finish(['_i64' + type[0].toUpperCase() + type.substr(1) + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 + ')', 'tempRet0']); + } switch (op) { // basic integer ops case 'or': { @@ -2059,7 +2064,7 @@ function processMathop(item) { // Dangerous, rounded operations. TODO: Fully emulate case 'add': { if (PRECISE_I64_MATH) { - return i64PreciseOp('add'); + return i64PreciseLib('add'); } else { warnI64_1(); return finish(splitI64(mergeI64(idents[0]) + '+' + mergeI64(idents[1]), true));