diff options
-rw-r--r-- | src/intertyper.js | 3 | ||||
-rw-r--r-- | src/jsifier.js | 34 | ||||
-rw-r--r-- | src/modules.js | 6 | ||||
-rw-r--r-- | src/parseTools.js | 6 | ||||
-rw-r--r-- | src/preamble.js | 11 | ||||
-rw-r--r-- | src/settings.js | 5 | ||||
-rwxr-xr-x | tests/runner.py | 10 |
7 files changed, 63 insertions, 12 deletions
diff --git a/src/intertyper.js b/src/intertyper.js index 1ad51b96..dbd5f458 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -507,6 +507,9 @@ function intertyper(data, sidePass, baseLineNums) { private_: private_, lineNum: item.lineNum }; + if (NUM_NAMED_GLOBALS >= 0) { + Variables.globals[ret.ident].type = ret.type; + } Types.needAnalysis[ret.type] = 0; if (ident == '@llvm.global_ctors') { ret.ctors = []; diff --git a/src/jsifier.js b/src/jsifier.js index c361278a..ac8037df 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -262,6 +262,20 @@ function JSify(data, functionsOnly, givenFunctions) { '\n]);\n'; return ret; } else { + var constant = null; + var allocator = (BUILD_AS_SHARED_LIB && !item.external) ? 'ALLOC_NORMAL' : 'ALLOC_STATIC'; + var index = null; + if (NUM_NAMED_GLOBALS >= 0) { + if (Variables.seenGlobals < NUM_NAMED_GLOBALS) { + Variables.seenGlobals++; // named + } else { + // indexed + Variables.indexedGlobals[item.ident] = Variables.nextIndexedOffset; + index = makeGlobalUse(item.ident); + Variables.nextIndexedOffset += Runtime.alignMemory(calcAllocatedSize(Variables.globals[item.ident].type)); + allocator = 'ALLOC_NONE'; + } + } if (item.external && BUILD_AS_SHARED_LIB) { // External variables in shared libraries should not be declared as // they would shadow similarly-named globals in the parent. @@ -269,7 +283,7 @@ function JSify(data, functionsOnly, givenFunctions) { } else { item.JS = makeGlobalDef(item.ident); } - var constant = null; + if (item.external) { // Import external global variables from the library if available. var shortident = item.ident.slice(1); @@ -284,7 +298,7 @@ function JSify(data, functionsOnly, givenFunctions) { padding = makeEmptyStruct(item.type); } var padded = val.concat(padding.slice(val.length)); - var js = item.ident + '=' + makePointer(JSON.stringify(padded), null, 'ALLOC_STATIC', item.type) + ';' + var js = item.ident + '=' + makePointer(JSON.stringify(padded), null, allocator, item.type, index) + ';' if (LibraryManager.library[shortident + '__postset']) { js += '\n' + LibraryManager.library[shortident + '__postset']; } @@ -314,15 +328,14 @@ function JSify(data, functionsOnly, givenFunctions) { } // NOTE: This is the only place that could potentially create static // allocations in a shared library. - constant = makePointer(constant, null, BUILD_AS_SHARED_LIB ? 'ALLOC_NORMAL' : 'ALLOC_STATIC', item.type); - + constant = makePointer(constant, null, allocator, item.type, index); var js; - js = makeGlobalUse(item.ident) + '=' + constant + ';'; + js = (index !== null ? '' : item.ident + '=') + constant + ';'; // Special case: class vtables. We make sure they are null-terminated, to allow easy runtime operations if (item.ident.substr(0, 5) == '__ZTV') { - js += '\n' + makePointer('[0]', null, BUILD_AS_SHARED_LIB ? 'ALLOC_NORMAL' : 'ALLOC_STATIC', ['void*']) + ';'; + js += '\n' + makePointer('[0]', null, allocator, ['void*'], index) + ';'; } if (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS)) { js += '\nModule["' + item.ident + '"] = ' + item.ident + ';'; @@ -1270,6 +1283,15 @@ function JSify(data, functionsOnly, givenFunctions) { // if (!mainPass) { + if (phase == 'pre' && !Variables.generatedGlobalBase) { + Variables.generatedGlobalBase = true; + if (Variables.nextIndexedOffset > 0) { + // Variables have been calculated, print out the base generation before we print them + print('var GLOBAL_BASE = STATICTOP;\n'); + print('STATICTOP += ' + Variables.nextIndexedOffset + ';\n'); + print('assert(STATICTOP < TOTAL_MEMORY);\n'); + } + } var generated = itemsDict.function.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.GlobalVariable).concat(itemsDict.GlobalVariablePostSet); if (!DEBUG_MEMORY) print(generated.map(function(item) { return item.JS }).join('\n')); return; diff --git a/src/modules.js b/src/modules.js index 9ef87691..4f7fc784 100644 --- a/src/modules.js +++ b/src/modules.js @@ -174,7 +174,11 @@ var PreProcessor = { }; var Variables = { - globals: {} + globals: {}, + indexedGlobals: {}, // for indexed globals, ident ==> index + // Used in calculation of indexed globals + nextIndexedOffset: 0, + seenGlobals: 0, }; var Types = { diff --git a/src/parseTools.js b/src/parseTools.js index 2591a94a..786d55c8 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -357,10 +357,12 @@ function hasVarArgs(params) { } function makeGlobalDef(ident) { + if (ident in Variables.indexedGlobals) return ''; return 'var ' + ident + ';'; // TODO: add option for namespacing or offsetting to allow reducing the number of globals } function makeGlobalUse(ident) { + if (ident in Variables.indexedGlobals) return getFastValue('GLOBAL_BASE', '+', Variables.indexedGlobals[ident]); return ident; // TODO: add option for namespacing or offsetting to allow reducing the number of globals } @@ -1267,7 +1269,7 @@ function makeGetPos(ptr) { var IHEAP_FHEAP = set('IHEAP', 'IHEAPU', 'FHEAP'); -function makePointer(slab, pos, allocator, type) { +function makePointer(slab, pos, allocator, type, ptr) { assert(type, 'makePointer requires type info'); if (slab.substr(0, 4) === 'HEAP' || (USE_TYPED_ARRAYS == 1 && slab in IHEAP_FHEAP)) return pos; var types = generateStructTypes(type); @@ -1297,7 +1299,7 @@ function makePointer(slab, pos, allocator, type) { types = de[0]; } } - return 'allocate(' + slab + ', ' + JSON.stringify(types) + (allocator ? ', ' + allocator : '') + ')'; + return 'allocate(' + slab + ', ' + JSON.stringify(types) + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ')'; } function makeGetSlabs(ptr, type, allowMultiple, unsigned) { diff --git a/src/preamble.js b/src/preamble.js index 14bf4d36..9342bf2b 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -472,9 +472,11 @@ 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 Module['ALLOC_NORMAL'] = ALLOC_NORMAL; Module['ALLOC_STACK'] = ALLOC_STACK; Module['ALLOC_STATIC'] = ALLOC_STATIC; +Module['ALLOC_NONE'] = ALLOC_NONE; // allocate(): This is for internal use. You can use it yourself as well, but the interface // is a little tricky (see docs right below). The reason is that it is optimized @@ -489,7 +491,7 @@ Module['ALLOC_STATIC'] = ALLOC_STATIC; // is initial data - if @slab is a number, then this does not matter at all and is // ignored. // @allocator: How to allocate memory, see ALLOC_* -function allocate(slab, types, allocator) { +function allocate(slab, types, allocator, ptr) { var zeroinit, size; if (typeof slab === 'number') { zeroinit = true; @@ -501,7 +503,12 @@ function allocate(slab, types, allocator) { var singleType = typeof types === 'string' ? types : null; - var ret = [_malloc, Runtime.stackAlloc, Runtime.staticAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); + var ret; + 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)); + } if (zeroinit) { _memset(ret, 0, size); diff --git a/src/settings.js b/src/settings.js index 5dc1e2eb..4881e149 100644 --- a/src/settings.js +++ b/src/settings.js @@ -194,6 +194,11 @@ var PGO = 0; // Profile-guided optimization. // All CORRECT_* options default to 1 with PGO builds. // See https://github.com/kripken/emscripten/wiki/Optimizing-Code for more info +var NUM_NAMED_GLOBALS = -1; // If >= 0, the number of globals we allow to be named. Other globals + // are then referred to by a base plus an offset (called an indexed global), + // saving global variables but adding runtime overhead. If -1, then we + // allow all globals to be named. + var PROFILE = 0; // Enables runtime profiling. See test_profiling for a usage example. var EXPORT_ALL = 0; // If true, we export all the symbols diff --git a/tests/runner.py b/tests/runner.py index deeb5689..1d85b3df 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -1645,7 +1645,15 @@ c5,de,15,8a return 0; } ''' - self.do_run(src, '4:10,177,543,def\n4\nwowie\ntoo\n76\n5\n(null)\n/* a comment */\n// another\ntest\n', ['wowie', 'too', '74']) + for named, expected in [(-1, 0), (0, 100), (1, 98), (5, 88), (1000, 0)]: + print named + Settings.NUM_NAMED_GLOBALS = named + self.do_run(src, '4:10,177,543,def\n4\nwowie\ntoo\n76\n5\n(null)\n/* a comment */\n// another\ntest\n', ['wowie', 'too', '74']) + if self.emcc_args == []: + gen = open(self.in_dir('src.cpp.o.js')).read() + count = gen.count('GLOBAL_BASE') + assert count == expected + print ' counted' def test_strcmp_uni(self): src = ''' |