diff options
-rw-r--r-- | src/jsifier.js | 14 | ||||
-rw-r--r-- | src/modules.js | 3 | ||||
-rw-r--r-- | system/include/libcxx/ostream | 6 | ||||
-rw-r--r-- | tests/runner.py | 9 | ||||
-rw-r--r-- | tools/eliminator/eliminator-test-output.js | 18 | ||||
-rw-r--r-- | tools/eliminator/eliminator-test.js | 20 | ||||
-rw-r--r-- | tools/eliminator/eliminator.coffee | 26 | ||||
-rw-r--r-- | tools/shared.py | 8 |
8 files changed, 81 insertions, 23 deletions
diff --git a/src/jsifier.js b/src/jsifier.js index bd432001..d8ed589e 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -411,7 +411,6 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { funcs: {}, seen: {}, processItem: function(item) { - if (IGNORED_FUNCTIONS.indexOf(item.ident) >= 0) return null; if (this.seen[item.__uid__]) return null; if (item.intertype == 'function') { this.funcs[item.ident] = item; @@ -435,6 +434,8 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { // We have this function all reconstructed, go and finalize it's JS! + if (IGNORED_FUNCTIONS.indexOf(func.ident) >= 0) return null; + func.JS = '\nfunction ' + func.ident + '(' + func.paramIdents.join(', ') + ') {\n'; if (PROFILE) { @@ -931,7 +932,14 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { } generated = generated.concat(itemsDict.function).concat(data.unparsedFunctions); - if (!mainPass) return generated.map(function(item) { return item.JS }).join('\n'); + if (!mainPass) { + Functions.allIdents = Functions.allIdents.concat(itemsDict.function.map(function(func) { + return func.ident; + }).filter(function(func) { + return IGNORED_FUNCTIONS.indexOf(func.ident) < 0; + })); + return generated.map(function(item) { return item.JS }).join('\n'); + } // We are ready to print out the data, but must do so carefully - we are // dealing with potentially *huge* strings. Convenient replacements and @@ -967,6 +975,8 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { print(Functions.generateIndexing()); // done last, as it may rely on aliases set in postsets print(postParts[1]); print(shellParts[1]); + // Print out some useful metadata (for additional optimizations later, like the eliminator) + print('// EMSCRIPTEN_GENERATED_FUNCTIONS: ' + JSON.stringify(Functions.allIdents) + '\n'); return null; } diff --git a/src/modules.js b/src/modules.js index 3b370878..f04731f8 100644 --- a/src/modules.js +++ b/src/modules.js @@ -228,6 +228,9 @@ var Functions = { // All functions that will be implemented in this file implementedFunctions: null, + // All the function idents seen so far + allIdents: [], + indexedFunctions: [0, 0], // Start at a non-0 (even, see below) value // Mark a function as needing indexing, and returns the index diff --git a/system/include/libcxx/ostream b/system/include/libcxx/ostream index c70f3c15..c7d294e2 100644 --- a/system/include/libcxx/ostream +++ b/system/include/libcxx/ostream @@ -204,7 +204,6 @@ protected: basic_ostream() {} // extension, intentially does not initialize }; -/* template <class _CharT, class _Traits> class _LIBCPP_VISIBLE basic_ostream<_CharT, _Traits>::sentry { @@ -1286,9 +1285,8 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const bitset<_Size>& __x) use_facet<ctype<_CharT> >(__os.getloc()).widen('1')); } -extern template class basic_ostream<char>; -extern template class basic_ostream<wchar_t>; -*/ +//extern template class basic_ostream<char>; /* XXX EMScripten */ +//extern template class basic_ostream<wchar_t>; /* XXX EMScripten */ _LIBCPP_END_NAMESPACE_STD diff --git a/tests/runner.py b/tests/runner.py index 8da2c8dd..e0362cd0 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -163,6 +163,13 @@ class RunnerCore(unittest.TestCase): def run_native(self, filename, args): Popen([filename+'.native'] + args, stdout=PIPE, stderr=STDOUT).communicate()[0] + def assertIdentical(self, x, y): + if x != y: + raise Exception("Expected to have '%s' == '%s', diff:\n\n%s" % ( + limit_size(x), limit_size(y), + limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(x.split('\n'), y.split('\n'), fromfile='expected', tofile='actual')])) + )) + def assertContained(self, value, string): if type(value) is not str: value = value() # lazy loading if type(string) is not str: string = string() @@ -4178,7 +4185,7 @@ TT = %s input = open(path_from_root('tools', 'eliminator', 'eliminator-test.js')).read() expected = open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read() output = Popen([COFFEESCRIPT, VARIABLE_ELIMINATOR], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(input)[0] - self.assertEquals(output, expected) + self.assertIdentical(expected, output) else: # Benchmarks. Run them with argument |benchmark|. To run a specific test, do diff --git a/tools/eliminator/eliminator-test-output.js b/tools/eliminator/eliminator-test-output.js index 2324124e..b7a983cc 100644 --- a/tools/eliminator/eliminator-test-output.js +++ b/tools/eliminator/eliminator-test-output.js @@ -5,7 +5,7 @@ function f() { HEAP[123] = (GLOB[1] + 1) / 2; } -var g = (function(a1, a2) { +function g(a1, a2) { var a = 1; var c = a * 2 - 1; @@ -39,7 +39,7 @@ var g = (function(a1, a2) { unquoted: 3, 4: 5 }; -}); +} function h() { var out; bar(hello); @@ -91,3 +91,17 @@ function py() { var $8 = HEAP[__PyThreadState_Current] + 12; HEAP[$8] = $7; } +function otherPy() { + var $4 = HEAP[__PyThreadState_Current]; + var $5 = $4 + 12; + var $7 = HEAP[$5] + 1; + var $8 = $4 + 12; + HEAP[$8] = $7; +} +var anon = (function(x) { + var $4 = HEAP[__PyThreadState_Current]; + var $5 = $4 + 12; + var $7 = HEAP[$5] + 1; + var $8 = $4 + 12; + HEAP[$8] = $7; +}); diff --git a/tools/eliminator/eliminator-test.js b/tools/eliminator/eliminator-test.js index 8a364c0a..681b6cf7 100644 --- a/tools/eliminator/eliminator-test.js +++ b/tools/eliminator/eliminator-test.js @@ -5,7 +5,7 @@ function f() { var z = y / 2; HEAP[123] = z; } -var g = function (a1, a2) { +function g(a1, a2) { var a = 1; var b = a * 2; var c = b - 1; @@ -39,7 +39,7 @@ var g = function (a1, a2) { unquoted: 3, 4: 5 }; -}; +} function h() { var out; bar(hello); @@ -91,3 +91,19 @@ function py() { var $8 = $4 + 12; HEAP[$8] = $7; } +function otherPy() { + var $4 = HEAP[__PyThreadState_Current]; + var $5 = $4 + 12; + var $7 = HEAP[$5] + 1; + var $8 = $4 + 12; + HEAP[$8] = $7; +} +var anon = function(x) { + var $4 = HEAP[__PyThreadState_Current]; + var $5 = $4 + 12; + var $7 = HEAP[$5] + 1; + var $8 = $4 + 12; + HEAP[$8] = $7; +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py"] + diff --git a/tools/eliminator/eliminator.coffee b/tools/eliminator/eliminator.coffee index c07e5974..c6de8690 100644 --- a/tools/eliminator/eliminator.coffee +++ b/tools/eliminator/eliminator.coffee @@ -20,6 +20,10 @@ uglify = require 'uglify-js' fs = require 'fs' +# Functions which have been generated by Emscripten. We optimize only those. +generatedFunctions = [] +GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS:' + # Maximum number of uses to consider a variable not worth eliminating. MAX_USES = 3 @@ -79,6 +83,8 @@ traverse = (node, callback) -> # function/defun node and call run() to apply the optimization (in-place). class Eliminator constructor: (func) -> + @ident = func[1] + # The statements of the function to analyze. @body = func[3] @@ -107,7 +113,7 @@ class Eliminator # @returns: The number of variables eliminated, or undefined if skipped. run: -> # Our optimization does not account for closures. - if @hasClosures @body then return undefined + if not @isGenerated() then return undefined @calculateBasicVarStats() @analyzeInitialValues() @@ -128,16 +134,8 @@ class Eliminator return eliminated # Determines if a function is Emscripten-generated. - hasClosures: -> - closureFound = false - - traverse @body, (node, type) -> - if type in ['defun', 'function', 'with'] - closureFound = true - return false - return undefined - - return closureFound + isGenerated: -> + return @ident in generatedFunctions # Runs the basic variable scan pass. Fills the following member variables: # isLocal @@ -337,6 +335,12 @@ class Eliminator main = -> # Get the parse tree. src = fs.readFileSync('/dev/stdin').toString() + + throw 'Cannot identify generated functions' if GENERATED_FUNCTIONS_MARKER in src + generatedFunctionsLine = src.split('\n').filter (line) -> + return line.indexOf(GENERATED_FUNCTIONS_MARKER) == 0 + generatedFunctions = eval(generatedFunctionsLine[0].replace(GENERATED_FUNCTIONS_MARKER, '')) + ast = uglify.parser.parse src # Run the eliminator on all functions. diff --git a/tools/shared.py b/tools/shared.py index 7cd0fcb8..d5629b50 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -238,6 +238,12 @@ class Building: except: old_dir = None os.chdir(project_dir) + generated_libs = map(lambda lib: os.path.join(project_dir, lib), generated_libs) + #for lib in generated_libs: + # try: + # os.unlink(lib) # make sure compilation completed successfully + # except: + # pass env = os.environ.copy() env['RANLIB'] = env['AR'] = env['CXX'] = env['CC'] = env['LIBTOOL'] = EMMAKEN env['EMMAKEN_COMPILER'] = Building.COMPILER @@ -253,7 +259,7 @@ class Building: Popen(make + make_args, stdout=open(os.path.join(output_dir, 'make_'), 'w'), stderr=open(os.path.join(output_dir, 'make_err'), 'w'), env=env).communicate()[0] bc_file = os.path.join(project_dir, 'bc.bc') - Building.link(map(lambda lib: os.path.join(project_dir, lib), generated_libs), bc_file) + Building.link(generated_libs, bc_file) if cache is not None: cache[cache_name] = open(bc_file, 'rb').read() if old_dir: |