diff options
-rwxr-xr-x | emcc | 50 | ||||
-rw-r--r-- | src/analyzer.js | 320 | ||||
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 9 | ||||
-rw-r--r-- | src/library.js | 11 | ||||
-rw-r--r-- | src/parseTools.js | 169 | ||||
-rw-r--r-- | src/preamble.js | 35 | ||||
-rw-r--r-- | src/runtime.js | 10 | ||||
-rw-r--r-- | src/settings.js | 8 | ||||
-rw-r--r-- | system/lib/debugging.cpp | 22 | ||||
-rw-r--r-- | tests/cases/legalizer_ta2.ll | 85 | ||||
-rw-r--r-- | tests/cases/legalizer_ta2.txt | 13 | ||||
-rw-r--r-- | tests/cases/phiself.ll | 69 | ||||
-rw-r--r-- | tests/cases/phiself.py | 3 | ||||
-rw-r--r-- | tests/cases/phiself.txt | 1 | ||||
-rw-r--r-- | tests/parseInt/output.txt | 20 | ||||
-rw-r--r-- | tests/parseInt/output_i64mode1.txt | 30 | ||||
-rwxr-xr-x | tests/runner.py | 143 | ||||
-rw-r--r-- | tools/eliminator/eliminator-test-output.js | 8 | ||||
-rw-r--r-- | tools/eliminator/eliminator-test.js | 10 | ||||
-rw-r--r-- | tools/eliminator/eliminator.coffee | 6 | ||||
-rwxr-xr-x | tools/exec_llvm.py | 7 | ||||
-rw-r--r-- | tools/find_bigis.py | 18 | ||||
-rw-r--r-- | tools/nativize_llvm.py | 31 | ||||
-rw-r--r-- | tools/shared.py | 12 |
25 files changed, 876 insertions, 216 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 @@ -230,8 +235,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']: @@ -285,7 +288,7 @@ try: newargs = sys.argv[1:] opt_level = 0 - llvm_opt_level = None + llvm_opts = None closure = None js_transform = None compress_whitespace = None @@ -304,8 +307,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'): @@ -336,7 +338,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 @@ -434,7 +436,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: @@ -459,7 +461,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 @@ -468,9 +470,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(): @@ -535,16 +537,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.' @@ -569,6 +571,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..7eca1d5a 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -67,7 +67,7 @@ function analyzer(data, sidePass) { if (subItem.intertype == 'function') { item.functions.push(subItem); subItem.endLineNum = null; - subItem.lines = []; + subItem.lines = []; // We will fill in the function lines after the legalizer, since it can modify them subItem.labels = []; // no explicit 'entry' label in clang on LLVM 2.8 - most of the time, but not all the time! - so we add one if necessary @@ -87,7 +87,6 @@ function analyzer(data, sidePass) { } else if (item.functions.length > 0 && item.functions.slice(-1)[0].endLineNum === null) { // Internal line if (!currLabelFinished) { - item.functions.slice(-1)[0].lines.push(subItem); item.functions.slice(-1)[0].labels.slice(-1)[0].lines.push(subItem); // If this line fails, perhaps missing a label? LLVM_STYLE related? if (subItem.intertype === 'branch') { currLabelFinished = true; @@ -100,7 +99,316 @@ function analyzer(data, sidePass) { } } delete item.items; - this.forwardItem(item, 'Typevestigator'); + this.forwardItem(item, 'Legalizer'); + } + }); + + // Legalize LLVM unrealistic types into realistic types. + // + // With full LLVM optimizations, it can generate types like i888 which do not exist in + // any actual hardware implementation, but are useful during optimization. LLVM then + // legalizes these types into real ones during code generation. Sadly, there is no LLVM + // IR pass to legalize them, which would have been useful and nice from a design perspective. + // The LLVM community is also not interested in receiving patches to implement that + // functionality, since it would duplicate existing code from the code generation + // component. Therefore, we implement legalization here in Emscripten. + // + // Currently we just legalize completely unrealistic types into bundles of i32s, and just + // the most common instructions that can be involved with such types: load, store, shifts, + // trunc and zext. + // + // TODO: Expand this also into legalization of i64 into i32,i32, which can then + // replace our i64 mode 1 implementation. Legalizing i64s is harder though + // as they can appear in function arguments and we would also need to implement + // an unfolder (to uninline inline LLVM function calls, so that each LLVM line + // has a single LLVM instruction). + substrate.addActor('Legalizer', { + processItem: function(data) { + // Legalization + if (USE_TYPED_ARRAYS == 2) { + function isIllegalType(type) { + return getBits(type) > 64; + } + function getLegalVars(base, bits) { + var ret = new Array(Math.ceil(bits/32)); + var i = 0; + while (bits > 0) { + ret[i] = { ident: base + '$' + i, bits: Math.min(32, bits) }; + bits -= 32; + i++; + } + return ret; + } + function getLegalLiterals(text, bits) { + var parsed = parseArbitraryInt(text, bits); + var ret = new Array(Math.ceil(bits/32)); + var i = 0; + while (bits > 0) { + ret[i] = { ident: parsed[i], bits: Math.min(32, bits) }; + bits -= 32; + i++; + } + return ret; + } + data.functions.forEach(function(func) { + func.labels.forEach(function(label) { + var i = 0, bits; + while (i < label.lines.length) { + var item = label.lines[i]; + if (item.intertype == 'store') { + if (isIllegalType(item.valueType)) { + dprint('legalizer', 'Legalizing store at line ' + item.lineNum); + bits = getBits(item.valueType); + assert(item.value.intertype == 'value', 'TODO: unfolding'); + var elements; + if (isNumber(item.value.ident)) { + elements = getLegalLiterals(item.value.ident, bits); + } else { + elements = getLegalVars(item.value.ident, bits); + } + label.lines.splice(i, 1); + var j = 0; + elements.forEach(function(element) { + var tempVar = '$st$' + i + '$' + j; + label.lines.splice(i+j*2, 0, { + intertype: 'assign', + ident: tempVar, + value: { + intertype: 'getelementptr', + ident: item.pointer.ident, + type: '[0 x i32]*', + params: [ + { intertype: 'value', ident: item.pointer.ident, type: '[0 x i32]*' }, // technically a bitcase is needed in llvm, but not for us + { intertype: 'value', ident: '0', type: 'i32' }, + { intertype: 'value', ident: j.toString(), type: 'i32' } + ], + }, + lineNum: item.lineNum + (j/100) + }); + var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits + label.lines.splice(i+j*2+1, 0, { + intertype: 'store', + valueType: actualSizeType, + value: { intertype: 'value', ident: element.ident, type: actualSizeType }, + pointer: { intertype: 'value', ident: tempVar, type: actualSizeType + '*' }, + ident: tempVar, + pointerType: actualSizeType + '*', + align: item.align, + lineNum: item.lineNum + ((j+0.5)/100) + }); + j++; + }); + Types.needAnalysis['[0 x i32]'] = 0; + i += j*2; + continue; + } + } else if (item.intertype == 'assign') { + var value = item.value; + switch (value.intertype) { + case 'load': { + if (isIllegalType(value.valueType)) { + dprint('legalizer', 'Legalizing load at line ' + item.lineNum); + bits = getBits(value.valueType); + assert(value.pointer.intertype == 'value', 'TODO: unfolding'); + var elements = getLegalVars(item.ident, bits); + label.lines.splice(i, 1); + var j = 0; + elements.forEach(function(element) { + var tempVar = '$st$' + i + '$' + j; + label.lines.splice(i+j*2, 0, { + intertype: 'assign', + ident: tempVar, + value: { + intertype: 'getelementptr', + ident: value.pointer.ident, + type: '[0 x i32]*', + params: [ + { intertype: 'value', ident: value.pointer.ident, type: '[0 x i32]*' }, // technically bitcast is needed in llvm, but not for us + { intertype: 'value', ident: '0', type: 'i32' }, + { intertype: 'value', ident: j.toString(), type: 'i32' } + ], + }, + lineNum: item.lineNum + (j/100) + }); + var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits + label.lines.splice(i+j*2+1, 0, { + intertype: 'assign', + ident: element.ident, + value: { + intertype: 'load', + pointerType: actualSizeType + '*', + valueType: actualSizeType, + type: actualSizeType, // XXX why is this missing from intertyper? + pointer: { intertype: 'value', ident: tempVar, type: actualSizeType + '*' }, + ident: tempVar, + pointerType: actualSizeType + '*', + align: value.align, + }, + lineNum: item.lineNum + ((j+0.5)/100) + }); + j++; + }); + Types.needAnalysis['[0 x i32]'] = 0; + i += j*2; + continue; + } + } + case 'mathop': { + if (isIllegalType(value.type)) { + dprint('legalizer', 'Legalizing mathop at line ' + item.lineNum); + label.lines.splice(i, 1); + var toAdd = []; + assert(value.param1.intertype == 'value', 'TODO: unfolding'); + var sourceBits = getBits(value.param1.type); + var sourceElements; + if (sourceBits <= 64) { + // The input is a legal type + if (sourceBits <= 32) { + sourceElements = [{ ident: value.param1.ident, bits: sourceBits }]; + } else if (sourceBits == 64 && I64_MODE == 1) { + sourceElements = [{ ident: value.param1.ident + '[0]', bits: 32 }, + { ident: value.param1.ident + '[1]', bits: 32 }]; + // Add the source element as a param so that it is not eliminated as unneeded (the idents are not a simple ident here) + toAdd.push({ + intertype: 'value', ident: ';', type: 'rawJS', + params: [{ intertype: 'value', ident: value.param1.ident, type: 'i32' }] + }); + } else { + throw 'Invalid legal type as source of legalization ' + sourceBits; + } + } else { + sourceElements = getLegalVars(value.param1.ident, sourceBits); + } + // All mathops can be parametrized by how many shifts we do, and how big the source is + var shifts = 0; + var targetBits; + switch (value.op) { + case 'lshr': { + assert(value.param2.intertype == 'value', 'TODO: unfolding'); + shifts = parseInt(value.param2.ident); + targetBits = sourceBits; + break; + } + case 'shl': { + assert(value.param2.intertype == 'value', 'TODO: unfolding'); + shifts = -parseInt(value.param2.ident); + targetBits = sourceBits; + break; + } + case 'trunc': case 'zext': { + assert(value.param2.intertype == 'type' || value.param2.intertype == 'value', 'TODO: unfolding'); + targetBits = getBits(value.param2.ident); + break; + } + default: throw 'Invalid mathop for legalization: ' + [value.op, item.lineNum, dump(item)]; + } + // Do the legalization + assert(isNumber(shifts), 'TODO: handle nonconstant shifts'); + var targetElements = getLegalVars(item.ident, targetBits); + var sign = shifts >= 0 ? 1 : -1; + var shiftOp = shifts >= 0 ? 'shl' : 'lshr'; + var shiftOpReverse = shifts >= 0 ? 'lshr' : 'shl'; + var whole = shifts >= 0 ? Math.floor(shifts/32) : Math.ceil(shifts/32); + var fraction = Math.abs(shifts % 32); + for (var j = 0; j < targetElements.length; j++) { + var result = { + intertype: 'value', + ident: (j + whole >= 0 && j + whole < sourceElements.length) ? sourceElements[j + whole].ident : '0', + type: 'i32', + }; + if (fraction != 0) { + var other = { + intertype: 'value', + ident: (j + sign + whole >= 0 && j + sign + whole < sourceElements.length) ? sourceElements[j + sign + whole].ident : '0', + type: 'i32', + }; + other = { + intertype: 'mathop', + op: shiftOp, + type: 'i32', + param1: other, + param2: { intertype: 'value', ident: (32 - fraction).toString(), type: 'i32' } + }; + result = { + intertype: 'mathop', + op: shiftOpReverse, + type: 'i32', + param1: result, + param2: { intertype: 'value', ident: fraction.toString(), type: 'i32' } + }; + result = { + intertype: 'mathop', + op: 'or', + type: 'i32', + param1: result, + param2: other + } + } + if (targetElements[j].bits < 32 && shifts < 0) { + // truncate bits that fall off the end. This is not needed in most cases, can probably be optimized out + result = { + intertype: 'mathop', + op: 'and', + type: 'i32', + param1: result, + param2: { intertype: 'value', ident: (Math.pow(2, targetElements[j].bits)-1).toString(), type: 'i32' } + } + } + toAdd.push({ + intertype: 'assign', + ident: targetElements[j].ident, + value: result, + lineNum: item.lineNum + (j/100) + }); + } + if (targetBits <= 64) { + // We are generating a normal legal type here + var legalValue; + if (targetBits == 64 && I64_MODE == 1) { + // Generate an i64-1 [low,high]. This will be unnecessary when we legalize i64s + legalValue = { + intertype: 'value', + ident: '[' + targetElements[0].ident + ',' + targetElements[1].ident + ']', + type: 'rawJS', + // Add the target elements as params so that they are not eliminated as unneeded (the ident is not a simple ident here) + params: targetElements.map(function(element) { + return { intertype: 'value', ident: element.ident, type: 'i32' }; + }) + }; + } else if (targetBits <= 32) { + legalValue = { intertype: 'value', ident: targetElements[0].ident, type: 'rawJS' }; + // truncation to smaller than 32 bits has already been done, if necessary + } else { + throw 'Invalid legal type as target of legalization ' + targetBits; + } + toAdd.push({ + intertype: 'assign', + ident: item.ident, + value: legalValue, + lineNum: item.lineNum + ((j+1)/100) + }); + } + Array.prototype.splice.apply(label.lines, [i, 0].concat(toAdd)); + i += toAdd.length; + continue; + } + } + } + } + i++; + continue; + } + }); + }); + } + + // Add function lines to func.lines, after our modifications to the label lines + data.functions.forEach(function(func) { + func.labels.forEach(function(label) { + func.lines = func.lines.concat(label.lines); + }); + }); + this.forwardItem(data, 'Typevestigator'); } }); @@ -463,6 +771,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/intertyper.js b/src/intertyper.js index ae9794b8..cf1d28ed 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -864,7 +864,7 @@ function intertyper(data, sidePass, baseLineNums) { var ret = { intertype: 'store', valueType: item.tokens[1].text, - value: parseLLVMSegment(segments[0]), // TODO: Make everything use this method, with finalizeLLVMParameter too + value: parseLLVMSegment(segments[0]), pointer: parseLLVMSegment(segments[1]), lineNum: item.lineNum }; diff --git a/src/jsifier.js b/src/jsifier.js index 657a9673..62cab3d5 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({ @@ -719,6 +719,9 @@ function JSify(data, functionsOnly, givenFunctions) { } }); } + makeFuncLineActor('value', function(item) { + return item.ident; + }); makeFuncLineActor('noop', function(item) { return ';'; }); @@ -824,7 +827,7 @@ function JSify(data, functionsOnly, givenFunctions) { }); labelSets.forEach(function(labelSet) { walkInterdata(labelSet.value, function mark(item) { - if (item.intertype == 'value' && item.ident in deps) { + if (item.intertype == 'value' && item.ident in deps && labelSet.ident != item.ident) { deps[labelSet.ident][item.ident] = true; } }); @@ -845,7 +848,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } // If we got here, we have circular dependencies, and must break at least one. - pre = 'var ' + idents[0] + '$phi = ' + valueJSes[idents[i]] + ';' + pre; + pre = 'var ' + idents[0] + '$phi = ' + valueJSes[idents[0]] + ';' + pre; post += 'var ' + idents[0] + ' = ' + idents[0] + '$phi;'; remove(idents[0]); } diff --git a/src/library.js b/src/library.js index 45c64bc7..47e15aae 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..d4ef27eb 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -129,6 +129,11 @@ function isIntImplemented(type) { return type[0] == 'i' || isPointerType(type); } +function getBits(type) { + if (!type || type[0] != 'i') return 0; + return parseInt(type.substr(1)); +} + function isVoidType(type) { return type == 'void'; } @@ -558,22 +563,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 +585,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 @@ -604,13 +600,12 @@ function makeCopyI64(value) { return value + '.slice(0)'; } -function parseI64Constant(str) { - assert(I64_MODE == 1); +// Given a string representation of an integer of arbitrary size, return it +// split up into 32-bit chunks +function parseArbitraryInt(str, bits) { + // We parse the string into a vector of digits, base 10. This is convenient to work on. - if (!isNumber(str)) { - // This is a variable. Copy it, so we do not modify the original - return makeCopyI64(str); - } + assert(bits % 32 == 0 || ('i' + (bits % 32)) in Runtime.INT_TYPES, 'Arbitrary-sized ints must tails that are of legal size'); function str2vec(s) { // index 0 is the highest value var ret = []; @@ -668,6 +663,7 @@ function parseI64Constant(str) { if (str[0] == '-') { // twos-complement is needed + assert(bits == 64, "we only support 64-bit two's complement so far"); str = str.substr(1); v = str2vec('18446744073709551616'); // 2^64 subtract(v, str2vec(str)); @@ -675,23 +671,30 @@ function parseI64Constant(str) { v = str2vec(str); } - var bits = []; + var bitsv = []; while (!isZero(v)) { - bits.push((v[v.length-1] % 2 != 0)+0); + bitsv.push((v[v.length-1] % 2 != 0)+0); v[v.length-1] = v[v.length-1] & 0xfe; divide2(v); } - var low = 0, high = 0; - for (var i = 0; i < bits.length; i++) { - if (i <= 31) { - low += bits[i]*Math.pow(2, i); - } else { - high += bits[i]*Math.pow(2, i-32); - } + var ret = zeros(Math.ceil(bits/32)); + for (var i = 0; i < bitsv.length; i++) { + ret[Math.floor(i/32)] += bitsv[i]*Math.pow(2, i % 32); } + return ret; +} - return '[' + low + ',' + high + ']'; +function parseI64Constant(str) { + assert(I64_MODE == 1); + + if (!isNumber(str)) { + // This is a variable. Copy it, so we do not modify the original + return makeCopyI64(str); + } + + var parsed = parseArbitraryInt(str, 64); + return '[' + parsed[0] + ',' + parsed[1] + ']'; } function parseNumerical(value, type) { @@ -912,23 +915,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 +1006,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; } @@ -1447,6 +1464,8 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) { return finalizeBlockAddress(param); } else if (param.intertype === 'type') { return param.ident; // we don't really want the type here + } else if (param.intertype == 'mathop') { + return processMathop(param); } else { throw 'invalid llvm parameter: ' + param.intertype; } @@ -1465,7 +1484,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 +1492,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 +1608,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 9370fa54..d0391af5 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'; @@ -648,6 +642,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..16a7b665 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. @@ -225,6 +218,7 @@ var DEBUG_TAGS_SHOWING = []; // relooping // unparsedFunctions // metadata + // legalizer // A cached set of defines, generated from the header files. This 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/cases/legalizer_ta2.ll b/tests/cases/legalizer_ta2.ll new file mode 100644 index 00000000..45cdf432 --- /dev/null +++ b/tests/cases/legalizer_ta2.ll @@ -0,0 +1,85 @@ +; ModuleID = 'tests/hello_world.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +define i32 @main() { +entry: + %buffer = alloca i8, i32 1000, align 4 + %bundled = bitcast i8* %buffer to i104* + store i104 31079605376604435891501163880, i104* %bundled, align 4 ; hello world in there + call i32 (i8*)* @puts(i8* %buffer) + + %loaded = load i104* %bundled, align 4 ; save for later + + %backcast = bitcast i104* %bundled to i8* + call i32 (i8*)* @puts(i8* %backcast) + + %temp.buffer = bitcast i8* %buffer to [0 x i8]* + %buffer1 = getelementptr [0 x i8]* %temp.buffer, i32 0, i32 1 + %bundled1 = bitcast i8* %buffer1 to i104* + store i104 31079605376604435891501163880, i104* %bundled1, align 1 ; unaligned + call i32 (i8*)* @puts(i8* %buffer) + +; shifts + %shifted = lshr i104 %loaded, 16 + store i104 %shifted, i104* %bundled, align 4 + call i32 (i8*)* @puts(i8* %buffer) + %shifted2 = lshr i104 %loaded, 32 + store i104 %shifted2, i104* %bundled, align 4 + call i32 (i8*)* @puts(i8* %buffer) + +; store %loaded, make sure has not been modified + store i104 %loaded, i104* %bundled, align 4 + call i32 (i8*)* @puts(i8* %buffer) + + %shifted3 = shl i104 %loaded, 8 + store i104 %shifted3, i104* %bundled, align 4 + store i8 113, i8* %buffer ; remove initial 0 ; 'q' + call i32 (i8*)* @puts(i8* %buffer) + +; trunc + %shifted4 = shl i104 %loaded, 64 + store i104 %shifted4, i104* %bundled, align 4 + %nonzero64 = trunc i104 %loaded to i64 ; remove initial zeros + %bundled64 = bitcast i104* %bundled to i64* + store i64 %nonzero64, i64* %bundled64, align 4 + call i32 (i8*)* @puts(i8* %buffer) + + store i104 0, i104* %bundled, align 4 ; wipe it out + %small32 = trunc i104 %loaded to i32 + %buffer32 = bitcast i8* %buffer to i32* + store i32 %small32, i32* %buffer32, align 4 + call i32 (i8*)* @puts(i8* %buffer) + + store i104 0, i104* %bundled, align 4 ; wipe it out + %small16 = trunc i104 %loaded to i16 + %buffer16 = bitcast i8* %buffer to i16* + store i16 %small16, i16* %buffer16, align 4 + call i32 (i8*)* @puts(i8* %buffer) + + store i104 0, i104* %bundled, align 4 ; wipe it out + %small64 = trunc i104 %loaded to i64 + %buffer64 = bitcast i8* %buffer to i64* + store i64 %small64, i64* %buffer64, align 4 + call i32 (i8*)* @puts(i8* %buffer) + +; zext + store i104 0, i104* %bundled, align 4 ; wipe it out + %pre32 = or i32 6382179, 0 + %big = zext i32 %pre32 to i104 + store i104 %big, i104* %bundled, align 4 + call i32 (i8*)* @puts(i8* %buffer) + + store i104 0, i104* %bundled, align 4 ; wipe it out + %pre64 = zext i32 1684366951 to i64 + %post64 = shl i64 %pre64, 32 + %big64 = or i64 %pre64, %post64 + %bigb = zext i64 %big64 to i104 + store i104 %bigb, i104* %bundled, align 4 + call i32 (i8*)* @puts(i8* %buffer) + + ret i32 1 +} + +declare i32 @puts(i8*) + diff --git a/tests/cases/legalizer_ta2.txt b/tests/cases/legalizer_ta2.txt new file mode 100644 index 00000000..ae9b4a47 --- /dev/null +++ b/tests/cases/legalizer_ta2.txt @@ -0,0 +1,13 @@ +hello, world +hello, world +hhello, world +llo, world +o, world +hello, world +qhello, world +hello, whello +hell +he +hello, w +cba +gfedgfed diff --git a/tests/cases/phiself.ll b/tests/cases/phiself.ll new file mode 100644 index 00000000..81249799 --- /dev/null +++ b/tests/cases/phiself.ll @@ -0,0 +1,69 @@ +; ModuleID = '/tmp/emscripten_temp/src.cpp.o' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [7 x i8] c"cheez\0A\00", align 1 +@.str1 = private unnamed_addr constant [6 x i8] c"*%d*\0A\00", align 1 + +define i32 @main() { +entry: + %retval = alloca i32, align 4 + %x = alloca i32, align 4 + %i = alloca i32, align 4 + store i32 0, i32* %retval + store i32 5, i32* %x, align 4, !dbg !15 + store i32 0, i32* %i, align 4, !dbg !19 + br label %for.cond, !dbg !19 + +for.cond: ; preds = %for.inc, %entry + %xxx = phi i32 [ 43, %entry ], [add (i32 %xxx, i32 3), %for.inc ] ; [#uses=1 type=i1] + %yyy = phi i32 [ 32, %entry ], [add (i32 %yyy, i32 2), %for.inc ] ; [#uses=1 type=i1] + %zzz = phi i32 [ 21, %entry ], [add (i32 %zzz, i32 1), %for.inc ] ; [#uses=1 type=i1] + %0 = load i32* %i, align 4, !dbg !19 + %cmp = icmp slt i32 %0, 6, !dbg !19 + br i1 %cmp, label %for.body, label %for.end, !dbg !19 + +for.body: ; preds = %for.cond + %1 = load i32* %x, align 4, !dbg !20 + %2 = load i32* %i, align 4, !dbg !20 + %mul = mul nsw i32 %1, %2, !dbg !20 + %3 = load i32* %x, align 4, !dbg !20 + %add = add nsw i32 %3, %mul, !dbg !20 + store i32 %add, i32* %x, align 4, !dbg !20 + %4 = load i32* %x, align 4, !dbg !22 + %cmp1 = icmp sgt i32 %4, 1000, !dbg !22 + br i1 %cmp1, label %if.then, label %if.end4, !dbg !22 + +if.then: ; preds = %for.body + %5 = load i32* %x, align 4, !dbg !23 + %rem = srem i32 %5, 7, !dbg !23 + %cmp2 = icmp eq i32 %rem, 0, !dbg !23 + br i1 %cmp2, label %if.then3, label %if.end, !dbg !23 + +if.then3: ; preds = %if.then + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([7 x i8]* @.str, i32 0, i32 0)), !dbg !25 + br label %if.end, !dbg !25 + +if.end: ; preds = %if.then3, %if.then + %6 = load i32* %x, align 4, !dbg !26 + %div = sdiv i32 %6, 2, !dbg !26 + store i32 %div, i32* %x, align 4, !dbg !26 + br label %for.end, !dbg !27 + +if.end4: ; preds = %for.body + br label %for.inc, !dbg !28 + +for.inc: ; preds = %if.end4 + %7 = load i32* %i, align 4, !dbg !29 + %inc = add nsw i32 %7, 1, !dbg !29 + store i32 %inc, i32* %i, align 4, !dbg !29 + br label %for.cond, !dbg !29 + +for.end: ; preds = %if.end, %for.cond + %8 = load i32* %x, align 4, !dbg !30 + %call5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str1, i32 0, i32 0), i32 %zzz), !dbg !30 + ret i32 0, !dbg !31 +} + +declare i32 @printf(i8*, ...) + diff --git a/tests/cases/phiself.py b/tests/cases/phiself.py new file mode 100644 index 00000000..14cf8e21 --- /dev/null +++ b/tests/cases/phiself.py @@ -0,0 +1,3 @@ +if Settings.MICRO_OPTS: + assert '$phi' not in generated, 'we should optimize out self-phis' + diff --git a/tests/cases/phiself.txt b/tests/cases/phiself.txt new file mode 100644 index 00000000..0d4cb717 --- /dev/null +++ b/tests/cases/phiself.txt @@ -0,0 +1 @@ +*26* 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 f92793d4..cbff37eb 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: @@ -412,6 +412,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); @@ -428,7 +439,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') @@ -497,12 +508,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. @@ -515,11 +540,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 @@ -705,7 +740,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 { @@ -3901,7 +3936,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> @@ -4026,7 +4061,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> @@ -4093,7 +4128,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 @@ -4124,7 +4159,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 @@ -4183,7 +4218,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']; @@ -4228,7 +4263,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: @@ -4250,7 +4285,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 @@ -4395,7 +4430,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 @@ -4418,6 +4453,9 @@ def process(filename): for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): shortname = name.replace('.ll', '') if '' not in shortname: continue + if '_ta2' in shortname and not Settings.USE_TYPED_ARRAYS == 2: + print self.skip('case "%s" only relevant for ta2' % shortname) + continue print >> sys.stderr, "Testing case '%s'..." % shortname output_file = path_from_root('tests', 'cases', shortname + '.txt') if Settings.QUANTUM_SIZE == 1: @@ -4523,58 +4561,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): @@ -4813,6 +4799,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') @@ -5024,6 +5012,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 @@ -5201,8 +5191,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) @@ -5297,7 +5288,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)) @@ -5656,6 +5647,14 @@ 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' + assert False, 'Make it easy to disable full llvm opts and use just normal ones' + 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/eliminator/eliminator-test-output.js b/tools/eliminator/eliminator-test-output.js index 594508d0..da9be5cc 100644 --- a/tools/eliminator/eliminator-test-output.js +++ b/tools/eliminator/eliminator-test-output.js @@ -105,4 +105,10 @@ function t() { __label__ = 4; } } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t"] +function f2() { + var $_pre = HEAPU32[($vla + ($storemerge312 << 2) | 0) >> 2]; + var $storemerge312 = $storemerge312 + 1 | 0; + var $8 = $_pre; + c($8); +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2"] diff --git a/tools/eliminator/eliminator-test.js b/tools/eliminator/eliminator-test.js index 02ac0c08..3410499d 100644 --- a/tools/eliminator/eliminator-test.js +++ b/tools/eliminator/eliminator-test.js @@ -114,5 +114,13 @@ function t() { var $cmp3=($12) < ($13); if (!($cmp3)) { __label__ = 4; } } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t"] +function f2() { + var $arrayidx64_phi_trans_insert = $vla + ($storemerge312 << 2) | 0; + var $_pre = HEAPU32[$arrayidx64_phi_trans_insert >> 2]; + var $phitmp = $storemerge312 + 1 | 0; + var $storemerge312 = $phitmp; + var $8 = $_pre; + c($8); +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2"] diff --git a/tools/eliminator/eliminator.coffee b/tools/eliminator/eliminator.coffee index 8b99338a..84e544b2 100644 --- a/tools/eliminator/eliminator.coffee +++ b/tools/eliminator/eliminator.coffee @@ -262,8 +262,14 @@ class Eliminator else if type is 'var' for [varName, varValue] in node[1] if varValue? then traverse varValue, checkForMutations + # Mark the variable as live if @isSingleDef[varName] isLive[varName] = true + # Mark variables that depend on it as no longer live + if @dependsOn[varName]? + for varNameDep of @dependsOn[varName] + if isLive[varNameDep] + isLive[varNameDep] = false return node else checkForMutations node, type 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/find_bigis.py b/tools/find_bigis.py new file mode 100644 index 00000000..d11c1a81 --- /dev/null +++ b/tools/find_bigis.py @@ -0,0 +1,18 @@ +''' +Simple tool to find big i types in an .ll file. Anything over i64 is of interest. +''' + +import os, sys, re + +filename = sys.argv[1] +data = open(filename).read() +iss = re.findall('[^%]i\d+ [^=]', data) +set_iss = set(iss) +bigs = [] +for iss in set_iss: + size = int(iss[2:-2]) + if size > 64: + bigs.append(size) +bigs.sort() +print bigs + 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 |