diff options
author | Alon Zakai <alonzakai@gmail.com> | 2011-05-26 13:32:57 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2011-05-26 13:32:57 -0700 |
commit | 78ee9a8f64d734405e8e85e03c3d3768fce45e41 (patch) | |
tree | 14f97e1a795561dfdc8a465aca09cb1979199f64 | |
parent | d3efb7dcb71f0d9d6df09fee687fa1dbd0bde9bf (diff) |
improve CHECK_* options, add initial version of AUTO_OPTIMIZE
-rw-r--r-- | src/library.js | 19 | ||||
-rw-r--r-- | src/parseTools.js | 12 | ||||
-rw-r--r-- | src/preamble.js | 69 | ||||
-rw-r--r-- | src/settings.js | 13 | ||||
-rw-r--r-- | tests/runner.py | 44 |
5 files changed, 135 insertions, 22 deletions
diff --git a/src/library.js b/src/library.js index 471bbc96..bd9491af 100644 --- a/src/library.js +++ b/src/library.js @@ -1297,9 +1297,15 @@ function reSign(value, bits, ignore) { if (value <= 0) return value; var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32 : Math.pow(2, bits-1); +#if CHECK_SIGNS + var noted = false; +#endif if (value >= half) { #if CHECK_SIGNS - if (!ignore) CorrectionsMonitor.note('ReSign'); + if (!ignore) { + CorrectionsMonitor.note('ReSign'); + noted = true; + } #endif value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts } @@ -1308,9 +1314,18 @@ function reSign(value, bits, ignore) { // without CHECK_SIGNS, we would just do the |0 shortcut, so check that that // would indeed give the exact same result. if (bits === 32 && (value|0) !== value && typeof value !== 'boolean') { - if (!ignore) CorrectionsMonitor.note('ReSign'); + if (!ignore) { + CorrectionsMonitor.note('ReSign'); + noted = true; + } } + if (!noted) CorrectionsMonitor.note('ReSign', true); #endif return value; } +// Just a stub. We don't care about noting compile-time corrections. But they are called. +var CorrectionsMonitor = { + note: function(){} +}; + diff --git a/src/parseTools.js b/src/parseTools.js index 9e0fc24c..5dec458e 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -848,9 +848,15 @@ function getGetElementPtrIndexes(item) { function handleOverflow(text, bits) { if (!bits) return text; - if (bits <= 32 && correctOverflows()) text = '(' + text + ')&' + (Math.pow(2, bits) - 1); - if (!CHECK_OVERFLOWS || correctSpecificOverflow()) return text; // If we are correcting a specific overflow here, do not check for it - return 'CHECK_OVERFLOW(' + text + ', ' + bits + ')'; + var correct = correctOverflows(); + warn(!correct || bits <= 32, 'Cannot correct overflows of this many bits: ' + bits); + if (CHECK_OVERFLOWS) return 'CHECK_OVERFLOW(' + text + ', ' + bits + ')'; + if (!correct) return text; + if (bits <= 32) { + return '(' + text + ')&' + (Math.pow(2, bits) - 1); + } else { + return text; // We warned about this earlier + } } // From parseLLVMSegment diff --git a/src/preamble.js b/src/preamble.js index e05e1e95..cb4e8571 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -103,19 +103,47 @@ function __Z18UNPROTECT_HEAPADDRPv(dest) { #endif var CorrectionsMonitor = { - MAX_ALLOWED: 0, // Infinity, +#if AUTO_OPTIMIZE + MAX_ALLOWED: Infinity, +#else + MAX_ALLOWED: 0, // XXX +#endif corrections: 0, sigs: {}, - note: function(type) { - var sig = type + '|' + new Error().stack; + note: function(type, succeed, sig) { + if (!succeed) { + this.corrections++; + if (this.corrections >= this.MAX_ALLOWED) abort('\n\nToo many corrections!'); + } +#if AUTO_OPTIMIZE + if (succeed) return; // XXX - enable this later on, as a profiling tool + if (!sig) + sig = (new Error().stack).toString().split('\n')[2].split(':').slice(-1)[0]; // Spidermonkey-specific FIXME + sig = type + '|' + sig; if (!this.sigs[sig]) { - print('Correction: ' + sig); - this.sigs[sig] = 0; + //print('Correction: ' + sig); + this.sigs[sig] = [0, 0]; // fail, succeed + } + this.sigs[sig][succeed ? 1 : 0]++; +#endif + }, + + print: function() { + var items = []; + for (var sig in this.sigs) { + items.push({ + sig: sig, + fails: this.sigs[sig][0], + succeeds: this.sigs[sig][1], + total: this.sigs[sig][0] + this.sigs[sig][1] + }); + } + items.sort(function(x, y) { return y.total - x.total; }); + for (var i = 0; i < items.length; i++) { + var item = items[i]; + print(item.sig + ' : ' + item.total + ' hits, %' + (Math.floor(100*item.fails/item.total)) + ' failures'); } - this.sigs[sig]++; - this.corrections++; - if (this.corrections >= this.MAX_ALLOWED) abort('\n\nToo many corrections!'); } }; @@ -131,8 +159,28 @@ function cRound(x) { //======================================== function CHECK_OVERFLOW(value, bits, ignore) { if (ignore) return value; - if (value === Infinity || value === -Infinity || Math.abs(value) >= Math.pow(2, bits)) { + var twopbits = Math.pow(2, bits); + var twopbits1 = Math.pow(2, bits-1); + // For signedness issue here, see settings.js, CHECK_SIGNED_OVERFLOWS +#if CHECK_SIGNED_OVERFLOWS + if (value === Infinity || value === -Infinity || value >= twopbits1 || value < -twopbits1) { + CorrectionsMonitor.note('SignedOverflow'); + if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) CorrectionsMonitor.note('Overflow'); +#else + if (value === Infinity || value === -Infinity || Math.abs(value) >= twopbits) { CorrectionsMonitor.note('Overflow'); +#endif +#if CORRECT_OVERFLOWS + // Fail on >32 bits - we warned at compile time + if (bits <= 32) { + value = value & (twopbits - 1); + } +#endif + } else { +#if CHECK_SIGNED_OVERFLOWS + CorrectionsMonitor.note('SignedOverflow', 1); +#endif + CorrectionsMonitor.note('Overflow', 1); } return value; } @@ -296,6 +344,9 @@ function __shutdownRuntime__() { func(atexit.arg); } //HEAP = IHEAP = FHEAP = null; // allow browser to GC? + + // Print summary of correction activity + CorrectionsMonitor.print(); } diff --git a/src/settings.js b/src/settings.js index b50ac04d..45934c8d 100644 --- a/src/settings.js +++ b/src/settings.js @@ -63,9 +63,10 @@ CHECK_OVERFLOWS = 0; // Add code that checks for overflows in integer math opera // some factor, in order to get 'random' hash values - by taking // that |value & hash_table_size| - then multiplying enough times will overflow. // But instead, you can do |value = value & 30_BITS| in each iteration. - // Note: CHECK_OVERFLOWS checks for overflows *after* CORRECT_OVERFLOWS - // has operated (if it has). So you must run *without* CORRECT_OVERFLOWS - // for this option to work. (Sorry for the discrepancy with CHECK_SIGNS - FIXME.) +CHECK_SIGNED_OVERFLOWS = 0; // Whether to allow *signed* overflows - our correction for overflows generates signed + // values (since we use &). This means that we correct some things are not strictly overflows, + // and we cause them to be signed (which may lead to unnecessary unSign()ing later). + // With this enabled, we check signed overflows for CHECK_OVERFLOWS CORRECT_OVERFLOWS = 1; // Experimental code that tries to prevent unexpected JS overflows in integer // mathops, by doing controlled overflows (sort of parallel to a CPU). // Note that as mentioned above in CHECK_OVERFLOWS, the best thing is to @@ -83,6 +84,12 @@ CORRECT_ROUNDINGS = 1; // C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no // Math.floor is to negative, ceil to positive. With CORRECT_ROUNDINGS, // we will do slow but correct C rounding operations. +AUTO_OPTIMIZE = 0; // When run with the CHECK_* options, will not fail on errors. Instead, will + // keep a record of which checks succeeded and which failed. On shutdown, will + // print out that information. This is useful for knowing which lines need + // checking enabled and which do not, that is, this is a way to automate the + // generation of line data for CORRECT_*_LINES options + EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported, so they are guaranteed to // be accessible outside of the generated code. diff --git a/tests/runner.py b/tests/runner.py index d24b2920..b978acb4 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -228,7 +228,7 @@ class RunnerCore(unittest.TestCase): def do_emscripten(self, filename, output_processor=None): # Run Emscripten exported_settings = {} - for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK']: + for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'AUTO_OPTIMIZE']: value = eval(setting) exported_settings[setting] = value compiler_output = timeout_run(Popen([EMSCRIPTEN, filename + '.o.ll', str(exported_settings).replace("'", '"'), filename + '.o.js'], stdout=PIPE, stderr=STDOUT), TIMEOUT, 'Compiling') @@ -437,6 +437,7 @@ if 'benchmark' not in sys.argv: def test_unsigned(self): global CORRECT_SIGNS; CORRECT_SIGNS = 1 # We test for exactly this sort of thing here + global CHECK_SIGNS; CHECK_SIGNS = 0 src = ''' #include <stdio.h> const signed char cvals[2] = { -1, -2 }; // compiler can store this is a string, so -1 becomes \FF, and needs re-signing @@ -1750,6 +1751,7 @@ if 'benchmark' not in sys.argv: # Overflows happen in hash loop global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1 + global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0 self.do_test(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') @@ -1770,6 +1772,7 @@ if 'benchmark' not in sys.argv: # Overflows in luaS_newlstr hash loop global SAFE_HEAP; SAFE_HEAP = 0 # Has various warnings, with copied HEAP_HISTORY values (fixed if we copy 'null' as the type) global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1 + global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0 global CORRECT_SIGNS; CORRECT_SIGNS = 1 # Not sure why, but needed global INIT_STACK; INIT_STACK = 1 # TODO: Investigate why this is necessary @@ -1903,6 +1906,7 @@ if 'benchmark' not in sys.argv: global SAFE_HEAP; SAFE_HEAP = 0 # Has variable object #global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1 + global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0 #global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 1 #global CHECK_SIGNS; CHECK_SIGNS = 1 @@ -2055,6 +2059,7 @@ if 'benchmark' not in sys.argv: def test_python(self): # Overflows in string_hash global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1 + global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0 global RELOOP; RELOOP = 0 # Too slow; we do care about typed arrays and OPTIMIZE though global SAFE_HEAP; SAFE_HEAP = 0 # Has bitfields which are false positives. Also the PyFloat_Init tries to detect endianness. global CORRECT_SIGNS; CORRECT_SIGNS = 1 # Not sure why, but needed @@ -2065,6 +2070,7 @@ if 'benchmark' not in sys.argv: ### Test cases in separate files def test_cases(self): + global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0 if LLVM_OPTS: return self.skip() # Our code is not exactly 'normal' llvm assembly for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): shortname = name.replace('.ll', '') @@ -2325,6 +2331,8 @@ if 'benchmark' not in sys.argv: CORRECT_SIGNS_LINES = ["src.cpp:9"] self.do_test(src, '*1*') + # Overflows + src = ''' #include<stdio.h> int main() { @@ -2337,8 +2345,6 @@ if 'benchmark' not in sys.argv: } ''' - # Overflows - correct = '*186854335,63*' CORRECT_OVERFLOWS = 0 try: @@ -2422,13 +2428,40 @@ if 'benchmark' not in sys.argv: self.do_test(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') self.do_test(src.replace('TYPE', 'int'), '*-2**2**-5**5*') + def test_autooptimize(self): + global CHECK_OVERFLOWS, CORRECT_OVERFLOWS, AUTO_OPTIMIZE + + AUTO_OPTIMIZE = 1 + CHECK_OVERFLOWS = 1 + CORRECT_OVERFLOWS = 1 + + src = ''' + #include<stdio.h> + int main() { + int t = 77; + for (int i = 0; i < 30; i++) { + t = t*5 + 1; + } + printf("*%d,%d*\\n", t, t & 127); + return 0; + } + ''' + + def check(output): + # TODO: check the line # + assert 'Overflow' in output, 'no indication of Overflow corrections' + assert '12 hits, %100 failures' in output, 'no notice of the amount of hits and failures' + return output + + self.do_test(src, '*186854335,63*', output_nicerizer=check) + # Generate tests for all our compilers def make_test(name, compiler, llvm_opts, embetter, quantum_size): exec(''' class %s(T): def setUp(self): - global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, ASSERTIONS, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CORRECT_OVERFLOWS_LINES, CORRECT_SIGNS, CORRECT_SIGNS_LINES, CHECK_SIGNS, COMPILER_TEST_OPTS, CORRECT_ROUNDINGS, CORRECT_ROUNDINGS_LINES, INVOKE_RUN, SAFE_HEAP_LINES, INIT_STACK + global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, ASSERTIONS, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CORRECT_OVERFLOWS_LINES, CORRECT_SIGNS, CORRECT_SIGNS_LINES, CHECK_SIGNS, COMPILER_TEST_OPTS, CORRECT_ROUNDINGS, CORRECT_ROUNDINGS_LINES, INVOKE_RUN, SAFE_HEAP_LINES, INIT_STACK, AUTO_OPTIMIZE COMPILER = '%s' llvm_opts = %d @@ -2440,6 +2473,7 @@ class %s(T): ASSERTIONS = 1-embetter SAFE_HEAP = 1-(embetter and llvm_opts) LLVM_OPTS = llvm_opts + AUTO_OPTIMIZE = 0 CHECK_OVERFLOWS = 1-(embetter or llvm_opts) CORRECT_OVERFLOWS = 1-(embetter and llvm_opts) CORRECT_SIGNS = 0 @@ -2487,7 +2521,7 @@ else: QUANTUM_SIZE = 1 RELOOP = OPTIMIZE = 1 USE_TYPED_ARRAYS = 0 - ASSERTIONS = SAFE_HEAP = CHECK_OVERFLOWS = CORRECT_OVERFLOWS = CHECK_SIGNS = INIT_STACK = 0 + ASSERTIONS = SAFE_HEAP = CHECK_OVERFLOWS = CORRECT_OVERFLOWS = CHECK_SIGNS = INIT_STACK = AUTO_OPTIMIZE = 0 INVOKE_RUN = 1 CORRECT_SIGNS = 0 CORRECT_ROUNDINGS = 0 |