diff options
-rwxr-xr-x | emcc | 50 | ||||
-rw-r--r-- | src/analyzer.js | 6 | ||||
-rw-r--r-- | src/jsifier.js | 2 | ||||
-rw-r--r-- | src/library.js | 11 | ||||
-rw-r--r-- | src/parseTools.js | 123 | ||||
-rw-r--r-- | src/preamble.js | 35 | ||||
-rw-r--r-- | src/runtime.js | 10 | ||||
-rw-r--r-- | src/settings.js | 7 | ||||
-rw-r--r-- | system/lib/debugging.cpp | 22 | ||||
-rw-r--r-- | tests/parseInt/output.txt | 20 | ||||
-rw-r--r-- | tests/parseInt/output_i64mode1.txt | 30 | ||||
-rwxr-xr-x | tests/runner.py | 139 | ||||
-rwxr-xr-x | tools/exec_llvm.py | 7 | ||||
-rw-r--r-- | tools/nativize_llvm.py | 31 | ||||
-rw-r--r-- | tools/shared.py | 12 |
15 files changed, 313 insertions, 192 deletions
@@ -77,6 +77,8 @@ import os, sys, shutil, tempfile from subprocess import Popen, PIPE, STDOUT from tools import shared +MAX_LLVM_OPT_LEVEL = 3 + DEBUG = os.environ.get('EMCC_DEBUG') TEMP_DIR = os.environ.get('EMCC_TEMP_DIR') LEAVE_INPUTS_RAW = os.environ.get('EMCC_LEAVE_INPUTS_RAW') # Do not compile .ll files into .bc, just compile them with emscripten directly @@ -84,10 +86,16 @@ LEAVE_INPUTS_RAW = os.environ.get('EMCC_LEAVE_INPUTS_RAW') # Do not compile .ll # specific need. # One major limitation with this mode is that dlmalloc and libc++ cannot be # added in. Also, LLVM optimizations will not be done, nor dead code elimination +AUTODEBUG = os.environ.get('EMCC_AUTODEBUG') # If set to 1, we will run the autodebugger (the automatic debugging tool, see tools/autodebugger). + # Note that this will disable inclusion of libraries. This is useful because including + # dlmalloc makes it hard to compare native and js builds if DEBUG: print >> sys.stderr, 'emcc: ', ' '.join(sys.argv) if DEBUG and LEAVE_INPUTS_RAW: print >> sys.stderr, 'emcc: leaving inputs raw' +stdout = PIPE if not DEBUG else None # suppress output of child processes +stderr = PIPE if not DEBUG else None # unless we are in DEBUG mode + shared.check_sanity() # Handle some global flags @@ -114,7 +122,7 @@ Most normal gcc/g++ options will work, for example: Options that are modified or new in %s include: -O0 No optimizations (default) - -O1 Simple optimizations, including safe LLVM + -O1 Simple optimizations, including LLVM -O1 optimizations, and no runtime assertions or C++ exception catching (to re-enable C++ exception catching, use @@ -123,7 +131,8 @@ Options that are modified or new in %s include: compiling to JavaScript, not to intermediate bitcode. -O2 As -O1, plus the relooper (loop recreation), - plus closure compiler advanced opts + plus closure compiler advanced opts, plus + LLVM -O2 optimizations Warning: Compiling with this takes a long time! -O3 As -O2, plus dangerous optimizations that may break the generated code! If that happens, try @@ -134,12 +143,8 @@ Options that are modified or new in %s include: --typed-arrays <mode> 0: No typed arrays 1: Parallel typed arrays 2: Shared (C-like) typed arrays (default) - --llvm-opts <level> 0: No LLVM optimizations (default in -O0) - 1: Safe/portable LLVM optimizations - (default in -O1 and above) - 2: Full, unsafe/unportable LLVM optimizations; - this will almost certainly break the - generated code! + --llvm-opts <on> 0: No LLVM optimizations (default in -O0) + 1: LLVM optimizations (default in -O1 +) --closure <on> 0: No closure compiler (default in -O0, -O1) 1: Run closure compiler (default in -O2, -O3) --js-transform <cmd> <cmd> will be called on the generated code @@ -223,8 +228,6 @@ def unsuffixed(name): def unsuffixed_basename(name): return os.path.basename(unsuffixed(name)) -LLVM_INTERNAL_OPT_LEVEL = 2 - # ---------------- End configs ------------- if len(sys.argv) == 1 or sys.argv[1] in ['x', 't']: @@ -278,7 +281,7 @@ try: newargs = sys.argv[1:] opt_level = 0 - llvm_opt_level = None + llvm_opts = None closure = None js_transform = None compress_whitespace = None @@ -296,8 +299,7 @@ try: newargs[i] = '' elif newargs[i].startswith('--llvm-opts'): check_bad_eq(newargs[i]) - llvm_opt_level = eval(newargs[i+1]) - assert 0 <= llvm_opt_level <= 1, 'Only two levels of LLVM optimizations are supported so far, 0 (none) and 1 (safe)' + llvm_opts = eval(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--closure'): @@ -323,7 +325,7 @@ try: newargs[i+1] = '' newargs = [ arg for arg in newargs if arg is not '' ] - if llvm_opt_level is None: llvm_opt_level = 1 if opt_level >= 1 else 0 + if llvm_opts is None: llvm_opts = 1 if opt_level >= 1 else 0 if closure is None: closure = 1 if opt_level >= 2 else 0 if compress_whitespace is None: compress_whitespace = closure # if closure is run, compress whitespace @@ -421,7 +423,7 @@ try: # If we were just asked to generate bitcode, stop there if final_suffix not in ['js', 'html']: - if llvm_opt_level > 0: + if llvm_opts > 0: print >> sys.stderr, 'emcc: warning: -Ox flags ignored, since not generating JavaScript' if not specified_target: for input_file in input_files: @@ -446,7 +448,7 @@ try: extra_files_to_link = [] - if not LEAVE_INPUTS_RAW: + if not LEAVE_INPUTS_RAW and not AUTODEBUG: # Check if we need to include some libraries that we compile. (We implement libc ourselves in js, but # compile a malloc implementation and stdlibc++.) # Note that we assume a single symbol is enough to know if we have/do not have dlmalloc etc. If you @@ -455,9 +457,9 @@ try: # dlmalloc def create_dlmalloc(): if DEBUG: print >> sys.stderr, 'emcc: building dlmalloc for cache' - Popen([shared.EMCC, shared.path_from_root('system', 'lib', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=PIPE, stderr=PIPE).communicate() + Popen([shared.EMCC, shared.path_from_root('system', 'lib', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=stdout, stderr=stderr).communicate() # we include the libc++ new stuff here, so that the common case of using just new/delete is quick to link - Popen([shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', 'new.cpp'), '-g', '-o', in_temp('new.o')], stdout=PIPE, stderr=PIPE).communicate() + Popen([shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', 'new.cpp'), '-g', '-o', in_temp('new.o')], stdout=stdout, stderr=stderr).communicate() shared.Building.link([in_temp('dlmalloc.o'), in_temp('new.o')], in_temp('dlmalloc_full.o')) return in_temp('dlmalloc_full.o') def fix_dlmalloc(): @@ -522,16 +524,16 @@ try: shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc')) # Optimize, if asked to - if llvm_opt_level > 0 and not LEAVE_INPUTS_RAW: + if llvm_opts > 0 and opt_level > 0 and not LEAVE_INPUTS_RAW: if DEBUG: print >> sys.stderr, 'emcc: LLVM opts' - shared.Building.llvm_opt(in_temp(target_basename + '.bc'), LLVM_INTERNAL_OPT_LEVEL, safe=llvm_opt_level < 2) + shared.Building.llvm_opt(in_temp(target_basename + '.bc'), min(opt_level, MAX_LLVM_OPT_LEVEL)) else: # If possible, remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it) if not LEAVE_INPUTS_RAW and not shared.Settings.BUILD_AS_SHARED_LIB and not shared.Settings.LINKABLE: if DEBUG: print >> sys.stderr, 'emcc: LLVM dead globals elimination' shared.Building.llvm_opt(in_temp(target_basename + '.bc'), ['-internalize', '-globaldce']) - # Emscripten + # Prepare .ll for Emscripten try: if shared.Settings.RELOOP: print >> sys.stderr, 'emcc: warning: The relooper optimization can be very slow.' @@ -556,6 +558,12 @@ try: final = input_files[0] if DEBUG: save_intermediate('ll', 'll') + if AUTODEBUG: + Popen(['python', shared.AUTODEBUGGER, final, final + '.ad.ll']).communicate()[0] + final += '.ad.ll' + if DEBUG: save_intermediate('autodebug', 'll') + + # Emscripten if DEBUG: print >> sys.stderr, 'emcc: LLVM => JS' final = shared.Building.emscripten(final, append_ext=False) if DEBUG: save_intermediate('original') diff --git a/src/analyzer.js b/src/analyzer.js index 7412be6d..a724d229 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -463,6 +463,12 @@ function analyzer(data, sidePass) { item.functions.forEach(function(func) { func.lines.forEach(function(line, i) { if (line.intertype === 'assign' && line.value.intertype === 'load') { + // Floats have no concept of signedness. Mark them as 'signed', which is the default, for which we do nothing + if (line.value.type in Runtime.FLOAT_TYPES) { + line.value.unsigned = false; + return; + } + // Booleans are always unsigned var data = func.variables[line.ident]; if (data.type === 'i1') { line.value.unsigned = true; diff --git a/src/jsifier.js b/src/jsifier.js index 657a9673..7bff588c 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -71,7 +71,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } } else { - libFuncsToInclude = ['memset', 'malloc', 'free']; + libFuncsToInclude = ['memcpy', 'memset', 'malloc', 'free']; } libFuncsToInclude.forEach(function(ident) { data.functionStubs.push({ diff --git a/src/library.js b/src/library.js index 5a429131..ad1ff696 100644 --- a/src/library.js +++ b/src/library.js @@ -2260,7 +2260,6 @@ LibraryManager.library = { } else if (type == 'i64') { ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}, {{{ makeGetValue('varargs', 'argIndex+4', 'i32', undefined, undefined, true) }}}]; - ret = unSign(ret[0], 32) + unSign(ret[1], 32)*Math.pow(2, 32); // Unsigned in this notation. Signed later if needed. // XXX - loss of precision #else } else if (type == 'i64') { ret = {{{ makeGetValue('varargs', 'argIndex', 'i64', undefined, undefined, true) }}}; @@ -2270,7 +2269,7 @@ LibraryManager.library = { ret = {{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}; } argIndex += Runtime.getNativeFieldSize(type); - return Number(ret); + return ret; } var ret = []; @@ -2392,6 +2391,12 @@ LibraryManager.library = { var signed = next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0); argSize = argSize || 4; var currArg = getNextArg('i' + (argSize * 8)); +#if I64_MODE == 1 + // Flatten i64-1 [low, high] into a (slightly rounded) double + if (argSize == 8) { + currArg = Runtime.makeBigInt(currArg[0], currArg[1], next == 'u'.charCodeAt(0)); + } +#endif // Truncate to requested size. if (argSize <= 4) { var limit = Math.pow(256, argSize) - 1; @@ -3444,7 +3449,7 @@ LibraryManager.library = { }, strtoll__deps: ['_parseInt'], strtoll: function(str, endptr, base) { - return __parseInt(str, endptr, base, -9223372036854775808, 9223372036854775807, 64); // LLONG_MIN, LLONG_MAX; imprecise. + return __parseInt(str, endptr, base, -9223372036854775200, 9223372036854775200, 64); // LLONG_MIN, LLONG_MAX; imprecise. }, strtol__deps: ['_parseInt'], strtol: function(str, endptr, base) { diff --git a/src/parseTools.js b/src/parseTools.js index ad6f2830..49e5b411 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -558,22 +558,13 @@ function makeInlineCalculation(expression, value, tempVar) { return '(' + expression.replace(/VALUE/g, value) + ')'; } -// Given two 32-bit unsigned parts of an emulated 64-bit number, combine them into a JS number (double). -// 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 -function makeBigInt(low, high) { - // here VALUE will be the big part - return '(' + high + ' <= 2147483648 ? (' + makeSignOp(low, 'i32', 'un', 1, 1) + '+(' + makeSignOp(high, 'i32', 'un', 1, 1) + '*4294967296))' + - ' : (' + makeSignOp(low, 'i32', 're', 1, 1) + '+(1+' + makeSignOp(high, 'i32', 're', 1, 1) + ')*4294967296))'; -} - // Makes a proper runtime value for a 64-bit value from low and high i32s. low and high are assumed to be unsigned. function makeI64(low, high) { high = high || '0'; if (I64_MODE == 1) { return '[' + makeSignOp(low, 'i32', 'un', 1, 1) + ',' + makeSignOp(high, 'i32', 'un', 1, 1) + ']'; } else { - if (high) return makeBigInt(low, high); + if (high) return RuntimeGenerator.makeBigInt(low, high); return low; } } @@ -589,7 +580,7 @@ function splitI64(value) { } function mergeI64(value) { assert(I64_MODE == 1); - return makeInlineCalculation(makeBigInt('VALUE[0]', 'VALUE[1]'), value, 'tempI64'); + return makeInlineCalculation(RuntimeGenerator.makeBigInt('VALUE[0]', 'VALUE[1]'), value, 'tempI64'); } // Takes an i64 value and changes it into the [low, high] form used in i64 mode 1. In that @@ -912,23 +903,32 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa 'tempDoubleF64[0])'; } - if (EMULATE_UNALIGNED_ACCESSES && USE_TYPED_ARRAYS == 2 && align && isIntImplemented(type)) { // TODO: support unaligned doubles and floats + if (USE_TYPED_ARRAYS == 2 && align) { // Alignment is important here. May need to split this up var bytes = Runtime.getNativeTypeSize(type); if (bytes > align) { - var ret = '/* unaligned */('; - if (bytes <= 4) { - for (var i = 0; i < bytes; i++) { - ret += 'tempInt' + (i == 0 ? '=' : (i < bytes-1 ? '+=((' : '+((')); - ret += makeSignOp(makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, unsigned, ignore), 'i8', 'un', true); - if (i > 0) ret += ')<<' + (8*i) + ')'; - if (i < bytes-1) ret += ','; + var ret = '('; + if (isIntImplemented(type)) { + if (bytes <= 4) { + for (var i = 0; i < bytes; i++) { + ret += 'tempInt' + (i == 0 ? '=' : '|=(('); + ret += makeGetValue(ptr, getFastValue(pos, '+', i), 'i8', noNeedFirst, 1, ignore); + if (i > 0) ret += ')<<' + (8*i) + ')'; + ret += ','; + } + ret += makeSignOp('tempInt', type, unsigned ? 'un' : 're', true); + } else { + assert(bytes == 8); + ret += 'tempBigInt=' + makeGetValue(ptr, pos, 'i32', noNeedFirst, true, ignore, align) + ','; + ret += 'tempBigInt2=' + makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, true, ignore, align) + ','; + ret += makeI64('tempBigInt', 'tempBigInt2'); } } else { - assert(bytes == 8); - ret += 'tempBigInt=' + makeGetValue(ptr, pos, 'i32', noNeedFirst, true, ignore, align) + ','; - ret += 'tempBigInt2=' + makeGetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'i32', noNeedFirst, true, ignore, align) + ','; - ret += makeI64('tempBigInt', 'tempBigInt2'); + if (type == 'float') { + ret += 'copyTempFloat(' + getFastValue(ptr, '+', pos) + '),tempDoubleF32[0]'; + } else { + ret += 'copyTempDouble(' + getFastValue(ptr, '+', pos) + '),tempDoubleF64[0]'; + } } ret += ')'; return ret; @@ -994,22 +994,27 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe) makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempDoubleI32[1]', 'i32', noNeedFirst, ignore, align) + ')'; } - if (EMULATE_UNALIGNED_ACCESSES && USE_TYPED_ARRAYS == 2 && align && isIntImplemented(type)) { // TODO: support unaligned doubles and floats + if (USE_TYPED_ARRAYS == 2 && align) { // Alignment is important here. May need to split this up var bytes = Runtime.getNativeTypeSize(type); if (bytes > align) { - var ret = '/* unaligned */'; - if (bytes <= 4) { - ret += 'tempBigInt=' + value + ';'; - for (var i = 0; i < bytes; i++) { - ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore) + ';'; - if (i < bytes-1) ret += 'tempBigInt>>=8;'; + var ret = ''; + if (isIntImplemented(type)) { + if (bytes <= 4) { + ret += 'tempBigInt=' + value + ';'; + for (var i = 0; i < bytes; i++) { + ret += makeSetValue(ptr, getFastValue(pos, '+', i), 'tempBigInt&0xff', 'i8', noNeedFirst, ignore) + ';'; + if (i < bytes-1) ret += 'tempBigInt>>=8;'; + } + } else { + assert(bytes == 8); + ret += 'tempPair=' + ensureI64_1(value) + ';'; + ret += makeSetValue(ptr, pos, 'tempPair[0]', 'i32', noNeedFirst, ignore, align) + ';'; + ret += makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempPair[1]', 'i32', noNeedFirst, ignore, align) + ';'; } } else { - assert(bytes == 8); - ret += 'tempPair=' + ensureI64_1(value) + ';'; - ret += makeSetValue(ptr, pos, 'tempPair[0]', 'i32', noNeedFirst, ignore, align) + ';'; - ret += makeSetValue(ptr, getFastValue(pos, '+', Runtime.getNativeTypeSize('i32')), 'tempPair[1]', 'i32', noNeedFirst, ignore, align) + ';'; + ret += makeSetValue('tempDoublePtr', 0, value, type, noNeedFirst, ignore, 8) + ';'; + ret += makeCopyValues(getFastValue(ptr, '+', pos), 'tempDoublePtr', Runtime.getNativeTypeSize(type), type, null, align); } return ret; } @@ -1465,7 +1470,7 @@ function makeSignOp(value, type, op, force, ignore) { var bits, full; if (type in Runtime.INT_TYPES) { bits = parseInt(type.substr(1)); - full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(correctSpecificSign() && !PGO) + ( + full = op + 'Sign(' + value + ', ' + bits + ', ' + Math.floor(ignore || (correctSpecificSign() && !PGO)) + ( PGO ? ', "' + (ignore ? '' : Debugging.getIdentifier()) + '"' : '' ) + ')'; // Always sign/unsign constants at compile time, regardless of CHECK/CORRECT @@ -1473,7 +1478,7 @@ function makeSignOp(value, type, op, force, ignore) { return eval(full).toString(); } } - if (!correctSigns() && !CHECK_SIGNS && !force) return value; + if ((ignore || !correctSigns()) && !CHECK_SIGNS && !force) return value; if (type in Runtime.INT_TYPES) { // shortcuts if (!CHECK_SIGNS || ignore) { @@ -1589,17 +1594,43 @@ function processMathop(item) { case 'xor': { return '[' + ident1 + '[0] ^ ' + ident2 + '[0], ' + ident1 + '[1] ^ ' + ident2 + '[1]]'; } - case 'shl': { - return '[' + ident1 + '[0] << ' + ident2 + ', ' + - '('+ident1 + '[1] << ' + ident2 + ') | ((' + ident1 + '[0]&((Math.pow(2, ' + ident2 + ')-1)<<(32-' + ident2 + '))) >>> (32-' + ident2 + '))]'; - } - case 'ashr': { - return '[('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&(Math.pow(2, ' + ident2 + ')-1))<<(32-' + ident2 + ')),' + - ident1 + '[1] >>> ' + ident2 + ']'; - } + case 'shl': + case 'ashr': case 'lshr': { - return '[('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&(Math.pow(2, ' + ident2 + ')-1))<<(32-' + ident2 + ')),' + - ident1 + '[1] >>> ' + ident2 + ']'; + assert(isNumber(ident2)); + bits = parseInt(ident2); + var ander = Math.pow(2, bits)-1; + if (bits < 32) { + switch (op) { + case 'shl': + return '[' + ident1 + '[0] << ' + ident2 + ', ' + + '('+ident1 + '[1] << ' + ident2 + ') | ((' + ident1 + '[0]&(' + ander + '<<' + (32 - bits) + ')) >>> (32-' + ident2 + '))]'; + case 'ashr': + return '[((('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&' + ander + ')<<' + (32 - bits) + ')) >> 0) >>> 0,' + + '(' + ident1 + '[1] >> ' + ident2 + ') >>> 0]'; + case 'lshr': + return '[(('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&' + ander + ')<<' + (32 - bits) + ')) >>> 0,' + + ident1 + '[1] >>> ' + ident2 + ']'; + } + } else if (bits == 32) { + switch (op) { + case 'shl': + return '[0, ' + ident1 + '[0]]'; + case 'ashr': + return '[' + ident1 + '[1], (' + ident1 + '[1]|0) < 0 ? ' + ander + ' : 0]'; + case 'lshr': + return '[' + ident1 + '[1], 0]'; + } + } else { // bits > 32 + switch (op) { + case 'shl': + return '[0, ' + ident1 + '[0] << ' + (bits - 32) + ']'; + case 'ashr': + return '[(' + ident1 + '[1] >> ' + (bits - 32) + ') >>> 0, (' + ident1 + '[1]|0) < 0 ? ' + ander + ' : 0]'; + case 'lshr': + return '[' + ident1 + '[1] >>> ' + (bits - 32) + ', 0]'; + } + } } case 'uitofp': case 'sitofp': return ident1 + '[0] + ' + ident1 + '[1]*4294967296'; case 'fptoui': case 'fptosi': return splitI64(ident1); diff --git a/src/preamble.js b/src/preamble.js index b9da766f..94add7f4 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -344,13 +344,6 @@ var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI #if I64_MODE == 1 var tempI64, tempI64b; #endif -#if DOUBLE_MODE == 1 -#if USE_TYPED_ARRAYS == 2 -var tempDoubleBuffer = new ArrayBuffer(8); -var tempDoubleI32 = new Int32Array(tempDoubleBuffer); -var tempDoubleF64 = new Float64Array(tempDoubleBuffer); -#endif -#endif function abort(text) { print(text + ':\n' + (new Error).stack); @@ -369,6 +362,7 @@ function assert(condition, text) { // makeSetValue is done at compile-time and generates the needed // code then, whereas this function picks the right code at // run-time. +// Note that setValue and getValue only do *aligned* writes and reads! function setValue(ptr, value, type, noSafe) { type = type || 'i8'; @@ -646,6 +640,33 @@ Module['HEAPF32'] = HEAPF32; STACK_ROOT = STACKTOP = Runtime.alignMemory(STATICTOP); STACK_MAX = STACK_ROOT + TOTAL_STACK; +#if DOUBLE_MODE == 1 +#if USE_TYPED_ARRAYS == 2 +var tempDoublePtr = Runtime.alignMemory(STACK_MAX, 8); +var tempDoubleI8 = HEAP8.subarray(tempDoublePtr); +var tempDoubleI32 = HEAP32.subarray(tempDoublePtr >> 2); +var tempDoubleF32 = HEAPF32.subarray(tempDoublePtr >> 2); +var tempDoubleF64 = new Float64Array(HEAP8.buffer).subarray(tempDoublePtr >> 3); +function copyTempFloat(ptr) { // functions, because inlining this code is increases code size too much + tempDoubleI8[0] = HEAP8[ptr]; + tempDoubleI8[1] = HEAP8[ptr+1]; + tempDoubleI8[2] = HEAP8[ptr+2]; + tempDoubleI8[3] = HEAP8[ptr+3]; +} +function copyTempDouble(ptr) { + tempDoubleI8[0] = HEAP8[ptr]; + tempDoubleI8[1] = HEAP8[ptr+1]; + tempDoubleI8[2] = HEAP8[ptr+2]; + tempDoubleI8[3] = HEAP8[ptr+3]; + tempDoubleI8[4] = HEAP8[ptr+4]; + tempDoubleI8[5] = HEAP8[ptr+5]; + tempDoubleI8[6] = HEAP8[ptr+6]; + tempDoubleI8[7] = HEAP8[ptr+7]; +} +STACK_MAX = tempDoublePtr + 8; +#endif +#endif + STATICTOP = alignMemoryPage(STACK_MAX); function callRuntimeCallbacks(callbacks) { diff --git a/src/runtime.js b/src/runtime.js index 6439d0ed..6f17028a 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -73,6 +73,15 @@ var RuntimeGenerator = { quantum = '(quantum ? quantum : {{{ QUANTUM_SIZE }}})'; } return target + ' = ' + Runtime.forceAlign(target, quantum); + }, + + // Given two 32-bit unsigned parts of an emulated 64-bit number, combine them into a JS number (double). + // 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) { + return '(' + unsigned + + ' ? (' + makeSignOp(low, 'i32', 'un', 1, 1) + '+(' + makeSignOp(high, 'i32', 'un', 1, 1) + '*4294967296))' + + ' : (' + makeSignOp(low, 'i32', 'un', 1, 1) + '+(' + makeSignOp(high, 'i32', 're', 1, 1) + '*4294967296)))'; } }; @@ -260,6 +269,7 @@ var Runtime = { Runtime.stackAlloc = unInline('stackAlloc', ['size']); Runtime.staticAlloc = unInline('staticAlloc', ['size']); Runtime.alignMemory = unInline('alignMemory', ['size', 'quantum']); +Runtime.makeBigInt = unInline('makeBigInt', ['low', 'high', 'unsigned']); function getRuntime() { var ret = 'var Runtime = {\n'; diff --git a/src/settings.js b/src/settings.js index 7e900ea9..ae07b1f4 100644 --- a/src/settings.js +++ b/src/settings.js @@ -73,13 +73,6 @@ var DOUBLE_MODE = 1; // How to load and store 64-bit doubles. Without typed arra // then load it aligned, and that load-store will make JS engines alter it if it is being // stored to a typed array for security reasons. That will 'fix' the number from being a // NaN or an infinite number. -var EMULATE_UNALIGNED_ACCESSES = 0; // If set, the compiler will 'emulate' loads and stores that are not known to - // be sufficiently aligned, by working on individual bytes. This can be - // important in USE_TYPED_ARRAYS == 2, where unaligned accesses do not work, - // specifically in the case where unsafe LLVM optimizations have generated possibly - // unaligned code. (Without unsafe LLVM optimizations, there should be no - // need for this option.) - // Currently this only works for integers, not doubles and floats. var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure // compiler. This potentially lets closure optimize the code better. diff --git a/system/lib/debugging.cpp b/system/lib/debugging.cpp new file mode 100644 index 00000000..ff9e0d68 --- /dev/null +++ b/system/lib/debugging.cpp @@ -0,0 +1,22 @@ + +// Some stuff to patch up an emscripten-sdk build so it can be built natively (see nativize_llvm) + +#include <stdio.h> +#include <stdlib.h> + +extern "C" { + +int *__errno() +{ + static int e = 0; + return &e; +} + +void __assert_func(const char *file, int line, const char *assertt, const char *cond) +{ + printf("assert-func: %s : %d : %s : %s\n", file, line, assertt, cond); + abort(); +} + +} + diff --git a/tests/parseInt/output.txt b/tests/parseInt/output.txt index e345e2ac..7ab00631 100644 --- a/tests/parseInt/output.txt +++ b/tests/parseInt/output.txt @@ -1,6 +1,6 @@ strtol("-9223372036854775809") = -2147483648 ERR 34 -strtoll("-9223372036854775809") = 9223372036854776000 +strtoll("-9223372036854775809") = -9223372036854775000 ERR 34 strtoul("-9223372036854775809") = 4294967295 ERR 34 @@ -8,7 +8,7 @@ strtoull("-9223372036854775809") = 9223372036854774000 strtol("-9223372036854775808") = -2147483648 ERR 34 -strtoll("-9223372036854775808") = 9223372036854776000 +strtoll("-9223372036854775808") = -9223372036854775000 ERR 34 strtoul("-9223372036854775808") = 4294967295 ERR 34 @@ -16,7 +16,7 @@ strtoull("-9223372036854775808") = 9223372036854774000 strtol("-9223372036854775807") = -2147483648 ERR 34 -strtoll("-9223372036854775807") = 9223372036854776000 +strtoll("-9223372036854775807") = -9223372036854775000 ERR 34 strtoul("-9223372036854775807") = 4294967295 ERR 34 @@ -24,7 +24,7 @@ strtoull("-9223372036854775807") = 9223372036854774000 strtol("-2147483649") = -2147483648 ERR 34 -strtoll("-2147483649") = -2147483648 +strtoll("-2147483649") = -2147483649 strtoul("-2147483649") = 2147483647 strtoull("-2147483649") = 18446744071562068000 @@ -34,17 +34,17 @@ strtoul("-2147483648") = 2147483648 strtoull("-2147483648") = 18446744071562068000 strtol("-2147483647") = -2147483647 -strtoll("-2147483647") = -2147483648 +strtoll("-2147483647") = -2147483647 strtoul("-2147483647") = 2147483649 strtoull("-2147483647") = 18446744071562068000 strtol("-5") = -5 -strtoll("-5") = 0 +strtoll("-5") = -5 strtoul("-5") = 4294967291 strtoull("-5") = 18446744069414584000 strtol("-1") = -1 -strtoll("-1") = 0 +strtoll("-1") = -1 strtoul("-1") = 4294967295 strtoull("-1") = 18446744069414584000 @@ -100,7 +100,7 @@ strtoull("4294967296") = 4294967296 strtol("18446744073709551614") = 2147483647 ERR 34 -strtoll("18446744073709551614") = 9223372036854776000 +strtoll("18446744073709551614") = 9223372036854775000 ERR 34 strtoul("18446744073709551614") = 4294967295 ERR 34 @@ -108,7 +108,7 @@ strtoull("18446744073709551614") = 18446744069414584000 strtol("18446744073709551615") = 2147483647 ERR 34 -strtoll("18446744073709551615") = 9223372036854776000 +strtoll("18446744073709551615") = 9223372036854775000 ERR 34 strtoul("18446744073709551615") = 4294967295 ERR 34 @@ -116,7 +116,7 @@ strtoull("18446744073709551615") = 18446744069414584000 strtol("18446744073709551616") = 2147483647 ERR 34 -strtoll("18446744073709551616") = 9223372036854776000 +strtoll("18446744073709551616") = 9223372036854775000 ERR 34 strtoul("18446744073709551616") = 4294967295 ERR 34 diff --git a/tests/parseInt/output_i64mode1.txt b/tests/parseInt/output_i64mode1.txt index 649500b0..7ab00631 100644 --- a/tests/parseInt/output_i64mode1.txt +++ b/tests/parseInt/output_i64mode1.txt @@ -1,6 +1,6 @@ strtol("-9223372036854775809") = -2147483648 ERR 34 -strtoll("-9223372036854775809") = 9223372036854776000 +strtoll("-9223372036854775809") = -9223372036854775000 ERR 34 strtoul("-9223372036854775809") = 4294967295 ERR 34 @@ -8,7 +8,7 @@ strtoull("-9223372036854775809") = 9223372036854774000 strtol("-9223372036854775808") = -2147483648 ERR 34 -strtoll("-9223372036854775808") = 9223372036854776000 +strtoll("-9223372036854775808") = -9223372036854775000 ERR 34 strtoul("-9223372036854775808") = 4294967295 ERR 34 @@ -16,7 +16,7 @@ strtoull("-9223372036854775808") = 9223372036854774000 strtol("-9223372036854775807") = -2147483648 ERR 34 -strtoll("-9223372036854775807") = 9223372036854776000 +strtoll("-9223372036854775807") = -9223372036854775000 ERR 34 strtoul("-9223372036854775807") = 4294967295 ERR 34 @@ -24,7 +24,7 @@ strtoull("-9223372036854775807") = 9223372036854774000 strtol("-2147483649") = -2147483648 ERR 34 -strtoll("-2147483649") = -2147483648 +strtoll("-2147483649") = -2147483649 strtoul("-2147483649") = 2147483647 strtoull("-2147483649") = 18446744071562068000 @@ -34,19 +34,19 @@ strtoul("-2147483648") = 2147483648 strtoull("-2147483648") = 18446744071562068000 strtol("-2147483647") = -2147483647 -strtoll("-2147483647") = -2147483648 +strtoll("-2147483647") = -2147483647 strtoul("-2147483647") = 2147483649 strtoull("-2147483647") = 18446744071562068000 strtol("-5") = -5 -strtoll("-5") = 0 +strtoll("-5") = -5 strtoul("-5") = 4294967291 -strtoull("-5") = 18446744073709552000 +strtoull("-5") = 18446744069414584000 strtol("-1") = -1 -strtoll("-1") = 0 +strtoll("-1") = -1 strtoul("-1") = 4294967295 -strtoull("-1") = 18446744073709552000 +strtoull("-1") = 18446744069414584000 strtol("0") = 0 strtoll("0") = 0 @@ -100,27 +100,27 @@ strtoull("4294967296") = 4294967296 strtol("18446744073709551614") = 2147483647 ERR 34 -strtoll("18446744073709551614") = 9223372036854776000 +strtoll("18446744073709551614") = 9223372036854775000 ERR 34 strtoul("18446744073709551614") = 4294967295 ERR 34 -strtoull("18446744073709551614") = 18446744073709552000 +strtoull("18446744073709551614") = 18446744069414584000 strtol("18446744073709551615") = 2147483647 ERR 34 -strtoll("18446744073709551615") = 9223372036854776000 +strtoll("18446744073709551615") = 9223372036854775000 ERR 34 strtoul("18446744073709551615") = 4294967295 ERR 34 -strtoull("18446744073709551615") = 18446744073709552000 +strtoull("18446744073709551615") = 18446744069414584000 strtol("18446744073709551616") = 2147483647 ERR 34 -strtoll("18446744073709551616") = 9223372036854776000 +strtoll("18446744073709551616") = 9223372036854775000 ERR 34 strtoul("18446744073709551616") = 4294967295 ERR 34 -strtoull("18446744073709551616") = 18446744073709552000 +strtoull("18446744073709551616") = 18446744069414584000 strtol("0x12", 0, 0) = 18 strtol("0x12", 0, 10) = 0 diff --git a/tests/runner.py b/tests/runner.py index 126071a0..eb96160e 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -126,7 +126,7 @@ process(sys.argv[1]) # Build JavaScript code from source code def build(self, src, dirname, filename, output_processor=None, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, extra_emscripten_args=[], post_build=None): - Building.pick_llvm_opts(3, safe=Building.LLVM_OPTS != 2) # pick llvm opts here, so we include changes to Settings in the test case code + Building.pick_llvm_opts(3) # pick llvm opts here, so we include changes to Settings in the test case code # Copy over necessary files for compiling the source if main_file is None: @@ -420,6 +420,17 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): #include <stdio.h> int main() { + long long a = 0x2b00505c10; + long long b = a >> 29; + long long c = a >> 32; + long long d = a >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\\n", a, b, c, d); + unsigned long long ua = 0x2b00505c10; + unsigned long long ub = ua >> 29; + unsigned long long uc = ua >> 32; + unsigned long long ud = ua >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\\n", ua, ub, uc, ud); + long long x = 0x0000def123450789ULL; // any bigger than this, and we long long y = 0x00020ef123456089ULL; // start to run into the double precision limit! printf("*%Ld,%Ld,%Ld,%Ld,%Ld*\\n", x, y, x | y, x & y, x ^ y, x >> 2, y << 2); @@ -436,7 +447,7 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): return 0; } ''' - self.do_run(src, '*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*') + self.do_run(src, '*184688860176,344,43,10*\n*184688860176,344,43,10*\n*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: i64 mode 1 for q1') @@ -505,12 +516,26 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): // global structs with i64s printf("*%d,%Ld*\n*%d,%Ld*\n", iub[0].c, iub[0].d, iub[1].c, iub[1].d); + // Bitshifts + { + int64_t a = -1; + int64_t b = a >> 29; + int64_t c = a >> 32; + int64_t d = a >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\n", a, b, c, d); + uint64_t ua = -1; + int64_t ub = ua >> 29; + int64_t uc = ua >> 32; + int64_t ud = ua >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\n", ua, ub, uc, ud); + } + // Math mixtures with doubles { uint64_t a = 5; double b = 6.8; uint64_t c = a * b; - printf("*prod:%llu*\n*%d,%d,%d*", c, (int)&a, (int)&b, (int)&c); // printing addresses prevents optimizations + printf("*prod:%llu*\n*%d,%d,%d*\n", c, (int)&a, (int)&b, (int)&c); // printing addresses prevents optimizations } // Basic (rounded, for now) math. Just check compilation. @@ -523,11 +548,21 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): return 0; } ''' - self.do_run(src, '*1311918518731868200\n0,0,0,1,1\n1,0,1,0,1*\n*245127260211081*\n*245127260209443*\n' + - '*18446744073709552000*\n*576460752303423500*\n' + - 'm1: 127\n*123*\n*127*\n' + - '*55,17179869201*\n*122,25769803837*\n' + - '*prod:34*\n') + self.do_run(src, '*1311918518731868200\n' + + '0,0,0,1,1\n' + + '1,0,1,0,1*\n' + + '*245127260211081*\n' + + '*245127260209443*\n' + + '*18446744073709552000*\n' + + '*576460752303423500*\n' + + 'm1: 127\n' + + '*123*\n' + + '*127*\n' + + '*55,17179869201*\n' + + '*122,25769803837*\n' + + '*-1,-1,-1,-1*\n' + + '*-1,34359738367,4294967295,1073741823*\n' + + '*prod:34*') Settings.CORRECT_SIGNS = 1 @@ -713,7 +748,7 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): self.do_run(src, '*255*\n*65535*\n*-1*\n*-1*\n*-1*') def test_bitfields(self): - Settings.SAFE_HEAP = 0 # bitfields do loads on invalid areas, by design + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # bitfields do loads on invalid areas, by design src = ''' #include <stdio.h> struct bitty { @@ -3877,7 +3912,7 @@ def process(filename): if self.emcc_args is None: if Building.LLVM_OPTS: return self.skip('optimizing bitcode before emcc can confuse libcxx inclusion') self.emcc_args = [] # libc++ auto-inclusion is only done if we use emcc - Settings.SAFE_HEAP = 0 # Some spurious warnings from libc++ internals + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Some spurious warnings from libc++ internals src = ''' #include <iostream> @@ -4002,7 +4037,7 @@ def process(filename): ''', 'hello world'); def test_static_variable(self): - Settings.SAFE_HEAP = 0 # LLVM mixes i64 and i8 in the guard check + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # LLVM mixes i64 and i8 in the guard check src = ''' #include <stdio.h> @@ -4069,7 +4104,7 @@ def process(filename): self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage Building.COMPILER_TEST_OPTS = [] # remove -g, so we have one test without it by default - Settings.SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code... + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code... # Overflows happen in hash loop Settings.CORRECT_OVERFLOWS = 1 @@ -4100,7 +4135,7 @@ def process(filename): if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') # Overflows in luaS_newlstr hash loop - Settings.SAFE_HEAP = 0 # Has various warnings, with copied HEAP_HISTORY values (fixed if we copy 'null' as the type) + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Has various warnings, with copied HEAP_HISTORY values (fixed if we copy 'null' as the type) Settings.CORRECT_OVERFLOWS = 1 Settings.CHECK_OVERFLOWS = 0 Settings.CORRECT_SIGNS = 1 # Not sure why, but needed @@ -4159,7 +4194,7 @@ def process(filename): Settings.CORRECT_SIGNS_LINES = pgo_data['signs_lines'] Settings.CORRECT_OVERFLOWS = 0 Settings.CORRECT_ROUNDINGS = 0 - Settings.SAFE_HEAP = 0 # uses time.h to set random bytes, other stuff + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # uses time.h to set random bytes, other stuff Settings.DISABLE_EXCEPTION_CATCHING = 1 Settings.FAST_MEMORY = 4*1024*1024 Settings.EXPORTED_FUNCTIONS = ['_main', '_sqlite3_open', '_sqlite3_close', '_sqlite3_exec', '_sqlite3_free', '_callback']; @@ -4204,7 +4239,7 @@ def process(filename): force_c=True) def test_the_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long - if Building.LLVM_OPTS: Settings.SAFE_HEAP = 0 # Optimizations make it so we do not have debug info on the line we need to ignore + if Building.LLVM_OPTS and self.emcc_args is None: Settings.SAFE_HEAP = 0 # Optimizations make it so we do not have debug info on the line we need to ignore # Note: this is also a good test of per-file and per-line changes (since we have multiple files, and correct specific lines) if Settings.SAFE_HEAP: @@ -4226,7 +4261,7 @@ def process(filename): def test_poppler(self): if not self.emcc_args == []: return self.skip('very slow, we only do this in default') - Settings.SAFE_HEAP = 0 # Has variable object + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Has variable object Settings.CORRECT_OVERFLOWS = 1 Settings.CORRECT_SIGNS = 1 @@ -4371,7 +4406,7 @@ def process(filename): # Overflows in string_hash Settings.CORRECT_OVERFLOWS = 1 Settings.CHECK_OVERFLOWS = 0 - Settings.SAFE_HEAP = 0 # Has bitfields which are false positives. Also the PyFloat_Init tries to detect endianness. + if self.emcc_args is None: Settings.SAFE_HEAP = 0 # Has bitfields which are false positives. Also the PyFloat_Init tries to detect endianness. Settings.CORRECT_SIGNS = 1 # Not sure why, but needed Settings.EXPORTED_FUNCTIONS = ['_main', '_PyRun_SimpleStringFlags'] # for the demo @@ -4500,58 +4535,6 @@ def process(filename): self.do_run(src, '''Profiling data: Block 0: ''', post_build=post1) - # Part 2: old JS version - - Settings.PROFILE = 1 - Settings.INVOKE_RUN = 0 - - src = ''' - #include <stdio.h> - - int inner1(int x) { - for (int i = 0; i < 20; i++) - x += x/3; - return x; - } - int inner2(int x) { - for (int i = 0; i < 10; i++) - x -= x/4; - return x; - } - int inner3(int x) { - for (int i = 0; i < 5; i++) - x += x/2; - x = inner1(x) - inner2(x); - for (int i = 0; i < 5; i++) - x -= x/2; - return x; - } - - int main() - { - int total = 0; - for (int i = 0; i < 5000; i++) - total += inner1(i) - 4*inner3(i); - printf("*%d*\\n", total); - return 0; - } - ''' - - post = ''' -def process(filename): - src = open(filename, 'a') - src.write(\'\'\' - startProfiling(); - run(); - stopProfiling(); - printProfiling(); - print('*ok*'); - \'\'\') - src.close() -''' - - self.do_run(src, ': __Z6inner1i (5000)\n', post_build=post) - ### Integration tests def test_scriptaclass(self): @@ -4790,6 +4773,8 @@ Child2:9 ''', post_build=post2) def test_typeinfo(self): + if self.emcc_args is not None and self.emcc_args != []: return self.skip('full LLVM opts optimize out all the code that uses the type') + Settings.RUNTIME_TYPE_INFO = 1 if Settings.QUANTUM_SIZE != 4: return self.skip('We assume normal sizes in the output here') @@ -5001,6 +4986,8 @@ def process(filename): assert 'Assertion failed' in str(e), str(e) def test_linespecific(self): + if self.emcc_args: self.emcc_args += ['--llvm-opts', '0'] # llvm full opts make the expected failures here not happen + Settings.CHECK_SIGNS = 0 Settings.CHECK_OVERFLOWS = 0 @@ -5178,8 +5165,9 @@ def process(filename): def check(output): # TODO: check the line # - assert 'Overflow|src.cpp:6 : 60 hits, %20 failures' in output, 'no indication of Overflow corrections: ' + output - assert 'UnSign|src.cpp:13 : 6 hits, %17 failures' in output, 'no indication of Sign corrections: ' + output + if self.emcc_args is None or self.emcc_args == []: # LLVM full opts optimize out some corrections + assert 'Overflow|src.cpp:6 : 60 hits, %20 failures' in output, 'no indication of Overflow corrections: ' + output + assert 'UnSign|src.cpp:13 : 6 hits, %17 failures' in output, 'no indication of Sign corrections: ' + output return output self.do_run(src, '*186854335,63*\n', output_nicerizer=check) @@ -5274,7 +5262,7 @@ class %s(T): else: Settings.I64_MODE = 0 - Building.pick_llvm_opts(3, safe=Building.LLVM_OPTS != 2) + Building.pick_llvm_opts(3) TT = %s ''' % (fullname, fullname, fullname, compiler, str(emcc_args), llvm_opts, embetter, quantum_size, typed_arrays, fullname)) @@ -5594,6 +5582,13 @@ f.close() output = Popen([NODE_JS, JS_OPTIMIZER, input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0] self.assertIdentical(expected, output.replace('\n\n', '\n')) + def test_reminder(self): + assert False, 'Optimize makeGet/SetValue to do 16-bit reads/writes when possible, not just 8' + assert False, 'Make sure unaligned loads are not done unnecessarily (add some comments and inspect the source)' + assert False, 'Why is libcxx/ created in e.g. test_python, with EMCC_DEBUG, when it does not need libcxx?' + assert False, 'Make sure Poppler builds with llvm full opts' + assert False, 'Check if we should use -Ox instead of -std-compile-opts' + elif 'benchmark' in str(sys.argv): # Benchmarks. Run them with argument |benchmark|. To run a specific test, do # |benchmark.test_X|. diff --git a/tools/exec_llvm.py b/tools/exec_llvm.py index 1b1bba1b..5cf55e46 100755 --- a/tools/exec_llvm.py +++ b/tools/exec_llvm.py @@ -26,11 +26,8 @@ it runs python $(EMSCRIPTEN_TOOLS)/exec_llvm.py THE_FILE PARAMS An alternative solution to this problem is to compile -the .ll into native code. This can be done as follows: - - * Use llc to generate x86 asm - * Use as to generate an object file - * Use g++ to link it to an executable +the .ll into native code, see nativize_llvm.py. That is +useful when this fails. ''' import os, sys diff --git a/tools/nativize_llvm.py b/tools/nativize_llvm.py new file mode 100644 index 00000000..de78dce2 --- /dev/null +++ b/tools/nativize_llvm.py @@ -0,0 +1,31 @@ +#!/usr/bin/python + +''' +Small utility to build some llvm bitcode into native code. Useful when lli (called +from exec_llvm) fails for some reason. + + * Use llc to generate x86 asm + * Use as to generate an object file + * Use g++ to link it to an executable +''' + +import os, sys +from subprocess import Popen, PIPE, STDOUT + +__rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +def path_from_root(*pathelems): + return os.path.join(__rootpath__, *pathelems) +exec(open(path_from_root('tools', 'shared.py'), 'r').read()) + +filename = sys.argv[1] +libs = sys.argv[2:] # e.g.: dl for dlopen/dlclose, util for openpty/forkpty + +print 'bc => clean bc' +Popen([LLVM_OPT, filename, '-strip-debug', '-o=' + filename + '.clean.bc']).communicate()[0] +print 'bc => s' +Popen([LLVM_COMPILER, filename + '.clean.bc', '-o=' + filename + '.s']).communicate()[0] +print 's => o' +Popen(['as', filename + '.s', '-o', filename + '.o']).communicate()[0] +print 'o => runnable' +Popen(['g++', path_from_root('system', 'lib', 'debugging.cpp'), filename + '.o', '-o', filename + '.run'] + ['-l' + lib for lib in libs]).communicate()[0] + diff --git a/tools/shared.py b/tools/shared.py index f20fc75c..81c7fcf8 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -440,9 +440,9 @@ class Building: # @param opt Either an integer, in which case it is the optimization level (-O1, -O2, etc.), or a list of raw # optimization passes passed to llvm opt @staticmethod - def llvm_opt(filename, opts, safe=True): + def llvm_opt(filename, opts): if type(opts) is int: - opts = Building.pick_llvm_opts(opts, safe) + opts = Building.pick_llvm_opts(opts) output = Popen([LLVM_OPT, filename] + opts + ['-o=' + filename + '.opt.bc'], stdout=PIPE).communicate()[0] assert os.path.exists(filename + '.opt.bc'), 'Failed to run llvm optimizations: ' + output shutil.move(filename + '.opt.bc', filename) @@ -528,7 +528,7 @@ class Building: return filename + '.o.js' @staticmethod - def pick_llvm_opts(optimization_level, safe=True): + def pick_llvm_opts(optimization_level): ''' It may be safe to use nonportable optimizations (like -OX) if we remove the platform info from the .ll (which we do in do_ll_opts) - but even there we have issues (even in TA2) with instruction combining @@ -539,14 +539,16 @@ class Building: llvm-as < /dev/null | opt -std-compile-opts -disable-output -debug-pass=Arguments ''' + safe = Settings.USE_TYPED_ARRAYS != 2 or Settings.BUILD_AS_SHARED_LIB or Settings.LINKABLE + print 'LLVM opts, safe?', safe opts = [] if optimization_level > 0: - #opts.append('-disable-inlining') # we prefer to let closure compiler do our inlining if not safe: + opts.append('-disable-inlining') # we prefer to let closure compiler do our inlining, to avoid overly aggressive inlining #opts.append('-O%d' % optimization_level) opts.append('-std-compile-opts') opts.append('-std-link-opts') - print 'Unsafe:', opts, + print 'Unsafe:', opts else: allow_nonportable = not safe optimize_size = True |