diff options
author | Alon Zakai <alonzakai@gmail.com> | 2012-02-03 15:00:45 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2012-02-03 15:00:45 -0800 |
commit | 3299f8dc407b76310428534ebe0f0b5caca016d9 (patch) | |
tree | c4a47ff405f1c3345661fffde042de01f1f22350 | |
parent | 1bb034f764022f3da1894f9d84fb5517020eef23 (diff) |
enable full optimizations with bindings generator
-rwxr-xr-x | emcc | 1 | ||||
-rwxr-xr-x | tests/runner.py | 71 | ||||
-rwxr-xr-x | tools/bindings_generator.py | 52 |
3 files changed, 72 insertions, 52 deletions
@@ -597,6 +597,7 @@ try: if DEBUG: save_intermediate('ll', 'll') if AUTODEBUG: + if DEBUG: print >> sys.stderr, 'emcc: autodebug' Popen(['python', shared.AUTODEBUGGER, final, final + '.ad.ll']).communicate()[0] final += '.ad.ll' if DEBUG: save_intermediate('autodebug', 'll') diff --git a/tests/runner.py b/tests/runner.py index 4fd7ddfc..2be6453b 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -111,28 +111,39 @@ class RunnerCore(unittest.TestCase): # that post_build is called on unoptimized JS, so we send it to emcc (otherwise, if run after # emcc, it would not apply on the optimized/minified JS) def ll_to_js(self, filename, extra_emscripten_args, post_build): + if type(post_build) in (list, tuple): + post1, post2 = post_build + else: + post1 = post_build + post2 = None + + def run_post(post): + if not post: return + exec post in locals() + shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js') + process(filename + '.o.js') + if self.emcc_args is None: Building.emscripten(filename, append_ext=True, extra_args=extra_emscripten_args) - if post_build is not None: - exec post_build in locals() - shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js') - process(filename + '.o.js') + run_post(post1) + run_post(post2) else: transform_args = [] - if post_build: + if post1: transform_filename = os.path.join(self.get_dir(), 'transform.py') transform = open(transform_filename, 'w') transform.write(''' import sys sys.path += ['%s'] ''' % path_from_root('')) - transform.write(post_build) + transform.write(post1) transform.write(''' process(sys.argv[1]) ''') transform.close() transform_args = ['--js-transform', "python %s" % transform_filename] Building.emcc(filename + '.o.ll', Settings.serialize() + self.emcc_args + transform_args, filename + '.o.js') + run_post(post2) # Build JavaScript code from source code def build(self, src, dirname, filename, output_processor=None, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, extra_emscripten_args=[], post_build=None): @@ -4684,6 +4695,9 @@ def process(filename): ''' # XXX disable due to possible v8 bug -- self.do_run(src, '*166*\n*ok*', post_build=post) + if self.emcc_args is not None and '-O2' in self.emcc_args: + self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right + # Way 2: use CppHeaderParser Settings.RUNTIME_TYPE_INFO = 1 @@ -4740,15 +4754,22 @@ def process(filename): post2 = ''' def process(filename): + src = open(filename, 'a') + src.write(open('bindingtest.js').read() + '\\n\\n') + src.close() +''' + + post3 = ''' +def process(filename): script_src_2 = \'\'\' - var sme = new Parent(42); + var sme = new Module.Parent(42); sme.mulVal(2); print('*') print(sme.getVal()); print('c1'); - var c1 = new Child1(); + var c1 = new Module.Child1(); print(c1.getVal()); c1.mulVal(2); print(c1.getVal()); @@ -4759,7 +4780,7 @@ def process(filename): print('c1 v2'); - c1 = new Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2 + c1 = new Module.Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2 print(c1.getVal()); c1.mulVal(2); print(c1.getVal()); @@ -4768,7 +4789,7 @@ def process(filename): print('c2') - var c2 = new Child2(); + var c2 = new Module.Child2(); print(c2.getVal()); c2.mulVal(2); print(c2.getVal()); @@ -4793,42 +4814,39 @@ def process(filename): } catch(e) {} print(succeeded); - Child2.prototype.printStatic(); // static calls go through the prototype + Module.Child2.prototype.printStatic(); // static calls go through the prototype // virtual function c2.virtualFunc(); - Child2.prototype.runVirtualFunc(c2); + Module.Child2.prototype.runVirtualFunc(c2); c2.virtualFunc2(); // extend the class from JS - var c3 = new Child2; - customizeVTable(c3, [{ - original: Child2.prototype.virtualFunc, + var c3 = new Module.Child2; + Module.customizeVTable(c3, [{ + original: Module.Child2.prototype.virtualFunc, replacement: function() { print('*js virtualf replacement*'); } }, { - original: Child2.prototype.virtualFunc2, + original: Module.Child2.prototype.virtualFunc2, replacement: function() { print('*js virtualf2 replacement*'); } }]); c3.virtualFunc(); - Child2.prototype.runVirtualFunc(c3); + Module.Child2.prototype.runVirtualFunc(c3); c3.virtualFunc2(); c2.virtualFunc(); // original should remain the same - Child2.prototype.runVirtualFunc(c2); + Module.Child2.prototype.runVirtualFunc(c2); c2.virtualFunc2(); print('*ok*'); \'\'\' - src = open(filename, 'r').read().replace( - '// {{MODULE_ADDITIONS}', - open('bindingtest.js').read() + '\\n\\n' + script_src_2 + '\\n\\n' + - '// {{MODULE_ADDITIONS}' - ) - open(filename, 'w').write(src) + src = open(filename, 'a') + src.write(script_src_2 + '\\n') + src.close() ''' self.do_run(src, '''* @@ -4871,7 +4889,7 @@ Child2:9 *virtualf* *virtualf2* *ok* -''', post_build=post2) +''', post_build=[post2, post3]) def test_typeinfo(self): if self.emcc_args is not None and self.emcc_args != []: return self.skip('full LLVM opts optimize out all the code that uses the type') @@ -5731,9 +5749,6 @@ f.close() output = Popen([NODE_JS, JS_OPTIMIZER, input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0] self.assertIdentical(expected, output.replace('\n\n', '\n')) - def test_reminder(self): - assert False, 'Ensure all opts including linktime apply to bindings generator. might need to adjust visibility of bindings C funcs' - elif 'benchmark' in str(sys.argv): # Benchmarks. Run them with argument |benchmark|. To run a specific test, do # |benchmark.test_X|. diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py index 8146215c..d02af6b4 100755 --- a/tools/bindings_generator.py +++ b/tools/bindings_generator.py @@ -29,12 +29,15 @@ We generate the following: change all arguments of type float& to float by "type_processor": "lambda t: t if t != 'float&' else 'float'" - export: If true, will export all bindings in the .js file. This allows + export: If false, will not export all bindings in the .js file. The + default is to export all bindings, which allows you to run something like closure compiler advanced opts on the library+bindings, and the bindings will remain accessible. prevent_dfe: If true, will use all the generated C functions, to prevent - dead function elimination from removing them. + dead function elimination from removing them. The use is + by creating an artificial "main" functions. You should then + remove that function later or just ignore it. For example, JSON can be { "ignored": "class1,class2::func" }. @@ -66,8 +69,8 @@ basename = sys.argv[1] ignored = [] type_processor = lambda t: t -export = 0 -prevent_dfe = 0 +export = 1 +prevent_dfe = 1 if '--' in sys.argv: index = sys.argv.index('--') @@ -363,7 +366,7 @@ for classname, clazz in parsed.classes.iteritems(): # TODO: Bind virtual functions using dynamic binding in the C binding code funcs = {} # name -> # of copies in the original, and originalname in a copy -c_funcs = [] +c_funcs = [] # the C functions generated, including dummy params we can use to keep them alive from dfe gen_c = open(basename + '.cpp', 'w') gen_js = open(basename + '.js', 'w') @@ -543,6 +546,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge #print 'POST arggggggg', classname, 'x', mname, 'x', args[i]['name'], 'x', args[i]['type'] ret = ((classname + ' *') if constructor else method['returns_text'])#.replace('virtual ', '') + has_return = ret.replace(' ', '') != 'void' callprefix = 'new ' if constructor else ('self->' if not static else (classname + '::')) '' # mname used in C @@ -558,6 +562,8 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge return map(lambda i: 'arg' + str(i), range(len(args))) def justtypes(args): # note: this ignores 'self' return map(lambda i: args[i]['type'], range(len(args))) + def dummyargs(args): + return ([] if not need_self else ['(%s*)argv[argc]' % classname]) + map(lambda i: '(%s)argv[argc]' % args[i]['type'], range(len(args))) fullname = ('emscripten_bind_' + generating_classname + '__' + mname).replace('::', '__') generating_classname_suffixed = generating_classname @@ -582,8 +588,9 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge i = len(args) # If we are returning a *copy* of an object, we return instead to a ref of a static held here. This seems the best compromise staticize = not constructor and ret.replace(' ', '') != 'void' and method['returns'] in classes and (not method['returns_reference'] and not method['returns_pointer']) + # Make sure to mark our bindings wrappers in a way that they will not be inlined, eliminated as unneeded, or optimized into other signatures gen_c.write(''' -%s %s_p%d(%s) {''' % (ret if not staticize else (ret + '&'), fullname, i, +%s __attribute__((externally_visible, used, noinline)) %s_p%d(%s) {''' % (ret if not staticize else (ret + '&'), fullname, i, ', '.join(typedargs(args)[:i + (0 if not need_self else 1)]))) if not staticize: gen_c.write('\n') @@ -596,7 +603,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge elif method.get('operator'): gen_c.write(method['operator']) else: # normal method - gen_c.write(''' %s%s%s(%s);''' % ('return ' if ret.replace(' ', '') != 'void' else '', + gen_c.write(''' %s%s%s(%s);''' % ('return ' if has_return else '', callprefix, actualmname, ', '.join(justargs(args)[:i]))) gen_c.write('\n') gen_c.write('}') @@ -610,7 +617,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge callprefix, actualmname, ', '.join(justargs(args)[:i]))) gen_c.write('\n}') - c_funcs.append(fullname + '_p' + str(i)) + c_funcs.append(('argc += (int)' if has_return else '') + fullname + '_p' + str(i) + '(' + ', '.join(dummyargs(args)[:i + (0 if not need_self else 1)]) + ')') # JS @@ -768,26 +775,23 @@ gen_c.write(''' if prevent_dfe: gen_c.write(''' - #include <stdio.h> + #include <stdio.h> + #include <string.h> + int main(int argc, char **argv) { + if (argc >= 1 && !strcmp(argv[0], "this is a dummy |main| function, just for bindings generator dfe prevention!")) { + // Actually 'use' the binding functions, so DFE will not eliminate them. + printf("We should never actually get here!\\n"); - struct EmscriptenEnsurer - { - EmscriptenEnsurer() { - // Actually use the binding functions, so DFE will not eliminate them - // FIXME: A negative side effect of this is that they take up space in FUNCTION_TABLE - int sum = 0; - void *seen = (void*)%s; - ''' % c_funcs[0]) +''') - for func in c_funcs[1:]: - gen_c.write(''' sum += (void*)%s == seen; - ''' % func) + for i in range(len(c_funcs)): + func = c_funcs[i] + gen_c.write(' if (argc == %d+1) %s;\n' % (i, func)) - gen_c.write(''' printf("(%d)\\n", sum); + gen_c.write(''' + } + return argc; } - }; - - EmscriptenEnsurer emscriptenEnsurer; ''') gen_c.close() |