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:
Родитель
e14e6ea75c
Коммит
a310827131
6
emcc
6
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
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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_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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 },
|
||||
|
|
Загрузка…
Ссылка в новой задаче