diff options
-rwxr-xr-x | emcc | 7 | ||||
-rwxr-xr-x | emscripten.py | 7 | ||||
-rw-r--r-- | src/compiler.js | 6 | ||||
-rw-r--r-- | src/jsifier.js | 45 | ||||
-rw-r--r-- | src/library.js | 83 | ||||
-rw-r--r-- | src/library_gc.js | 7 | ||||
-rw-r--r-- | src/parseTools.js | 5 | ||||
-rw-r--r-- | src/postamble.js | 6 | ||||
-rw-r--r-- | src/preamble.js | 59 | ||||
-rw-r--r-- | src/runtime.js | 17 | ||||
-rw-r--r-- | src/settings.js | 3 | ||||
-rw-r--r-- | tests/cases/gepoverflow.ll | 37 | ||||
-rw-r--r-- | tests/cases/gepoverflow.txt | 2 | ||||
-rw-r--r-- | tests/cases/gepoverflow_q1.txt | 1 | ||||
-rw-r--r-- | tests/fcntl/output.txt | 2 | ||||
-rw-r--r-- | tests/fcntl/src.c | 2 | ||||
-rwxr-xr-x | tests/runner.py | 23 | ||||
-rw-r--r-- | tests/sdl_alloctext.c | 36 | ||||
-rwxr-xr-x | tools/bindings_generator.py | 21 |
19 files changed, 203 insertions, 166 deletions
@@ -1131,8 +1131,6 @@ try: not shared.Settings.BUILD_AS_SHARED_LIB == 2: # shared lib 2 use the library in the parent # Check if we need to include some libraries that we compile. (We implement libc ourselves in js, but # compile a malloc implementation and stdlibc++.) - # Note that we assume a single symbol is enough to know if we have/do not have dlmalloc etc. If you - # include just a few symbols but want the rest, this will not work. def read_symbols(path, exclude=None): symbols = map(lambda line: line.strip().split(' ')[1], open(path).readlines()) @@ -1328,9 +1326,10 @@ try: #logging.info('using libcxxabi, this may need CORRECT_* options') #shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1 - # If we have libcxx, we must force inclusion of libc, since libcxx uses new internally. Note: this is kind of hacky # Settings this in the environment will avoid checking dependencies and make building big projects a little faster force = os.environ.get('EMCC_FORCE_STDLIBS') + + # If we have libcxx, we must force inclusion of libc, since libcxx uses new internally. Note: this is kind of hacky. has = need = None for name, create, fix, library_symbols in [('libcxx', create_libcxx, fix_libcxx, libcxx_symbols), ('libcextra', create_libcextra, fix_libcextra, libcextra_symbols), @@ -1571,7 +1570,7 @@ try: if os.path.abspath(memfile) != os.path.abspath(memfile): shutil.copyfile(memfile, temp_memfile) return 'loadMemoryInitializer("%s");' % os.path.basename(memfile) - src = re.sub('/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, TOTAL_STACK\)', repl, src, count=1) + src = re.sub('/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, Runtime\.GLOBAL_BASE\)', repl, src, count=1) open(final + '.mem.js', 'w').write(src) final += '.mem.js' if DEBUG: diff --git a/emscripten.py b/emscripten.py index 924012af..35edc515 100755 --- a/emscripten.py +++ b/emscripten.py @@ -309,6 +309,11 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, forwarded_json['Functions']['indexedFunctions'][indexed] = i # make sure not to modify this python object later - we use it in indexize i += 2 forwarded_json['Functions']['nextIndex'] = i + function_table_size = forwarded_json['Functions']['nextIndex'] + i = 1 + while i < function_table_size: + i *= 2 + function_table_size = i def split_32(x): x = int(x) @@ -371,7 +376,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None, sig = use[8:len(use)-4] if sig not in last_forwarded_json['Functions']['tables']: if DEBUG: print >> sys.stderr, 'add empty function table', sig - last_forwarded_json['Functions']['tables'][sig] = 'var FUNCTION_TABLE_' + sig + ' = [0,0];\n' + last_forwarded_json['Functions']['tables'][sig] = 'var FUNCTION_TABLE_' + sig + ' = [' + ','.join(['0']*function_table_size) + '];\n' def make_table(sig, raw): i = Counter.i diff --git a/src/compiler.js b/src/compiler.js index 313fd5f7..8b9606f1 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -184,6 +184,7 @@ if (ASM_JS) { assert((TOTAL_MEMORY&(TOTAL_MEMORY-1)) == 0, 'asm.js heap must be power of 2'); } assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have named globals +assert(!BUILD_AS_SHARED_LIB, 'shared libs are deprecated'); // Output some info and warnings based on settings @@ -208,7 +209,10 @@ load('parseTools.js'); load('intertyper.js'); load('analyzer.js'); load('jsifier.js'); -if (RELOOP) load(RELOOPER) +if (RELOOP) { + load(RELOOPER); + assert(typeof Relooper != 'undefined'); +} globalEval(processMacros(preprocess(read('runtime.js')))); Runtime.QUANTUM_SIZE = QUANTUM_SIZE; diff --git a/src/jsifier.js b/src/jsifier.js index 46d8ae74..8270b443 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -490,7 +490,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (!LINKABLE && !LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) { if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + shortident); if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) printErr('warning: unresolved symbol: ' + shortident); - if (ASM_JS) { + if (ASM_JS || item.ident in DEAD_FUNCTIONS) { // emit a stub that will fail during runtime. this allows asm validation to succeed. LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);"); } else { @@ -1546,13 +1546,9 @@ function JSify(data, functionsOnly, givenFunctions) { if (!mainPass) { if (phase == 'pre' && !Variables.generatedGlobalBase) { Variables.generatedGlobalBase = true; - if (Variables.nextIndexedOffset > 0) { - // Variables have been calculated, get to base stuff before we print them - // GLOBAL_BASE is statically known to be equal to STACK_MAX and to TOTAL_STACK, assert on this - print('assert(STATICTOP == STACK_MAX); assert(STACK_MAX == TOTAL_STACK);\n'); - print('STATICTOP += ' + Variables.nextIndexedOffset + ';\n'); - print('assert(STATICTOP < TOTAL_MEMORY);\n'); - } + // Globals are done, here is the rest of static memory + print('STATIC_BASE = ' + Runtime.GLOBAL_BASE + ';\n'); + print('STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n'); } var generated = itemsDict.function.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.GlobalVariable); print(generated.map(function(item) { return item.JS }).join('\n')); @@ -1573,13 +1569,13 @@ function JSify(data, functionsOnly, givenFunctions) { // possibly function table {{{ FT_* }}} etc. if (value.indexOf('{{ ') < 0) return true; } - writeInt8s(memoryInitialization, target - TOTAL_STACK, value, type); + writeInt8s(memoryInitialization, target - Runtime.GLOBAL_BASE, value, type); return false; } return true; }); // write out the singleton big memory initialization value - print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'TOTAL_STACK', true)); // we assert on TOTAL_STACK == GLOBAL_BASE + print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'Runtime.GLOBAL_BASE', true)); } else { print('/* no memory initializer */'); // test purposes } @@ -1589,6 +1585,27 @@ function JSify(data, functionsOnly, givenFunctions) { print(itemsDict.GlobalVariablePostSet.map(function(item) { return item.JS }).join('\n')); print('}\n'); print('if (!awaitingMemoryInitializer) runPostSets();\n'); // if we load the memory initializer, this is done later + + if (USE_TYPED_ARRAYS == 2) { + print('var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n'); + print('assert(tempDoublePtr % 8 == 0);\n'); + print('function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much\n'); + print(' HEAP8[tempDoublePtr] = HEAP8[ptr];\n'); + print(' HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];\n'); + print(' HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];\n'); + print(' HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];\n'); + print('}\n'); + print('function copyTempDouble(ptr) {\n'); + print(' HEAP8[tempDoublePtr] = HEAP8[ptr];\n'); + print(' HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];\n'); + print(' HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];\n'); + print(' HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];\n'); + print(' HEAP8[tempDoublePtr+4] = HEAP8[ptr+4];\n'); + print(' HEAP8[tempDoublePtr+5] = HEAP8[ptr+5];\n'); + print(' HEAP8[tempDoublePtr+6] = HEAP8[ptr+6];\n'); + print(' HEAP8[tempDoublePtr+7] = HEAP8[ptr+7];\n'); + print('}\n'); + } } return; @@ -1622,6 +1639,12 @@ function JSify(data, functionsOnly, givenFunctions) { legalizedI64s = legalizedI64sDefault; + print('STACK_BASE = STACKTOP = Runtime.alignMemory(STATICTOP);\n'); + print('staticSealed = true; // seal the static portion of memory\n'); + print('STACK_MAX = STACK_BASE + ' + TOTAL_STACK + ';\n'); + print('DYNAMIC_BASE = DYNAMICTOP = Runtime.alignMemory(STACK_MAX);\n'); + print('assert(DYNAMIC_BASE < TOTAL_MEMORY); // Stack must fit in TOTAL_MEMORY; allocations from here on may enlarge TOTAL_MEMORY\n'); + if (asmLibraryFunctions.length > 0) { print('// ASM_LIBRARY FUNCTIONS'); function fix(f) { // fix indenting to not confuse js optimizer @@ -1631,6 +1654,7 @@ function JSify(data, functionsOnly, givenFunctions) { } print(asmLibraryFunctions.map(fix).join('\n')); } + } else { if (singlePhase) { assert(data.unparsedGlobalss[0].lines.length == 0, dump([phase, data.unparsedGlobalss])); @@ -1664,6 +1688,7 @@ function JSify(data, functionsOnly, givenFunctions) { assert(typeof dep == 'function'); var text = dep(); assert(text.indexOf('\n') < 0); + text = text.replace('ALLOC_STATIC', 'ALLOC_DYNAMIC'); print('/* PRE_ASM */ ' + text + '\n'); }); } diff --git a/src/library.js b/src/library.js index dc0dcdd2..cfe83c6e 100644 --- a/src/library.js +++ b/src/library.js @@ -15,16 +15,18 @@ // object. For convenience, the short name appears here. Note that if you add a // new function with an '_', it will not be found. +// Memory allocated during startup, in postsets, should only be ALLOC_STATIC + LibraryManager.library = { // ========================================================================== // File system base. // ========================================================================== // keep this low in memory, because we flatten arrays with them in them - stdin: 'allocate(1, "i32*", ALLOC_STACK)', - stdout: 'allocate(1, "i32*", ALLOC_STACK)', - stderr: 'allocate(1, "i32*", ALLOC_STACK)', - _impure_ptr: 'allocate(1, "i32*", ALLOC_STACK)', + stdin: 'allocate(1, "i32*", ALLOC_STATIC)', + stdout: 'allocate(1, "i32*", ALLOC_STATIC)', + stderr: 'allocate(1, "i32*", ALLOC_STATIC)', + _impure_ptr: 'allocate(1, "i32*", ALLOC_STATIC)', $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], $FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + @@ -573,7 +575,7 @@ LibraryManager.library = { eof: false, ungotten: [] }; - assert(Math.max(_stdin, _stdout, _stderr) < 1024); // make sure these are low, we flatten arrays with these + // TODO: put these low in memory like we used to assert on: assert(Math.max(_stdin, _stdout, _stderr) < 15000); // make sure these are low, we flatten arrays with these {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 1, 'void*') }}}; {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 2, 'void*') }}}; {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 3, 'void*') }}}; @@ -590,11 +592,11 @@ LibraryManager.library = { FS.streams[_stderr] = FS.streams[3]; #if ASSERTIONS FS.checkStreams(); - assert(FS.streams.length < 1024); // at this early stage, we should not have a large set of file descriptors - just a few + // see previous TODO on stdin etc.: assert(FS.streams.length < 1024); // at this early stage, we should not have a large set of file descriptors - just a few #endif allocate([ allocate( {{{ Runtime.QUANTUM_SIZE === 4 ? '[0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0]' : '[0, _stdin, _stdout, _stderr]' }}}, - 'void*', ALLOC_STATIC) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}}); + 'void*', ALLOC_DYNAMIC) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}}); }, quit: function() { @@ -2425,22 +2427,17 @@ LibraryManager.library = { // Implement a Linux-like 'memory area' for our 'process'. // Changes the size of the memory area by |bytes|; returns the // address of the previous top ('break') of the memory area - - // We need to make sure no one else allocates unfreeable memory! - // We must control this entirely. So we don't even need to do - // unfreeable allocations - the HEAP is ours, from STATICTOP up. - // TODO: We could in theory slice off the top of the HEAP when - // sbrk gets a negative increment in |bytes|... + // We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP var self = _sbrk; if (!self.called) { - STATICTOP = alignMemoryPage(STATICTOP); // make sure we start out aligned + DYNAMICTOP = alignMemoryPage(DYNAMICTOP); // make sure we start out aligned self.called = true; -#if GC_SUPPORT - _sbrk.DYNAMIC_START = STATICTOP; -#endif + assert(Runtime.dynamicAlloc); + self.alloc = Runtime.dynamicAlloc; + Runtime.dynamicAlloc = function() { abort('cannot dynamically allocate, sbrk now has control') }; } - var ret = STATICTOP; - if (bytes != 0) Runtime.staticAlloc(bytes); + var ret = DYNAMICTOP; + if (bytes != 0) self.alloc(bytes); return ret; // Previous break location. }, open64: 'open', @@ -3007,16 +3004,20 @@ LibraryManager.library = { } case 's': { // String. - var arg = getNextArg('i8*') || nullString; - var argLength = _strlen(arg); + var arg = getNextArg('i8*'); + var argLength = arg ? _strlen(arg) : '(null)'.length; if (precisionSet) argLength = Math.min(argLength, precision); if (!flagLeftAlign) { while (argLength < width--) { ret.push({{{ charCode(' ') }}}); } } - for (var i = 0; i < argLength; i++) { - ret.push({{{ makeGetValue('arg++', 0, 'i8', null, true) }}}); + if (arg) { + for (var i = 0; i < argLength; i++) { + ret.push({{{ makeGetValue('arg++', 0, 'i8', null, true) }}}); + } + } else { + ret = ret.concat(intArrayFromString('(null)'.substr(0, argLength), true)); } if (flagLeftAlign) { while (argLength < width--) { @@ -3750,10 +3751,17 @@ LibraryManager.library = { * implementation (replaced by dlmalloc normally) so * not an issue. */ - var ptr = Runtime.staticAlloc(bytes + 8); +#if ASSERTIONS + Runtime.warnOnce('using stub malloc (reference it from C to have the real one included)'); +#endif + var ptr = Runtime.dynamicAlloc(bytes + 8); return (ptr+8) & 0xFFFFFFF8; }, - free: function(){}, + free: function() { +#if ASSERTIONS + Runtime.warnOnce('using stub free (reference it from C to have the real one included)'); +#endif +}, calloc__deps: ['malloc'], calloc: function(n, s) { @@ -4029,7 +4037,7 @@ LibraryManager.library = { _free(temp); }, - environ: 'allocate(1, "i32*", ALLOC_STACK)', + environ: 'allocate(1, "i32*", ALLOC_STATIC)', __environ__deps: ['environ'], __environ: '_environ', __buildEnvironment__deps: ['__environ'], @@ -4925,7 +4933,7 @@ LibraryManager.library = { } return 8; } - return 'var ctlz_i8 = allocate([' + range(256).map(function(x) { return ctlz(x) }).join(',') + '], "i8", ALLOC_STACK);'; + return 'var ctlz_i8 = allocate([' + range(256).map(function(x) { return ctlz(x) }).join(',') + '], "i8", ALLOC_STATIC);'; }], llvm_ctlz_i32__asm: true, llvm_ctlz_i32__sig: 'ii', @@ -4961,7 +4969,7 @@ LibraryManager.library = { } return 8; } - return 'var cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STACK);'; + return 'var cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STATIC);'; }], llvm_cttz_i32__asm: true, llvm_cttz_i32__sig: 'ii', @@ -5919,11 +5927,11 @@ LibraryManager.library = { ['i32', 'tm_gmtoff'], ['i32', 'tm_zone']]), // Statically allocated time struct. - __tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)', + __tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)', // Statically allocated timezone strings. __tm_timezones: {}, // Statically allocated time strings. - __tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)', + __tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)', mktime__deps: ['__tm_struct_layout', 'tzset'], mktime: function(tmPtr) { @@ -6057,9 +6065,9 @@ LibraryManager.library = { // TODO: Initialize these to defaults on startup from system settings. // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm) - _tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STACK)', - _daylight: 'allocate(1, "i32*", ALLOC_STACK)', - _timezone: 'allocate(1, "i32*", ALLOC_STACK)', + _tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STATIC)', + _daylight: 'allocate(1, "i32*", ALLOC_STATIC)', + _timezone: 'allocate(1, "i32*", ALLOC_STATIC)', tzset__deps: ['_tzname', '_daylight', '_timezone'], tzset: function() { // TODO: Use (malleable) environment variables instead of system settings. @@ -6756,15 +6764,18 @@ LibraryManager.library = { 26: 'Text file busy', 18: 'Invalid cross-device link' }, - __setErrNo__postset: '___setErrNo(0);', __setErrNo: function(value) { // For convenient setting and returning of errno. - if (!___setErrNo.ret) ___setErrNo.ret = allocate([0], 'i32', ALLOC_STATIC); + if (!___setErrNo.ret) ___setErrNo.ret = allocate([0], 'i32', ALLOC_NORMAL); {{{ makeSetValue('___setErrNo.ret', '0', 'value', 'i32') }}} return value; }, __errno_location__deps: ['__setErrNo'], __errno_location: function() { + if (!___setErrNo.ret) { + ___setErrNo.ret = allocate([0], 'i32', ALLOC_NORMAL); + {{{ makeSetValue('___setErrNo.ret', '0', '0', 'i32') }}} + } return ___setErrNo.ret; }, __errno: '__errno_location', @@ -6877,7 +6888,7 @@ LibraryManager.library = { void **restrict stackaddr, size_t *restrict stacksize); */ /*FIXME: assumes that there is only one thread, and that attr is the current thread*/ - {{{ makeSetValue('stackaddr', '0', 'STACK_ROOT', 'i8*') }}} + {{{ makeSetValue('stackaddr', '0', 'STACK_BASE', 'i8*') }}} {{{ makeSetValue('stacksize', '0', 'TOTAL_STACK', 'i32') }}} return 0; }, diff --git a/src/library_gc.js b/src/library_gc.js index f6db74d8..2a164250 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -148,12 +148,9 @@ if (GC_SUPPORT) { prep: function() { // Clear reachables and scan for roots GC.reachable = {}; // 1 if reachable. XXX GC.reachableList = []; // each reachable is added once to this. XXX - // static data areas - var staticStart = STACK_MAX; - var staticEnd = _sbrk.DYNAMIC_START || STATICTOP; // after DYNAMIC_START, sbrk manages it (but it might not exist yet) - GC.scan(staticStart, staticEnd); + GC.scan(STATIC_BASE, STATICTOP); // TODO: scan stack and registers. Currently we assume we run from a timeout or such, so no stack/regs - // stack: STACK_ROOT to STACKTOP + // stack: STACK_BASE to STACKTOP // registers: call scanners }, diff --git a/src/parseTools.js b/src/parseTools.js index 5cbefdb1..db834206 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -465,8 +465,7 @@ function makeGlobalUse(ident) { UNINDEXABLE_GLOBALS[ident] = 1; return ident; } - // We know and assert on TOTAL_STACK being equal to GLOBAL_BASE - return (TOTAL_STACK + index).toString(); + return (Runtime.GLOBAL_BASE + index).toString(); } return ident; } @@ -1594,7 +1593,7 @@ function makePointer(slab, pos, allocator, type, ptr, finalMemoryInitialization) // writing out into memory, without a normal allocation. We put all of these into a single big chunk. assert(typeof slab == 'object'); assert(slab.length % QUANTUM_SIZE == 0, slab.length); // must be aligned already - var offset = ptr - TOTAL_STACK; // we assert on GLOBAL_BASE being equal to TOTAL_STACK + var offset = ptr - Runtime.GLOBAL_BASE; for (var i = 0; i < slab.length; i++) { memoryInitialization[offset + i] = slab[i]; } diff --git a/src/postamble.js b/src/postamble.js index 8dd01589..12471a19 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -15,14 +15,14 @@ Module.callMain = function callMain(args) { argv.push(0); } } - var argv = [allocate(intArrayFromString("/bin/this.program"), 'i8', ALLOC_STATIC) ]; + var argv = [allocate(intArrayFromString("/bin/this.program"), 'i8', ALLOC_NORMAL) ]; pad(); for (var i = 0; i < argc-1; i = i + 1) { - argv.push(allocate(intArrayFromString(args[i]), 'i8', ALLOC_STATIC)); + argv.push(allocate(intArrayFromString(args[i]), 'i8', ALLOC_NORMAL)); pad(); } argv.push(0); - argv = allocate(argv, 'i32', ALLOC_STATIC); + argv = allocate(argv, 'i32', ALLOC_NORMAL); #if BENCHMARK var start = Date.now(); diff --git a/src/preamble.js b/src/preamble.js index cac0be4c..d10771e3 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -38,14 +38,14 @@ var ACCEPTABLE_SAFE_HEAP_ERRORS = 0; function SAFE_HEAP_ACCESS(dest, type, store, ignore, storeValue) { //if (dest === A_NUMBER) Module.print ([dest, type, store, ignore, storeValue] + ' ' + new Error().stack); // Something like this may be useful, in debugging - assert(dest >= STACK_ROOT, 'segmentation fault: null pointer, or below normal memory'); + assert(dest > 0, 'segmentation fault'); #if USE_TYPED_ARRAYS // When using typed arrays, reads over the top of TOTAL_MEMORY will fail silently, so we must // correct that by growing TOTAL_MEMORY as needed. Without typed arrays, memory is a normal // JS array so it will work (potentially slowly, depending on the engine). - assert(ignore || dest < STATICTOP); - assert(ignore || STATICTOP <= TOTAL_MEMORY); + assert(ignore || dest < Math.max(DYNAMICTOP, STATICTOP)); + assert(ignore || DYNAMICTOP <= TOTAL_MEMORY); #endif #if USE_TYPED_ARRAYS == 2 @@ -417,10 +417,12 @@ Module['getValue'] = getValue; var ALLOC_NORMAL = 0; // Tries to use _malloc() var ALLOC_STACK = 1; // Lives for the duration of the current function call var ALLOC_STATIC = 2; // Cannot be freed -var ALLOC_NONE = 3; // Do not allocate +var ALLOC_DYNAMIC = 3; // Cannot be freed except through sbrk +var ALLOC_NONE = 4; // Do not allocate Module['ALLOC_NORMAL'] = ALLOC_NORMAL; Module['ALLOC_STACK'] = ALLOC_STACK; Module['ALLOC_STATIC'] = ALLOC_STATIC; +Module['ALLOC_DYNAMIC'] = ALLOC_DYNAMIC; Module['ALLOC_NONE'] = ALLOC_NONE; // allocate(): This is for internal use. You can use it yourself as well, but the interface @@ -452,7 +454,7 @@ function allocate(slab, types, allocator, ptr) { if (allocator == ALLOC_NONE) { ret = ptr; } else { - ret = [_malloc, Runtime.stackAlloc, Runtime.staticAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); + ret = [_malloc, Runtime.stackAlloc, Runtime.staticAlloc, Runtime.dynamicAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); } if (zeroinit) { @@ -573,8 +575,10 @@ var FHEAP; var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; #endif -var STACK_ROOT, STACKTOP, STACK_MAX; -var STATICTOP; +var STATIC_BASE = 0, STATICTOP = 0, staticSealed = false; // static area +var STACK_BASE = 0, STACKTOP = 0, STACK_MAX = 0; // stack area +var DYNAMIC_BASE = 0, DYNAMICTOP = 0; // dynamic area handled by sbrk + #if USE_TYPED_ARRAYS function enlargeMemory() { #if ALLOW_MEMORY_GROWTH == 0 @@ -584,13 +588,13 @@ function enlargeMemory() { abort('Cannot enlarge memory arrays in asm.js. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, or (2) set Module.TOTAL_MEMORY before the program runs.'); #endif #else - // TOTAL_MEMORY is the current size of the actual array, and STATICTOP is the new top. + // TOTAL_MEMORY is the current size of the actual array, and DYNAMICTOP is the new top. #if ASSERTIONS - Module.printErr('Warning: Enlarging memory arrays, this is not fast, and ALLOW_MEMORY_GROWTH is not fully tested with all optimizations on! ' + [STATICTOP, TOTAL_MEMORY]); // We perform safe elimination instead of elimination in this mode, but if you see this error, try to disable it and other optimizations entirely - assert(STATICTOP >= TOTAL_MEMORY); + Module.printErr('Warning: Enlarging memory arrays, this is not fast, and ALLOW_MEMORY_GROWTH is not fully tested with all optimizations on! ' + [DYNAMICTOP, TOTAL_MEMORY]); // We perform safe elimination instead of elimination in this mode, but if you see this error, try to disable it and other optimizations entirely + assert(DYNAMICTOP >= TOTAL_MEMORY); assert(TOTAL_MEMORY > 4); // So the loop below will not be infinite #endif - while (TOTAL_MEMORY <= STATICTOP) { // Simple heuristic. Override enlargeMemory() if your program has something more optimal for it + while (TOTAL_MEMORY <= DYNAMICTOP) { // Simple heuristic. Override enlargeMemory() if your program has something more optimal for it TOTAL_MEMORY = alignMemoryPage(2*TOTAL_MEMORY); } assert(TOTAL_MEMORY <= Math.pow(2, 30)); // 2^30==1GB is a practical maximum - 2^31 is already close to possible negative numbers etc. @@ -680,35 +684,6 @@ Module['HEAPF32'] = HEAPF32; Module['HEAPF64'] = HEAPF64; #endif -STACK_ROOT = STACKTOP = Runtime.alignMemory(1); -STACK_MAX = TOTAL_STACK; // we lose a little stack here, but TOTAL_STACK is nice and round so use that as the max - -#if USE_TYPED_ARRAYS == 2 -var tempDoublePtr = Runtime.alignMemory(allocate(12, 'i8', ALLOC_STACK), 8); -assert(tempDoublePtr % 8 == 0); -function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much - HEAP8[tempDoublePtr] = HEAP8[ptr]; - HEAP8[tempDoublePtr+1] = HEAP8[ptr+1]; - HEAP8[tempDoublePtr+2] = HEAP8[ptr+2]; - HEAP8[tempDoublePtr+3] = HEAP8[ptr+3]; -} -function copyTempDouble(ptr) { - HEAP8[tempDoublePtr] = HEAP8[ptr]; - HEAP8[tempDoublePtr+1] = HEAP8[ptr+1]; - HEAP8[tempDoublePtr+2] = HEAP8[ptr+2]; - HEAP8[tempDoublePtr+3] = HEAP8[ptr+3]; - HEAP8[tempDoublePtr+4] = HEAP8[ptr+4]; - HEAP8[tempDoublePtr+5] = HEAP8[ptr+5]; - HEAP8[tempDoublePtr+6] = HEAP8[ptr+6]; - HEAP8[tempDoublePtr+7] = HEAP8[ptr+7]; -} -#endif - -STATICTOP = STACK_MAX; -assert(STATICTOP < TOTAL_MEMORY); // Stack must fit in TOTAL_MEMORY; allocations from here on may enlarge TOTAL_MEMORY - -var nullString = allocate(intArrayFromString('(null)'), 'i8', ALLOC_STACK); - function callRuntimeCallbacks(callbacks) { while(callbacks.length > 0) { var callback = callbacks.shift(); @@ -907,9 +882,9 @@ var awaitingMemoryInitializer = false; function loadMemoryInitializer(filename) { function applyData(data) { #if USE_TYPED_ARRAYS == 2 - HEAPU8.set(data, TOTAL_STACK); + HEAPU8.set(data, STATIC_BASE); #else - allocate(data, 'i8', ALLOC_NONE, TOTAL_STACK); + allocate(data, 'i8', ALLOC_NONE, STATIC_BASE); #endif runPostSets(); } diff --git a/src/runtime.js b/src/runtime.js index 9daab820..9bedfe68 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -61,8 +61,16 @@ var RuntimeGenerator = { // An allocation that cannot normally be free'd (except through sbrk, which once // called, takes control of STATICTOP) staticAlloc: function(size) { + if (ASSERTIONS) size = '(assert(!staticSealed),' + size + ')'; // static area must not be sealed var ret = RuntimeGenerator.alloc(size, 'STATIC', INIT_HEAP); - if (USE_TYPED_ARRAYS) ret += '; if (STATICTOP >= TOTAL_MEMORY) enlargeMemory();' + return ret; + }, + + // allocation on the top of memory, adjusted dynamically by sbrk + dynamicAlloc: function(size) { + if (ASSERTIONS) size = '(assert(DYNAMICTOP > 0),' + size + ')'; // dynamic area must be ready + var ret = RuntimeGenerator.alloc(size, 'DYNAMIC', INIT_HEAP); + if (USE_TYPED_ARRAYS) ret += '; if (DYNAMICTOP >= TOTAL_MEMORY) enlargeMemory();' return ret; }, @@ -466,6 +474,7 @@ var Runtime = { Runtime.stackAlloc = unInline('stackAlloc', ['size']); Runtime.staticAlloc = unInline('staticAlloc', ['size']); +Runtime.dynamicAlloc = unInline('dynamicAlloc', ['size']); Runtime.alignMemory = unInline('alignMemory', ['size', 'quantum']); Runtime.makeBigInt = unInline('makeBigInt', ['low', 'high', 'unsigned']); @@ -529,3 +538,9 @@ function reSign(value, bits, ignore, sig) { return value; } +// The address globals begin at. Very low in memory, for code size and optimization opportunities. +// Above 0 is static memory, starting with globals. +// Then the stack. +// Then 'dynamic' memory for sbrk. +Runtime.GLOBAL_BASE = Runtime.alignMemory(1); + diff --git a/src/settings.js b/src/settings.js index 2e4c2550..ba1f6a83 100644 --- a/src/settings.js +++ b/src/settings.js @@ -279,7 +279,8 @@ var PRINT_SPLIT_FILE_MARKER = 0; // Prints markers in Javascript generation to s var BUILD_AS_SHARED_LIB = 0; // Whether to build the code as a shared library // 0 here means this is not a shared lib: It is a main file. - // 1 means this is a normal shared lib, load it with dlopen(). + // All shared library options (1 and 2) are currently deprecated XXX + // 1 means this is a normal shared lib, load it with dlopen() // 2 means this is a shared lib that will be linked at runtime, // which means it will insert its functions into // the global namespace. See STATIC_LIBS_TO_LOAD. diff --git a/tests/cases/gepoverflow.ll b/tests/cases/gepoverflow.ll deleted file mode 100644 index d09afd87..00000000 --- a/tests/cases/gepoverflow.ll +++ /dev/null @@ -1,37 +0,0 @@ -; ModuleID = 'src.cpp.o' -target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32" -target triple = "i386-pc-linux-gnu" - -; XXX Note: the .txt file contains raw pointer data! The important thing is that the two pointers -; on the first line are of the same scale. If one is huge and the other not, then we failed. -; That is an overflow, which overflow checks should catch. -; If we change memory stuff - like the default stack! - we will need to change this. - -@.str2 = private constant [14 x i8] c"hello, world!\00", align 1 ; [#uses=0] -@.str = private constant [9 x i8] c"*%d,%d*\0A\00", align 1 ; [#uses=1] -@base = internal constant [31 x i16] [i16 3, i16 4, i16 5, i16 6, i16 7, i16 8, i16 9, i16 10, i16 11, i16 13, i16 15, i16 17, i16 19, i16 23, i16 27, i16 31, i16 35, i16 43, i16 51, i16 59, i16 67, i16 83, i16 99, i16 115, i16 131, i16 163, i16 195, i16 227, i16 258, i16 0, i16 0], align 32 ; [#uses=2] - -; [#uses=2] -declare i32 @printf(i8* noalias, ...) nounwind - -; [#uses=0] -define i32 @main() { -entry: - %retval = alloca i32 ; [#uses=1] - %"alloca point" = bitcast i32 0 to i32 ; [#uses=0] - %baseint = ptrtoint [31 x i16]* @base to i32 ; [#uses=1] - %0 = getelementptr [31 x i16]* @base, i32 69273657, i32 24 ; [#uses=1] - %1 = ptrtoint i16* %0 to i32 ; [#uses=2] - %2 = add i32 %1, 570 ; [#uses=2] - %3 = sub i32 %1, %baseint ; [#uses=1] - %4 = sub i32 %2, %baseint ; [#uses=1] - %5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i32 %1, i32 %2) ; [#uses=0] - %6 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i32 %3, i32 %4) ; [#uses=0] - br label %return - -return: ; preds = %entry - store i32 0, i32* %retval - %retval1 = load i32* %retval ; [#uses=1] - ret i32 %retval1 -} - diff --git a/tests/cases/gepoverflow.txt b/tests/cases/gepoverflow.txt deleted file mode 100644 index 31d67161..00000000 --- a/tests/cases/gepoverflow.txt +++ /dev/null @@ -1,2 +0,0 @@ -*5242366,5242936* -*-514,56* diff --git a/tests/cases/gepoverflow_q1.txt b/tests/cases/gepoverflow_q1.txt deleted file mode 100644 index e32e76dd..00000000 --- a/tests/cases/gepoverflow_q1.txt +++ /dev/null @@ -1 +0,0 @@ -skip diff --git a/tests/fcntl/output.txt b/tests/fcntl/output.txt index 1ed740e7..bad9708e 100644 --- a/tests/fcntl/output.txt +++ b/tests/fcntl/output.txt @@ -1,4 +1,4 @@ -F_DUPFD: 100 +F_DUPFD: 1 errno: 0 F_DUPFD/error1: -1 diff --git a/tests/fcntl/src.c b/tests/fcntl/src.c index c8c71c8a..c60e578e 100644 --- a/tests/fcntl/src.c +++ b/tests/fcntl/src.c @@ -5,7 +5,7 @@ int main() { int f = open("/test", O_RDWR, 0777); - printf("F_DUPFD: %d\n", fcntl(f, F_DUPFD, 100)); + printf("F_DUPFD: %d\n", fcntl(f, F_DUPFD, 100) >= 100); printf("errno: |