aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2011-05-26 13:32:57 -0700
committerAlon Zakai <alonzakai@gmail.com>2011-05-26 13:32:57 -0700
commit78ee9a8f64d734405e8e85e03c3d3768fce45e41 (patch)
tree14f97e1a795561dfdc8a465aca09cb1979199f64
parentd3efb7dcb71f0d9d6df09fee687fa1dbd0bde9bf (diff)
improve CHECK_* options, add initial version of AUTO_OPTIMIZE
-rw-r--r--src/library.js19
-rw-r--r--src/parseTools.js12
-rw-r--r--src/preamble.js69
-rw-r--r--src/settings.js13
-rw-r--r--tests/runner.py44
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