enable a form of safe heap in asm, using js optimizer pass to ensure full coverage and support for fastcomp

This commit is contained in:
Alon Zakai 2014-01-16 11:25:22 -08:00
Родитель e14e6ea75c
Коммит a310827131
7 изменённых файлов: 165 добавлений и 18 удалений

6
emcc
Просмотреть файл

@ -1196,7 +1196,6 @@ try:
shared.Settings.ASM_JS = 1 shared.Settings.ASM_JS = 1
assert shared.Settings.ALLOW_MEMORY_GROWTH == 0, 'memory growth not supported in fastcomp yet' 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.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.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.SAFE_DYNCALLS == 0, 'safe dyncalls not supported in fastcomp'
assert shared.Settings.RESERVED_FUNCTION_POINTERS == 0, 'reserved function pointers 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 shared.Settings.CORRECT_OVERFLOWS = 1
assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode' 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: 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 debug_level = 4 # must keep debug info to do line-by-line operations
@ -1991,6 +1993,8 @@ try:
if DEBUG: save_intermediate('closure') if DEBUG: save_intermediate('closure')
if js_opts: 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: if shared.Settings.OUTLINING_LIMIT > 0 and shared.Settings.ASM_JS:
js_optimizer_queue += ['outline'] js_optimizer_queue += ['outline']
js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT

Просмотреть файл

@ -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] 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['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['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
if settings['ASSERTIONS']: if settings['ASSERTIONS']:
basic_funcs += ['nullFunc'] 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] 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['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['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
if settings['ASSERTIONS']: if settings['ASSERTIONS']:
basic_funcs += ['nullFunc'] basic_funcs += ['nullFunc']

Просмотреть файл

@ -1327,8 +1327,13 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
var printType = type; var printType = type;
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"'; if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
if (printType[0] === '#') printType = printType.substr(1); 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); 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 { } else {
return asmCoercion('SAFE_HEAP_LOAD(' + offset + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type);
}
}
var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']'; var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']';
if (ASM_JS && (phase == 'funcs' || forceAsm)) { if (ASM_JS && (phase == 'funcs' || forceAsm)) {
ret = asmCoercion(ret, type); ret = asmCoercion(ret, type);
@ -1338,7 +1343,6 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int')); 'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int'));
} }
return ret; return ret;
}
} }
function makeGetValueAsm(ptr, pos, type, unsigned) { function makeGetValueAsm(ptr, pos, type, unsigned) {
@ -1435,10 +1439,14 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
var printType = type; var printType = type;
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"'; if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
if (printType[0] === '#') printType = printType.substr(1); if (printType[0] === '#') printType = printType.substr(1);
return 'SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')'; 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 { } else {
return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep); 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) { function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) {

Просмотреть файл

@ -21,6 +21,7 @@ Module.print = Module.printErr = function(){};
#endif #endif
#if SAFE_HEAP #if SAFE_HEAP
#if ASM_JS == 0
//======================================== //========================================
// Debugging tools - Heap // 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 #endif
#if CHECK_HEAP_ALIGN #if CHECK_HEAP_ALIGN

Просмотреть файл

@ -49,7 +49,7 @@ var RuntimeGenerator = {
stackExit: function(initial, force) { stackExit: function(initial, force) {
if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return ''; if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
var ret = ''; 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 }'; ret += 'var i = sp; while ((i|0) < (STACKTOP|0)) { SAFE_HEAP_CLEAR(i|0); i = (i+1)|0 }';
} }
return ret += 'STACKTOP=sp'; return ret += 'STACKTOP=sp';

Просмотреть файл

@ -1486,7 +1486,7 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
def test_segfault(self): 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 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 Settings.SAFE_HEAP = 1

Просмотреть файл

@ -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 // 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) // 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 minifyWhitespace = false, printMetadata = true, asm = false, last = false;
var passes = { var passes = {
// passes
dumpAst: dumpAst, dumpAst: dumpAst,
dumpSrc: dumpSrc, dumpSrc: dumpSrc,
unGlobalize: unGlobalize, unGlobalize: unGlobalize,
@ -3996,6 +4095,9 @@ var passes = {
minifyLocals: minifyLocals, minifyLocals: minifyLocals,
relocate: relocate, relocate: relocate,
outline: outline, outline: outline,
safeHeap: safeHeap,
// flags
minifyWhitespace: function() { minifyWhitespace = true }, minifyWhitespace: function() { minifyWhitespace = true },
noPrintMetadata: function() { printMetadata = false }, noPrintMetadata: function() { printMetadata = false },
asm: function() { asm = true }, asm: function() { asm = true },