aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc6
-rwxr-xr-xemscripten.py4
-rw-r--r--src/parseTools.js34
-rw-r--r--src/preamble.js33
-rw-r--r--src/runtime.js2
-rw-r--r--tests/test_core.py2
-rw-r--r--tools/js-optimizer.js102
7 files changed, 165 insertions, 18 deletions
diff --git a/emcc b/emcc
index 958b018c..4be24e20 100755
--- a/emcc
+++ b/emcc
@@ -1196,7 +1196,6 @@ try:
shared.Settings.ASM_JS = 1
assert shared.Settings.ALLOW_MEMORY_GROWTH == 0, 'memory growth not supported in fastcomp yet'
assert shared.Settings.UNALIGNED_MEMORY == 0, 'forced unaligned memory not supported in fastcomp'
- assert shared.Settings.SAFE_HEAP == 0, 'safe heap not supported in fastcomp yet'
assert shared.Settings.CHECK_HEAP_ALIGN == 0, 'check heap align not supported in fastcomp yet'
assert shared.Settings.SAFE_DYNCALLS == 0, 'safe dyncalls not supported in fastcomp'
assert shared.Settings.RESERVED_FUNCTION_POINTERS == 0, 'reserved function pointers not supported in fastcomp'
@@ -1230,6 +1229,9 @@ try:
shared.Settings.CORRECT_OVERFLOWS = 1
assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode'
+ if shared.Settings.SAFE_HEAP and not js_opts:
+ logging.warning('asm.js+SAFE_HEAP requires js opts to be run (-O1 or above by default)')
+
if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2:
debug_level = 4 # must keep debug info to do line-by-line operations
@@ -1991,6 +1993,8 @@ try:
if DEBUG: save_intermediate('closure')
if js_opts:
+ if shared.Settings.ASM_JS and shared.Settings.SAFE_HEAP: js_optimizer_queue += ['safeHeap']
+
if shared.Settings.OUTLINING_LIMIT > 0 and shared.Settings.ASM_JS:
js_optimizer_queue += ['outline']
js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT
diff --git a/emscripten.py b/emscripten.py
index befad8d5..aeace63d 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -455,7 +455,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
- if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR']
+ if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE']
if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
if settings['ASSERTIONS']:
basic_funcs += ['nullFunc']
@@ -956,7 +956,7 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None,
basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
- if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR']
+ if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE']
if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
if settings['ASSERTIONS']:
basic_funcs += ['nullFunc']
diff --git a/src/parseTools.js b/src/parseTools.js
index be0cbcab..036ccfc1 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1327,18 +1327,22 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
var printType = type;
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
if (printType[0] === '#') printType = printType.substr(1);
- return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type);
- } else {
- var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']';
- if (ASM_JS && (phase == 'funcs' || forceAsm)) {
- ret = asmCoercion(ret, type);
- }
- if (ASM_HEAP_LOG) {
- ret = makeInlineCalculation('(asmPrint' + (type in Runtime.FLOAT_TYPES ? 'Float' : 'Int') + '(' + (asmPrintCounter++) + ',' + asmCoercion('VALUE', type) + '), VALUE)', ret,
- 'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int'));
+ if (ASM_JS) {
+ if (!ignore) return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ')', type);
+ // else fall through
+ } else {
+ return asmCoercion('SAFE_HEAP_LOAD(' + offset + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type);
}
- return ret;
}
+ var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']';
+ if (ASM_JS && (phase == 'funcs' || forceAsm)) {
+ ret = asmCoercion(ret, type);
+ }
+ if (ASM_HEAP_LOG) {
+ ret = makeInlineCalculation('(asmPrint' + (type in Runtime.FLOAT_TYPES ? 'Float' : 'Int') + '(' + (asmPrintCounter++) + ',' + asmCoercion('VALUE', type) + '), VALUE)', ret,
+ 'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int'));
+ }
+ return ret;
}
function makeGetValueAsm(ptr, pos, type, unsigned) {
@@ -1435,10 +1439,14 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
var printType = type;
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
if (printType[0] === '#') printType = printType.substr(1);
- return 'SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
- } else {
- return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep);
+ if (ASM_JS) {
+ if (!ignore) return asmCoercion('SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ')', type);
+ // else fall through
+ } else {
+ return 'SAFE_HEAP_STORE(' + offset + ', ' + value + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
+ }
}
+ return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep);
}
function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) {
diff --git a/src/preamble.js b/src/preamble.js
index ac6ee7b3..d70ef4b1 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -21,6 +21,7 @@ Module.print = Module.printErr = function(){};
#endif
#if SAFE_HEAP
+#if ASM_JS == 0
//========================================
// Debugging tools - Heap
//========================================
@@ -166,6 +167,38 @@ function SAFE_HEAP_FILL_HISTORY(from, to, type) {
}
//==========================================
+#else
+// ASM_JS safe heap
+
+function getSafeHeapType(bytes, isFloat) {
+ switch (bytes) {
+ case 1: return 'i8';
+ case 2: return 'i16';
+ case 4: return isFloat ? 'float' : 'i32';
+ case 8: return 'double';
+ default: assert(0);
+ }
+}
+
+function SAFE_HEAP_STORE(dest, value, bytes, isFloat) {
+#if SAFE_HEAP_LOG
+ Module.print('SAFE_HEAP store: ' + [dest, value, bytes, isFloat]);
+#endif
+ assert(dest > 0, 'segmentation fault');
+ assert(dest % bytes === 0);
+ setValue(dest, value, getSafeHeapType(bytes, isFloat), 1);
+}
+
+function SAFE_HEAP_LOAD(dest, bytes, isFloat) {
+#if SAFE_HEAP_LOG
+ Module.print('SAFE_HEAP load: ' + [dest, bytes, isFloat]);
+#endif
+ assert(dest > 0, 'segmentation fault');
+ assert(dest % bytes === 0);
+ return getValue(dest, getSafeHeapType(bytes, isFloat), 1);
+}
+
+#endif
#endif
#if CHECK_HEAP_ALIGN
diff --git a/src/runtime.js b/src/runtime.js
index cd3afb4b..1fc9e026 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -49,7 +49,7 @@ var RuntimeGenerator = {
stackExit: function(initial, force) {
if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
var ret = '';
- if (SAFE_HEAP) {
+ if (SAFE_HEAP && !ASM_JS) {
ret += 'var i = sp; while ((i|0) < (STACKTOP|0)) { SAFE_HEAP_CLEAR(i|0); i = (i+1)|0 }';
}
return ret += 'STACKTOP=sp';
diff --git a/tests/test_core.py b/tests/test_core.py
index 458e04fb..ce778209 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1486,7 +1486,7 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
def test_segfault(self):
if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults')
- if Settings.ASM_JS: return self.skip('asm does not support safe heap')
+ if os.environ.get('EMCC_FAST_COMPILER') == '1' and '-O2' not in self.emcc_args: return self.skip('todo in non-jsopts-enabled fastcomp')
Settings.SAFE_HEAP = 1
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 9391ff8e..fa59dbec 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -3888,6 +3888,104 @@ function outline(ast) {
});
}
+function safeHeap(ast) {
+ function fixPtr(ptr, heap) {
+ switch (heap) {
+ case 'HEAP8': case 'HEAPU8': break;
+ case 'HEAP16': case 'HEAPU16': {
+ if (ptr[0] === 'binary') {
+ assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 1);
+ ptr = ptr[2]; // skip the shift
+ } else {
+ ptr = ['binary', '*', ptr, ['num', 2]]; // was unshifted, convert to absolute address
+ }
+ break;
+ }
+ case 'HEAP32': case 'HEAPU32': {
+ if (ptr[0] === 'binary') {
+ assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 2);
+ ptr = ptr[2]; // skip the shift
+ } else {
+ ptr = ['binary', '*', ptr, ['num', 4]]; // was unshifted, convert to absolute address
+ }
+ break;
+ }
+ case 'HEAPF32': {
+ if (ptr[0] === 'binary') {
+ assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 2);
+ ptr = ptr[2]; // skip the shift
+ } else {
+ ptr = ['binary', '*', ptr, ['num', 4]]; // was unshifted, convert to absolute address
+ }
+ break;
+ }
+ case 'HEAPF64': {
+ if (ptr[0] === 'binary') {
+ assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 3);
+ ptr = ptr[2]; // skip the shift
+ } else {
+ ptr = ['binary', '*', ptr, ['num', 8]]; // was unshifted, convert to absolute address
+ }
+ break;
+ }
+ default: throw 'bad heap ' + heap;
+ }
+ ptr = ['binary', '|', ptr, ['num', 0]];
+ return ptr;
+ }
+ traverseGenerated(ast, function(node, type) {
+ if (type === 'assign') {
+ if (node[1] === true && node[2][0] === 'sub') {
+ var heap = node[2][1][1];
+ var ptr = fixPtr(node[2][2], heap);
+ var value = node[3];
+ // SAFE_HEAP_STORE(ptr, value, bytes, isFloat)
+ switch (heap) {
+ case 'HEAP8': case 'HEAPU8': {
+ return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 1], ['num', '0']]];
+ }
+ case 'HEAP16': case 'HEAPU16': {
+ return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 2], ['num', '0']]];
+ }
+ case 'HEAP32': case 'HEAPU32': {
+ return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 4], ['num', '0']]];
+ }
+ case 'HEAPF32': {
+ return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_FLOAT), ['num', 4], ['num', '1']]];
+ }
+ case 'HEAPF64': {
+ return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_DOUBLE), ['num', 8], ['num', '1']]];
+ }
+ default: throw 'bad heap ' + heap;
+ }
+ }
+ } else if (type === 'sub') {
+ var heap = node[1][1];
+ if (heap[0] !== 'H') return;
+ var ptr = fixPtr(node[2], heap);
+ // SAFE_HEAP_LOAD(ptr, bytes, isFloat)
+ switch (heap) {
+ case 'HEAP8': case 'HEAPU8': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0']]], ASM_INT);
+ }
+ case 'HEAP16': case 'HEAPU16': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0']]], ASM_INT);
+ }
+ case 'HEAP32': case 'HEAPU32': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0']]], ASM_INT);
+ }
+ case 'HEAPF32': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '1']]], ASM_FLOAT);
+ }
+ case 'HEAPF64': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 8], ['num', '1']]], ASM_DOUBLE);
+ }
+ default: throw 'bad heap ' + heap;
+ }
+ }
+ });
+}
+
// Last pass utilities
// Change +5 to DOT$ZERO(5). We then textually change 5 to 5.0 (uglify's ast cannot differentiate between 5 and 5.0 directly)
@@ -3978,6 +4076,7 @@ function asmLastOpts(ast) {
var minifyWhitespace = false, printMetadata = true, asm = false, last = false;
var passes = {
+ // passes
dumpAst: dumpAst,
dumpSrc: dumpSrc,
unGlobalize: unGlobalize,
@@ -3996,6 +4095,9 @@ var passes = {
minifyLocals: minifyLocals,
relocate: relocate,
outline: outline,
+ safeHeap: safeHeap,
+
+ // flags
minifyWhitespace: function() { minifyWhitespace = true },
noPrintMetadata: function() { printMetadata = false },
asm: function() { asm = true },