aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@mozilla.com>2010-12-19 13:43:26 -0800
committerAlon Zakai <azakai@mozilla.com>2010-12-19 13:43:26 -0800
commit64cfd1d3c852c51caa0c7af7964fff293da16892 (patch)
tree254aa7db90a0d7c9fa30526d7033d51a37cbed47
parent566ea2c09419d73710a16d064ad77c565f5787a8 (diff)
CHECK_OVERFLOWS option
-rw-r--r--src/analyzer.js1
-rw-r--r--src/jsifier.js43
-rw-r--r--src/preamble.js19
-rw-r--r--src/settings.js8
-rw-r--r--src/utility.js2
-rw-r--r--tests/runner.py32
6 files changed, 72 insertions, 33 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 936482a3..4aad192b 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -241,6 +241,7 @@ function analyzer(data, givenTypes) {
// Normal variables
func.lines.forEach(function(item) {
if (item.intertype in set('assign', 'fastgetelementptrload')) {
+ if (!item.value.tokens.slice(-1)[0].item) throw 'Did you run llvm-dis with -show-annotations?';
func.variables[item.ident] = {
ident: item.ident,
type: item.value.type,
diff --git a/src/jsifier.js b/src/jsifier.js
index f4565bcc..1404e63f 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -156,7 +156,7 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions) {
return [finalizeLLVMFunctionCall(value)];
} else if (Runtime.isNumberType(type) || pointingLevels(type) >= 1) {
return indexizeFunctions(parseNumerical(toNiceIdent(value.text)));
- } else if (value.text == 'zeroinitializer') {
+ } else if (value.text in set('zeroinitializer', 'undef')) { // undef doesn't really need initting, but why not
return makeEmptyStruct(type);
} else if (value.text && value.text[0] == '"') {
value.text = value.text.substr(1, value.text.length-2);
@@ -709,18 +709,24 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions) {
ident2 = makeUnSign(ident2, type);
}
}
+ function checkOverflow(text) {
+ if (!CHECK_OVERFLOWS) return text;
+ if (item.type[0] !== 'i') return text;
+ var bits = item.type.substr(1);
+ return 'CHECK_OVERFLOW(' + text + ', ' + bits + ')';
+ }
switch (op) {
- case 'add': return ident1 + ' + ' + ident2;
- case 'sub': return ident1 + ' - ' + ident2;
- case 'sdiv': case 'udiv': return 'Math.floor(' + ident1 + ' / ' + ident2 + ')';
- case 'mul': return ident1 + ' * ' + ident2;
+ case 'add': return checkOverflow(ident1 + ' + ' + ident2);
+ case 'sub': return checkOverflow(ident1 + ' - ' + ident2);
+ case 'sdiv': case 'udiv': return checkOverflow('Math.floor(' + ident1 + ' / ' + ident2 + ')');
+ case 'mul': return checkOverflow(ident1 + ' * ' + ident2);
case 'urem': case 'srem': return 'Math.floor(' + ident1 + ' % ' + ident2 + ')';
- case 'or': return ident1 + ' | ' + ident2;
- case 'and': return ident1 + ' & ' + ident2;
- case 'xor': return ident1 + ' ^ ' + ident2;
- case 'shl': return ident1 + ' << ' + ident2;
- case 'ashr': return ident1 + ' >> ' + ident2;
- case 'lshr': return ident1 + ' >>> ' + ident2;
+ case 'or': return ident1 + ' | ' + ident2; // XXX this forces into a 32-bit int - add overflow-style checks?
+ case 'and': return ident1 + ' & ' + ident2; // XXX ^
+ case 'xor': return ident1 + ' ^ ' + ident2; // XXX ^
+ case 'shl': return ident1 + ' << ' + ident2; // XXX ^
+ case 'ashr': return ident1 + ' >> ' + ident2; // XXX ^
+ case 'lshr': return ident1 + ' >>> ' + ident2; // XXX ^
case 'fadd': return ident1 + ' + ' + ident2;
case 'fsub': return ident1 + ' - ' + ident2;
case 'fdiv': return ident1 + ' / ' + ident2;
@@ -775,18 +781,6 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions) {
default: throw 'Unknown mathcmp op: ' + item.op;
}
} });
-/*
- return [{
- __result__: true,
- intertype: 'mathop',
- op: op,
- variant: variant,
- type: item.tokens[1].text,
- ident: item.tokens[2].text,
- value: item.tokens[4].text,
- lineNum: item.lineNum,
- }];
-*/
// Given two values and an operation, returns the result of that operation.
// Tries to do as much as possible at compile time.
@@ -881,7 +875,8 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions) {
case 'icmp': case 'mul': case 'zext': // TODO: Other mathops
var temp = {
op: item.intertype,
- variant: item.variant
+ variant: item.variant,
+ type: item.type
};
for (var i = 1; i <= 4; i++) {
if (item.params[i-1]) {
diff --git a/src/preamble.js b/src/preamble.js
index c895231f..a4e685d0 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -75,6 +75,14 @@ function __Z18UNPROTECT_HEAPADDRPv(dest) {
//==========================================
#endif
+#if CHECK_OVERFLOWS
+function CHECK_OVERFLOW(value, bits) {
+ assert(value !== Infinity && value !== -Infinity, 'Infinity!');
+ assert(Math.abs(value) < Math.pow(2, bits-1), 'Overflow!');
+ return value;
+}
+#endif
+
#if LABEL_DEBUG
var INDENT = '';
#endif
@@ -87,12 +95,15 @@ var ABORT = false;
var undef = 0;
+function abort(text) {
+ print(text + ':\n' + (new Error).stack);
+ ABORT = true;
+ throw "Assertion: " + text;
+}
+
function assert(condition, text) {
if (!condition) {
- var text = "Assertion failed: " + text;
- print(text + ':\n' + (new Error).stack);
- ABORT = true;
- throw "Assertion: " + text;
+ abort('Assertion failed: ' + text);
}
}
diff --git a/src/settings.js b/src/settings.js
index 04bfa79a..62143fa9 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -34,6 +34,14 @@ SAFE_HEAP = 0; // Check each write to the heap against a list of blocked address
LABEL_DEBUG = 0; // Print out labels and functions as we enter them
EXCEPTION_DEBUG = 0; // Print out exceptions in emscriptened code
EXECUTION_TIMEOUT = -1; // Throw an exception after X seconds - useful to debug infinite loops
+CHECK_OVERFLOWS = 0; // Add code that checks for overflows in integer math operations.
+ // There is currently not much to do to handle overflows if they occur.
+ // We can add code to simulate i32/i64 overflows in JS, but that would
+ // be very slow. It probably makes more sense to avoid overflows in
+ // C/C++ code. For example, if you have an int that you multiply by
+ // 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.
// Compiler debugging options
DEBUG_TAGS_SHOWING = [];
diff --git a/src/utility.js b/src/utility.js
index 8b014957..ec4bf99b 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -16,7 +16,7 @@ function dump(item) {
return ret + '// ' + text;
}
try {
- return lineify(JSON.stringify(item));
+ return lineify(JSON.stringify(item).substr(0, 80*25));
} catch(e) {
var ret = [];
for (var i in item) {
diff --git a/tests/runner.py b/tests/runner.py
index 8b9be185..23d826ae 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -82,7 +82,7 @@ class RunnerCore(unittest.TestCase):
if optimization_level > 1:
LLVM_OPT_OPTS.append('-constmerge')
-
+
## Build JavaScript code from source code
def build(self, src, dirname, filename, output_processor=None, main_file=None):
# Copy over necessary files for compiling the source
@@ -127,7 +127,7 @@ class RunnerCore(unittest.TestCase):
def do_emscripten(self, filename, output_processor=None):
# Run Emscripten
exported_settings = {}
- for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'GUARD_MEMORY', 'USE_TYPED_ARRAYS', 'SAFE_HEAP']:
+ for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'GUARD_MEMORY', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS']:
exported_settings[setting] = eval(setting)
out = open(filename + '.o.js', 'w') if not OUTPUT_TO_SCREEN else None
timeout_run(Popen([EMSCRIPTEN, filename + '.o.ll', COMPILER_ENGINE[0], str(exported_settings).replace("'", '"')], stdout=out, stderr=STDOUT), TIMEOUT, 'Compiling')
@@ -1280,6 +1280,7 @@ if 'benchmark' not in sys.argv:
assert COMPILER_ENGINE != SPIDERMONKEY_ENGINE
global SAFE_HEAP; SAFE_HEAP = 0 # Has some actual loads of unwritten-to places, in the C++ code...
+ global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0 # Overflows in hash loop... seems to work though, doesn't overflow too much
self.do_test(path_from_root(['tests', 'cubescript']), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp')
@@ -1301,12 +1302,14 @@ if 'benchmark' not in sys.argv:
self.do_ll_test(path_from_root(['tests', 'bullet', 'bulletTest.ll']), open(path_from_root(['tests', 'bullet', 'output.txt']), 'r').read())
def test_lua(self):
+ global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0 # Overflows in luaS_newlstr hash loop... seems to work though, doesn't overflow too much
self.do_ll_test(path_from_root(['tests', 'lua', 'lua.ll']),
'hello lua world!\n\n\n17.00000000000\n\n\n1.00000000000\n\n\n2.00000000000\n\n\n3.00000000000\n\n\n4.00000000000\n\n\n7.00000000000',
args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''],
f_opt_ll_file=path_from_root(['tests', 'lua', 'lua.Os.ll']))
def test_python(self):
+ global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0 # Overflows in string_hash... seems to work though, doesn't overflow too much
global RELOOP; RELOOP = 0 # Too slow; we do care about typed arrays and OPTIMIZE though
global SAFE_HEAP; SAFE_HEAP = 0 # Has bitfields etc.
self.do_ll_test(path_from_root(['tests', 'python', 'python.ll']),
@@ -1382,16 +1385,37 @@ if 'benchmark' not in sys.argv:
except Exception, e:
assert 'Assertion failed: Load-store consistency assumption failure!' in str(e), str(e)
+ def test_check_overflow(self):
+ if LLVM_OPTS: return # We check for overflows when !LLVM_OPTS
+ src = '''
+ #include<stdio.h>
+ int main() {
+ int t = 77;
+ for (int i = 0; i < 30; i++) {
+ //t = (t << 2) + t + 1; // This would have worked, since << forces into 32-bit int...
+ t = t*5 + 1; // Python lookdict_string has ~the above line, which turns into this one with optimizations...
+ printf("%d,%d\\n", t, t & 127);
+ }
+ return 0;
+ }
+ '''
+ try:
+ self.do_test(src, '*nothingatall*')
+ except Exception, e:
+ assert 'Overflow!' in str(e), str(e)
+
+
# Generate tests for all our compilers
def make_test(compiler, llvm_opts, embetter):
class TT(T):
def setUp(self):
- global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, GUARD_MEMORY, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP
+ global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, GUARD_MEMORY, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS
COMPILER = compiler['path']
QUANTUM_SIZE = compiler['quantum_size']
RELOOP = OPTIMIZE = USE_TYPED_ARRAYS = embetter
GUARD_MEMORY = SAFE_HEAP = 1-embetter
LLVM_OPTS = llvm_opts
+ CHECK_OVERFLOWS = 1 - llvm_opts
if LLVM_OPTS:
self.pick_llvm_opts(3, True)
@@ -1415,7 +1439,7 @@ else:
QUANTUM_SIZE = 4
RELOOP = OPTIMIZE = USE_TYPED_ARRAYS = 1
- GUARD_MEMORY = SAFE_HEAP = 0
+ GUARD_MEMORY = SAFE_HEAP = CHECK_OVERFLOWS = 0
LLVM_OPTS = 1
TEST_REPS = 10