diff options
-rw-r--r-- | src/jsifier.js | 3 | ||||
-rw-r--r-- | src/library.js | 92 | ||||
-rw-r--r-- | tests/box2d/Benchmark.cpp | 76 | ||||
-rw-r--r-- | tests/test_core.py | 40 |
4 files changed, 156 insertions, 55 deletions
diff --git a/src/jsifier.js b/src/jsifier.js index 726a5eda..f4819584 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -1844,7 +1844,7 @@ function JSify(data, functionsOnly) { // rest of the output that we started to print out earlier (see comment on the // "Final shape that will be created"). if (PRECISE_I64_MATH && Types.preciseI64MathUsed) { - if (!INCLUDE_FULL_LIBRARY) { + if (!INCLUDE_FULL_LIBRARY && !SIDE_MODULE) { // first row are utilities called from generated code, second are needed from fastLong ['i64Add', 'i64Subtract', 'bitshift64Shl', 'bitshift64Lshr', 'bitshift64Ashr', 'llvm_ctlz_i32', 'llvm_cttz_i32'].forEach(function(func) { @@ -1866,6 +1866,7 @@ function JSify(data, functionsOnly) { } }); } + // these may be duplicated in side modules and the main module without issue print(read('fastLong.js')); print('// EMSCRIPTEN_END_FUNCS\n'); print(read('long.js')); diff --git a/src/library.js b/src/library.js index bc577e78..fc2e8a64 100644 --- a/src/library.js +++ b/src/library.js @@ -4318,21 +4318,36 @@ LibraryManager.library = { _ZTVN10__cxxabiv120__si_class_type_infoE: [2], // yes inherited classes #endif + // We store an extra header in front of the exception data provided + // by the user. + // This header is: + // * type + // * destructor function pointer + // This is then followed by the actual exception data. + __cxa_exception_header_size: 8, + __cxa_last_thrown_exception: 0, + __cxa_caught_exceptions: [], + // Exceptions + __cxa_allocate_exception__deps: ['__cxa_exception_header_size'], __cxa_allocate_exception: function(size) { - return _malloc(size); + var ptr = _malloc(size + ___cxa_exception_header_size); + return ptr + ___cxa_exception_header_size; }, + __cxa_free_exception__deps: ['__cxa_exception_header_size'], __cxa_free_exception: function(ptr) { try { - return _free(ptr); + return _free(ptr - ___cxa_exception_header_size); } catch(e) { // XXX FIXME #if ASSERTIONS Module.printErr('exception during cxa_free_exception: ' + e); #endif } }, + // Here, we throw an exception after recording a couple of values that we need to remember + // We also remember that it was the last exception thrown as we need to know that later. __cxa_throw__sig: 'viii', - __cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], + __cxa_throw__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch', '__cxa_exception_header_size', '__cxa_last_thrown_exception'], __cxa_throw: function(ptr, type, destructor) { if (!___cxa_throw.initialized) { try { @@ -4349,28 +4364,34 @@ LibraryManager.library = { #if EXCEPTION_DEBUG Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + stackTrace()); #endif - {{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}}; - {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'type', 'void*') }}}; - {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'destructor', 'void*') }}}; + var header = ptr - ___cxa_exception_header_size; + {{{ makeSetValue('header', 0, 'type', 'void*') }}} + {{{ makeSetValue('header', 4, 'destructor', 'void*') }}} + ___cxa_last_thrown_exception = ptr; if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { __ZSt18uncaught_exceptionv.uncaught_exception = 1; } else { __ZSt18uncaught_exceptionv.uncaught_exception++; } - {{{ makeThrow('ptr') }}}; + {{{ makeThrow('ptr') }}} }, - __cxa_rethrow__deps: ['llvm_eh_exception', '__cxa_end_catch'], + // This exception will be caught twice, but while begin_catch runs twice, + // we early-exit from end_catch when the exception has been rethrown, so + // pop that here from the caught exceptions. + __cxa_rethrow__deps: ['__cxa_end_catch', '__cxa_caught_exceptions'], __cxa_rethrow: function() { ___cxa_end_catch.rethrown = true; - {{{ makeThrow(makeGetValue('_llvm_eh_exception.buf', '0', 'void*')) }}}; + var ptr = ___cxa_caught_exceptions.pop(); + {{{ makeThrow('ptr') }}} }, - llvm_eh_exception__postset: '_llvm_eh_exception.buf = allocate(12, "void*", ALLOC_STATIC);', + llvm_eh_exception__deps: ['__cxa_last_thrown_exception'], llvm_eh_exception: function() { - return {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; + return ___cxa_last_thrown_exception; }, llvm_eh_selector__jsargs: true, + llvm_eh_selector__deps: ['__cxa_last_thrown_exception'], llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) { - var type = {{{ makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') }}} + var type = ___cxa_last_thrown_exception; for (var i = 2; i < arguments.length; i++) { if (arguments[i] == type) return type; } @@ -4379,12 +4400,22 @@ LibraryManager.library = { llvm_eh_typeid_for: function(type) { return type; }, - __cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv'], + // Note that we push the last thrown exception here rather than the ptr. + // This is because if the exception is a pointer (as in test 3 of test_exceptions_typed), + // we don't actually get the value that we allocated, but something else. Easiest + // to remember that the last exception thrown is going to be the first to be caught, + // so just use that value instead as it is what we're really looking for. + __cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv', '__cxa_caught_exceptions'], __cxa_begin_catch: function(ptr) { __ZSt18uncaught_exceptionv.uncaught_exception--; + ___cxa_caught_exceptions.push(___cxa_last_thrown_exception); return ptr; }, - __cxa_end_catch__deps: ['llvm_eh_exception', '__cxa_free_exception'], + // We're done with a catch. Now, we can run the destructor if there is one + // and free the exception. Note that if the dynCall on the destructor fails + // due to calling apply on undefined, that means that the destructor is + // an invalid index into the FUNCTION_TABLE, so something has gone wrong. + __cxa_end_catch__deps: ['__cxa_free_exception', '__cxa_last_thrown_exception', '___cxa_exception_header_size', '__cxa_caught_exceptions'], __cxa_end_catch: function() { if (___cxa_end_catch.rethrown) { ___cxa_end_catch.rethrown = false; @@ -4396,22 +4427,20 @@ LibraryManager.library = { #else __THREW__ = 0; #endif - // Clear type. - {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, '0', 'void*') }}}; // Call destructor if one is registered then clear it. - var ptr = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; - var destructor = {{{ makeGetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'void*') }}}; - if (destructor) { - Runtime.dynCall('vi', destructor, [ptr]); - {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, '0', 'i32') }}}; - } - // Free ptr if it isn't null. + var ptr = ___cxa_caught_exceptions.pop(); if (ptr) { + header = ptr - ___cxa_exception_header_size; + var destructor = {{{ makeGetValue('header', 4, 'void*') }}}; + if (destructor) { + Runtime.dynCall('vi', destructor, [ptr]); + {{{ makeSetValue('header', 4, '0', 'i32') }}} + } ___cxa_free_exception(ptr); - {{{ makeSetValue('_llvm_eh_exception.buf', '0', '0', 'void*') }}}; + ___cxa_last_thrown_exception = 0; } }, - __cxa_get_exception_ptr__deps: ['llvm_eh_exception'], + __cxa_get_exception_ptr__deps: ['___cxa_last_thrown_exception'], __cxa_get_exception_ptr: function(ptr) { return ptr; }, @@ -4431,7 +4460,7 @@ LibraryManager.library = { terminate: '__cxa_call_unexpected', - __gxx_personality_v0__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], + __gxx_personality_v0__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], __gxx_personality_v0: function() { }, @@ -4464,10 +4493,11 @@ LibraryManager.library = { // functionality boils down to picking a suitable 'catch' block. // We'll do that here, instead, to keep things simpler. - __cxa_find_matching_catch__deps: ['__cxa_does_inherit', '__cxa_is_number_type', '__resumeException'], + __cxa_find_matching_catch__deps: ['__cxa_does_inherit', '__cxa_is_number_type', '__resumeException', '__cxa_last_thrown_exception', '__cxa_exception_header_size'], __cxa_find_matching_catch: function(thrown, throwntype) { - if (thrown == -1) thrown = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; - if (throwntype == -1) throwntype = {{{ makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') }}}; + if (thrown == -1) thrown = ___cxa_last_thrown_exception; + header = thrown - ___cxa_exception_header_size; + if (throwntype == -1) throwntype = {{{ makeGetValue('header', 0, 'void*') }}}; var typeArray = Array.prototype.slice.call(arguments, 2); // If throwntype is a pointer, this means a pointer has been @@ -4498,8 +4528,8 @@ LibraryManager.library = { #if EXCEPTION_DEBUG Module.print("Resuming exception"); #endif - if ({{{ makeGetValue('_llvm_eh_exception.buf', 0, 'void*') }}} == 0) {{{ makeSetValue('_llvm_eh_exception.buf', 0, 'ptr', 'void*') }}}; - {{{ makeThrow('ptr') }}}; + if (!___cxa_last_thrown_exception) { ___cxa_last_thrown_exception = ptr; } + {{{ makeThrow('ptr') }}} }, // Recursively walks up the base types of 'possibilityType' diff --git a/tests/box2d/Benchmark.cpp b/tests/box2d/Benchmark.cpp index 14aa1591..4fd79651 100644 --- a/tests/box2d/Benchmark.cpp +++ b/tests/box2d/Benchmark.cpp @@ -56,11 +56,15 @@ result_t measure(clock_t *times) { return r; } -int main(int argc, char **argv) { -#if EMSCRIPTEN - emscripten_run_script("if (Module.reportStartedUp) Module.reportStartedUp()"); -#endif +b2World *world; +clock_t *times, minn = CLOCKS_PER_SEC * 1000 * 100, maxx = -1; +b2Body* topBody; +int32 frameCounter = 0; +int responsive_main_loop; +void iter(); + +int main(int argc, char **argv) { int arg = argc > 1 ? argv[1][0] - '0' : 3; switch(arg) { case 0: return 0; break; @@ -76,24 +80,24 @@ int main(int argc, char **argv) { FRAMES += WARMUP; WARMUP = 0; + times = new clock_t[FRAMES]; + // Define the gravity vector. b2Vec2 gravity(0.0f, -10.0f); // Construct a world object, which will hold and simulate the rigid bodies. - b2World world(gravity); - world.SetAllowSleeping(false); + world = new b2World(gravity); + world->SetAllowSleeping(false); { b2BodyDef bd; - b2Body* ground = world.CreateBody(&bd); + b2Body* ground = world->CreateBody(&bd); b2EdgeShape shape; shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); ground->CreateFixture(&shape, 0.0f); } - b2Body* topBody; - { float32 a = 0.5f; b2PolygonShape shape; @@ -111,7 +115,7 @@ int main(int argc, char **argv) { b2BodyDef bd; bd.type = b2_dynamicBody; bd.position = y; - b2Body* body = world.CreateBody(&bd); + b2Body* body = world->CreateBody(&bd); body->CreateFixture(&shape, 5.0f); topBody = body; @@ -124,28 +128,54 @@ int main(int argc, char **argv) { } for (int32 i = 0; i < WARMUP; ++i) { - world.Step(1.0f/60.0f, 3, 3); + world->Step(1.0f/60.0f, 3, 3); } - clock_t times[FRAMES], min = CLOCKS_PER_SEC * 1000 * 100, max = -1; - for (int32 i = 0; i < FRAMES; ++i) { - clock_t start = clock(); - world.Step(1.0f/60.0f, 3, 3); - clock_t end = clock(); +#if EMSCRIPTEN + responsive_main_loop = argc > 2 ? argv[2][0] - '0' : 0; + if (responsive_main_loop) { + printf("responsive main loop\n"); + emscripten_set_main_loop(iter, 60, 1); + } else { +#endif + do { + iter(); + } while (frameCounter <= FRAMES); +#if EMSCRIPTEN + } +#endif + + return 0; +} + +void iter() { + if (frameCounter < FRAMES) { + clock_t start = clock(); + world->Step(1.0f/60.0f, 3, 3); + clock_t end = clock(); clock_t curr = end - start; - times[i] = curr; - if (curr < min) min = curr; - if (curr > max) max = curr; + times[frameCounter] = curr; + if (curr < minn) minn = curr; + if (curr > maxx) maxx = curr; #if DEBUG printf("%f :: ", topBody->GetPosition().y); - printf("%f\n", (float32)(end - start) / CLOCKS_PER_SEC * 1000); + printf("%f\n", (float32)(end - start) / CLOCKS_PER_SEC * 1000); #endif - } + frameCounter++; + return; + } + + // that's it! + + frameCounter++; result_t result = measure(times); - printf("frame averages: %.3f +- %.3f, range: %.3f to %.3f \n", result.mean, result.stddev, float(min)/CLOCKS_PER_SEC * 1000, float(max)/CLOCKS_PER_SEC * 1000); + printf("frame averages: %.3f +- %.3f, range: %.3f to %.3f \n", result.mean, result.stddev, float(minn)/CLOCKS_PER_SEC * 1000, float(maxx)/CLOCKS_PER_SEC * 1000); - return 0; +#if EMSCRIPTEN + emscripten_run_script("if (Module.reportCompletion) Module.reportCompletion()"); + if (responsive_main_loop) emscripten_cancel_main_loop(); +#endif } diff --git a/tests/test_core.py b/tests/test_core.py index 99c69459..97cba68f 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2809,6 +2809,46 @@ def process(filename): self.do_run(src, 'Constructing main object.\nConstructing lib object.\n', post_build=self.dlfcn_post_build) + def test_dlfcn_i64(self): + if not self.can_dlfcn(): return + if not Settings.ASM_JS: return self.skip('TODO') + + self.prep_dlfcn_lib() + Settings.EXPORTED_FUNCTIONS = ['_foo'] + lib_src = ''' + int foo(int x) { + return (long long)x / (long long)1234; + } + ''' + dirname = self.get_dir() + filename = os.path.join(dirname, 'liblib.c') + self.build(lib_src, dirname, filename) + shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so')) + + self.prep_dlfcn_main() + Settings.EXPORTED_FUNCTIONS = ['_main'] + src = r''' + #include <stdio.h> + #include <stdlib.h> + #include <dlfcn.h> + + typedef int (*intfunc)(int); + + void *p; + + int main() { + p = malloc(1024); + void *lib_handle = dlopen("liblib.so", 0); + printf("load %p\n", lib_handle); + intfunc x = (intfunc)dlsym(lib_handle, "foo"); + printf("foo func %p\n", x); + if (p == 0) return 1; + printf("|%d|\n", x(81234567)); + return 0; + } + ''' + self.do_run(src, '|65830|', post_build=self.dlfcn_post_build) + def test_dlfcn_qsort(self): if not self.can_dlfcn(): return |