aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@mozilla.com>2010-11-21 17:43:22 -0800
committerAlon Zakai <azakai@mozilla.com>2010-11-21 17:43:22 -0800
commit52d04311943f4ccd5ec86bb6982c6f755d7db888 (patch)
tree61d957c9bbac42f2f2a049138dcb238e5e79b5fc /src
parentfa5bac952a9eb74d4964b8497454aac1b32299a5 (diff)
SAFE_HEAP now validates the load-store consistency assumption, plus minor related fixes
Diffstat (limited to 'src')
-rw-r--r--src/compiler.js6
-rw-r--r--src/intertyper.js9
-rw-r--r--src/jsifier.js26
-rw-r--r--src/library.js8
-rw-r--r--src/parseTools.js4
-rw-r--r--src/preamble.js40
-rw-r--r--src/runtime.js20
-rw-r--r--src/utility.js5
8 files changed, 98 insertions, 20 deletions
diff --git a/src/compiler.js b/src/compiler.js
index 7626bde5..395c7acd 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -12,12 +12,10 @@ load('settings.js');
load('utility.js');
load('enzymatic.js');
-load('library.js');
load('parseTools.js');
load('intertyper.js');
load('analyzer.js');
load('jsifier.js');
-load('runtime.js');
//===============================
// Main
@@ -28,6 +26,9 @@ var settings = JSON.parse(readline());
for (setting in settings) {
this[setting] = settings[setting];
}
+var CONSTANTS = { 'QUANTUM_SIZE': QUANTUM_SIZE };
+
+load('runtime.js');
// Sanity of settings
@@ -43,5 +44,6 @@ do {
} while(true);
// Do it
+eval(preprocess(read('library.js'), CONSTANTS));
print(JSify(analyzer(intertyper(lines))));
diff --git a/src/intertyper.js b/src/intertyper.js
index e98012f9..db1a58f5 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -335,11 +335,17 @@ function intertyper(data, parseFunctions, baseLineNum) {
var ident = item.tokens[0].text;
while (item.tokens[2].text in set('private', 'constant', 'appending', 'global', 'weak_odr', 'internal', 'linkonce', 'linkonce_odr', 'weak', 'hidden'))
item.tokens.splice(2, 1);
+ var external = false;
+ if (item.tokens[2].text === 'external') {
+ external = true;
+ item.tokens.splice(2, 1);
+ }
var ret = {
__result__: true,
intertype: 'globalVariable',
ident: ident,
type: item.tokens[2].text,
+ external: external,
lineNum: item.lineNum,
};
if (ident == '@llvm.global_ctors') {
@@ -699,6 +705,9 @@ function intertyper(data, parseFunctions, baseLineNum) {
// external function stub
substrate.addZyme('External', {
processItem: function(item) {
+ if (item.tokens[1].text == 'noalias') {
+ item.tokens.splice(1, 1);
+ }
return [{
__result__: true,
intertype: 'functionStub',
diff --git a/src/jsifier.js b/src/jsifier.js
index fb813e30..4720798c 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -76,7 +76,12 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions) {
return '{ ' + ret.join(', ') + ' }';
}
- return makeGetSlab(ptr, type) + '[' + calcFastOffset(ptr, pos, noNeedFirst) + ']';
+ var offset = calcFastOffset(ptr, pos, noNeedFirst);
+ if (SAFE_HEAP) {
+ return 'SAFE_HEAP_LOAD(' + offset + ', "' + safeQuote(type) + '")';
+ } else {
+ return makeGetSlab(ptr, type) + '[' + offset + ']';
+ }
}
function indexizeFunctions(value) { // TODO: Also check for other functions (externals, library, etc.)
@@ -99,15 +104,13 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions) {
value = indexizeFunctions(value);
var offset = calcFastOffset(ptr, pos, noNeedFirst);
if (SAFE_HEAP) {
- return 'SAFE_HEAP_STORE(' + offset + ', ' + value + ')';
+ return 'SAFE_HEAP_STORE(' + offset + ', ' + value + ', "' + safeQuote(type) + '")';
} else {
return makeGetSlab(ptr, type) + '[' + offset + '] = ' + value;
}
}
function makeEmptyStruct(type) {
- dprint('types', '??makeemptystruct?? ' + dump(type));
- // XXX hardcoded ptr impl
var ret = [];
var typeData = TYPES[type];
assertTrue(typeData);
@@ -218,13 +221,16 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions) {
item.JS = '\n__globalConstructor__ = function() {\n' +
item.ctors.map(function(ctor) { return ' ' + toNiceIdent(ctor) + '();' }).join('\n') +
'\n}\n';
- } else if (item.type == 'external') {
- item.JS = 'var ' + item.ident + ' = ' + '0; /* external value? */';
} else {
item.JS = 'var ' + item.ident + ';';
return [item, {
intertype: 'GlobalVariable',
- JS: 'globalFuncs.push(function() { return ' + item.ident + ' = ' + parseConst(item.value, item.type) + ' });',
+ JS: 'globalFuncs.push(function() { return ' + item.ident + ' = ' + (
+ item.external ?
+ makePointer(JSON.stringify(makeEmptyStruct(item.type)), null, 'ALLOC_STATIC', item.type) + ' /* external value? */'
+ :
+ parseConst(item.value, item.type)
+ ) + ' });',
__result__: true,
}];
}
@@ -868,10 +874,11 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions) {
function makeFunctionCall(ident, params, funcData) {
// Special cases
if (ident == '_llvm_va_start') {
+ // varargs
var args = 'Array.prototype.slice.call(arguments, __numArgs__)';
var data = 'Pointer_make([' + args + '.length].concat(' + args + '), 0)';
if (SAFE_HEAP) {
- return 'SAFE_HEAP_STORE(' + params[0].ident + ', ' + data + ', 0)';
+ return 'SAFE_HEAP_STORE(' + params[0].ident + ', ' + data + ', null)';
} else {
return 'IHEAP[' + params[0].ident + '] = ' + data;
}
@@ -927,8 +934,7 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions) {
if (functionsOnly) return ret;
- var params = { 'QUANTUM_SIZE': QUANTUM_SIZE };
- var body = preprocess(read('preamble.js').replace('{{RUNTIME}}', getRuntime()) + ret + read('postamble.js'), params);
+ var body = preprocess(read('preamble.js').replace('{{RUNTIME}}', getRuntime()) + ret + read('postamble.js'), CONSTANTS);
function reverse_(x) {
if (LLVM_STYLE === 'old') {
return x.reverse();
diff --git a/src/library.js b/src/library.js
index a052c44b..c1312e1f 100644
--- a/src/library.js
+++ b/src/library.js
@@ -117,7 +117,11 @@ var Library = {
strcpy: function(pdest, psrc) {
var i = 0;
do {
+#if SAFE_HEAP
+ SAFE_HEAP_STORE(pdest+i, IHEAP[psrc+i], null);
+#else
IHEAP[pdest+i] = IHEAP[psrc+i];
+#endif
i ++;
} while (IHEAP[psrc+i-1] != 0);
},
@@ -125,7 +129,11 @@ var Library = {
strncpy: function(pdest, psrc, num) {
var padding = false;
for (var i = 0; i < num; i++) {
+#if SAFE_HEAP
+ SAFE_HEAP_STORE(pdest+i, padding ? 0 : IHEAP[psrc+i], null);
+#else
IHEAP[pdest+i] = padding ? 0 : IHEAP[psrc+i];
+#endif
padding = padding || IHEAP[psrc+i] == 0;
}
},
diff --git a/src/parseTools.js b/src/parseTools.js
index d2e3655f..37e0a881 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -387,6 +387,10 @@ function parseNumerical(value, type) {
} else if (value == 'null') {
// NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.)
value = '0';
+ } else if (value === 'true') {
+ return '1';
+ } else if (value === 'false') {
+ return '0';
}
if (isNumber(value)) {
return eval(value).toString(); // will change e.g. 5.000000e+01 to 50
diff --git a/src/preamble.js b/src/preamble.js
index 1623d387..f02ada7a 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -16,13 +16,47 @@ var __ATEXIT__ = [];
#if SAFE_HEAP
// Semi-manual memory corruption debugging
var HEAP_WATCHED = {};
-function SAFE_HEAP_STORE(dest, value) {
+var HEAP_HISTORY = {};
+function SAFE_HEAP_CLEAR(dest) {
+ HEAP_HISTORY[dest] = [];
+}
+function SAFE_HEAP_ACCESS(dest, type, store) {
+ if (type && type[type.length-1] == '*') type = 'i32'; // pointers are ints, for our purposes here
+ // Note that this will pass even with unions: You can store X, load X, then store Y and load Y.
+ // You cannot, however, do the nonportable act of store X and load Y!
+ if (store) {
+ HEAP_HISTORY[dest] = [{ type: type, /*stack: new Error().stack */ }]; // |stack| is useful for debugging
+ } else {
+ if (!HEAP[dest] && HEAP[dest] !== 0) {
+ print('Warning: Reading an invalid value at ' + dest + ' :: ' + new Error().stack + '\n');
+ }
+ var history = HEAP_HISTORY[dest];
+ assert((history && history[0]) /* || HEAP[dest] === 0 */, "Loading from where there was no store! " + dest + ',' + HEAP[dest] + ',' + type + ', \n\n' + new Error().stack + '\n');
+ if (history[0].type && history[0].type !== type) {
+ print('Load-store consistency assumption failure! ' + dest);
+ print('\n');
+ print(history.map(function(item) {
+ return item.type + ' :: ' + JSON.stringify(item.stack);
+ }).join('\n\n'));
+ print('\n');
+ print('LOAD: ' + type + ', ' + new Error().stack);
+ print('\n');
+ assert(0, 'Load-store consistency assumption failure!');
+ }
+ }
+}
+function SAFE_HEAP_STORE(dest, value, type) {
+ SAFE_HEAP_ACCESS(dest, type, true);
if (dest in HEAP_WATCHED) {
print((new Error()).stack);
throw "Bad store!" + dest;
}
HEAP[dest] = value;
}
+function SAFE_HEAP_LOAD(dest, type) {
+ SAFE_HEAP_ACCESS(dest, type);
+ return HEAP[dest];
+}
function __Z16PROTECT_HEAPADDRPv(dest) {
HEAP_WATCHED[dest] = true;
}
@@ -120,7 +154,7 @@ function Pointer_make(slab, pos, allocator) {
curr = Runtime.getFunctionIndex(curr);
}
#if SAFE_HEAP
- SAFE_HEAP_STORE(ret + i, curr);
+ SAFE_HEAP_STORE(ret + i, curr, null);
#else
#if USE_TYPED_ARRAYS
// TODO: Check - also in non-typedarray case - for functions, and if so add |.__index__|
@@ -303,7 +337,7 @@ function _atoi(s) {
function _llvm_memcpy_i32(dest, src, num, idunno) {
for (var i = 0; i < num; i++) {
#if SAFE_HEAP
- SAFE_HEAP_STORE(dest + i, HEAP[src + i]);
+ SAFE_HEAP_STORE(dest + i, HEAP[src + i], null);
#else
HEAP[dest + i] = HEAP[src + i];
#if USE_TYPED_ARRAYS
diff --git a/src/runtime.js b/src/runtime.js
index daa9086d..2ad54295 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -6,6 +6,9 @@ RuntimeGenerator = {
alloc: function(size, type) {
var ret = type + 'TOP';
// ret += '; for (var i = 0; i < ' + size + '; i++) HEAP[' + type + 'TOP+i] = 0'; // No need for typed arrays - per the spec, initialized to 0 anyhow
+ if (SAFE_HEAP) {
+ ret += '; for (var j = 0; j < ' + size + '; j++) SAFE_HEAP_CLEAR(' + type + 'TOP+j);';
+ }
if (GUARD_MEMORY) {
ret += '; assert(' + size + ' > 0)';
}
@@ -26,11 +29,14 @@ RuntimeGenerator = {
},
stackEnter: function(initial) {
- if (initial === 0) return ''; // XXX Note that we don't even push the stack! This is faster, but
- // means that we don't clear stack allocations done in this function
- // until the parent unwinds its stack. So potentially if we are in
- // a loop, we can use a lot of memory.
+ if (!GUARD_MEMORY && initial === 0) return ''; // XXX Note that we don't even push the stack! This is faster, but
+ // means that we don't clear stack allocations done in this function
+ // until the parent unwinds its stack. So potentially if we are in
+ // a loop, we can use a lot of memory.
var ret = 'var __stackBase__ = STACKTOP; STACKTOP += ' + initial;
+ if (SAFE_HEAP) {
+ ret += '; for (var i = __stackBase__; i < STACKTOP; i++) SAFE_HEAP_STORE(i, 0, null);';
+ }
if (GUARD_MEMORY) {
ret += '; assert(STACKTOP < STACK_MAX)';
}
@@ -39,7 +45,11 @@ RuntimeGenerator = {
stackExit: function(initial) {
if (initial === 0) return ''; // XXX See comment in stackEnter
- return 'STACKTOP = __stackBase__';
+ var ret = '';
+ if (SAFE_HEAP) {
+ ret += 'for (var i = __stackBase__; i < STACKTOP; i++) SAFE_HEAP_CLEAR(i);';
+ }
+ return ret += 'STACKTOP = __stackBase__';
},
// An allocation that cannot be free'd
diff --git a/src/utility.js b/src/utility.js
index 3085fc61..fd8a1bce 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -1,5 +1,10 @@
// General JS utilities
+function safeQuote(x) {
+ return x.replace(/"/g, '\\"')
+ .replace(/'/g, "\\'");
+}
+
function dump(item) {
var CHUNK = 500;
function lineify(text) {