diff --git a/em++ b/em++ index ba09e1a2d472d..1a90915aeeeec 100755 --- a/em++ +++ b/em++ @@ -7,6 +7,5 @@ See emcc.py. This script forwards to there, noting that we want C++ and not C by import os, subprocess, sys from tools import shared -os.environ['EMMAKEN_CXX'] = '1' -exit(subprocess.call([shared.PYTHON, shared.EMCC] + sys.argv[1:])) +exit(subprocess.call([shared.PYTHON, shared.EMCC] + sys.argv[1:] + ['--emscripten-cxx'])) diff --git a/embuilder.py b/embuilder.py new file mode 100755 index 0000000000000..931c655d413df --- /dev/null +++ b/embuilder.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python2 + +''' +Tool to manage building of various useful things, such as libc, libc++, native optimizer, as well as fetch and build ports like zlib and sdl2 +''' + +import os, sys +import tools.shared as shared + +if len(sys.argv) < 2 or sys.argv[1] in ['-v', '-help', '--help', '-?', '?']: + print ''' +Emscripten System Builder Tool +============================== + +You can use this tool to manually build parts of the emscripten system +environment. In general emcc will build them automatically on demand, so +you do not strictly need to use this tool, but it gives you more control +over the process (in particular, if emcc does this automatically, and you +are running multiple build commands in parallel, confusion can occur). + +Usage: + + embuilder.py OPERATION TASK1 [TASK2..] + +Available operations and tasks: + + build libc + libcxx + libcxxabi + gl + struct_info + native_optimizer + zlib + sdl2 + sdl2-image + +It is also possible to build native_optimizer manually by using CMake. To +do that, run + + 1. cd $EMSCRIPTEN/tools/optimizer + 2. cmake . -DCMAKE_BUILD_TYPE=Release + 3. make (or mingw32-make/vcbuild/msbuild on Windows) + +and set up the location to the native optimizer in ~/.emscripten + +''' + sys.exit(0) + +temp_files = shared.configuration.get_temp_files() + +def build(src, result_libs, args=[]): + temp = temp_files.get('.cpp').name + open(temp, 'w').write(src) + temp_js = temp_files.get('.js').name + shared.Building.emcc(temp, args, output_filename=temp_js) + assert os.path.exists(temp_js), 'failed to build file' + for lib in result_libs: + assert os.path.exists(shared.Cache.get_path(lib)), 'not seeing that requested library %s has been built' % lib + +operation = sys.argv[1] + +if operation == 'build': + for what in sys.argv[2:]: + shared.logging.info('building and verifying ' + what) + if what == 'libc': + build(''' + #include + #include + int main() { + return int(malloc(10)) + int(strchr("str", 'c')); + } + ''', ['libc.bc', 'libcextra.bc']) + elif what == 'libcxx': + build(''' + #include + int main() { + std::cout << "hello"; + return 0; + } + ''', ['libcxx.bc']) + elif what == 'libcxxabi': + build(''' + struct X { int x; virtual void a() {} }; + struct Y : X { int y; virtual void a() { y = 10; }}; + int main(int argc, char **argv) { + Y* y = dynamic_cast((X*)argv[1]); + y->a(); + return y->y; + } + ''', ['libcxxabi.bc']) + elif what == 'gl': + build(''' + extern "C" { extern void* emscripten_GetProcAddress(const char *x); } + int main() { + return int(emscripten_GetProcAddress("waka waka")); + } + ''', ['gl.bc']) + elif what == 'struct_info': + build(''' + int main() {} + ''', ['struct_info.compiled.json']) + elif what == 'native_optimizer': + build(''' + int main() {} + ''', ['optimizer.exe'], ['-O2']) + elif what == 'zlib': + build(''' + int main() {} + ''', [os.path.join('ports-builds', 'zlib', 'libz.a')], ['-s', 'USE_ZLIB=1']) + elif what == 'sdl2': + build(''' + int main() {} + ''', [os.path.join('ports-builds', 'sdl2', 'libsdl2.bc')], ['-s', 'USE_SDL=2']) + elif what == 'sdl2-image': + build(''' + int main() {} + ''', [os.path.join('ports-builds', 'sdl2-image', 'libsdl2_image.bc')], ['-s', 'USE_SDL=2', '-s', 'USE_SDL_IMAGE=2']) + else: + shared.logging.error('unfamiliar build target: ' + what) + sys.exit(1) + + shared.logging.info('...success') + +else: + shared.logging.error('unfamiliar operation: ' + operation) + sys.exit(1) + diff --git a/emcc b/emcc index fd4e4c75ff6cd..6813cf8707660 100755 --- a/emcc +++ b/emcc @@ -104,6 +104,9 @@ if DEBUG and LEAVE_INPUTS_RAW: logging.warning('leaving inputs raw') stdout = PIPE if not DEBUG else None # suppress output of child processes stderr = PIPE if not DEBUG else None # unless we are in DEBUG mode +EMCC_CXX = '--emscripten-cxx' in sys.argv +sys.argv = filter(lambda x: x != '--emscripten-cxx', sys.argv) + shared.check_sanity(force=DEBUG) # Handle some global flags @@ -126,8 +129,6 @@ while response_file: break if len(sys.argv) == 1 or '--help' in sys.argv: - this = os.path.basename('em++' if os.environ.get('EMMAKEN_CXX') else 'emcc') - # Documentation for emcc and its options must be updated in: # site/source/docs/tools_reference/emcc.rst # A prebuilt local version of the documentation is available at: @@ -212,7 +213,7 @@ if CONFIGURE_CONFIG or CMAKE_CONFIG: if debug_configure: open(tempout, 'a').write('Forcing clang since uses fopen to write\n') compiler = os.environ.get('CONFIGURE_CC') or (shared.CLANG if not use_js else shared.EMCC) # if CONFIGURE_CC is defined, use that. let's you use local gcc etc. if you need that - if not ('CXXCompiler' in ' '.join(sys.argv) or os.environ.get('EMMAKEN_CXX')): + if not ('CXXCompiler' in ' '.join(sys.argv) or EMCC_CXX): compiler = shared.to_cc(compiler) def filter_emscripten_options(argv): @@ -276,7 +277,7 @@ else: CC = shared.to_cc(CXX) # If we got here from a redirection through emmakenxx.py, then force a C++ compiler here -if os.environ.get('EMMAKEN_CXX'): +if EMCC_CXX: CC = CXX CC_ADDITIONAL_ARGS = shared.COMPILER_OPTS @@ -830,7 +831,7 @@ try: break if found: break if found: break - if not found and lib not in ['GL', 'GLU', 'glut']: # whitelist our default libraries + if not found and lib not in ['GL', 'GLU', 'glut', 'SDL']: # whitelist our default libraries logging.warning('emcc: cannot find library "%s"', lib) # If not compiling to JS, then we are compiling to an intermediate bitcode objects or library, so @@ -865,6 +866,8 @@ try: # Set ASM_JS default here so that we can override it from the command line. shared.Settings.ASM_JS = 1 if opt_level > 0 else 2 + pre_fastcomp_opts = [] + # Apply -s settings in newargs here (after optimization levels, so they can override them) for change in settings_changes: key, value = change.split('=') @@ -902,7 +905,6 @@ try: logging.warning('jcache is deprecated and not supported in fastcomp (you should not need it anyhow), disabling') jcache = False - pre_fastcomp_opts = [] fastcomp_opts = [] if shared.Settings.NO_EXIT_RUNTIME: pre_fastcomp_opts += ['-emscripten-no-exit-runtime'] @@ -1235,6 +1237,11 @@ try: if not shared.Building.can_inline(): link_opts.append('-disable-inlining') # do not internalize in std-link-opts - it ignores internalize-public-api-list - and add a manual internalize link_opts += ['-disable-internalize'] + shared.Building.get_safe_internalize() + ['-std-link-opts'] + # execute it now, so it is done entirely before we get to the stage of legalization etc. + shared.Building.llvm_opt(final, pre_fastcomp_opts + link_opts) + if DEBUG: save_intermediate('lto', 'bc') + pre_fastcomp_opts = [] + link_opts = [] else: # At minimum remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it) link_opts += shared.Building.get_safe_internalize() + ['-globaldce'] diff --git a/emscripten-version.txt b/emscripten-version.txt index b7825b8d76746..a5096ba657ec9 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.28.2 +1.28.3 diff --git a/emscripten.py b/emscripten.py index acfb42f2c5bfb..8501ab3d42984 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1103,11 +1103,9 @@ def make_emulated_param(i): 'splat', 'swizzle', 'shuffle', 'withX', 'withY', 'withZ', 'withW', 'load', 'store'] - # TODO: fromInt32x4 simdfloatfuncs = simdfuncs + ['div', 'min', 'max', 'minNum', 'maxNum', 'sqrt', - 'abs', 'fromInt32x4Bits']; - # TODO: fromFloat32x4 - simdintfuncs = simdfuncs + ['fromFloat32x4Bits', + 'abs', 'fromInt32x4', 'fromInt32x4Bits']; + simdintfuncs = simdfuncs + ['fromFloat32x4', 'fromFloat32x4Bits', 'shiftRightArithmeticByScalar', 'shiftRightLogicalByScalar', 'shiftLeftByScalar']; diff --git a/site/source/docs/getting_started/FAQ.rst b/site/source/docs/getting_started/FAQ.rst index b5da7f27c024a..7a70ccbb3ddb6 100644 --- a/site/source/docs/getting_started/FAQ.rst +++ b/site/source/docs/getting_started/FAQ.rst @@ -207,6 +207,11 @@ or What happens in practice is that when code is ready to be run, we check for ``Module._main``. If present, we call it. If a ``main()`` function was compiled from C, it will be there (and it will be a JavaScript function). But, you can also just define a JavaScript function there, either will work. +Another option is to define an ``onRuntimeInitialized`` function, + + Module['onRuntimeInitialized'] = function() { ... }; + +That method will be called when the runtime is ready and it is ok for you to call compiled code. In practice, that is exactly the same time at which ``main()`` would be called, so ``onRuntimeInitialized`` doesn't let you do anything new, but it can be convenient in some cases - for example, if you use ``onRuntimeInitialized`` and don't define a ``main()`` function, then the runtime will not be shut down after ``main()`` exits, and you can keep calling compiled methods (you can also have a ``main()`` and build with ``-s NO_EXIT_RUNTIME=1`` to keep the runtime from being shut down). Thus, for libraries, ``onRuntimeInitialized`` can be convenient. .. _faq-dead-code-elimination: diff --git a/src/ecmascript_simd.js b/src/ecmascript_simd.js index 3b2737ef4cb65..f2c9ec682acf1 100644 --- a/src/ecmascript_simd.js +++ b/src/ecmascript_simd.js @@ -19,8 +19,11 @@ */ if (typeof SIMD === "undefined") { - // SIMD module. - var SIMD = {}; + // SIMD module. We don't use the var keyword here, so that we put the + // SIMD object in the global scope even if this polyfill code is included + // within some other scope. The theory is that we're anticipating a + // future where SIMD is predefined in the global scope. + SIMD = {}; } // private stuff. @@ -254,7 +257,7 @@ if (typeof SIMD.float32x4.fromUnsignedInt32x4 === "undefined") { * @param {int32x4} t An instance of int32x4. * @return {float32x4} An unsigned integer to float conversion copy of t. */ - SIMD.float32x4.fromInt32x4 = function(t) { + SIMD.float32x4.fromUnsignedInt32x4 = function(t) { t = SIMD.int32x4(t); return SIMD.float32x4(t.x>>>0, t.y>>>0, t.z>>>0, t.w>>>0); } @@ -385,7 +388,7 @@ if (typeof SIMD.float64x2.fromUnsignedInt32x4 === "undefined") { * @param {int32x4} t An instance of int32x4. * @return {float64x2} A float64x2 with .x>>>0 and .y>>>0 from t */ - SIMD.float64x2.fromInt32x4 = function(t) { + SIMD.float64x2.fromUnsignedInt32x4 = function(t) { t = SIMD.int32x4(t); return SIMD.float64x2(t.x>>>0, t.y>>>0); } @@ -534,7 +537,7 @@ if (typeof SIMD.int32x4.fromFloat32x4ToUnsigned === "undefined") { * @param {float32x4} t An instance of float32x4. * @return {int32x4} with an unsigned integer to float conversion of t. */ - SIMD.int32x4.fromFloat32x4 = function(t) { + SIMD.int32x4.fromFloat32x4ToUnsigned = function(t) { t = SIMD.float32x4(t); return SIMD.int32x4(t.x>>>0, t.y>>>0, t.z>>>0, t.w>>>0); } @@ -556,7 +559,7 @@ if (typeof SIMD.int32x4.fromFloat64x2ToUnsigned === "undefined") { * @param {float64x2} t An instance of float64x2. * @return {int32x4} An int32x4 with .x>>>0 and .y>>>0 from t */ - SIMD.int32x4.fromFloat64x2 = function(t) { + SIMD.int32x4.fromFloat64x2ToUnsigned = function(t) { t = SIMD.float64x2(t); return SIMD.int32x4(t.x>>>0, t.y>>>0, 0, 0); } @@ -567,7 +570,7 @@ if (typeof SIMD.int32x4.fromInt16x8 === "undefined") { * @param {int16x8} t An instance of int16x8. * @return {int32x4} with the s0, s1, s2, and s3 from t, sign-extended */ - SIMD.int32x4.fromFloat32x4 = function(t) { + SIMD.int32x4.fromInt16x8 = function(t) { t = SIMD.int16x8(t); return SIMD.int32x4(t.s0, t.s1, t.s2, t.s3); } @@ -578,8 +581,8 @@ if (typeof SIMD.int32x4.fromUnsignedInt8x16 === "undefined") { * @param {int8x16} t An instance of int8x16. * @return {int32x4} with the s0, s1, s2, and s3 from t, zero-extended */ - SIMD.int32x4.fromFloat32x4 = function(t) { - t = SIMD.intint8x16(t); + SIMD.int32x4.fromUnsignedInt8x16 = function(t) { + t = SIMD.int8x16(t); return SIMD.int32x4(t.s0>>>0, t.s1>>>0, t.s2>>>0, t.s3>>>0); } } @@ -589,7 +592,7 @@ if (typeof SIMD.int32x4.fromUnsignedInt16x8 === "undefined") { * @param {int16x8} t An instance of int16x8. * @return {int32x4} with the s0, s1, s2, and s3 from t, zero-extended */ - SIMD.int32x4.fromFloat32x4 = function(t) { + SIMD.int32x4.fromUnsignedInt16x8 = function(t) { t = SIMD.int16x8(t); return SIMD.int32x4(t.s0>>>0, t.s1>>>0, t.s2>>>0, t.s3>>>0); } @@ -600,8 +603,8 @@ if (typeof SIMD.int32x4.fromInt8x16 === "undefined") { * @param {int8x16} t An instance of int8x16. * @return {int32x4} with the s0, s1, s2, and s3 from t */ - SIMD.int32x4.fromFloat32x4 = function(t) { - t = SIMD.intint8x16(t); + SIMD.int32x4.fromInt8x16 = function(t) { + t = SIMD.int8x16(t); return SIMD.int32x4(t.s0, t.s1, t.s2, t.s3); } } diff --git a/src/library.js b/src/library.js index 6a90ec4cfa28a..eaa440f9eddfe 100644 --- a/src/library.js +++ b/src/library.js @@ -6385,8 +6385,10 @@ LibraryManager.library = { // TODO: Implement for real. getrlimit: function(resource, rlp) { // int getrlimit(int resource, struct rlimit *rlp); - {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_cur, '-1', 'i32') }}} // RLIM_INFINITY - {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_max, '-1', 'i32') }}} // RLIM_INFINITY + {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_cur, '-1', 'i32') }}}; // RLIM_INFINITY + {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_cur + 4, '-1', 'i32') }}}; // RLIM_INFINITY + {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_max, '-1', 'i32') }}}; // RLIM_INFINITY + {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_max + 4, '-1', 'i32') }}}; // RLIM_INFINITY return 0; }, setrlimit: function(resource, rlp) { diff --git a/src/postamble.js b/src/postamble.js index bac17ab259d9a..59c4caa46650b 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -142,9 +142,9 @@ function run(args) { Module.printErr('pre-main prep time: ' + (Date.now() - preloadStartTime) + ' ms'); } - if (Module['_main'] && shouldRunNow) { - Module['callMain'](args); - } + if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); + + if (Module['_main'] && shouldRunNow) Module['callMain'](args); postRun(); } diff --git a/system/include/emscripten/vector.h b/system/include/emscripten/vector.h index 0b51af3581745..541ccb6a7e661 100644 --- a/system/include/emscripten/vector.h +++ b/system/include/emscripten/vector.h @@ -19,6 +19,7 @@ float32x4 emscripten_float32x4_sqrt(float32x4 __a) __attribute__((__nothrow__, _ int32x4 emscripten_float32x4_lessThan(float32x4 __a, float32x4 __b) __attribute__((__nothrow__, __const__)); int32x4 emscripten_float32x4_lessThanOrEqual(float32x4 __a, float32x4 __b) __attribute__((__nothrow__, __const__)); int32x4 emscripten_float32x4_equal(float32x4 __a, float32x4 __b) __attribute__((__nothrow__, __const__)); +int32x4 emscripten_float32x4_notEqual(float32x4 __a, float32x4 __b) __attribute__((__nothrow__, __const__)); int32x4 emscripten_float32x4_greaterThanOrEqual(float32x4 __a, float32x4 __b) __attribute__((__nothrow__, __const__)); int32x4 emscripten_float32x4_greaterThan(float32x4 __a, float32x4 __b) __attribute__((__nothrow__, __const__)); float32x4 emscripten_float32x4_and(float32x4 __a, float32x4 __b) __attribute__((__nothrow__, __const__)); @@ -33,6 +34,7 @@ int32x4 emscripten_int32x4_fromFloat32x4(float32x4 __a) __attribute__((__nothrow int32x4 emscripten_int32x4_lessThan(int32x4 __a, int32x4 __b) __attribute__((__nothrow__, __const__)); int32x4 emscripten_int32x4_lessThanOrEqual(int32x4 __a, int32x4 __b) __attribute__((__nothrow__, __const__)); int32x4 emscripten_int32x4_equal(int32x4 __a, int32x4 __b) __attribute__((__nothrow__, __const__)); +int32x4 emscripten_int32x4_notEqual(int32x4 __a, int32x4 __b) __attribute__((__nothrow__, __const__)); int32x4 emscripten_int32x4_greaterThanOrEqual(int32x4 __a, int32x4 __b) __attribute__((__nothrow__, __const__)); int32x4 emscripten_int32x4_greaterThan(int32x4 __a, int32x4 __b) __attribute__((__nothrow__, __const__)); int32x4 emscripten_int32x4_select(int32x4 __a, int32x4 __b, int32x4 __c) __attribute__((__nothrow__, __const__)); diff --git a/system/include/emscripten/xmmintrin.h b/system/include/emscripten/xmmintrin.h index 55ae928c8adba..8e74688ff5cc1 100644 --- a/system/include/emscripten/xmmintrin.h +++ b/system/include/emscripten/xmmintrin.h @@ -53,19 +53,24 @@ _mm_load_ps(const float *__p) static __inline__ __m128 __attribute__((__always_inline__)) _mm_loadl_pi(__m128 __a, const void /*__m64*/ *__p) { + // TODO: This actually corresponds to the SIMD.float32x4.loadXY function in + // SIMD.js. Use that instead. return (__m128){ ((const float*)__p)[0], ((const float*)__p)[1], __a[2], __a[3] }; } static __inline__ __m128 __attribute__((__always_inline__)) _mm_loadh_pi(__m128 __a, const void /*__m64*/ *__p) { + // TODO: Due to alignment masking, this would probably be faster as a loadXY + // followed by a shuffle. return (__m128){ __a[0], __a[1], ((const float*)__p)[0], ((const float*)__p)[1] }; } static __inline__ __m128 __attribute__((__always_inline__)) _mm_loadr_ps(const float *__p) { - return (__m128){ ((const float*)__p)[3], ((const float*)__p)[2], ((const float*)__p)[1], ((const float*)__p)[0] }; + __m128 __v = _mm_load_ps(__p); + return __builtin_shufflevector(__v, __v, 3, 2, 1, 0); } static __inline__ __m128 __attribute__((__always_inline__)) @@ -81,20 +86,24 @@ _mm_loadu_ps(const float *__p) static __inline__ __m128 __attribute__((__always_inline__)) _mm_load_ps1(const float *__p) { - return (__m128){ *__p, *__p, *__p, *__p }; + float __s = *__p; + return (__m128){ __s, __s, __s, __s }; } #define _mm_load1_ps _mm_load_ps1 static __inline__ __m128 __attribute__((__always_inline__)) _mm_load_ss(const float *__p) { - // TODO: This actually corresponds to the SIMD.float32x4.loadX function in SIMD.js. Use that instead. + // TODO: This actually corresponds to the SIMD.float32x4.loadX function in + // SIMD.js. Use that instead. return (__m128){ *__p, 0.0f, 0.0f, 0.0f }; } static __inline__ void __attribute__((__always_inline__)) _mm_storel_pi(void /*__m64*/ *__p, __m128 __a) { + // TODO: This actually corresponds to the SIMD.float32x4.storeXY function in + // SIMD.js. Use that instead. ((float*)__p)[0] = __a[0]; ((float*)__p)[1] = __a[1]; } @@ -102,6 +111,8 @@ _mm_storel_pi(void /*__m64*/ *__p, __m128 __a) static __inline__ void __attribute__((__always_inline__)) _mm_storeh_pi(void /*__m64*/ *__p, __m128 __a) { + // TODO: Due to alignment masking, on x64 this would be faster as a sizzle + // and a storeXY, so we should use that instead. ((float*)__p)[0] = __a[2]; ((float*)__p)[1] = __a[3]; } @@ -371,44 +382,26 @@ _mm_cmpgt_ss(__m128 __a, __m128 __b) return _mm_move_ss(__a, _mm_cmpgt_ps(__a, __b)); } -static __inline__ int __internal_isnan(float __f) -{ - return (*(unsigned int*)&__f << 1) > 0xFF000000u; -} - static __inline__ __m128 __attribute__((__always_inline__)) _mm_cmpord_ps(__m128 __a, __m128 __b) { - unsigned int r[4]; - r[0] = (!__internal_isnan(__a[0]) && !__internal_isnan(__b[0])) ? 0xFFFFFFFFU : 0; - r[1] = (!__internal_isnan(__a[1]) && !__internal_isnan(__b[1])) ? 0xFFFFFFFFU : 0; - r[2] = (!__internal_isnan(__a[2]) && !__internal_isnan(__b[2])) ? 0xFFFFFFFFU : 0; - r[3] = (!__internal_isnan(__a[3]) && !__internal_isnan(__b[3])) ? 0xFFFFFFFFU : 0; - return _mm_loadu_ps((float*)r); + return emscripten_float32x4_and(emscripten_float32x4_equal(__a, __a), + emscripten_float32x4_equal(__b, __b)); } static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) _mm_cmpord_ss(__m128 __a, __m128 __b) { - unsigned int r = (!__internal_isnan(__a[0]) && !__internal_isnan(__b[0])) ? 0xFFFFFFFFU : 0; - return _mm_move_ss(__a, _mm_set_ss(*(float*)&r)); + return _mm_move_ss(__a, _mm_cmpord_ps(__a, __b)); } static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) _mm_cmpunord_ps(__m128 __a, __m128 __b) { - union { - unsigned int r[4]; - __m128 m; - } u; - u.r[0] = (__internal_isnan(__a[0]) || __internal_isnan(__b[0])) ? 0xFFFFFFFFU : 0; - u.r[1] = (__internal_isnan(__a[1]) || __internal_isnan(__b[1])) ? 0xFFFFFFFFU : 0; - u.r[2] = (__internal_isnan(__a[2]) || __internal_isnan(__b[2])) ? 0xFFFFFFFFU : 0; - u.r[3] = (__internal_isnan(__a[3]) || __internal_isnan(__b[3])) ? 0xFFFFFFFFU : 0; - return u.m; + return emscripten_float32x4_or(emscripten_float32x4_notEqual(__a, __a), + emscripten_float32x4_notEqual(__b, __b)); } static __inline__ __m128 __attribute__((__always_inline__, __nodebug__)) _mm_cmpunord_ss(__m128 __a, __m128 __b) { - unsigned int r = (__internal_isnan(__a[0]) || __internal_isnan(__b[0])) ? 0xFFFFFFFFU : 0; - return _mm_move_ss(__a, _mm_set_ss(*(float*)&r)); + return _mm_move_ss(__a, _mm_cmpunord_ps(__a, __b)); } static __inline__ __m128 __attribute__((__always_inline__)) @@ -435,11 +428,10 @@ _mm_xor_ps(__m128 __a, __m128 __b) return emscripten_float32x4_xor(__a, __b); } -// TODO: Use SIMD.float32x4.notEqual static __inline__ __m128 __attribute__((__always_inline__)) _mm_cmpneq_ps(__m128 __a, __m128 __b) { - return emscripten_float32x4_not(_mm_cmpeq_ps(__a, __b)); + return emscripten_float32x4_notEqual(__a, __b); } static __inline__ __m128 __attribute__((__always_inline__)) @@ -553,13 +545,13 @@ _mm_ucomigt_ss(__m128 __a, __m128 __b) static __inline__ int __attribute__((__always_inline__)) _mm_ucomile_ss(__m128 __a, __m128 __b) { - return __a[0] <= __b[0]; + return !(__a[0] > __b[0]); } static __inline__ int __attribute__((__always_inline__)) _mm_ucomilt_ss(__m128 __a, __m128 __b) { - return __a[0] < __b[0]; + return !(__a[0] >= __b[0]); } static __inline__ int __attribute__((__always_inline__)) diff --git a/system/lib/libc/musl/src/complex/__cexp.c b/system/lib/libc/musl/src/complex/__cexp.c new file mode 100644 index 0000000000000..05ac28c75c627 --- /dev/null +++ b/system/lib/libc/musl/src/complex/__cexp.c @@ -0,0 +1,87 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/k_exp.c */ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libm.h" + +static const uint32_t k = 1799; /* constant for reduction */ +static const double kln2 = 1246.97177782734161156; /* k * ln2 */ + +/* + * Compute exp(x), scaled to avoid spurious overflow. An exponent is + * returned separately in 'expt'. + * + * Input: ln(DBL_MAX) <= x < ln(2 * DBL_MAX / DBL_MIN_DENORM) ~= 1454.91 + * Output: 2**1023 <= y < 2**1024 + */ +static double __frexp_exp(double x, int *expt) +{ + double exp_x; + uint32_t hx; + + /* + * We use exp(x) = exp(x - kln2) * 2**k, carefully chosen to + * minimize |exp(kln2) - 2**k|. We also scale the exponent of + * exp_x to MAX_EXP so that the result can be multiplied by + * a tiny number without losing accuracy due to denormalization. + */ + exp_x = exp(x - kln2); + GET_HIGH_WORD(hx, exp_x); + *expt = (hx >> 20) - (0x3ff + 1023) + k; + SET_HIGH_WORD(exp_x, (hx & 0xfffff) | ((0x3ff + 1023) << 20)); + return exp_x; +} + +/* + * __ldexp_cexp(x, expt) compute exp(x) * 2**expt. + * It is intended for large arguments (real part >= ln(DBL_MAX)) + * where care is needed to avoid overflow. + * + * The present implementation is narrowly tailored for our hyperbolic and + * exponential functions. We assume expt is small (0 or -1), and the caller + * has filtered out very large x, for which overflow would be inevitable. + */ +double complex __ldexp_cexp(double complex z, int expt) +{ + double x, y, exp_x, scale1, scale2; + int ex_expt, half_expt; + + x = creal(z); + y = cimag(z); + exp_x = __frexp_exp(x, &ex_expt); + expt += ex_expt; + + /* + * Arrange so that scale1 * scale2 == 2**expt. We use this to + * compensate for scalbn being horrendously slow. + */ + half_expt = expt / 2; + INSERT_WORDS(scale1, (0x3ff + half_expt) << 20, 0); + half_expt = expt - half_expt; + INSERT_WORDS(scale2, (0x3ff + half_expt) << 20, 0); + + return CMPLX(cos(y) * exp_x * scale1 * scale2, sin(y) * exp_x * scale1 * scale2); +} diff --git a/system/lib/libc/musl/src/complex/__cexpf.c b/system/lib/libc/musl/src/complex/__cexpf.c new file mode 100644 index 0000000000000..69b54045afd4a --- /dev/null +++ b/system/lib/libc/musl/src/complex/__cexpf.c @@ -0,0 +1,68 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/k_expf.c */ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libm.h" + +static const uint32_t k = 235; /* constant for reduction */ +static const float kln2 = 162.88958740F; /* k * ln2 */ + +/* + * See __cexp.c for details. + * + * Input: ln(FLT_MAX) <= x < ln(2 * FLT_MAX / FLT_MIN_DENORM) ~= 192.7 + * Output: 2**127 <= y < 2**128 + */ +static float __frexp_expf(float x, int *expt) +{ + float exp_x; + uint32_t hx; + + exp_x = expf(x - kln2); + GET_FLOAT_WORD(hx, exp_x); + *expt = (hx >> 23) - (0x7f + 127) + k; + SET_FLOAT_WORD(exp_x, (hx & 0x7fffff) | ((0x7f + 127) << 23)); + return exp_x; +} + +float complex __ldexp_cexpf(float complex z, int expt) +{ + float x, y, exp_x, scale1, scale2; + int ex_expt, half_expt; + + x = crealf(z); + y = cimagf(z); + exp_x = __frexp_expf(x, &ex_expt); + expt += ex_expt; + + half_expt = expt / 2; + SET_FLOAT_WORD(scale1, (0x7f + half_expt) << 23); + half_expt = expt - half_expt; + SET_FLOAT_WORD(scale2, (0x7f + half_expt) << 23); + + return CMPLXF(cosf(y) * exp_x * scale1 * scale2, + sinf(y) * exp_x * scale1 * scale2); +} diff --git a/system/lib/libc/musl/src/complex/cabs.c b/system/lib/libc/musl/src/complex/cabs.c new file mode 100644 index 0000000000000..f61d364e47d26 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cabs.c @@ -0,0 +1,6 @@ +#include "libm.h" + +double cabs(double complex z) +{ + return hypot(creal(z), cimag(z)); +} diff --git a/system/lib/libc/musl/src/complex/cabsf.c b/system/lib/libc/musl/src/complex/cabsf.c new file mode 100644 index 0000000000000..30b25c70315dd --- /dev/null +++ b/system/lib/libc/musl/src/complex/cabsf.c @@ -0,0 +1,6 @@ +#include "libm.h" + +float cabsf(float complex z) +{ + return hypotf(crealf(z), cimagf(z)); +} diff --git a/system/lib/libc/musl/src/complex/cabsl.c b/system/lib/libc/musl/src/complex/cabsl.c new file mode 100644 index 0000000000000..40a067c1c59f1 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cabsl.c @@ -0,0 +1,13 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double cabsl(long double complex z) +{ + return cabs(z); +} +#else +long double cabsl(long double complex z) +{ + return hypotl(creall(z), cimagl(z)); +} +#endif diff --git a/system/lib/libc/musl/src/complex/cacos.c b/system/lib/libc/musl/src/complex/cacos.c new file mode 100644 index 0000000000000..27c356364c8c6 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cacos.c @@ -0,0 +1,11 @@ +#include "libm.h" + +// FIXME: Hull et al. "Implementing the complex arcsine and arccosine functions using exception handling" 1997 + +/* acos(z) = pi/2 - asin(z) */ + +double complex cacos(double complex z) +{ + z = casin(z); + return CMPLX(M_PI_2 - creal(z), -cimag(z)); +} diff --git a/system/lib/libc/musl/src/complex/cacosf.c b/system/lib/libc/musl/src/complex/cacosf.c new file mode 100644 index 0000000000000..11852659523a4 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cacosf.c @@ -0,0 +1,9 @@ +#include "libm.h" + +// FIXME + +float complex cacosf(float complex z) +{ + z = casinf(z); + return CMPLXF((float)M_PI_2 - crealf(z), -cimagf(z)); +} diff --git a/system/lib/libc/musl/src/complex/cacosh.c b/system/lib/libc/musl/src/complex/cacosh.c new file mode 100644 index 0000000000000..8c68cb01fd603 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cacosh.c @@ -0,0 +1,9 @@ +#include "libm.h" + +/* acosh(z) = i acos(z) */ + +double complex cacosh(double complex z) +{ + z = cacos(z); + return CMPLX(-cimag(z), creal(z)); +} diff --git a/system/lib/libc/musl/src/complex/cacoshf.c b/system/lib/libc/musl/src/complex/cacoshf.c new file mode 100644 index 0000000000000..ade01c0907c83 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cacoshf.c @@ -0,0 +1,7 @@ +#include "libm.h" + +float complex cacoshf(float complex z) +{ + z = cacosf(z); + return CMPLXF(-cimagf(z), crealf(z)); +} diff --git a/system/lib/libc/musl/src/complex/cacoshl.c b/system/lib/libc/musl/src/complex/cacoshl.c new file mode 100644 index 0000000000000..65342557f9a54 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cacoshl.c @@ -0,0 +1,14 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex cacoshl(long double complex z) +{ + return cacosh(z); +} +#else +long double complex cacoshl(long double complex z) +{ + z = cacosl(z); + return CMPLXL(-cimagl(z), creall(z)); +} +#endif diff --git a/system/lib/libc/musl/src/complex/cacosl.c b/system/lib/libc/musl/src/complex/cacosl.c new file mode 100644 index 0000000000000..7fd4a2f6b4424 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cacosl.c @@ -0,0 +1,16 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex cacosl(long double complex z) +{ + return cacos(z); +} +#else +// FIXME +#define PI_2 1.57079632679489661923132169163975144L +long double complex cacosl(long double complex z) +{ + z = casinl(z); + return CMPLXL(PI_2 - creall(z), -cimagl(z)); +} +#endif diff --git a/system/lib/libc/musl/src/complex/carg.c b/system/lib/libc/musl/src/complex/carg.c new file mode 100644 index 0000000000000..d2d1b4623a7fc --- /dev/null +++ b/system/lib/libc/musl/src/complex/carg.c @@ -0,0 +1,6 @@ +#include "libm.h" + +double carg(double complex z) +{ + return atan2(cimag(z), creal(z)); +} diff --git a/system/lib/libc/musl/src/complex/cargf.c b/system/lib/libc/musl/src/complex/cargf.c new file mode 100644 index 0000000000000..ce183c4b335be --- /dev/null +++ b/system/lib/libc/musl/src/complex/cargf.c @@ -0,0 +1,6 @@ +#include "libm.h" + +float cargf(float complex z) +{ + return atan2f(cimagf(z), crealf(z)); +} diff --git a/system/lib/libc/musl/src/complex/cargl.c b/system/lib/libc/musl/src/complex/cargl.c new file mode 100644 index 0000000000000..e0d504782f1fc --- /dev/null +++ b/system/lib/libc/musl/src/complex/cargl.c @@ -0,0 +1,13 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double cargl(long double complex z) +{ + return carg(z); +} +#else +long double cargl(long double complex z) +{ + return atan2l(cimagl(z), creall(z)); +} +#endif diff --git a/system/lib/libc/musl/src/complex/casin.c b/system/lib/libc/musl/src/complex/casin.c new file mode 100644 index 0000000000000..dfdda988bafcd --- /dev/null +++ b/system/lib/libc/musl/src/complex/casin.c @@ -0,0 +1,16 @@ +#include "libm.h" + +// FIXME + +/* asin(z) = -i log(i z + sqrt(1 - z*z)) */ + +double complex casin(double complex z) +{ + double complex w; + double x, y; + + x = creal(z); + y = cimag(z); + w = CMPLX(1.0 - (x - y)*(x + y), -2.0*x*y); + return clog(CMPLX(-y, x) + csqrt(w)); +} diff --git a/system/lib/libc/musl/src/complex/casinf.c b/system/lib/libc/musl/src/complex/casinf.c new file mode 100644 index 0000000000000..93f0e335092b0 --- /dev/null +++ b/system/lib/libc/musl/src/complex/casinf.c @@ -0,0 +1,14 @@ +#include "libm.h" + +// FIXME + +float complex casinf(float complex z) +{ + float complex w; + float x, y; + + x = crealf(z); + y = cimagf(z); + w = CMPLXF(1.0 - (x - y)*(x + y), -2.0*x*y); + return clogf(CMPLXF(-y, x) + csqrtf(w)); +} diff --git a/system/lib/libc/musl/src/complex/casinh.c b/system/lib/libc/musl/src/complex/casinh.c new file mode 100644 index 0000000000000..b57fe8c40ba3b --- /dev/null +++ b/system/lib/libc/musl/src/complex/casinh.c @@ -0,0 +1,9 @@ +#include "libm.h" + +/* asinh(z) = -i asin(i z) */ + +double complex casinh(double complex z) +{ + z = casin(CMPLX(-cimag(z), creal(z))); + return CMPLX(cimag(z), -creal(z)); +} diff --git a/system/lib/libc/musl/src/complex/casinhf.c b/system/lib/libc/musl/src/complex/casinhf.c new file mode 100644 index 0000000000000..a11bf902d86f9 --- /dev/null +++ b/system/lib/libc/musl/src/complex/casinhf.c @@ -0,0 +1,7 @@ +#include "libm.h" + +float complex casinhf(float complex z) +{ + z = casinf(CMPLXF(-cimagf(z), crealf(z))); + return CMPLXF(cimagf(z), -crealf(z)); +} diff --git a/system/lib/libc/musl/src/complex/casinhl.c b/system/lib/libc/musl/src/complex/casinhl.c new file mode 100644 index 0000000000000..714f189359071 --- /dev/null +++ b/system/lib/libc/musl/src/complex/casinhl.c @@ -0,0 +1,14 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex casinhl(long double complex z) +{ + return casinh(z); +} +#else +long double complex casinhl(long double complex z) +{ + z = casinl(CMPLXL(-cimagl(z), creall(z))); + return CMPLXL(cimagl(z), -creall(z)); +} +#endif diff --git a/system/lib/libc/musl/src/complex/casinl.c b/system/lib/libc/musl/src/complex/casinl.c new file mode 100644 index 0000000000000..0916c60f2a6b3 --- /dev/null +++ b/system/lib/libc/musl/src/complex/casinl.c @@ -0,0 +1,20 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex casinl(long double complex z) +{ + return casin(z); +} +#else +// FIXME +long double complex casinl(long double complex z) +{ + long double complex w; + long double x, y; + + x = creall(z); + y = cimagl(z); + w = CMPLXL(1.0 - (x - y)*(x + y), -2.0*x*y); + return clogl(CMPLXL(-y, x) + csqrtl(w)); +} +#endif diff --git a/system/lib/libc/musl/src/complex/catan.c b/system/lib/libc/musl/src/complex/catan.c new file mode 100644 index 0000000000000..39ce6cf2ff6fc --- /dev/null +++ b/system/lib/libc/musl/src/complex/catan.c @@ -0,0 +1,119 @@ +/* origin: OpenBSD /usr/src/lib/libm/src/s_catan.c */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Complex circular arc tangent + * + * + * SYNOPSIS: + * + * double complex catan(); + * double complex z, w; + * + * w = catan (z); + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * 1 ( 2x ) + * Re w = - arctan(-----------) + k PI + * 2 ( 2 2) + * (1 - x - y ) + * + * ( 2 2) + * 1 (x + (y+1) ) + * Im w = - log(------------) + * 4 ( 2 2) + * (x + (y-1) ) + * + * Where k is an arbitrary integer. + * + * catan(z) = -i catanh(iz). + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 5900 1.3e-16 7.8e-18 + * IEEE -10,+10 30000 2.3e-15 8.5e-17 + * The check catan( ctan(z) ) = z, with |x| and |y| < PI/2, + * had peak relative error 1.5e-16, rms relative error + * 2.9e-17. See also clog(). + */ + +#include "libm.h" + +#define MAXNUM 1.0e308 + +static const double DP1 = 3.14159265160560607910E0; +static const double DP2 = 1.98418714791870343106E-9; +static const double DP3 = 1.14423774522196636802E-17; + +static double _redupi(double x) +{ + double t; + long i; + + t = x/M_PI; + if (t >= 0.0) + t += 0.5; + else + t -= 0.5; + + i = t; /* the multiple */ + t = i; + t = ((x - t * DP1) - t * DP2) - t * DP3; + return t; +} + +double complex catan(double complex z) +{ + double complex w; + double a, t, x, x2, y; + + x = creal(z); + y = cimag(z); + + if (x == 0.0 && y > 1.0) + goto ovrf; + + x2 = x * x; + a = 1.0 - x2 - (y * y); + if (a == 0.0) + goto ovrf; + + t = 0.5 * atan2(2.0 * x, a); + w = _redupi(t); + + t = y - 1.0; + a = x2 + (t * t); + if (a == 0.0) + goto ovrf; + + t = y + 1.0; + a = (x2 + t * t)/a; + w = w + (0.25 * log(a)) * I; + return w; + +ovrf: + // FIXME + w = MAXNUM + MAXNUM * I; + return w; +} diff --git a/system/lib/libc/musl/src/complex/catanf.c b/system/lib/libc/musl/src/complex/catanf.c new file mode 100644 index 0000000000000..8533bde397fba --- /dev/null +++ b/system/lib/libc/musl/src/complex/catanf.c @@ -0,0 +1,115 @@ +/* origin: OpenBSD /usr/src/lib/libm/src/s_catanf.c */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Complex circular arc tangent + * + * + * SYNOPSIS: + * + * float complex catanf(); + * float complex z, w; + * + * w = catanf( z ); + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * 1 ( 2x ) + * Re w = - arctan(-----------) + k PI + * 2 ( 2 2) + * (1 - x - y ) + * + * ( 2 2) + * 1 (x + (y+1) ) + * Im w = - log(------------) + * 4 ( 2 2) + * (x + (y-1) ) + * + * Where k is an arbitrary integer. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 2.3e-6 5.2e-8 + */ + +#include "libm.h" + +#define MAXNUMF 1.0e38F + +static const double DP1 = 3.140625; +static const double DP2 = 9.67502593994140625E-4; +static const double DP3 = 1.509957990978376432E-7; + +static float _redupif(float xx) +{ + float x, t; + long i; + + x = xx; + t = x/(float)M_PI; + if (t >= 0.0f) + t += 0.5f; + else + t -= 0.5f; + + i = t; /* the multiple */ + t = i; + t = ((x - t * DP1) - t * DP2) - t * DP3; + return t; +} + +float complex catanf(float complex z) +{ + float complex w; + float a, t, x, x2, y; + + x = crealf(z); + y = cimagf(z); + + if ((x == 0.0f) && (y > 1.0f)) + goto ovrf; + + x2 = x * x; + a = 1.0f - x2 - (y * y); + if (a == 0.0f) + goto ovrf; + + t = 0.5f * atan2f(2.0f * x, a); + w = _redupif(t); + + t = y - 1.0f; + a = x2 + (t * t); + if (a == 0.0f) + goto ovrf; + + t = y + 1.0f; + a = (x2 + (t * t))/a; + w = w + (0.25f * logf (a)) * I; + return w; + +ovrf: + // FIXME + w = MAXNUMF + MAXNUMF * I; + return w; +} diff --git a/system/lib/libc/musl/src/complex/catanh.c b/system/lib/libc/musl/src/complex/catanh.c new file mode 100644 index 0000000000000..e248d9b934135 --- /dev/null +++ b/system/lib/libc/musl/src/complex/catanh.c @@ -0,0 +1,9 @@ +#include "libm.h" + +/* atanh = -i atan(i z) */ + +double complex catanh(double complex z) +{ + z = catan(CMPLX(-cimag(z), creal(z))); + return CMPLX(cimag(z), -creal(z)); +} diff --git a/system/lib/libc/musl/src/complex/catanhf.c b/system/lib/libc/musl/src/complex/catanhf.c new file mode 100644 index 0000000000000..4a5eb04079b9d --- /dev/null +++ b/system/lib/libc/musl/src/complex/catanhf.c @@ -0,0 +1,7 @@ +#include "libm.h" + +float complex catanhf(float complex z) +{ + z = catanf(CMPLXF(-cimagf(z), crealf(z))); + return CMPLXF(cimagf(z), -crealf(z)); +} diff --git a/system/lib/libc/musl/src/complex/catanhl.c b/system/lib/libc/musl/src/complex/catanhl.c new file mode 100644 index 0000000000000..a5dd538e44535 --- /dev/null +++ b/system/lib/libc/musl/src/complex/catanhl.c @@ -0,0 +1,14 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex catanhl(long double complex z) +{ + return catanh(z); +} +#else +long double complex catanhl(long double complex z) +{ + z = catanl(CMPLXL(-cimagl(z), creall(z))); + return CMPLXL(cimagl(z), -creall(z)); +} +#endif diff --git a/system/lib/libc/musl/src/complex/catanl.c b/system/lib/libc/musl/src/complex/catanl.c new file mode 100644 index 0000000000000..5ace7704fdc08 --- /dev/null +++ b/system/lib/libc/musl/src/complex/catanl.c @@ -0,0 +1,126 @@ +/* origin: OpenBSD /usr/src/lib/libm/src/s_catanl.c */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Complex circular arc tangent + * + * + * SYNOPSIS: + * + * long double complex catanl(); + * long double complex z, w; + * + * w = catanl( z ); + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * 1 ( 2x ) + * Re w = - arctan(-----------) + k PI + * 2 ( 2 2) + * (1 - x - y ) + * + * ( 2 2) + * 1 (x + (y+1) ) + * Im w = - log(------------) + * 4 ( 2 2) + * (x + (y-1) ) + * + * Where k is an arbitrary integer. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 5900 1.3e-16 7.8e-18 + * IEEE -10,+10 30000 2.3e-15 8.5e-17 + * The check catan( ctan(z) ) = z, with |x| and |y| < PI/2, + * had peak relative error 1.5e-16, rms relative error + * 2.9e-17. See also clog(). + */ + +#include +#include +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex catanl(long double complex z) +{ + return catan(z); +} +#else +static const long double PIL = 3.141592653589793238462643383279502884197169L; +static const long double DP1 = 3.14159265358979323829596852490908531763125L; +static const long double DP2 = 1.6667485837041756656403424829301998703007e-19L; +static const long double DP3 = 1.8830410776607851167459095484560349402753e-39L; + +static long double redupil(long double x) +{ + long double t; + long i; + + t = x / PIL; + if (t >= 0.0L) + t += 0.5L; + else + t -= 0.5L; + + i = t; /* the multiple */ + t = i; + t = ((x - t * DP1) - t * DP2) - t * DP3; + return t; +} + +long double complex catanl(long double complex z) +{ + long double complex w; + long double a, t, x, x2, y; + + x = creall(z); + y = cimagl(z); + + if ((x == 0.0L) && (y > 1.0L)) + goto ovrf; + + x2 = x * x; + a = 1.0L - x2 - (y * y); + if (a == 0.0L) + goto ovrf; + + t = atan2l(2.0L * x, a) * 0.5L; + w = redupil(t); + + t = y - 1.0L; + a = x2 + (t * t); + if (a == 0.0L) + goto ovrf; + + t = y + 1.0L; + a = (x2 + (t * t)) / a; + w = w + (0.25L * logl(a)) * I; + return w; + +ovrf: + // FIXME + w = LDBL_MAX + LDBL_MAX * I; + return w; +} +#endif diff --git a/system/lib/libc/musl/src/complex/ccos.c b/system/lib/libc/musl/src/complex/ccos.c new file mode 100644 index 0000000000000..645aec29a9dc0 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ccos.c @@ -0,0 +1,8 @@ +#include "libm.h" + +/* cos(z) = cosh(i z) */ + +double complex ccos(double complex z) +{ + return ccosh(CMPLX(-cimag(z), creal(z))); +} diff --git a/system/lib/libc/musl/src/complex/ccosf.c b/system/lib/libc/musl/src/complex/ccosf.c new file mode 100644 index 0000000000000..9a67241f1cfb4 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ccosf.c @@ -0,0 +1,6 @@ +#include "libm.h" + +float complex ccosf(float complex z) +{ + return ccoshf(CMPLXF(-cimagf(z), crealf(z))); +} diff --git a/system/lib/libc/musl/src/complex/ccosh.c b/system/lib/libc/musl/src/complex/ccosh.c new file mode 100644 index 0000000000000..401f3c604237c --- /dev/null +++ b/system/lib/libc/musl/src/complex/ccosh.c @@ -0,0 +1,140 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_ccosh.c */ +/*- + * Copyright (c) 2005 Bruce D. Evans and Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Hyperbolic cosine of a complex argument z = x + i y. + * + * cosh(z) = cosh(x+iy) + * = cosh(x) cos(y) + i sinh(x) sin(y). + * + * Exceptional values are noted in the comments within the source code. + * These values and the return value were taken from n1124.pdf. + */ + +#include "libm.h" + +static const double huge = 0x1p1023; + +double complex ccosh(double complex z) +{ + double x, y, h; + int32_t hx, hy, ix, iy, lx, ly; + + x = creal(z); + y = cimag(z); + + EXTRACT_WORDS(hx, lx, x); + EXTRACT_WORDS(hy, ly, y); + + ix = 0x7fffffff & hx; + iy = 0x7fffffff & hy; + + /* Handle the nearly-non-exceptional cases where x and y are finite. */ + if (ix < 0x7ff00000 && iy < 0x7ff00000) { + if ((iy | ly) == 0) + return CMPLX(cosh(x), x * y); + if (ix < 0x40360000) /* small x: normal case */ + return CMPLX(cosh(x) * cos(y), sinh(x) * sin(y)); + + /* |x| >= 22, so cosh(x) ~= exp(|x|) */ + if (ix < 0x40862e42) { + /* x < 710: exp(|x|) won't overflow */ + h = exp(fabs(x)) * 0.5; + return CMPLX(h * cos(y), copysign(h, x) * sin(y)); + } else if (ix < 0x4096bbaa) { + /* x < 1455: scale to avoid overflow */ + z = __ldexp_cexp(CMPLX(fabs(x), y), -1); + return CMPLX(creal(z), cimag(z) * copysign(1, x)); + } else { + /* x >= 1455: the result always overflows */ + h = huge * x; + return CMPLX(h * h * cos(y), h * sin(y)); + } + } + + /* + * cosh(+-0 +- I Inf) = dNaN + I sign(d(+-0, dNaN))0. + * The sign of 0 in the result is unspecified. Choice = normally + * the same as dNaN. Raise the invalid floating-point exception. + * + * cosh(+-0 +- I NaN) = d(NaN) + I sign(d(+-0, NaN))0. + * The sign of 0 in the result is unspecified. Choice = normally + * the same as d(NaN). + */ + if ((ix | lx) == 0 && iy >= 0x7ff00000) + return CMPLX(y - y, copysign(0, x * (y - y))); + + /* + * cosh(+-Inf +- I 0) = +Inf + I (+-)(+-)0. + * + * cosh(NaN +- I 0) = d(NaN) + I sign(d(NaN, +-0))0. + * The sign of 0 in the result is unspecified. + */ + if ((iy | ly) == 0 && ix >= 0x7ff00000) { + if (((hx & 0xfffff) | lx) == 0) + return CMPLX(x * x, copysign(0, x) * y); + return CMPLX(x * x, copysign(0, (x + x) * y)); + } + + /* + * cosh(x +- I Inf) = dNaN + I dNaN. + * Raise the invalid floating-point exception for finite nonzero x. + * + * cosh(x + I NaN) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero x. Choice = don't raise (except for signaling NaNs). + */ + if (ix < 0x7ff00000 && iy >= 0x7ff00000) + return CMPLX(y - y, x * (y - y)); + + /* + * cosh(+-Inf + I NaN) = +Inf + I d(NaN). + * + * cosh(+-Inf +- I Inf) = +Inf + I dNaN. + * The sign of Inf in the result is unspecified. Choice = always +. + * Raise the invalid floating-point exception. + * + * cosh(+-Inf + I y) = +Inf cos(y) +- I Inf sin(y) + */ + if (ix >= 0x7ff00000 && ((hx & 0xfffff) | lx) == 0) { + if (iy >= 0x7ff00000) + return CMPLX(x * x, x * (y - y)); + return CMPLX((x * x) * cos(y), x * sin(y)); + } + + /* + * cosh(NaN + I NaN) = d(NaN) + I d(NaN). + * + * cosh(NaN +- I Inf) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception. + * Choice = raise. + * + * cosh(NaN + I y) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero y. Choice = don't raise (except for signaling NaNs). + */ + return CMPLX((x * x) * (y - y), (x + x) * (y - y)); +} diff --git a/system/lib/libc/musl/src/complex/ccoshf.c b/system/lib/libc/musl/src/complex/ccoshf.c new file mode 100644 index 0000000000000..90acfe05829f3 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ccoshf.c @@ -0,0 +1,90 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_ccoshf.c */ +/*- + * Copyright (c) 2005 Bruce D. Evans and Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Hyperbolic cosine of a complex argument. See s_ccosh.c for details. + */ + +#include "libm.h" + +static const float huge = 0x1p127; + +float complex ccoshf(float complex z) +{ + float x, y, h; + int32_t hx, hy, ix, iy; + + x = crealf(z); + y = cimagf(z); + + GET_FLOAT_WORD(hx, x); + GET_FLOAT_WORD(hy, y); + + ix = 0x7fffffff & hx; + iy = 0x7fffffff & hy; + + if (ix < 0x7f800000 && iy < 0x7f800000) { + if (iy == 0) + return CMPLXF(coshf(x), x * y); + if (ix < 0x41100000) /* small x: normal case */ + return CMPLXF(coshf(x) * cosf(y), sinhf(x) * sinf(y)); + + /* |x| >= 9, so cosh(x) ~= exp(|x|) */ + if (ix < 0x42b17218) { + /* x < 88.7: expf(|x|) won't overflow */ + h = expf(fabsf(x)) * 0.5f; + return CMPLXF(h * cosf(y), copysignf(h, x) * sinf(y)); + } else if (ix < 0x4340b1e7) { + /* x < 192.7: scale to avoid overflow */ + z = __ldexp_cexpf(CMPLXF(fabsf(x), y), -1); + return CMPLXF(crealf(z), cimagf(z) * copysignf(1, x)); + } else { + /* x >= 192.7: the result always overflows */ + h = huge * x; + return CMPLXF(h * h * cosf(y), h * sinf(y)); + } + } + + if (ix == 0 && iy >= 0x7f800000) + return CMPLXF(y - y, copysignf(0, x * (y - y))); + + if (iy == 0 && ix >= 0x7f800000) { + if ((hx & 0x7fffff) == 0) + return CMPLXF(x * x, copysignf(0, x) * y); + return CMPLXF(x * x, copysignf(0, (x + x) * y)); + } + + if (ix < 0x7f800000 && iy >= 0x7f800000) + return CMPLXF(y - y, x * (y - y)); + + if (ix >= 0x7f800000 && (hx & 0x7fffff) == 0) { + if (iy >= 0x7f800000) + return CMPLXF(x * x, x * (y - y)); + return CMPLXF((x * x) * cosf(y), x * sinf(y)); + } + + return CMPLXF((x * x) * (y - y), (x + x) * (y - y)); +} diff --git a/system/lib/libc/musl/src/complex/ccoshl.c b/system/lib/libc/musl/src/complex/ccoshl.c new file mode 100644 index 0000000000000..9b2aed9ef16c4 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ccoshl.c @@ -0,0 +1,7 @@ +#include "libm.h" + +//FIXME +long double complex ccoshl(long double complex z) +{ + return ccosh(z); +} diff --git a/system/lib/libc/musl/src/complex/ccosl.c b/system/lib/libc/musl/src/complex/ccosl.c new file mode 100644 index 0000000000000..d787047fec8d5 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ccosl.c @@ -0,0 +1,13 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex ccosl(long double complex z) +{ + return ccos(z); +} +#else +long double complex ccosl(long double complex z) +{ + return ccoshl(CMPLXL(-cimagl(z), creall(z))); +} +#endif diff --git a/system/lib/libc/musl/src/complex/cexp.c b/system/lib/libc/musl/src/complex/cexp.c new file mode 100644 index 0000000000000..5118e00ea5b74 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cexp.c @@ -0,0 +1,83 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_cexp.c */ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libm.h" + +static const uint32_t +exp_ovfl = 0x40862e42, /* high bits of MAX_EXP * ln2 ~= 710 */ +cexp_ovfl = 0x4096b8e4; /* (MAX_EXP - MIN_DENORM_EXP) * ln2 */ + +double complex cexp(double complex z) +{ + double x, y, exp_x; + uint32_t hx, hy, lx, ly; + + x = creal(z); + y = cimag(z); + + EXTRACT_WORDS(hy, ly, y); + hy &= 0x7fffffff; + + /* cexp(x + I 0) = exp(x) + I 0 */ + if ((hy | ly) == 0) + return CMPLX(exp(x), y); + EXTRACT_WORDS(hx, lx, x); + /* cexp(0 + I y) = cos(y) + I sin(y) */ + if (((hx & 0x7fffffff) | lx) == 0) + return CMPLX(cos(y), sin(y)); + + if (hy >= 0x7ff00000) { + if (lx != 0 || (hx & 0x7fffffff) != 0x7ff00000) { + /* cexp(finite|NaN +- I Inf|NaN) = NaN + I NaN */ + return CMPLX(y - y, y - y); + } else if (hx & 0x80000000) { + /* cexp(-Inf +- I Inf|NaN) = 0 + I 0 */ + return CMPLX(0.0, 0.0); + } else { + /* cexp(+Inf +- I Inf|NaN) = Inf + I NaN */ + return CMPLX(x, y - y); + } + } + + if (hx >= exp_ovfl && hx <= cexp_ovfl) { + /* + * x is between 709.7 and 1454.3, so we must scale to avoid + * overflow in exp(x). + */ + return __ldexp_cexp(z, 0); + } else { + /* + * Cases covered here: + * - x < exp_ovfl and exp(x) won't overflow (common case) + * - x > cexp_ovfl, so exp(x) * s overflows for all s > 0 + * - x = +-Inf (generated by exp()) + * - x = NaN (spurious inexact exception from y) + */ + exp_x = exp(x); + return CMPLX(exp_x * cos(y), exp_x * sin(y)); + } +} diff --git a/system/lib/libc/musl/src/complex/cexpf.c b/system/lib/libc/musl/src/complex/cexpf.c new file mode 100644 index 0000000000000..1a09964cbb52d --- /dev/null +++ b/system/lib/libc/musl/src/complex/cexpf.c @@ -0,0 +1,83 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_cexpf.c */ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libm.h" + +static const uint32_t +exp_ovfl = 0x42b17218, /* MAX_EXP * ln2 ~= 88.722839355 */ +cexp_ovfl = 0x43400074; /* (MAX_EXP - MIN_DENORM_EXP) * ln2 */ + +float complex cexpf(float complex z) +{ + float x, y, exp_x; + uint32_t hx, hy; + + x = crealf(z); + y = cimagf(z); + + GET_FLOAT_WORD(hy, y); + hy &= 0x7fffffff; + + /* cexp(x + I 0) = exp(x) + I 0 */ + if (hy == 0) + return CMPLXF(expf(x), y); + GET_FLOAT_WORD(hx, x); + /* cexp(0 + I y) = cos(y) + I sin(y) */ + if ((hx & 0x7fffffff) == 0) + return CMPLXF(cosf(y), sinf(y)); + + if (hy >= 0x7f800000) { + if ((hx & 0x7fffffff) != 0x7f800000) { + /* cexp(finite|NaN +- I Inf|NaN) = NaN + I NaN */ + return CMPLXF(y - y, y - y); + } else if (hx & 0x80000000) { + /* cexp(-Inf +- I Inf|NaN) = 0 + I 0 */ + return CMPLXF(0.0, 0.0); + } else { + /* cexp(+Inf +- I Inf|NaN) = Inf + I NaN */ + return CMPLXF(x, y - y); + } + } + + if (hx >= exp_ovfl && hx <= cexp_ovfl) { + /* + * x is between 88.7 and 192, so we must scale to avoid + * overflow in expf(x). + */ + return __ldexp_cexpf(z, 0); + } else { + /* + * Cases covered here: + * - x < exp_ovfl and exp(x) won't overflow (common case) + * - x > cexp_ovfl, so exp(x) * s overflows for all s > 0 + * - x = +-Inf (generated by exp()) + * - x = NaN (spurious inexact exception from y) + */ + exp_x = expf(x); + return CMPLXF(exp_x * cosf(y), exp_x * sinf(y)); + } +} diff --git a/system/lib/libc/musl/src/complex/cexpl.c b/system/lib/libc/musl/src/complex/cexpl.c new file mode 100644 index 0000000000000..a27f85c052c1b --- /dev/null +++ b/system/lib/libc/musl/src/complex/cexpl.c @@ -0,0 +1,7 @@ +#include "libm.h" + +//FIXME +long double complex cexpl(long double complex z) +{ + return cexp(z); +} diff --git a/system/lib/libc/musl/src/complex/cimag.c b/system/lib/libc/musl/src/complex/cimag.c new file mode 100644 index 0000000000000..00955641b14a9 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cimag.c @@ -0,0 +1,6 @@ +#include "libm.h" + +double (cimag)(double complex z) +{ + return cimag(z); +} diff --git a/system/lib/libc/musl/src/complex/cimagf.c b/system/lib/libc/musl/src/complex/cimagf.c new file mode 100644 index 0000000000000..f7bcd76e439fe --- /dev/null +++ b/system/lib/libc/musl/src/complex/cimagf.c @@ -0,0 +1,6 @@ +#include "libm.h" + +float (cimagf)(float complex z) +{ + return cimagf(z); +} diff --git a/system/lib/libc/musl/src/complex/cimagl.c b/system/lib/libc/musl/src/complex/cimagl.c new file mode 100644 index 0000000000000..9ec24eeeafc56 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cimagl.c @@ -0,0 +1,6 @@ +#include "libm.h" + +long double (cimagl)(long double complex z) +{ + return cimagl(z); +} diff --git a/system/lib/libc/musl/src/complex/clog.c b/system/lib/libc/musl/src/complex/clog.c new file mode 100644 index 0000000000000..12aae9c7e9a02 --- /dev/null +++ b/system/lib/libc/musl/src/complex/clog.c @@ -0,0 +1,14 @@ +#include "libm.h" + +// FIXME + +/* log(z) = log(|z|) + i arg(z) */ + +double complex clog(double complex z) +{ + double r, phi; + + r = cabs(z); + phi = carg(z); + return CMPLX(log(r), phi); +} diff --git a/system/lib/libc/musl/src/complex/clogf.c b/system/lib/libc/musl/src/complex/clogf.c new file mode 100644 index 0000000000000..e9b32e6087bf2 --- /dev/null +++ b/system/lib/libc/musl/src/complex/clogf.c @@ -0,0 +1,12 @@ +#include "libm.h" + +// FIXME + +float complex clogf(float complex z) +{ + float r, phi; + + r = cabsf(z); + phi = cargf(z); + return CMPLXF(logf(r), phi); +} diff --git a/system/lib/libc/musl/src/complex/clogl.c b/system/lib/libc/musl/src/complex/clogl.c new file mode 100644 index 0000000000000..18f16088d7a31 --- /dev/null +++ b/system/lib/libc/musl/src/complex/clogl.c @@ -0,0 +1,18 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex clogl(long double complex z) +{ + return clog(z); +} +#else +// FIXME +long double complex clogl(long double complex z) +{ + long double r, phi; + + r = cabsl(z); + phi = cargl(z); + return CMPLXL(logl(r), phi); +} +#endif diff --git a/system/lib/libc/musl/src/complex/conj.c b/system/lib/libc/musl/src/complex/conj.c new file mode 100644 index 0000000000000..0b3f5f46306c7 --- /dev/null +++ b/system/lib/libc/musl/src/complex/conj.c @@ -0,0 +1,6 @@ +#include "libm.h" + +double complex conj(double complex z) +{ + return CMPLX(creal(z), -cimag(z)); +} diff --git a/system/lib/libc/musl/src/complex/conjf.c b/system/lib/libc/musl/src/complex/conjf.c new file mode 100644 index 0000000000000..9af6b2c3b4549 --- /dev/null +++ b/system/lib/libc/musl/src/complex/conjf.c @@ -0,0 +1,6 @@ +#include "libm.h" + +float complex conjf(float complex z) +{ + return CMPLXF(crealf(z), -cimagf(z)); +} diff --git a/system/lib/libc/musl/src/complex/conjl.c b/system/lib/libc/musl/src/complex/conjl.c new file mode 100644 index 0000000000000..67f11b9dde8ee --- /dev/null +++ b/system/lib/libc/musl/src/complex/conjl.c @@ -0,0 +1,6 @@ +#include "libm.h" + +long double complex conjl(long double complex z) +{ + return CMPLXL(creall(z), -cimagl(z)); +} diff --git a/system/lib/libc/musl/src/complex/cpow.c b/system/lib/libc/musl/src/complex/cpow.c new file mode 100644 index 0000000000000..f863588fe7881 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cpow.c @@ -0,0 +1,8 @@ +#include "libm.h" + +/* pow(z, c) = exp(c log(z)), See C99 G.6.4.1 */ + +double complex cpow(double complex z, double complex c) +{ + return cexp(c * clog(z)); +} diff --git a/system/lib/libc/musl/src/complex/cpowf.c b/system/lib/libc/musl/src/complex/cpowf.c new file mode 100644 index 0000000000000..53c65dcb1112a --- /dev/null +++ b/system/lib/libc/musl/src/complex/cpowf.c @@ -0,0 +1,6 @@ +#include "libm.h" + +float complex cpowf(float complex z, float complex c) +{ + return cexpf(c * clogf(z)); +} diff --git a/system/lib/libc/musl/src/complex/cpowl.c b/system/lib/libc/musl/src/complex/cpowl.c new file mode 100644 index 0000000000000..c1a80a7b255bd --- /dev/null +++ b/system/lib/libc/musl/src/complex/cpowl.c @@ -0,0 +1,13 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex cpowl(long double complex z, long double complex c) +{ + return cpow(z, c); +} +#else +long double complex cpowl(long double complex z, long double complex c) +{ + return cexpl(c * clogl(z)); +} +#endif diff --git a/system/lib/libc/musl/src/complex/cproj.c b/system/lib/libc/musl/src/complex/cproj.c new file mode 100644 index 0000000000000..15f358a14f045 --- /dev/null +++ b/system/lib/libc/musl/src/complex/cproj.c @@ -0,0 +1,8 @@ +#include "libm.h" + +double complex cproj(double complex z) +{ + if (isinf(creal(z)) || isinf(cimag(z))) + return CMPLX(INFINITY, copysign(0.0, creal(z))); + return z; +} diff --git a/system/lib/libc/musl/src/complex/cprojf.c b/system/lib/libc/musl/src/complex/cprojf.c new file mode 100644 index 0000000000000..653be5e87cfcb --- /dev/null +++ b/system/lib/libc/musl/src/complex/cprojf.c @@ -0,0 +1,8 @@ +#include "libm.h" + +float complex cprojf(float complex z) +{ + if (isinf(crealf(z)) || isinf(cimagf(z))) + return CMPLXF(INFINITY, copysignf(0.0, crealf(z))); + return z; +} diff --git a/system/lib/libc/musl/src/complex/cprojl.c b/system/lib/libc/musl/src/complex/cprojl.c new file mode 100644 index 0000000000000..6731aaa2aaaeb --- /dev/null +++ b/system/lib/libc/musl/src/complex/cprojl.c @@ -0,0 +1,15 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex cprojl(long double complex z) +{ + return cproj(z); +} +#else +long double complex cprojl(long double complex z) +{ + if (isinf(creall(z)) || isinf(cimagl(z))) + return CMPLXL(INFINITY, copysignl(0.0, creall(z))); + return z; +} +#endif diff --git a/system/lib/libc/musl/src/complex/creal.c b/system/lib/libc/musl/src/complex/creal.c new file mode 100644 index 0000000000000..f6703040d06fd --- /dev/null +++ b/system/lib/libc/musl/src/complex/creal.c @@ -0,0 +1,6 @@ +#include + +double (creal)(double complex z) +{ + return creal(z); +} diff --git a/system/lib/libc/musl/src/complex/crealf.c b/system/lib/libc/musl/src/complex/crealf.c new file mode 100644 index 0000000000000..5dc3ff1d3099d --- /dev/null +++ b/system/lib/libc/musl/src/complex/crealf.c @@ -0,0 +1,6 @@ +#include + +float (crealf)(float complex z) +{ + return crealf(z); +} diff --git a/system/lib/libc/musl/src/complex/creall.c b/system/lib/libc/musl/src/complex/creall.c new file mode 100644 index 0000000000000..fd9dc3470c253 --- /dev/null +++ b/system/lib/libc/musl/src/complex/creall.c @@ -0,0 +1,6 @@ +#include + +long double (creall)(long double complex z) +{ + return creall(z); +} diff --git a/system/lib/libc/musl/src/complex/csin.c b/system/lib/libc/musl/src/complex/csin.c new file mode 100644 index 0000000000000..ad8ae67ad99a5 --- /dev/null +++ b/system/lib/libc/musl/src/complex/csin.c @@ -0,0 +1,9 @@ +#include "libm.h" + +/* sin(z) = -i sinh(i z) */ + +double complex csin(double complex z) +{ + z = csinh(CMPLX(-cimag(z), creal(z))); + return CMPLX(cimag(z), -creal(z)); +} diff --git a/system/lib/libc/musl/src/complex/csinf.c b/system/lib/libc/musl/src/complex/csinf.c new file mode 100644 index 0000000000000..60b3cbaa8681a --- /dev/null +++ b/system/lib/libc/musl/src/complex/csinf.c @@ -0,0 +1,7 @@ +#include "libm.h" + +float complex csinf(float complex z) +{ + z = csinhf(CMPLXF(-cimagf(z), crealf(z))); + return CMPLXF(cimagf(z), -crealf(z)); +} diff --git a/system/lib/libc/musl/src/complex/csinh.c b/system/lib/libc/musl/src/complex/csinh.c new file mode 100644 index 0000000000000..0f8035d1ca41f --- /dev/null +++ b/system/lib/libc/musl/src/complex/csinh.c @@ -0,0 +1,141 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_csinh.c */ +/*- + * Copyright (c) 2005 Bruce D. Evans and Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Hyperbolic sine of a complex argument z = x + i y. + * + * sinh(z) = sinh(x+iy) + * = sinh(x) cos(y) + i cosh(x) sin(y). + * + * Exceptional values are noted in the comments within the source code. + * These values and the return value were taken from n1124.pdf. + */ + +#include "libm.h" + +static const double huge = 0x1p1023; + +double complex csinh(double complex z) +{ + double x, y, h; + int32_t hx, hy, ix, iy, lx, ly; + + x = creal(z); + y = cimag(z); + + EXTRACT_WORDS(hx, lx, x); + EXTRACT_WORDS(hy, ly, y); + + ix = 0x7fffffff & hx; + iy = 0x7fffffff & hy; + + /* Handle the nearly-non-exceptional cases where x and y are finite. */ + if (ix < 0x7ff00000 && iy < 0x7ff00000) { + if ((iy | ly) == 0) + return CMPLX(sinh(x), y); + if (ix < 0x40360000) /* small x: normal case */ + return CMPLX(sinh(x) * cos(y), cosh(x) * sin(y)); + + /* |x| >= 22, so cosh(x) ~= exp(|x|) */ + if (ix < 0x40862e42) { + /* x < 710: exp(|x|) won't overflow */ + h = exp(fabs(x)) * 0.5; + return CMPLX(copysign(h, x) * cos(y), h * sin(y)); + } else if (ix < 0x4096bbaa) { + /* x < 1455: scale to avoid overflow */ + z = __ldexp_cexp(CMPLX(fabs(x), y), -1); + return CMPLX(creal(z) * copysign(1, x), cimag(z)); + } else { + /* x >= 1455: the result always overflows */ + h = huge * x; + return CMPLX(h * cos(y), h * h * sin(y)); + } + } + + /* + * sinh(+-0 +- I Inf) = sign(d(+-0, dNaN))0 + I dNaN. + * The sign of 0 in the result is unspecified. Choice = normally + * the same as dNaN. Raise the invalid floating-point exception. + * + * sinh(+-0 +- I NaN) = sign(d(+-0, NaN))0 + I d(NaN). + * The sign of 0 in the result is unspecified. Choice = normally + * the same as d(NaN). + */ + if ((ix | lx) == 0 && iy >= 0x7ff00000) + return CMPLX(copysign(0, x * (y - y)), y - y); + + /* + * sinh(+-Inf +- I 0) = +-Inf + I +-0. + * + * sinh(NaN +- I 0) = d(NaN) + I +-0. + */ + if ((iy | ly) == 0 && ix >= 0x7ff00000) { + if (((hx & 0xfffff) | lx) == 0) + return CMPLX(x, y); + return CMPLX(x, copysign(0, y)); + } + + /* + * sinh(x +- I Inf) = dNaN + I dNaN. + * Raise the invalid floating-point exception for finite nonzero x. + * + * sinh(x + I NaN) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero x. Choice = don't raise (except for signaling NaNs). + */ + if (ix < 0x7ff00000 && iy >= 0x7ff00000) + return CMPLX(y - y, x * (y - y)); + + /* + * sinh(+-Inf + I NaN) = +-Inf + I d(NaN). + * The sign of Inf in the result is unspecified. Choice = normally + * the same as d(NaN). + * + * sinh(+-Inf +- I Inf) = +Inf + I dNaN. + * The sign of Inf in the result is unspecified. Choice = always +. + * Raise the invalid floating-point exception. + * + * sinh(+-Inf + I y) = +-Inf cos(y) + I Inf sin(y) + */ + if (ix >= 0x7ff00000 && ((hx & 0xfffff) | lx) == 0) { + if (iy >= 0x7ff00000) + return CMPLX(x * x, x * (y - y)); + return CMPLX(x * cos(y), INFINITY * sin(y)); + } + + /* + * sinh(NaN + I NaN) = d(NaN) + I d(NaN). + * + * sinh(NaN +- I Inf) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception. + * Choice = raise. + * + * sinh(NaN + I y) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero y. Choice = don't raise (except for signaling NaNs). + */ + return CMPLX((x * x) * (y - y), (x + x) * (y - y)); +} diff --git a/system/lib/libc/musl/src/complex/csinhf.c b/system/lib/libc/musl/src/complex/csinhf.c new file mode 100644 index 0000000000000..49697f02f4a35 --- /dev/null +++ b/system/lib/libc/musl/src/complex/csinhf.c @@ -0,0 +1,90 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_csinhf.c */ +/*- + * Copyright (c) 2005 Bruce D. Evans and Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Hyperbolic sine of a complex argument z. See s_csinh.c for details. + */ + +#include "libm.h" + +static const float huge = 0x1p127; + +float complex csinhf(float complex z) +{ + float x, y, h; + int32_t hx, hy, ix, iy; + + x = crealf(z); + y = cimagf(z); + + GET_FLOAT_WORD(hx, x); + GET_FLOAT_WORD(hy, y); + + ix = 0x7fffffff & hx; + iy = 0x7fffffff & hy; + + if (ix < 0x7f800000 && iy < 0x7f800000) { + if (iy == 0) + return CMPLXF(sinhf(x), y); + if (ix < 0x41100000) /* small x: normal case */ + return CMPLXF(sinhf(x) * cosf(y), coshf(x) * sinf(y)); + + /* |x| >= 9, so cosh(x) ~= exp(|x|) */ + if (ix < 0x42b17218) { + /* x < 88.7: expf(|x|) won't overflow */ + h = expf(fabsf(x)) * 0.5f; + return CMPLXF(copysignf(h, x) * cosf(y), h * sinf(y)); + } else if (ix < 0x4340b1e7) { + /* x < 192.7: scale to avoid overflow */ + z = __ldexp_cexpf(CMPLXF(fabsf(x), y), -1); + return CMPLXF(crealf(z) * copysignf(1, x), cimagf(z)); + } else { + /* x >= 192.7: the result always overflows */ + h = huge * x; + return CMPLXF(h * cosf(y), h * h * sinf(y)); + } + } + + if (ix == 0 && iy >= 0x7f800000) + return CMPLXF(copysignf(0, x * (y - y)), y - y); + + if (iy == 0 && ix >= 0x7f800000) { + if ((hx & 0x7fffff) == 0) + return CMPLXF(x, y); + return CMPLXF(x, copysignf(0, y)); + } + + if (ix < 0x7f800000 && iy >= 0x7f800000) + return CMPLXF(y - y, x * (y - y)); + + if (ix >= 0x7f800000 && (hx & 0x7fffff) == 0) { + if (iy >= 0x7f800000) + return CMPLXF(x * x, x * (y - y)); + return CMPLXF(x * cosf(y), INFINITY * sinf(y)); + } + + return CMPLXF((x * x) * (y - y), (x + x) * (y - y)); +} diff --git a/system/lib/libc/musl/src/complex/csinhl.c b/system/lib/libc/musl/src/complex/csinhl.c new file mode 100644 index 0000000000000..c566653b3356f --- /dev/null +++ b/system/lib/libc/musl/src/complex/csinhl.c @@ -0,0 +1,7 @@ +#include "libm.h" + +//FIXME +long double complex csinhl(long double complex z) +{ + return csinh(z); +} diff --git a/system/lib/libc/musl/src/complex/csinl.c b/system/lib/libc/musl/src/complex/csinl.c new file mode 100644 index 0000000000000..4e9f86c3ae4dc --- /dev/null +++ b/system/lib/libc/musl/src/complex/csinl.c @@ -0,0 +1,14 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex csinl(long double complex z) +{ + return csin(z); +} +#else +long double complex csinl(long double complex z) +{ + z = csinhl(CMPLXL(-cimagl(z), creall(z))); + return CMPLXL(cimagl(z), -creall(z)); +} +#endif diff --git a/system/lib/libc/musl/src/complex/csqrt.c b/system/lib/libc/musl/src/complex/csqrt.c new file mode 100644 index 0000000000000..8a2ba60801223 --- /dev/null +++ b/system/lib/libc/musl/src/complex/csqrt.c @@ -0,0 +1,100 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_csqrt.c */ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libm.h" + +/* + * gcc doesn't implement complex multiplication or division correctly, + * so we need to handle infinities specially. We turn on this pragma to + * notify conforming c99 compilers that the fast-but-incorrect code that + * gcc generates is acceptable, since the special cases have already been + * handled. + */ +#pragma STDC CX_LIMITED_RANGE ON + +/* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */ +#define THRESH 0x1.a827999fcef32p+1022 + +double complex csqrt(double complex z) +{ + double complex result; + double a, b; + double t; + int scale; + + a = creal(z); + b = cimag(z); + + /* Handle special cases. */ + if (z == 0) + return CMPLX(0, b); + if (isinf(b)) + return CMPLX(INFINITY, b); + if (isnan(a)) { + t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ + return CMPLX(a, t); /* return NaN + NaN i */ + } + if (isinf(a)) { + /* + * csqrt(inf + NaN i) = inf + NaN i + * csqrt(inf + y i) = inf + 0 i + * csqrt(-inf + NaN i) = NaN +- inf i + * csqrt(-inf + y i) = 0 + inf i + */ + if (signbit(a)) + return CMPLX(fabs(b - b), copysign(a, b)); + else + return CMPLX(a, copysign(b - b, b)); + } + /* + * The remaining special case (b is NaN) is handled just fine by + * the normal code path below. + */ + + /* Scale to avoid overflow. */ + if (fabs(a) >= THRESH || fabs(b) >= THRESH) { + a *= 0.25; + b *= 0.25; + scale = 1; + } else { + scale = 0; + } + + /* Algorithm 312, CACM vol 10, Oct 1967. */ + if (a >= 0) { + t = sqrt((a + hypot(a, b)) * 0.5); + result = CMPLX(t, b / (2 * t)); + } else { + t = sqrt((-a + hypot(a, b)) * 0.5); + result = CMPLX(fabs(b) / (2 * t), copysign(t, b)); + } + + /* Rescale. */ + if (scale) + result *= 2; + return result; +} diff --git a/system/lib/libc/musl/src/complex/csqrtf.c b/system/lib/libc/musl/src/complex/csqrtf.c new file mode 100644 index 0000000000000..ab5102f035b9e --- /dev/null +++ b/system/lib/libc/musl/src/complex/csqrtf.c @@ -0,0 +1,82 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_csqrtf.c */ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libm.h" + +/* + * gcc doesn't implement complex multiplication or division correctly, + * so we need to handle infinities specially. We turn on this pragma to + * notify conforming c99 compilers that the fast-but-incorrect code that + * gcc generates is acceptable, since the special cases have already been + * handled. + */ +#pragma STDC CX_LIMITED_RANGE ON + +float complex csqrtf(float complex z) +{ + float a = crealf(z), b = cimagf(z); + double t; + + /* Handle special cases. */ + if (z == 0) + return CMPLXF(0, b); + if (isinf(b)) + return CMPLXF(INFINITY, b); + if (isnan(a)) { + t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ + return CMPLXF(a, t); /* return NaN + NaN i */ + } + if (isinf(a)) { + /* + * csqrtf(inf + NaN i) = inf + NaN i + * csqrtf(inf + y i) = inf + 0 i + * csqrtf(-inf + NaN i) = NaN +- inf i + * csqrtf(-inf + y i) = 0 + inf i + */ + if (signbit(a)) + return CMPLXF(fabsf(b - b), copysignf(a, b)); + else + return CMPLXF(a, copysignf(b - b, b)); + } + /* + * The remaining special case (b is NaN) is handled just fine by + * the normal code path below. + */ + + /* + * We compute t in double precision to avoid overflow and to + * provide correct rounding in nearly all cases. + * This is Algorithm 312, CACM vol 10, Oct 1967. + */ + if (a >= 0) { + t = sqrt((a + hypot(a, b)) * 0.5); + return CMPLXF(t, b / (2.0 * t)); + } else { + t = sqrt((-a + hypot(a, b)) * 0.5); + return CMPLXF(fabsf(b) / (2.0 * t), copysignf(t, b)); + } +} diff --git a/system/lib/libc/musl/src/complex/csqrtl.c b/system/lib/libc/musl/src/complex/csqrtl.c new file mode 100644 index 0000000000000..0600ef3bebcd3 --- /dev/null +++ b/system/lib/libc/musl/src/complex/csqrtl.c @@ -0,0 +1,7 @@ +#include "libm.h" + +//FIXME +long double complex csqrtl(long double complex z) +{ + return csqrt(z); +} diff --git a/system/lib/libc/musl/src/complex/ctan.c b/system/lib/libc/musl/src/complex/ctan.c new file mode 100644 index 0000000000000..c09263744b869 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ctan.c @@ -0,0 +1,9 @@ +#include "libm.h" + +/* tan(z) = -i tanh(i z) */ + +double complex ctan(double complex z) +{ + z = ctanh(CMPLX(-cimag(z), creal(z))); + return CMPLX(cimag(z), -creal(z)); +} diff --git a/system/lib/libc/musl/src/complex/ctanf.c b/system/lib/libc/musl/src/complex/ctanf.c new file mode 100644 index 0000000000000..009b1921ba356 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ctanf.c @@ -0,0 +1,7 @@ +#include "libm.h" + +float complex ctanf(float complex z) +{ + z = ctanhf(CMPLXF(-cimagf(z), crealf(z))); + return CMPLXF(cimagf(z), -crealf(z)); +} diff --git a/system/lib/libc/musl/src/complex/ctanh.c b/system/lib/libc/musl/src/complex/ctanh.c new file mode 100644 index 0000000000000..0461050d73301 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ctanh.c @@ -0,0 +1,127 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_ctanh.c */ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Hyperbolic tangent of a complex argument z = x + i y. + * + * The algorithm is from: + * + * W. Kahan. Branch Cuts for Complex Elementary Functions or Much + * Ado About Nothing's Sign Bit. In The State of the Art in + * Numerical Analysis, pp. 165 ff. Iserles and Powell, eds., 1987. + * + * Method: + * + * Let t = tan(x) + * beta = 1/cos^2(y) + * s = sinh(x) + * rho = cosh(x) + * + * We have: + * + * tanh(z) = sinh(z) / cosh(z) + * + * sinh(x) cos(y) + i cosh(x) sin(y) + * = --------------------------------- + * cosh(x) cos(y) + i sinh(x) sin(y) + * + * cosh(x) sinh(x) / cos^2(y) + i tan(y) + * = ------------------------------------- + * 1 + sinh^2(x) / cos^2(y) + * + * beta rho s + i t + * = ---------------- + * 1 + beta s^2 + * + * Modifications: + * + * I omitted the original algorithm's handling of overflow in tan(x) after + * verifying with nearpi.c that this can't happen in IEEE single or double + * precision. I also handle large x differently. + */ + +#include "libm.h" + +double complex ctanh(double complex z) +{ + double x, y; + double t, beta, s, rho, denom; + uint32_t hx, ix, lx; + + x = creal(z); + y = cimag(z); + + EXTRACT_WORDS(hx, lx, x); + ix = hx & 0x7fffffff; + + /* + * ctanh(NaN + i 0) = NaN + i 0 + * + * ctanh(NaN + i y) = NaN + i NaN for y != 0 + * + * The imaginary part has the sign of x*sin(2*y), but there's no + * special effort to get this right. + * + * ctanh(+-Inf +- i Inf) = +-1 +- 0 + * + * ctanh(+-Inf + i y) = +-1 + 0 sin(2y) for y finite + * + * The imaginary part of the sign is unspecified. This special + * case is only needed to avoid a spurious invalid exception when + * y is infinite. + */ + if (ix >= 0x7ff00000) { + if ((ix & 0xfffff) | lx) /* x is NaN */ + return CMPLX(x, (y == 0 ? y : x * y)); + SET_HIGH_WORD(x, hx - 0x40000000); /* x = copysign(1, x) */ + return CMPLX(x, copysign(0, isinf(y) ? y : sin(y) * cos(y))); + } + + /* + * ctanh(x + i NAN) = NaN + i NaN + * ctanh(x +- i Inf) = NaN + i NaN + */ + if (!isfinite(y)) + return CMPLX(y - y, y - y); + + /* + * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the + * approximation sinh^2(huge) ~= exp(2*huge) / 4. + * We use a modified formula to avoid spurious overflow. + */ + if (ix >= 0x40360000) { /* x >= 22 */ + double exp_mx = exp(-fabs(x)); + return CMPLX(copysign(1, x), 4 * sin(y) * cos(y) * exp_mx * exp_mx); + } + + /* Kahan's algorithm */ + t = tan(y); + beta = 1.0 + t * t; /* = 1 / cos^2(y) */ + s = sinh(x); + rho = sqrt(1 + s * s); /* = cosh(x) */ + denom = 1 + beta * s * s; + return CMPLX((beta * rho * s) / denom, t / denom); +} diff --git a/system/lib/libc/musl/src/complex/ctanhf.c b/system/lib/libc/musl/src/complex/ctanhf.c new file mode 100644 index 0000000000000..a7e1a5fc0dccf --- /dev/null +++ b/system/lib/libc/musl/src/complex/ctanhf.c @@ -0,0 +1,66 @@ +/* origin: FreeBSD /usr/src/lib/msun/src/s_ctanhf.c */ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Hyperbolic tangent of a complex argument z. See s_ctanh.c for details. + */ + +#include "libm.h" + +float complex ctanhf(float complex z) +{ + float x, y; + float t, beta, s, rho, denom; + uint32_t hx, ix; + + x = crealf(z); + y = cimagf(z); + + GET_FLOAT_WORD(hx, x); + ix = hx & 0x7fffffff; + + if (ix >= 0x7f800000) { + if (ix & 0x7fffff) + return CMPLXF(x, (y == 0 ? y : x * y)); + SET_FLOAT_WORD(x, hx - 0x40000000); + return CMPLXF(x, copysignf(0, isinf(y) ? y : sinf(y) * cosf(y))); + } + + if (!isfinite(y)) + return CMPLXF(y - y, y - y); + + if (ix >= 0x41300000) { /* x >= 11 */ + float exp_mx = expf(-fabsf(x)); + return CMPLXF(copysignf(1, x), 4 * sinf(y) * cosf(y) * exp_mx * exp_mx); + } + + t = tanf(y); + beta = 1.0 + t * t; + s = sinhf(x); + rho = sqrtf(1 + s * s); + denom = 1 + beta * s * s; + return CMPLXF((beta * rho * s) / denom, t / denom); +} diff --git a/system/lib/libc/musl/src/complex/ctanhl.c b/system/lib/libc/musl/src/complex/ctanhl.c new file mode 100644 index 0000000000000..89a75d1334670 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ctanhl.c @@ -0,0 +1,7 @@ +#include "libm.h" + +//FIXME +long double complex ctanhl(long double complex z) +{ + return ctanh(z); +} diff --git a/system/lib/libc/musl/src/complex/ctanl.c b/system/lib/libc/musl/src/complex/ctanl.c new file mode 100644 index 0000000000000..ac1c3e0ad86d7 --- /dev/null +++ b/system/lib/libc/musl/src/complex/ctanl.c @@ -0,0 +1,14 @@ +#include "libm.h" + +#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 +long double complex ctanl(long double complex z) +{ + return ctan(z); +} +#else +long double complex ctanl(long double complex z) +{ + z = ctanhl(CMPLXL(-cimagl(z), creall(z))); + return CMPLXL(cimagl(z), -creall(z)); +} +#endif diff --git a/system/lib/libc/musl/src/math/__expo2.c b/system/lib/libc/musl/src/math/__expo2.c new file mode 100644 index 0000000000000..740ac680e8570 --- /dev/null +++ b/system/lib/libc/musl/src/math/__expo2.c @@ -0,0 +1,16 @@ +#include "libm.h" + +/* k is such that k*ln2 has minimal relative error and x - kln2 > log(DBL_MIN) */ +static const int k = 2043; +static const double kln2 = 0x1.62066151add8bp+10; + +/* exp(x)/2 for x >= log(DBL_MAX), slightly better than 0.5*exp(x/2)*exp(x/2) */ +double __expo2(double x) +{ + double scale; + + /* note that k is odd and scale*scale overflows */ + INSERT_WORDS(scale, (uint32_t)(0x3ff + k/2) << 20, 0); + /* exp(x - k ln2) * 2**(k-1) */ + return exp(x - kln2) * scale * scale; +} diff --git a/system/lib/libc/musl/src/math/__expo2f.c b/system/lib/libc/musl/src/math/__expo2f.c new file mode 100644 index 0000000000000..5163e4180033b --- /dev/null +++ b/system/lib/libc/musl/src/math/__expo2f.c @@ -0,0 +1,16 @@ +#include "libm.h" + +/* k is such that k*ln2 has minimal relative error and x - kln2 > log(FLT_MIN) */ +static const int k = 235; +static const float kln2 = 0x1.45c778p+7f; + +/* expf(x)/2 for x >= log(FLT_MAX), slightly better than 0.5f*expf(x/2)*expf(x/2) */ +float __expo2f(float x) +{ + float scale; + + /* note that k is odd and scale*scale overflows */ + SET_FLOAT_WORD(scale, (uint32_t)(0x7f + k/2) << 23); + /* exp(x - k ln2) * 2**(k-1) */ + return expf(x - kln2) * scale * scale; +} diff --git a/system/lib/libcextra.symbols b/system/lib/libcextra.symbols index 15983edf3f54c..e08f7f185cff0 100644 --- a/system/lib/libcextra.symbols +++ b/system/lib/libcextra.symbols @@ -253,3 +253,71 @@ T strcoll T __strcoll_l W strcoll_l + T cabs + T cabsf + T cabsl + T cacos + T cacosf + T cacosh + T cacoshf + T cacoshl + T cacosl + T carg + T cargf + T cargl + T casin + T casinf + T casinh + T casinhf + T casinhl + T casinl + T catan + T catanf + T catanh + T catanhf + T catanhl + T catanl + T ccos + T ccosf + T ccosh + T ccoshf + T ccoshl + T ccosl + T __cexp + T cexp + T __cexpf + T cexpf + T cexpl + T cimag + T cimagf + T cimagl + T clog + T clogf + T clogl + T conj + T conjf + T conjl + T cpow + T cpowf + T cpowl + T cproj + T cprojf + T cprojl + T creal + T crealf + T creall + T csin + T csinf + T csinh + T csinhf + T csinhl + T csinl + T csqrt + T csqrtf + T csqrtl + T ctan + T ctanf + T ctanh + T ctanhf + T ctanhl + T ctanl diff --git a/tests/fuzz/csmith_driver.py b/tests/fuzz/csmith_driver.py index d004c2c723d1e..f606d36c872e9 100755 --- a/tests/fuzz/csmith_driver.py +++ b/tests/fuzz/csmith_driver.py @@ -48,6 +48,7 @@ print '1) Generate source' extra_args = [] if random.random() < 0.5: extra_args += ['--no-math64'] + #if random.random() < 0.5: extra_args += ['--float'] # XXX hits undefined behavior on float=>int conversions (too big to fit) suffix = '.c' COMP = shared.CLANG_CC if random.random() < 0.5: @@ -95,9 +96,9 @@ def try_js(args): shared.try_delete(filename + '.js') print '(compile)' - shared.check_execute([shared.PYTHON, shared.EMCC, opts, fullname, '-o', filename + '.js'] + CSMITH_CFLAGS + args) + shared.check_execute([shared.PYTHON, shared.EMCC, opts, fullname, '-o', filename + '.js', '-s', 'PRECISE_F32=1'] + CSMITH_CFLAGS + args) assert os.path.exists(filename + '.js') - print '(run)' + print '(run in %s)' % engine1 js = shared.run_js(filename + '.js', engine=engine1, check_timeout=True, assert_returncode=None, cwd='/tmp/emscripten_temp') js = js.split('\n')[0] + '\n' # remove any extra printed stuff (node workarounds) assert correct1 == js or correct2 == js, ''.join([a.rstrip()+'\n' for a in difflib.unified_diff(correct1.split('\n'), js.split('\n'), fromfile='expected', tofile='actual')]) diff --git a/tests/fuzz/csmith.h b/tests/fuzz/include/csmith.h similarity index 100% rename from tests/fuzz/csmith.h rename to tests/fuzz/include/csmith.h diff --git a/tests/fuzz/platform_generic.h b/tests/fuzz/include/platform_generic.h similarity index 100% rename from tests/fuzz/platform_generic.h rename to tests/fuzz/include/platform_generic.h diff --git a/tests/fuzz/random_inc.h b/tests/fuzz/include/random_inc.h similarity index 100% rename from tests/fuzz/random_inc.h rename to tests/fuzz/include/random_inc.h diff --git a/tests/fuzz/safe_math.h b/tests/fuzz/include/safe_math.h similarity index 100% rename from tests/fuzz/safe_math.h rename to tests/fuzz/include/safe_math.h diff --git a/tests/fuzz/test.sh b/tests/fuzz/test.sh index 90d6b1a47ab15..e8680d5860e17 100755 --- a/tests/fuzz/test.sh +++ b/tests/fuzz/test.sh @@ -10,8 +10,8 @@ gcc $@ -m32 -I/home/alon/Dev/csmith/runtime -o n1.out &> /dev/null #EMCC_FAST_COMPILER=0 ~/Dev/emscripten/emcc $@ -I/home/alon/Dev/csmith/runtime -o js.out.js &> /dev/null #EMCC_FAST_COMPILER=0~/Dev/emscripten/emcc $@ -s UNALIGNED_MEMORY=1 -I/home/alon/Dev/csmith/runtime -o ua.out.js &> /dev/null #EMCC_FAST_COMPILER=0~/Dev/emscripten/emcc $@ -s SAFE_HEAP=1 -I/home/alon/Dev/csmith/runtime -o sh.out.js &> /dev/null -~/Dev/emscripten/emcc $@ -I/home/alon/Dev/csmith/runtime -o fc.out.js &> /dev/null -~/Dev/emscripten/emcc $@ -s SAFE_HEAP=1 -I/home/alon/Dev/csmith/runtime -o fc-sh.out.js &> /dev/null +~/Dev/emscripten/emcc $@ -I/home/alon/Dev/csmith/runtime -s PRECISE_F32=1 -o fc.out.js &> /dev/null +~/Dev/emscripten/emcc $@ -s SAFE_HEAP=1 -I/home/alon/Dev/csmith/runtime -s PRECISE_F32=1 -o fc-sh.out.js &> /dev/null echo "run n1" ./n1.out &> n1 echo "run n2" diff --git a/tests/fuzz/testpp.sh b/tests/fuzz/testpp.sh index 49378645002c8..55e3bcf7d6e85 100755 --- a/tests/fuzz/testpp.sh +++ b/tests/fuzz/testpp.sh @@ -10,8 +10,8 @@ g++ $@ -m32 -I/home/alon/Dev/csmith/runtime -o n1.out &> /dev/null #EMCC_FAST_COMPILER=0 ~/Dev/emscripten/em++ $@ -I/home/alon/Dev/csmith/runtime -o js.out.js &> /dev/null #EMCC_FAST_COMPILER=0~/Dev/emscripten/em++ $@ -s UNALIGNED_MEMORY=1 -I/home/alon/Dev/csmith/runtime -o ua.out.js &> /dev/null #EMCC_FAST_COMPILER=0~/Dev/emscripten/em++ $@ -s SAFE_HEAP=1 -I/home/alon/Dev/csmith/runtime -o sh.out.js &> /dev/null -~/Dev/emscripten/em++ $@ -I/home/alon/Dev/csmith/runtime -o fc.out.js &> /dev/null -~/Dev/emscripten/em++ $@ -s SAFE_HEAP=1 -I/home/alon/Dev/csmith/runtime -o fc-sh.out.js &> /dev/null +~/Dev/emscripten/em++ $@ -I/home/alon/Dev/csmith/runtime -s PRECISE_F32=1 -o fc.out.js &> /dev/null +~/Dev/emscripten/em++ $@ -s SAFE_HEAP=1 -I/home/alon/Dev/csmith/runtime -s PRECISE_F32=1 -o fc-sh.out.js &> /dev/null echo "run n1" ./n1.out &> n1 echo "run n2" diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tests/optimizer/asm-eliminator-test-output.js similarity index 100% rename from tools/eliminator/asm-eliminator-test-output.js rename to tests/optimizer/asm-eliminator-test-output.js diff --git a/tools/eliminator/asm-eliminator-test.js b/tests/optimizer/asm-eliminator-test.js similarity index 100% rename from tools/eliminator/asm-eliminator-test.js rename to tests/optimizer/asm-eliminator-test.js diff --git a/tools/eliminator/eliminator-test-output.js b/tests/optimizer/eliminator-test-output.js similarity index 100% rename from tools/eliminator/eliminator-test-output.js rename to tests/optimizer/eliminator-test-output.js diff --git a/tools/eliminator/eliminator-test.js b/tests/optimizer/eliminator-test.js similarity index 100% rename from tools/eliminator/eliminator-test.js rename to tests/optimizer/eliminator-test.js diff --git a/tools/eliminator/safe-eliminator-test-output.js b/tests/optimizer/safe-eliminator-test-output.js similarity index 100% rename from tools/eliminator/safe-eliminator-test-output.js rename to tests/optimizer/safe-eliminator-test-output.js diff --git a/tools/eliminator/safe-eliminator-test.js b/tests/optimizer/safe-eliminator-test.js similarity index 100% rename from tools/eliminator/safe-eliminator-test.js rename to tests/optimizer/safe-eliminator-test.js diff --git a/tools/test-js-optimizer-asm-last-output.js b/tests/optimizer/test-js-optimizer-asm-last-output.js similarity index 94% rename from tools/test-js-optimizer-asm-last-output.js rename to tests/optimizer/test-js-optimizer-asm-last-output.js index 220407f422768..cab9bee2edaa4 100644 --- a/tools/test-js-optimizer-asm-last-output.js +++ b/tests/optimizer/test-js-optimizer-asm-last-output.js @@ -32,6 +32,10 @@ function finall(x) { a = -0xde0b6b000000000; a = 1.1234567890123457e+21; f(g() | 0); + x = 1.7976931348623157e+308; + a = 9007199254740992; + a = 9007199254740992; + a = 9007199254740994; return 12.0e10; } function looop() { diff --git a/tools/test-js-optimizer-asm-last-output2.js b/tests/optimizer/test-js-optimizer-asm-last-output2.js similarity index 94% rename from tools/test-js-optimizer-asm-last-output2.js rename to tests/optimizer/test-js-optimizer-asm-last-output2.js index ee736a4e71a11..1869ce255e997 100644 --- a/tools/test-js-optimizer-asm-last-output2.js +++ b/tests/optimizer/test-js-optimizer-asm-last-output2.js @@ -32,6 +32,10 @@ function finall(x) { a = -999999984306749440; a = 1123456789012345651200.0; f(g() | 0); + x = 1797693134862315708145274e284; + a = 9007199254740992; + a = 9007199254740992; + a = 9007199254740994; return 12.0e10; } function looop() { diff --git a/tools/test-js-optimizer-asm-last.js b/tests/optimizer/test-js-optimizer-asm-last.js similarity index 88% rename from tools/test-js-optimizer-asm-last.js rename to tests/optimizer/test-js-optimizer-asm-last.js index 1a30066ddc1a0..97997356c18e3 100644 --- a/tools/test-js-optimizer-asm-last.js +++ b/tests/optimizer/test-js-optimizer-asm-last.js @@ -32,6 +32,10 @@ function finall(x) { a = -0xde0b6b000000000; a = +0x3ce7184d470dd60000; f(g() & -1); + x = 1.7976931348623157e+308; + a = 9007199254740992; // 2^53, the largest integer that can be represented in a double such that all smaller integers are also representable. + a = 9007199254740993; // 2^53 + 1 cannot be represented as double. + a = 9007199254740994; // This is again representable as double. return +12e10; } function looop() { diff --git a/tools/test-js-optimizer-asm-lastOpts-output.js b/tests/optimizer/test-js-optimizer-asm-lastOpts-output.js similarity index 94% rename from tools/test-js-optimizer-asm-lastOpts-output.js rename to tests/optimizer/test-js-optimizer-asm-lastOpts-output.js index f6f090a6ed558..b5b4d98064331 100644 --- a/tools/test-js-optimizer-asm-lastOpts-output.js +++ b/tests/optimizer/test-js-optimizer-asm-lastOpts-output.js @@ -32,6 +32,10 @@ function finall(x) { a = -0xde0b6b000000000; a = +0x3ce7184d470dd60000; f(g() | 0); + x = 1.7976931348623157e+308; + a = 9007199254740992; + a = 9007199254740992; + a = 9007199254740994; return +12e10; } function looop() { diff --git a/tools/test-js-optimizer-asm-lastOpts-output2.js b/tests/optimizer/test-js-optimizer-asm-lastOpts-output2.js similarity index 94% rename from tools/test-js-optimizer-asm-lastOpts-output2.js rename to tests/optimizer/test-js-optimizer-asm-lastOpts-output2.js index 0c18b8c924a8d..3200dbbd244f6 100644 --- a/tools/test-js-optimizer-asm-lastOpts-output2.js +++ b/tests/optimizer/test-js-optimizer-asm-lastOpts-output2.js @@ -32,6 +32,10 @@ function finall(x) { a = -0xde0b6b000000000; a = +1123456789012345651200; f(g() | 0); + x = 1797693134862315708145274e284; + a = 9007199254740992; + a = 9007199254740992; + a = 9007199254740994; return +12e10; } function looop() { diff --git a/tools/test-js-optimizer-asm-minlast-output.js b/tests/optimizer/test-js-optimizer-asm-minlast-output.js similarity index 100% rename from tools/test-js-optimizer-asm-minlast-output.js rename to tests/optimizer/test-js-optimizer-asm-minlast-output.js diff --git a/tools/test-js-optimizer-asm-minlast.js b/tests/optimizer/test-js-optimizer-asm-minlast.js similarity index 100% rename from tools/test-js-optimizer-asm-minlast.js rename to tests/optimizer/test-js-optimizer-asm-minlast.js diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tests/optimizer/test-js-optimizer-asm-outline1-output.js similarity index 100% rename from tools/test-js-optimizer-asm-outline1-output.js rename to tests/optimizer/test-js-optimizer-asm-outline1-output.js diff --git a/tools/test-js-optimizer-asm-outline1.js b/tests/optimizer/test-js-optimizer-asm-outline1.js similarity index 100% rename from tools/test-js-optimizer-asm-outline1.js rename to tests/optimizer/test-js-optimizer-asm-outline1.js diff --git a/tools/test-js-optimizer-asm-outline2-output.js b/tests/optimizer/test-js-optimizer-asm-outline2-output.js similarity index 100% rename from tools/test-js-optimizer-asm-outline2-output.js rename to tests/optimizer/test-js-optimizer-asm-outline2-output.js diff --git a/tools/test-js-optimizer-asm-outline2.js b/tests/optimizer/test-js-optimizer-asm-outline2.js similarity index 100% rename from tools/test-js-optimizer-asm-outline2.js rename to tests/optimizer/test-js-optimizer-asm-outline2.js diff --git a/tools/test-js-optimizer-asm-outline3-output.js b/tests/optimizer/test-js-optimizer-asm-outline3-output.js similarity index 100% rename from tools/test-js-optimizer-asm-outline3-output.js rename to tests/optimizer/test-js-optimizer-asm-outline3-output.js diff --git a/tools/test-js-optimizer-asm-outline3.js b/tests/optimizer/test-js-optimizer-asm-outline3.js similarity index 100% rename from tools/test-js-optimizer-asm-outline3.js rename to tests/optimizer/test-js-optimizer-asm-outline3.js diff --git a/tools/test-js-optimizer-asm-pre-f32.js b/tests/optimizer/test-js-optimizer-asm-pre-f32.js similarity index 100% rename from tools/test-js-optimizer-asm-pre-f32.js rename to tests/optimizer/test-js-optimizer-asm-pre-f32.js diff --git a/tools/test-js-optimizer-asm-pre-output-f32-nosimp.js b/tests/optimizer/test-js-optimizer-asm-pre-output-f32-nosimp.js similarity index 100% rename from tools/test-js-optimizer-asm-pre-output-f32-nosimp.js rename to tests/optimizer/test-js-optimizer-asm-pre-output-f32-nosimp.js diff --git a/tools/test-js-optimizer-asm-pre-output-f32.js b/tests/optimizer/test-js-optimizer-asm-pre-output-f32.js similarity index 100% rename from tools/test-js-optimizer-asm-pre-output-f32.js rename to tests/optimizer/test-js-optimizer-asm-pre-output-f32.js diff --git a/tools/test-js-optimizer-asm-pre-output.js b/tests/optimizer/test-js-optimizer-asm-pre-output.js similarity index 99% rename from tools/test-js-optimizer-asm-pre-output.js rename to tests/optimizer/test-js-optimizer-asm-pre-output.js index f0fcd43b2ae5c..5ba72dd342823 100644 --- a/tools/test-js-optimizer-asm-pre-output.js +++ b/tests/optimizer/test-js-optimizer-asm-pre-output.js @@ -602,4 +602,5 @@ function bignum() { HEAP32[20] = -1515870811; if (($2814 | 0) < 0) return; } +function empty() {} diff --git a/tools/test-js-optimizer-asm-pre-output2.js b/tests/optimizer/test-js-optimizer-asm-pre-output2.js similarity index 99% rename from tools/test-js-optimizer-asm-pre-output2.js rename to tests/optimizer/test-js-optimizer-asm-pre-output2.js index 2711d862bf071..2652ee8846091 100644 --- a/tools/test-js-optimizer-asm-pre-output2.js +++ b/tests/optimizer/test-js-optimizer-asm-pre-output2.js @@ -602,4 +602,5 @@ function bignum() { HEAP32[20] = -1515870811; if (($2814 | 0) < 0) return; } +function empty() {} diff --git a/tools/test-js-optimizer-asm-pre.js b/tests/optimizer/test-js-optimizer-asm-pre.js similarity index 99% rename from tools/test-js-optimizer-asm-pre.js rename to tests/optimizer/test-js-optimizer-asm-pre.js index f93b1fbdc39d7..f9c6bc12ec32b 100644 --- a/tools/test-js-optimizer-asm-pre.js +++ b/tests/optimizer/test-js-optimizer-asm-pre.js @@ -613,4 +613,6 @@ function bignum() { HEAP32[20] = 2779096485 | 0; if (!(($2814 | 0) >= 0)) return; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "rett", "ret2t", "retf", "i32_8", "tempDoublePtr", "boxx", "_main", "badf", "badf2", "fcomp", "conditionalizeMe", "bignum"] +function empty() { +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "rett", "ret2t", "retf", "i32_8", "tempDoublePtr", "boxx", "_main", "badf", "badf2", "fcomp", "conditionalizeMe", "bignum", "empty"] diff --git a/tools/test-js-optimizer-asm-regs-harder-output.js b/tests/optimizer/test-js-optimizer-asm-regs-harder-output.js similarity index 100% rename from tools/test-js-optimizer-asm-regs-harder-output.js rename to tests/optimizer/test-js-optimizer-asm-regs-harder-output.js diff --git a/tools/test-js-optimizer-asm-regs-harder-output2.js b/tests/optimizer/test-js-optimizer-asm-regs-harder-output2.js similarity index 100% rename from tools/test-js-optimizer-asm-regs-harder-output2.js rename to tests/optimizer/test-js-optimizer-asm-regs-harder-output2.js diff --git a/tools/test-js-optimizer-asm-regs-harder-output3.js b/tests/optimizer/test-js-optimizer-asm-regs-harder-output3.js similarity index 100% rename from tools/test-js-optimizer-asm-regs-harder-output3.js rename to tests/optimizer/test-js-optimizer-asm-regs-harder-output3.js diff --git a/tools/test-js-optimizer-asm-regs-harder.js b/tests/optimizer/test-js-optimizer-asm-regs-harder.js similarity index 100% rename from tools/test-js-optimizer-asm-regs-harder.js rename to tests/optimizer/test-js-optimizer-asm-regs-harder.js diff --git a/tools/test-js-optimizer-asm-regs-min-output.js b/tests/optimizer/test-js-optimizer-asm-regs-min-output.js similarity index 100% rename from tools/test-js-optimizer-asm-regs-min-output.js rename to tests/optimizer/test-js-optimizer-asm-regs-min-output.js diff --git a/tools/test-js-optimizer-asm-regs-min.js b/tests/optimizer/test-js-optimizer-asm-regs-min.js similarity index 100% rename from tools/test-js-optimizer-asm-regs-min.js rename to tests/optimizer/test-js-optimizer-asm-regs-min.js diff --git a/tools/test-js-optimizer-asm-regs-output.js b/tests/optimizer/test-js-optimizer-asm-regs-output.js similarity index 100% rename from tools/test-js-optimizer-asm-regs-output.js rename to tests/optimizer/test-js-optimizer-asm-regs-output.js diff --git a/tools/test-js-optimizer-asm-regs.js b/tests/optimizer/test-js-optimizer-asm-regs.js similarity index 100% rename from tools/test-js-optimizer-asm-regs.js rename to tests/optimizer/test-js-optimizer-asm-regs.js diff --git a/tools/test-js-optimizer-asm-relocate-output.js b/tests/optimizer/test-js-optimizer-asm-relocate-output.js similarity index 100% rename from tools/test-js-optimizer-asm-relocate-output.js rename to tests/optimizer/test-js-optimizer-asm-relocate-output.js diff --git a/tools/test-js-optimizer-asm-relocate.js b/tests/optimizer/test-js-optimizer-asm-relocate.js similarity index 100% rename from tools/test-js-optimizer-asm-relocate.js rename to tests/optimizer/test-js-optimizer-asm-relocate.js diff --git a/tools/test-js-optimizer-ensureLabelSet-output.js b/tests/optimizer/test-js-optimizer-ensureLabelSet-output.js similarity index 100% rename from tools/test-js-optimizer-ensureLabelSet-output.js rename to tests/optimizer/test-js-optimizer-ensureLabelSet-output.js diff --git a/tools/test-js-optimizer-ensureLabelSet.js b/tests/optimizer/test-js-optimizer-ensureLabelSet.js similarity index 100% rename from tools/test-js-optimizer-ensureLabelSet.js rename to tests/optimizer/test-js-optimizer-ensureLabelSet.js diff --git a/tools/test-js-optimizer-localCSE-output.js b/tests/optimizer/test-js-optimizer-localCSE-output.js similarity index 100% rename from tools/test-js-optimizer-localCSE-output.js rename to tests/optimizer/test-js-optimizer-localCSE-output.js diff --git a/tools/test-js-optimizer-localCSE.js b/tests/optimizer/test-js-optimizer-localCSE.js similarity index 100% rename from tools/test-js-optimizer-localCSE.js rename to tests/optimizer/test-js-optimizer-localCSE.js diff --git a/tools/test-js-optimizer-output.js b/tests/optimizer/test-js-optimizer-output.js similarity index 98% rename from tools/test-js-optimizer-output.js rename to tests/optimizer/test-js-optimizer-output.js index 570fbaa2b0b95..f418360b5c143 100644 --- a/tools/test-js-optimizer-output.js +++ b/tests/optimizer/test-js-optimizer-output.js @@ -300,4 +300,6 @@ function asmy() { print("fleefl"); } } - +function dblMax() { + var x = +1.7976931348623157e+308; +} diff --git a/tools/test-js-optimizer-pointerMask-output.js b/tests/optimizer/test-js-optimizer-pointerMask-output.js similarity index 100% rename from tools/test-js-optimizer-pointerMask-output.js rename to tests/optimizer/test-js-optimizer-pointerMask-output.js diff --git a/tools/test-js-optimizer-pointerMask.js b/tests/optimizer/test-js-optimizer-pointerMask.js similarity index 100% rename from tools/test-js-optimizer-pointerMask.js rename to tests/optimizer/test-js-optimizer-pointerMask.js diff --git a/tools/test-js-optimizer-regs-output.js b/tests/optimizer/test-js-optimizer-regs-output.js similarity index 100% rename from tools/test-js-optimizer-regs-output.js rename to tests/optimizer/test-js-optimizer-regs-output.js diff --git a/tools/test-js-optimizer-regs.js b/tests/optimizer/test-js-optimizer-regs.js similarity index 100% rename from tools/test-js-optimizer-regs.js rename to tests/optimizer/test-js-optimizer-regs.js diff --git a/tools/test-js-optimizer-shiftsAggressive-output.js b/tests/optimizer/test-js-optimizer-shiftsAggressive-output.js similarity index 100% rename from tools/test-js-optimizer-shiftsAggressive-output.js rename to tests/optimizer/test-js-optimizer-shiftsAggressive-output.js diff --git a/tools/test-js-optimizer-shiftsAggressive.js b/tests/optimizer/test-js-optimizer-shiftsAggressive.js similarity index 100% rename from tools/test-js-optimizer-shiftsAggressive.js rename to tests/optimizer/test-js-optimizer-shiftsAggressive.js diff --git a/tools/test-js-optimizer-si-output.js b/tests/optimizer/test-js-optimizer-si-output.js similarity index 100% rename from tools/test-js-optimizer-si-output.js rename to tests/optimizer/test-js-optimizer-si-output.js diff --git a/tools/test-js-optimizer-si.js b/tests/optimizer/test-js-optimizer-si.js similarity index 100% rename from tools/test-js-optimizer-si.js rename to tests/optimizer/test-js-optimizer-si.js diff --git a/tools/test-js-optimizer-t2-output.js b/tests/optimizer/test-js-optimizer-t2-output.js similarity index 100% rename from tools/test-js-optimizer-t2-output.js rename to tests/optimizer/test-js-optimizer-t2-output.js diff --git a/tools/test-js-optimizer-t2.js b/tests/optimizer/test-js-optimizer-t2.js similarity index 100% rename from tools/test-js-optimizer-t2.js rename to tests/optimizer/test-js-optimizer-t2.js diff --git a/tools/test-js-optimizer-t2c-output.js b/tests/optimizer/test-js-optimizer-t2c-output.js similarity index 100% rename from tools/test-js-optimizer-t2c-output.js rename to tests/optimizer/test-js-optimizer-t2c-output.js diff --git a/tools/test-js-optimizer-t2c.js b/tests/optimizer/test-js-optimizer-t2c.js similarity index 100% rename from tools/test-js-optimizer-t2c.js rename to tests/optimizer/test-js-optimizer-t2c.js diff --git a/tools/test-js-optimizer-t3-output.js b/tests/optimizer/test-js-optimizer-t3-output.js similarity index 100% rename from tools/test-js-optimizer-t3-output.js rename to tests/optimizer/test-js-optimizer-t3-output.js diff --git a/tools/test-js-optimizer-t3.js b/tests/optimizer/test-js-optimizer-t3.js similarity index 100% rename from tools/test-js-optimizer-t3.js rename to tests/optimizer/test-js-optimizer-t3.js diff --git a/tools/test-js-optimizer.js b/tests/optimizer/test-js-optimizer.js similarity index 99% rename from tools/test-js-optimizer.js rename to tests/optimizer/test-js-optimizer.js index 3dc99779263e1..e717cc6ba0fbf 100644 --- a/tools/test-js-optimizer.js +++ b/tests/optimizer/test-js-optimizer.js @@ -402,4 +402,7 @@ function asmy() { print('fleefl'); } } +function dblMax() { + var x = +1.7976931348623157E+308; +} // EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps", "tricky", "asmy"] diff --git a/tests/runtime_misuse.cpp b/tests/runtime_misuse.cpp index d74d05ea4db9e..7a264d600dbe6 100644 --- a/tests/runtime_misuse.cpp +++ b/tests/runtime_misuse.cpp @@ -9,7 +9,7 @@ char* EMSCRIPTEN_KEEPALIVE note(int n) { EM_ASM_({ Module.print([$0, $1]) }, n, noted); noted += n; EM_ASM_({ Module.print(['noted is now', $0]) }, noted); - return "silly-string"; + return (char*)"silly-string"; } void free(void*) { // free is valid to call even after the runtime closes, so useful as a hack here for this test diff --git a/tests/runtime_misuse_2.cpp b/tests/runtime_misuse_2.cpp new file mode 100644 index 0000000000000..4da63d6651db2 --- /dev/null +++ b/tests/runtime_misuse_2.cpp @@ -0,0 +1,22 @@ +#include +#include + +extern "C" { + +int noted = 0; + +char* EMSCRIPTEN_KEEPALIVE note(int n) { + EM_ASM_({ Module.print([$0, $1]) }, n, noted); + noted += n; + EM_ASM_({ Module.print(['noted is now', $0]) }, noted); + return (char*)"silly-string"; +} + +void free(void*) { // free is valid to call even after the runtime closes, so useful as a hack here for this test + EM_ASM_({ Module.print(['reporting', $0]) }, noted); + int result = noted; + REPORT_RESULT(); +} + +} + diff --git a/tests/test_browser.py b/tests/test_browser.py index ddd4849ac6dbb..73e4998adfe77 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1850,15 +1850,34 @@ def test_runtime_misuse(self): setTimeout(Module['_free'], 1000); // free is valid to call even after the runtime closes ''' - print 'mem init, so async, call too early' - open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + post_test + post_hook) - self.btest('runtime_misuse.cpp', expected='600', args=['--post-js', 'post.js', '--memory-init-file', '1']) - print 'sync startup, call too late' - open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'Module.postRun.push(function() { ' + post_test + ' });' + post_hook); - self.btest('runtime_misuse.cpp', expected='600', args=['--post-js', 'post.js', '--memory-init-file', '0']) - print 'sync, runtime still alive, so all good' - open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'expected_ok = true; Module.postRun.push(function() { ' + post_test + ' });' + post_hook); - self.btest('runtime_misuse.cpp', expected='606', args=['--post-js', 'post.js', '--memory-init-file', '0', '-s', 'NO_EXIT_RUNTIME=1']) + open('pre_main.js', 'w').write(r''' + Module._main = function(){ + myJSCallback(); + return 0; + }; + ''') + + open('pre_runtime.js', 'w').write(r''' + Module.onRuntimeInitialized = function(){ + myJSCallback(); + }; + ''') + + for filename, extra_args, second_code in [ + ('runtime_misuse.cpp', [], 600), + ('runtime_misuse_2.cpp', ['--pre-js', 'pre_main.js'], 600), + ('runtime_misuse_2.cpp', ['--pre-js', 'pre_runtime.js'], 601) # 601, because no main means we *do* run another call after exit() + ]: + print '\n', filename, extra_args + print 'mem init, so async, call too early' + open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + post_test + post_hook) + self.btest(filename, expected='600', args=['--post-js', 'post.js', '--memory-init-file', '1'] + extra_args) + print 'sync startup, call too late' + open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'Module.postRun.push(function() { ' + post_test + ' });' + post_hook); + self.btest(filename, expected=str(second_code), args=['--post-js', 'post.js', '--memory-init-file', '0'] + extra_args) + print 'sync, runtime still alive, so all good' + open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'expected_ok = true; Module.postRun.push(function() { ' + post_test + ' });' + post_hook); + self.btest(filename, expected='606', args=['--post-js', 'post.js', '--memory-init-file', '0', '-s', 'NO_EXIT_RUNTIME=1'] + extra_args) def test_worker_api(self): Popen([PYTHON, EMCC, path_from_root('tests', 'worker_api_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-s', 'EXPORTED_FUNCTIONS=["_one"]']).communicate() diff --git a/tests/test_core.py b/tests/test_core.py index 1c3a0b74163c0..e53e0d61b093c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1688,6 +1688,34 @@ def test_polymorph(self): self.do_run_from_file(src, output) + def test_complex(self): + self.do_run(r''' +#include +#include + +int main(int argc, char**argv) +{ + float complex z1 = 1.0 + 3.0 * I; + printf("value = real %.2f imag %.2f\n",creal(z1),cimag(z1)); + float abs_value = cabsf(z1); + printf("abs = %.2f\n",abs_value); + float complex z2 = conjf(z1); + printf("value = real %.2f imag %.2f\n",creal(z2),cimag(z2)); + float complex z3 = cexpf(z1); + printf("value = real %.2f imag %.2f\n",creal(z3),cimag(z3)); + float complex z4 = conj(z1); + printf("value = real %.2f imag %.2f\n",creal(z4),cimag(z4)); + float complex z5 = cargf(z1); + printf("value = real %.2f imag %.2f\n",creal(z5),cimag(z5)); + return 0; +} +''', '''value = real 1.00 imag 3.00 +abs = 3.16 +value = real 1.00 imag -3.00 +value = real -2.69 imag 0.38 +value = real 1.00 imag -3.00 +value = real 1.25 imag 0.00''', force_c=True) + def test_segfault(self): if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults') @@ -5270,6 +5298,7 @@ def test_simd6(self): def test_simd7(self): # test_simd7 is to test negative zero handling. + return self.skip('see issue #3103') if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('needs fastcomp') @@ -5783,7 +5812,7 @@ def test_cases(self): def test_fuzz(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('needs ta2') - Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('tests', 'fuzz'), '-Wno-warn-absolute-paths'] + Building.COMPILER_TEST_OPTS += ['-I' + path_from_root('tests', 'fuzz', 'include'), '-Wno-warn-absolute-paths'] def run_all(x): print x diff --git a/tests/test_other.py b/tests/test_other.py index 3b513d30d8b02..8366643dc7a50 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -168,7 +168,8 @@ def test_emcc(self): # emcc -s RELOOP=1 src.cpp ==> should pass -s to emscripten.py. --typed-arrays is a convenient alias for -s USE_TYPED_ARRAYS for params, test, text in [ (['-O2'], lambda generated: 'function intArrayToString' in generated, 'shell has unminified utilities'), - (['-O2', '--closure', '1'], lambda generated: 'function intArrayToString' not in generated, 'closure minifies the shell'), + (['-O2', '--closure', '1'], lambda generated: 'function intArrayToString' not in generated and ';function' in generated, 'closure minifies the shell, removes whitespace'), + (['-O2', '--closure', '1', '-g1'], lambda generated: 'function intArrayToString' not in generated and ';function' not in generated, 'closure minifies the shell, -g1 makes it keep whitespace'), (['-O2'], lambda generated: 'var b=0' in generated and not 'function _main' in generated, 'registerize/minify is run by default in -O2'), (['-O2', '--minify', '0'], lambda generated: 'var b = 0' in generated and not 'function _main' in generated, 'minify is cancelled, but not registerize'), (['-O2', '--js-opts', '0'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'js opts are cancelled'), @@ -1881,57 +1882,57 @@ def test_fix_closure(self): def test_js_optimizer(self): for input, expected, passes in [ - (path_from_root('tools', 'test-js-optimizer.js'), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-output.js')).read(), ['hoistMultiples', 'removeAssignsToUndefined', 'simplifyExpressions']), - (path_from_root('tools', 'test-js-optimizer-t2c.js'), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-t2c.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-t2c-output.js')).read(), ['simplifyExpressions', 'optimizeShiftsConservative']), - (path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-t2.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-t2-output.js')).read(), ['simplifyExpressions', 'optimizeShiftsAggressive']), - (path_from_root('tools', 'test-js-optimizer-t3.js'), open(path_from_root('tools', 'test-js-optimizer-t3-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-t3.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-t3-output.js')).read(), ['optimizeShiftsAggressive']), - (path_from_root('tools', 'test-js-optimizer-si.js'), open(path_from_root('tools', 'test-js-optimizer-si-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-si.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-si-output.js')).read(), ['simplifyIfs']), - (path_from_root('tools', 'test-js-optimizer-regs.js'), open(path_from_root('tools', 'test-js-optimizer-regs-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-regs.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-regs-output.js')).read(), ['registerize']), - (path_from_root('tools', 'eliminator', 'eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read(), + (path_from_root('tests', 'optimizer', 'eliminator-test.js'), open(path_from_root('tests', 'optimizer', 'eliminator-test-output.js')).read(), ['eliminate']), - (path_from_root('tools', 'eliminator', 'safe-eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'safe-eliminator-test-output.js')).read(), + (path_from_root('tests', 'optimizer', 'safe-eliminator-test.js'), open(path_from_root('tests', 'optimizer', 'safe-eliminator-test-output.js')).read(), ['eliminateMemSafe']), - (path_from_root('tools', 'eliminator', 'asm-eliminator-test.js'), open(path_from_root('tools', 'eliminator', 'asm-eliminator-test-output.js')).read(), + (path_from_root('tests', 'optimizer', 'asm-eliminator-test.js'), open(path_from_root('tests', 'optimizer', 'asm-eliminator-test-output.js')).read(), ['asm', 'eliminate']), - (path_from_root('tools', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-output.js')).read(), ['asm', 'registerize']), - (path_from_root('tools', 'test-js-optimizer-asm-regs-harder.js'), [open(path_from_root('tools', 'test-js-optimizer-asm-regs-harder-output.js')).read(), open(path_from_root('tools', 'test-js-optimizer-asm-regs-harder-output2.js')).read(), open(path_from_root('tools', 'test-js-optimizer-asm-regs-harder-output3.js')).read()], + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-harder.js'), [open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-harder-output.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-harder-output2.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-harder-output3.js')).read()], ['asm', 'registerizeHarder']), - (path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-min-output.js')).read(), ['asm', 'registerize', 'minifyLocals']), - (path_from_root('tools', 'test-js-optimizer-asm-pre.js'), [open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output2.js')).read()], + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre.js'), [open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-output.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-output2.js')).read()], ['asm', 'simplifyExpressions']), - (path_from_root('tools', 'test-js-optimizer-asm-pre-f32.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output-f32.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-f32.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-output-f32.js')).read(), ['asm', 'asmPreciseF32', 'simplifyExpressions', 'optimizeFrounds']), - (path_from_root('tools', 'test-js-optimizer-asm-pre-f32.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output-f32-nosimp.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-f32.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-output-f32-nosimp.js')).read(), ['asm', 'asmPreciseF32', 'optimizeFrounds']), - (path_from_root('tools', 'test-js-optimizer-asm-last.js'), [open(path_from_root('tools', 'test-js-optimizer-asm-lastOpts-output.js')).read(), open(path_from_root('tools', 'test-js-optimizer-asm-lastOpts-output2.js')).read()], + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-last.js'), [open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-lastOpts-output.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-lastOpts-output2.js')).read()], ['asm', 'asmLastOpts']), - (path_from_root('tools', 'test-js-optimizer-asm-last.js'), [open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(), open(path_from_root('tools', 'test-js-optimizer-asm-last-output2.js')).read()], + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-last.js'), [open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-last-output.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-last-output2.js')).read()], ['asm', 'asmLastOpts', 'last']), - (path_from_root('tools', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tools', 'test-js-optimizer-asm-relocate-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-relocate-output.js')).read(), ['asm', 'relocate']), - (path_from_root('tools', 'test-js-optimizer-asm-outline1.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline1-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline1.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline1-output.js')).read(), ['asm', 'outline']), - (path_from_root('tools', 'test-js-optimizer-asm-outline2.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline2-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline2.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline2-output.js')).read(), ['asm', 'outline']), - (path_from_root('tools', 'test-js-optimizer-asm-outline3.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline3-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline3.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline3-output.js')).read(), ['asm', 'outline']), - (path_from_root('tools', 'test-js-optimizer-asm-minlast.js'), open(path_from_root('tools', 'test-js-optimizer-asm-minlast-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-minlast.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-minlast-output.js')).read(), ['asm', 'minifyWhitespace', 'asmLastOpts', 'last']), - (path_from_root('tools', 'test-js-optimizer-shiftsAggressive.js'), open(path_from_root('tools', 'test-js-optimizer-shiftsAggressive-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-shiftsAggressive.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-shiftsAggressive-output.js')).read(), ['asm', 'aggressiveVariableElimination']), - (path_from_root('tools', 'test-js-optimizer-pointerMask.js'), open(path_from_root('tools', 'test-js-optimizer-pointerMask-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-pointerMask.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-pointerMask-output.js')).read(), ['pointerMasking']), - (path_from_root('tools', 'test-js-optimizer-localCSE.js'), open(path_from_root('tools', 'test-js-optimizer-localCSE-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-localCSE.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-localCSE-output.js')).read(), ['asm', 'localCSE']), - (path_from_root('tools', 'test-js-optimizer-ensureLabelSet.js'), open(path_from_root('tools', 'test-js-optimizer-ensureLabelSet-output.js')).read(), + (path_from_root('tests', 'optimizer', 'test-js-optimizer-ensureLabelSet.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-ensureLabelSet-output.js')).read(), ['asm', 'ensureLabelSet']), ]: print input, passes @@ -1972,7 +1973,7 @@ def swap(func, stuff): self.assertIdentical(expected, js.replace('\r\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n')) check_js(output, expected) - if js_optimizer.use_native(passes): + if js_optimizer.use_native(passes) and js_optimizer.get_native_optimizer(): # test calling native def check_json(): Popen(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), output_temp, 'receiveJSON'], stdin=PIPE, stdout=open(output_temp + '.js', 'w')).communicate() diff --git a/tests/test_sanity.py b/tests/test_sanity.py index 485a4a4af8b4e..cff398388171f 100644 --- a/tests/test_sanity.py +++ b/tests/test_sanity.py @@ -568,54 +568,52 @@ def test_emcc_ports(self): # using ports - INCLUDING_MESSAGE = 'including port' RETRIEVING_MESSAGE = 'retrieving port' BUILDING_MESSAGE = 'building port' from tools import system_libs PORTS_DIR = system_libs.Ports.get_dir() - for i in [0, 1]: - print i - if i == 0: - try_delete(PORTS_DIR) - else: - self.do([PYTHON, EMCC, '--clear-ports']) - assert not os.path.exists(PORTS_DIR) - if i == 0: Cache.erase() # test with cache erased and without - - # Building a file that doesn't need ports should not trigger anything - output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp')]) - assert INCLUDING_MESSAGE not in output - assert RETRIEVING_MESSAGE not in output - assert BUILDING_MESSAGE not in output - assert not os.path.exists(PORTS_DIR) - - # Building a file that need a port does trigger stuff - output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2']) - assert INCLUDING_MESSAGE in output, output - assert RETRIEVING_MESSAGE in output, output - assert BUILDING_MESSAGE in output, output - assert os.path.exists(PORTS_DIR) - - def second_use(): - # Using it again avoids retrieve and build - output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2']) - assert INCLUDING_MESSAGE in output, output - assert RETRIEVING_MESSAGE not in output, output - assert BUILDING_MESSAGE not in output, output - - second_use() - - # if the version isn't sufficient, we retrieve and rebuild - open(os.path.join(PORTS_DIR, 'sdl2', 'SDL2-master', 'version.txt'), 'w').write('1') # current is >= 2, so this is too old - output = self.do([EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2']) - assert INCLUDING_MESSAGE in output, output - assert RETRIEVING_MESSAGE in output, output - assert BUILDING_MESSAGE in output, output - assert os.path.exists(PORTS_DIR) - - second_use() + for compiler in [EMCC, EMXX]: + print compiler + + for i in [0, 1]: + print i + if i == 0: + try_delete(PORTS_DIR) + else: + self.do([PYTHON, compiler, '--clear-ports']) + assert not os.path.exists(PORTS_DIR) + if i == 0: Cache.erase() # test with cache erased and without + + # Building a file that doesn't need ports should not trigger anything + output = self.do([compiler, path_from_root('tests', 'hello_world_sdl.cpp')]) + assert RETRIEVING_MESSAGE not in output + assert BUILDING_MESSAGE not in output + assert not os.path.exists(PORTS_DIR) + + # Building a file that need a port does trigger stuff + output = self.do([compiler, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2']) + assert RETRIEVING_MESSAGE in output, output + assert BUILDING_MESSAGE in output, output + assert os.path.exists(PORTS_DIR) + + def second_use(): + # Using it again avoids retrieve and build + output = self.do([compiler, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2']) + assert RETRIEVING_MESSAGE not in output, output + assert BUILDING_MESSAGE not in output, output + + second_use() + + # if the version isn't sufficient, we retrieve and rebuild + open(os.path.join(PORTS_DIR, 'sdl2', 'SDL2-master', 'version.txt'), 'w').write('1') # current is >= 2, so this is too old + output = self.do([compiler, path_from_root('tests', 'hello_world_sdl.cpp'), '-s', 'USE_SDL=2']) + assert RETRIEVING_MESSAGE in output, output + assert BUILDING_MESSAGE in output, output + assert os.path.exists(PORTS_DIR) + + second_use() def test_native_optimizer(self): restore() @@ -631,12 +629,12 @@ def test(): # basic usage or lack of usage for native in [None, 0, 1]: - print native + print 'phase 1, part', native Cache.erase() try: if native is not None: os.environ['EMCC_NATIVE_OPTIMIZER'] = str(native) output = build() - assert ('js optimizer using native' in output) == (not not (native or native is None)) + assert ('js optimizer using native' in output) == (not not (native or native is None)), output test() if native or native is None: # None means use the default, which is to use the native optimizer assert 'building native optimizer' in output @@ -650,40 +648,83 @@ def test(): # force a build failure, see we fall back to non-native - Cache.erase() - try: - # break it - f = path_from_root('tools', 'optimizer', 'optimizer.cpp') - src = open(f).read() - bad = src.replace('main', '!waka waka<') - assert bad != src - open(f, 'w').write(bad) - # first try - output = build() - assert 'failed to build native optimizer' in output, output - assert 'js optimizer using native' not in output - test() # still works, without native optimizer - # second try, see previous failure - output = build() - assert 'failed to build native optimizer' not in output - assert 'seeing that optimizer could not be built' in output - test() # still works, without native optimizer - # clear cache, try again - Cache.erase() - output = build() - assert 'failed to build native optimizer' in output - test() # still works, without native optimizer - finally: - open(f, 'w').write(src) + for native in [1, 'g']: + print 'phase 2, part', native + Cache.erase() + os.environ['EMCC_NATIVE_OPTIMIZER'] = str(native) + + try: + # break it + f = path_from_root('tools', 'optimizer', 'optimizer.cpp') + src = open(f).read() + bad = src.replace('main', '!waka waka<') + assert bad != src + open(f, 'w').write(bad) + # first try + output = build() + assert 'failed to build native optimizer' in output, output + if native == 1: + assert 'to see compiler errors, build with EMCC_NATIVE_OPTIMIZER=g' in output + assert 'waka waka' not in output + else: + assert 'output from attempt' in output, output + assert 'waka waka' in output, output + assert 'js optimizer using native' not in output + test() # still works, without native optimizer + # second try, see previous failure + output = build() + assert 'failed to build native optimizer' not in output + assert 'seeing that optimizer could not be built' in output + test() # still works, without native optimizer + # clear cache, try again + Cache.erase() + output = build() + assert 'failed to build native optimizer' in output + test() # still works, without native optimizer + finally: + open(f, 'w').write(src) - Cache.erase() + Cache.erase() - # now it should work again - output = build() - assert 'js optimizer using native' in output - test() # still works + # now it should work again + output = build() + assert 'js optimizer using native' in output + test() # still works + + finally: + del os.environ['EMCC_NATIVE_OPTIMIZER'] finally: del os.environ['EMCC_DEBUG'] + def test_embuilder(self): + restore() + + for command, expected, success, result_libs in [ + ([PYTHON, 'embuilder.py'], ['Emscripten System Builder Tool', 'build libc', 'native_optimizer'], True, []), + ([PYTHON, 'embuilder.py', 'build', 'waka'], 'ERROR', False, []), + ([PYTHON, 'embuilder.py', 'build', 'libc'], ['building and verifying libc', 'success'], True, ['libc.bc']), + ([PYTHON, 'embuilder.py', 'build', 'libcxx'], ['success'], True, ['libcxx.bc']), + ([PYTHON, 'embuilder.py', 'build', 'libcxxabi'], ['success'], True, ['libcxxabi.bc']), + ([PYTHON, 'embuilder.py', 'build', 'gl'], ['success'], True, ['gl.bc']), + ([PYTHON, 'embuilder.py', 'build', 'struct_info'], ['success'], True, ['struct_info.compiled.json']), + ([PYTHON, 'embuilder.py', 'build', 'native_optimizer'], ['success'], True, ['optimizer.exe']), + ([PYTHON, 'embuilder.py', 'build', 'zlib'], ['building and verifying zlib', 'success'], True, [os.path.join('ports-builds', 'zlib', 'libz.a')]), + ([PYTHON, 'embuilder.py', 'build', 'sdl2'], ['success'], True, [os.path.join('ports-builds', 'sdl2', 'libsdl2.bc')]), + ([PYTHON, 'embuilder.py', 'build', 'sdl2-image'], ['success'], True, [os.path.join('ports-builds', 'sdl2-image', 'libsdl2_image.bc')]), + ]: + print command + Cache.erase() + + proc = Popen(command, stdout=PIPE, stderr=STDOUT) + out, err = proc.communicate() + assert (proc.returncode == 0) == success, out + if type(expected) == str: expected = [expected] + for ex in expected: + print ' seek', ex + assert ex in out, out + for lib in result_libs: + print ' verify', lib + assert os.path.exists(Cache.get_path(lib)) + diff --git a/tests/test_sse1.cpp b/tests/test_sse1.cpp index 2fd2e937bd93f..87680279bc0d7 100644 --- a/tests/test_sse1.cpp +++ b/tests/test_sse1.cpp @@ -270,10 +270,12 @@ int main() // exception when one of the input operands is either a QNaN or a SNaN. #ifndef __EMSCRIPTEN__ // TODO: Fix ucomi support in SSE to treat NaNs properly. Assert(_mm_ucomieq_ss(a, b) == 0); Assert(_mm_ucomieq_ss(a, a) == 1); Assert(_mm_ucomieq_ss(a, nan1) == 1); +#endif Assert(_mm_ucomige_ss(a, b) == 0); Assert(_mm_ucomige_ss(a, a) == 1); Assert(_mm_ucomige_ss(a, nan1) == 0); Assert(_mm_ucomigt_ss(b, a) == 1); Assert(_mm_ucomigt_ss(a, a) == 0); Assert(_mm_ucomigt_ss(a, nan1) == 0); Assert(_mm_ucomile_ss(b, a) == 0); Assert(_mm_ucomile_ss(a, a) == 1); Assert(_mm_ucomile_ss(a, nan1) == 1); Assert(_mm_ucomilt_ss(a, b) == 1); Assert(_mm_ucomilt_ss(a, a) == 0); Assert(_mm_ucomilt_ss(a, nan1) == 1); +#ifndef __EMSCRIPTEN__ // TODO: Fix ucomi support in SSE to treat NaNs properly. Assert(_mm_ucomineq_ss(a, b) == 1); Assert(_mm_ucomineq_ss(a, a) == 0); Assert(_mm_ucomineq_ss(a, nan1) == 0); #endif diff --git a/tools/emterpretify.py b/tools/emterpretify.py index 636bad48912aa..f010ba0ea6f9d 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -772,6 +772,8 @@ def post_process_code(code): asm.staticbump += 1 stack_start = len(mem_init) asm.staticbump += EMT_STACK_MAX + while asm.staticbump % 8 != 0: + asm.staticbump += 1 open(out_mem_file, 'wb').write(''.join(map(chr, mem_init))) diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index 4f86b90e339da..125b9733f426e 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -9,7 +9,7 @@ def path_from_root(*pathelems): return os.path.join(__rootpath__, *pathelems) -NATIVE_PASSES = set(['asm', 'asmPreciseF32', 'receiveJSON', 'emitJSON', 'eliminate', 'eliminateMemSafe', 'simplifyExpressions', 'simplifyIfs', 'optimizeFrounds', 'registerize', 'registerizeHarder', 'minifyNames', 'minifyLocals', 'minifyWhitespace', 'cleanup', 'asmLastOpts', 'last', 'noop']) +NATIVE_PASSES = set(['asm', 'asmPreciseF32', 'receiveJSON', 'emitJSON', 'eliminate', 'eliminateMemSafe', 'simplifyExpressions', 'simplifyIfs', 'optimizeFrounds', 'registerize', 'registerizeHarder', 'minifyNames', 'minifyLocals', 'minifyWhitespace', 'cleanup', 'asmLastOpts', 'last', 'noop', 'closure']) JS_OPTIMIZER = path_from_root('tools', 'js-optimizer.js') @@ -30,13 +30,20 @@ def path_from_root(*pathelems): def get_native_optimizer(): if os.environ.get('EMCC_FAST_COMPILER') == '0': return None # need fastcomp for native optimizer + # Allow users to override the location of the optimizer executable by setting an environment variable EMSCRIPTEN_NATIVE_OPTIMIZER=/path/to/optimizer(.exe) + if os.environ.get('EMSCRIPTEN_NATIVE_OPTIMIZER') and len(os.environ.get('EMSCRIPTEN_NATIVE_OPTIMIZER')) > 0: return os.environ.get('EMSCRIPTEN_NATIVE_OPTIMIZER') + # Also, allow specifying the location of the optimizer in .emscripten configuration file under EMSCRIPTEN_NATIVE_OPTIMIZER='/path/to/optimizer' + if hasattr(shared, 'EMSCRIPTEN_NATIVE_OPTIMIZER') and len(shared.EMSCRIPTEN_NATIVE_OPTIMIZER) > 0: return shared.EMSCRIPTEN_NATIVE_OPTIMIZER + FAIL_MARKER = shared.Cache.get_path('optimizer.building_failed') if os.path.exists(FAIL_MARKER): shared.logging.debug('seeing that optimizer could not be built (run emcc --clear-cache or erase "optimizer.building_failed" in cache dir to retry)') return None - def get_optimizer(name, args): + def get_optimizer(name, args, handle_build_errors=None): class NativeOptimizerCreationException(Exception): pass + outs = [] + errs = [] try: def create_optimizer(): shared.logging.debug('building native optimizer: ' + name) @@ -45,11 +52,13 @@ def create_optimizer(): for compiler in [shared.CLANG, 'g++', 'clang++']: # try our clang first, otherwise hope for a system compiler in the path shared.logging.debug(' using ' + compiler) try: - subprocess.Popen([compiler, - shared.path_from_root('tools', 'optimizer', 'parser.cpp'), - shared.path_from_root('tools', 'optimizer', 'simple_ast.cpp'), - shared.path_from_root('tools', 'optimizer', 'optimizer.cpp'), - '-O3', '-std=c++11', '-fno-exceptions', '-fno-rtti', '-o', output] + args).communicate() + out, err = subprocess.Popen([compiler, + shared.path_from_root('tools', 'optimizer', 'parser.cpp'), + shared.path_from_root('tools', 'optimizer', 'simple_ast.cpp'), + shared.path_from_root('tools', 'optimizer', 'optimizer.cpp'), + '-O3', '-std=c++11', '-fno-exceptions', '-fno-rtti', '-o', output] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + outs.append(out) + errs.append(err) except OSError: if compiler == shared.CLANG: raise # otherwise, OSError is likely due to g++ or clang++ not being in the path if os.path.exists(output): return output @@ -57,15 +66,22 @@ def create_optimizer(): return shared.Cache.get(name, create_optimizer, extension='exe') except NativeOptimizerCreationException, e: shared.logging.debug('failed to build native optimizer') + handle_build_errors(outs, errs) open(FAIL_MARKER, 'w').write(':(') return None + def ignore_build_errors(outs, errs): + shared.logging.debug('to see compiler errors, build with EMCC_NATIVE_OPTIMIZER=g') + def show_build_errors(outs, errs): + for i in range(len(outs)): + shared.logging.debug('output from attempt ' + str(i) + ': ' + outs[i] + '\n===========\n' + errs[i]) + if NATIVE_OPTIMIZER == '1': - return get_optimizer('optimizer.exe', []) + return get_optimizer('optimizer.exe', [], ignore_build_errors) elif NATIVE_OPTIMIZER == '2': - return get_optimizer('optimizer.2.exe', ['-DNDEBUG']) + return get_optimizer('optimizer.2.exe', ['-DNDEBUG'], ignore_build_errors) elif NATIVE_OPTIMIZER == 'g': - return get_optimizer('optimizer.g.exe', ['-O0', '-g', '-fno-omit-frame-pointer']) + return get_optimizer('optimizer.g.exe', ['-O0', '-g', '-fno-omit-frame-pointer'], show_build_errors) # Check if we should run a pass or set of passes natively. if a set of passes, they must all be valid to run in the native optimizer at once. def use_native(x, source_map=False): @@ -150,12 +166,16 @@ def run_on_chunk(command): while os.path.exists(saved): saved = 'input' + str(int(saved.replace('input', '').replace('.txt', ''))+1) + '.txt' print >> sys.stderr, 'running js optimizer command', ' '.join(map(lambda c: c if c != filename else saved, command)) shutil.copyfile(filename, os.path.join('/tmp/emscripten_temp', saved)) + verbose_level = int(os.getenv('EM_BUILD_VERBOSE')) if os.getenv('EM_BUILD_VERBOSE') != None else 0 + if verbose_level >= 3: print >> sys.stderr, str(command) proc = subprocess.Popen(command, stdout=subprocess.PIPE) output = proc.communicate()[0] assert proc.returncode == 0, 'Error in optimizer: ' + output assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in optimizer: ' + output filename = temp_files.get(os.path.basename(filename) + '.jo.js').name - f = open(filename, 'w') + # Important to write out in binary mode, because the data we are writing contains Windows line endings '\r\n' because it was PIPED from console. + # Otherwise writing \r\n to ascii mode file will result in Windows amplifying \n to \r\n, generating bad \r\r\n line endings. + f = open(filename, 'wb') f.write(output) f.close() if DEBUG and not shared.WINDOWS: print >> sys.stderr, '.' # Skip debug progress indicator on Windows, since it doesn't buffer well with multiple threads printing to console. diff --git a/tools/optimizer/CMakeLists.txt b/tools/optimizer/CMakeLists.txt new file mode 100644 index 0000000000000..f7f2d08220b80 --- /dev/null +++ b/tools/optimizer/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 2.8) + +project(asmjs_optimizer) + +file(GLOB sourceFiles *.cpp) +file(GLOB headerFiles *.h) + +if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES ".*(gcc|clang|emcc).*" OR CMAKE_C_COMPILER_ID MATCHES ".*(GCC|Clang|emcc).*") + set(IS_GCC_LIKE TRUE) +else() + set(IS_GCC_LIKE FALSE) +endif() + +if (IS_GCC_LIKE) + set(cFlags "-std=c++11 -fno-exceptions -fno-rtti") +endif() + +if (MSVC) + set(cFlags "${cFlags} -D_CRT_SECURE_NO_WARNINGS=1") +endif() + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${cFlags}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${cFlags}") + +add_executable(optimizer ${sourceFiles} ${headerFiles}) diff --git a/tools/optimizer/istring.h b/tools/optimizer/istring.h index bc525b50667ed..c78d1f68ac7ac 100644 --- a/tools/optimizer/istring.h +++ b/tools/optimizer/istring.h @@ -78,11 +78,11 @@ struct IString { return strcmp(str ? str : "", other.str ? other.str : "") < 0; } - char operator[](int x) { + char operator[](int x) const { return str[x]; } - bool operator!() { // no string, or empty string + bool operator!() const { // no string, or empty string return !str || str[0] == 0; } @@ -121,7 +121,7 @@ class IStringSet : public std::unordered_set { IStringSet() {} IStringSet(const char *init) { // comma-delimited list int size = strlen(init); - char *curr = (char*)malloc(size+1); // leaked! + char *curr = new char[size+1]; // leaked! strcpy(curr, init); while (1) { char *end = strchr(curr, ' '); diff --git a/tools/optimizer/optimizer.cpp b/tools/optimizer/optimizer.cpp index f8d41459ffcf3..ef006795c1883 100644 --- a/tools/optimizer/optimizer.cpp +++ b/tools/optimizer/optimizer.cpp @@ -20,7 +20,7 @@ Ref doc, extraInfo; template int indexOf(T list, V value) { - for (int i = 0; i < list.size(); i++) { + for (size_t i = 0; i < list.size(); i++) { if (list[i] == value) return i; } return -1; @@ -108,6 +108,8 @@ Ref make3(IString type, Ref a, Ref b, Ref c); struct AsmData { struct Local { + Local() {} + Local(AsmType type, bool param) : type(type), param(param) {} AsmType type; bool param; // false if a var }; @@ -144,7 +146,7 @@ struct AsmData { // process initial params Ref stats = func[3]; - int i = 0; + size_t i = 0; while (i < stats->size()) { Ref node = stats[i]; if (node[0] != STAT || node[1][0] != ASSIGN || node[1][2][0] != NAME) break; @@ -154,7 +156,7 @@ struct AsmData { if (index < 0) break; // not an assign into a parameter, but a global IString& str = name->getIString(); if (locals.count(str) > 0) break; // already done that param, must be starting function body - locals[str] = { detectType(node[3]), true }; + locals[str] = Local(detectType(node[3]), true); params.push_back(str); stats[i] = makeEmpty(); i++; @@ -163,12 +165,12 @@ struct AsmData { while (i < stats->size()) { Ref node = stats[i]; if (node[0] != VAR) break; - for (int j = 0; j < node[1]->size(); j++) { + for (size_t j = 0; j < node[1]->size(); j++) { Ref v = node[1][j]; IString& name = v[0]->getIString(); Ref value = v[1]; if (locals.count(name) == 0) { - locals[name] = { detectType(value, nullptr, true), false }; + locals[name] = Local(detectType(value, nullptr, true), false); vars.push_back(name); v->setSize(1); // make an un-assigning var } else { @@ -202,7 +204,7 @@ struct AsmData { void denormalize() { Ref stats = func[3]; // Remove var definitions, if any - for (int i = 0; i < stats->size(); i++) { + for (size_t i = 0; i < stats->size(); i++) { if (stats[i][0] == VAR) { stats[i] = makeEmpty(); } else { @@ -215,13 +217,13 @@ struct AsmData { varDefs->push_back(makeAsmVarDef(v, locals[v].type)); } // each param needs a line; reuse emptyNodes as much as we can - int numParams = params.size(); - int emptyNodes = 0; + size_t numParams = params.size(); + size_t emptyNodes = 0; while (emptyNodes < stats->size()) { if (!isEmpty(stats[emptyNodes])) break; emptyNodes++; } - int neededEmptyNodes = numParams + (varDefs->size() ? 1 : 0); // params plus one big var if there are vars + size_t neededEmptyNodes = numParams + (varDefs->size() ? 1 : 0); // params plus one big var if there are vars if (neededEmptyNodes > emptyNodes) { stats->insert(0, neededEmptyNodes - emptyNodes); } else if (neededEmptyNodes < emptyNodes) { @@ -262,17 +264,17 @@ struct AsmData { } void addParam(IString name, AsmType type) { - locals[name] = { type, true }; + locals[name] = Local(type, true); params.push_back(name); } void addVar(IString name, AsmType type) { - locals[name] = { type, false }; + locals[name] = Local(type, false); vars.push_back(name); } void deleteVar(IString name) { locals.erase(name); - for (int i = 0; i < vars.size(); i++) { + for (size_t i = 0; i < vars.size(); i++) { if (vars[i] == name) { vars.erase(vars.begin() + i); break; @@ -639,7 +641,7 @@ void safeCopy(Ref target, Ref source) { // safely copy source onto target, even void clearEmptyNodes(Ref arr) { int skip = 0; - for (int i = 0; i < arr->size(); i++) { + for (size_t i = 0; i < arr->size(); i++) { if (skip) { arr[i-skip] = arr[i]; } @@ -651,7 +653,7 @@ void clearEmptyNodes(Ref arr) { } void clearUselessNodes(Ref arr) { int skip = 0; - for (int i = 0; i < arr->size(); i++) { + for (size_t i = 0; i < arr->size(); i++) { Ref curr = arr[i]; if (skip) { arr[i-skip] = curr; @@ -711,7 +713,7 @@ Ref unVarify(Ref vars) { // transform var x=1, y=2 etc. into (x=1, y=2), i.e., t } else { ret->push_back(makeArray()); Ref curr = ret[1]; - for (int i = 0; i < vars->size()-1; i++) { + for (size_t i = 0; i+1 < vars->size(); i++) { curr->push_back(makeString(SEQ)); curr->push_back(make3(ASSIGN, &(arena.alloc())->setBool(true), makeName(vars[i][0]->getIString()), vars[i][1])); if (i != vars->size()-2) { @@ -931,7 +933,7 @@ void eliminate(Ref ast, bool memSafe=false) { Ref type = node[0]; if (type == VAR) { Ref node1 = node[1]; - for (int i = 0; i < node1->size(); i++) { + for (size_t i = 0; i < node1->size(); i++) { Ref node1i = node1[i]; IString name = node1i[0]->getIString(); Ref value; @@ -1093,7 +1095,7 @@ void eliminate(Ref ast, bool memSafe=false) { temp.push_back(name); \ } \ } \ - for (int i = 0; i < temp.size(); i++) { \ + for (size_t i = 0; i < temp.size(); i++) { \ tracked.erase(temp[i]); \ } \ }; @@ -1110,7 +1112,7 @@ void eliminate(Ref ast, bool memSafe=false) { temp.push_back(name); } } - for (int i = 0; i < temp.size(); i++) { + for (size_t i = 0; i < temp.size(); i++) { tracked.erase(temp[i]); } }; @@ -1181,7 +1183,7 @@ void eliminate(Ref ast, bool memSafe=false) { } } else if (type == VAR) { Ref vars = node[1]; - for (int i = 0; i < vars->size(); i++) { + for (size_t i = 0; i < vars->size(); i++) { IString name = vars[i][0]->getIString(); Ref value; if (vars[i]->size() > 1 && !!(value = vars[i][1])) { @@ -1231,7 +1233,7 @@ void eliminate(Ref ast, bool memSafe=false) { } else if (type == CALL) { traverseInOrder(node[1], false, true); Ref args = node[2]; - for (int i = 0; i < args->size(); i++) { + for (size_t i = 0; i < args->size(); i++) { traverseInOrder(args[i], false, false); } if (callHasSideEffects(node)) { @@ -1262,7 +1264,7 @@ void eliminate(Ref ast, bool memSafe=false) { } else if (type == BLOCK) { Ref stats = getStatements(node); if (!!stats) { - for (int i = 0; i < stats->size(); i++) { + for (size_t i = 0; i < stats->size(); i++) { traverseInOrder(stats[i], false, false); } } @@ -1293,11 +1295,11 @@ void eliminate(Ref ast, bool memSafe=false) { traverseInOrder(node[1], false, false); Tracked originalTracked = tracked; Ref cases = node[2]; - for (int i = 0; i < cases->size(); i++) { + for (size_t i = 0; i < cases->size(); i++) { Ref c = cases[i]; assert(c[0]->isNull() || c[0][0] == NUM || (c[0][0] == UNARY_PREFIX && c[0][2][0] == NUM)); Ref stats = c[1]; - for (int j = 0; j < stats->size(); j++) { + for (size_t j = 0; j < stats->size(); j++) { traverseInOrder(stats[j], false, false); } // We cannot track from one switch case into another, undo all new trackings TODO: general framework here, use in if-else as well @@ -1356,10 +1358,10 @@ void eliminate(Ref ast, bool memSafe=false) { } if (!stats) return; tracked.clear(); - for (int i = 0; i < stats->size(); i++) { + for (size_t i = 0; i < stats->size(); i++) { Ref node = deStat(stats[i]); Ref type = node[0]; - if (type == RETURN && i < stats->size()-1) { + if (type == RETURN && i+1 < stats->size()) { stats->setSize(i+1); // remove any code after a return } // Check for things that affect elimination @@ -1421,7 +1423,7 @@ void eliminate(Ref ast, bool memSafe=false) { Ref assigns = ifFalse[1]; clearEmptyNodes(assigns); std::vector loopers, helpers; - for (int i = 0; i < assigns->size(); i++) { + for (size_t i = 0; i < assigns->size(); i++) { if (assigns[i][0] == STAT && assigns[i][1][0] == ASSIGN) { Ref assign = assigns[i][1]; if (assign[1]->isBool(true) && assign[2][0] == NAME && assign[3][0] == NAME) { @@ -1436,7 +1438,7 @@ void eliminate(Ref ast, bool memSafe=false) { } } // remove loop vars that are used in the rest of the else - for (int i = 0; i < assigns->size(); i++) { + for (size_t i = 0; i < assigns->size(); i++) { if (assigns[i][0] == STAT && assigns[i][1][0] == ASSIGN) { Ref assign = assigns[i][1]; if (!(assign[1]->isBool(true) && assign[2][0] == NAME && assign[3][0] == NAME) || indexOf(loopers, assign[2][1]->getIString()) < 0) { @@ -1466,12 +1468,12 @@ void eliminate(Ref ast, bool memSafe=false) { } }); if (loopers.size() == 0) return; - for (int l = 0; l < loopers.size(); l++) { + for (size_t l = 0; l < loopers.size(); l++) { IString looper = loopers[l]; IString helper = helpers[l]; // the remaining issue is whether loopers are used after the assignment to helper and before the last line (where we assign to it) int found = -1; - for (int i = stats->size()-2; i >= 0; i--) { + for (int i = (int)stats->size()-2; i >= 0; i--) { Ref curr = stats[i]; if (curr[0] == STAT && curr[1][0] == ASSIGN) { Ref currAssign = curr[1]; @@ -1494,8 +1496,8 @@ void eliminate(Ref ast, bool memSafe=false) { int firstLooperUsage = -1; int lastLooperUsage = -1; int firstHelperUsage = -1; - for (int i = found+1; i < stats->size(); i++) { - Ref curr = i < stats->size()-1 ? stats[i] : last[1]; // on the last line, just look in the condition + for (int i = found+1; i < (int)stats->size(); i++) { + Ref curr = i < (int)stats->size()-1 ? stats[i] : last[1]; // on the last line, just look in the condition traversePre(curr, [&](Ref node) { if (node[0] == NAME) { if (node[1] == looper) { @@ -1509,7 +1511,7 @@ void eliminate(Ref ast, bool memSafe=false) { } if (firstLooperUsage >= 0) { // the looper is used, we cannot simply merge the two variables - if ((firstHelperUsage < 0 || firstHelperUsage > lastLooperUsage) && lastLooperUsage+1 < stats->size() && triviallySafeToMove(stats[found], asmData) && + if ((firstHelperUsage < 0 || firstHelperUsage > lastLooperUsage) && lastLooperUsage+1 < (int)stats->size() && triviallySafeToMove(stats[found], asmData) && seenUses[helper] == namings[helper]) { // the helper is not used, or it is used after the last use of the looper, so they do not overlap, // and the last looper usage is not on the last line (where we could not append after it), and the @@ -1523,7 +1525,7 @@ void eliminate(Ref ast, bool memSafe=false) { IString temp(strdupe((std::string(looper.c_str()) + "$looptemp").c_str())); assert(!asmData.isLocal(temp)); for (int i = firstLooperUsage; i <= lastLooperUsage; i++) { - Ref curr = i < stats->size()-1 ? stats[i] : last[1]; // on the last line, just look in the condition + Ref curr = i < (int)stats->size()-1 ? stats[i] : last[1]; // on the last line, just look in the condition std::function looperToLooptemp = [&](Ref node) { if (node[0] == NAME) { @@ -1544,13 +1546,13 @@ void eliminate(Ref ast, bool memSafe=false) { } } } - for (int l = 0; l < helpers.size(); l++) { - for (int k = 0; k < helpers.size(); k++) { + for (size_t l = 0; l < helpers.size(); l++) { + for (size_t k = 0; k < helpers.size(); k++) { if (l != k && helpers[l] == helpers[k]) return; // it is complicated to handle a shared helper, abort } } // hurrah! this is safe to do - for (int l = 0; l < loopers.size(); l++) { + for (size_t l = 0; l < loopers.size(); l++) { IString looper = loopers[l]; IString helper = helpers[l]; varsToRemove[helper] = 2; @@ -1571,7 +1573,7 @@ void eliminate(Ref ast, bool memSafe=false) { last->pop_back(); } else { Ref elseStats = getStatements(last[3]); - for (int i = 0; i < elseStats->size(); i++) { + for (size_t i = 0; i < elseStats->size(); i++) { Ref stat = deStat(elseStats[i]); if (stat[0] == ASSIGN && stat[2][0] == NAME) { if (indexOf(loopers, stat[2][1]->getIString()) >= 0) { @@ -1699,7 +1701,8 @@ void simplifyExpressions(Ref ast) { } }; - traversePrePost(ast, process, [&stack](Ref Node) { + traversePrePost(ast, process, [&stack](Ref node) { + assert(!stack.empty()); stack.pop_back(); }); } @@ -1957,7 +1960,7 @@ void simplifyExpressions(Ref ast) { switch(asmData.getType(v.c_str())) { case ASM_INT: correctType = preciseF32 ? ASM_FLOAT : ASM_DOUBLE; break; case ASM_FLOAT: case ASM_DOUBLE: correctType = ASM_INT; break; - default: {} // pass + default: assert(0); } asmData.setType(v.c_str(), correctType); } @@ -1981,9 +1984,9 @@ void simplifyExpressions(Ref ast) { // expensive | cheap can be turned into cheap ? 1 : expensive, // so that we can avoid the expensive computation, if it has no side effects. auto conditionalize = [&emitsBoolean](Ref ast) { - const int MIN_COST = 7; traversePre(ast, [&emitsBoolean](Ref node) { - if (node[0] == BINARY && (node[1] == OR || node[1] == AND) && node[3][0] != NUM && node[2][0] != NUM) { + const int MIN_COST = 7; + if (node[0] == BINARY && (node[1] == OR || node[1] == AND) && node[3][0] != NUM && node[2][0] != NUM) { // logical operator on two non-numerical values Ref left = node[2]; Ref right = node[3]; @@ -2089,7 +2092,7 @@ void simplifyIfs(Ref ast) { if (stats->size() > 1) { // try to commaify - turn everything between the ifs into a comma operator inside the second if bool ok = true; - for (int i = 0; i < stats->size()-1; i++) { + for (size_t i = 0; i+1 < stats->size(); i++) { Ref curr = deStat(stats[i]); if (!commable(curr)) ok = false; } @@ -2123,7 +2126,7 @@ void simplifyIfs(Ref ast) { traversePre(func, [&labelAssigns, &abort](Ref node) { if (node[0] == ASSIGN && node[2][0] == NAME && node[2][1] == LABEL) { if (node[3][0] == NUM) { - int value = node[3][1]->getNumber(); + int value = node[3][1]->getInteger(); labelAssigns[value] = labelAssigns[value] + 1; } else { // label is assigned a dynamic value (like from indirectbr), we cannot do anything @@ -2139,7 +2142,7 @@ void simplifyIfs(Ref ast) { if (node[0] == BINARY && node[1] == EQ && node[2][0] == BINARY && node[2][1] == OR && node[2][2][0] == NAME && node[2][2][1] == LABEL) { if (node[3][0] == NUM) { - int value = node[3][1]->getNumber(); + int value = node[3][1]->getInteger(); labelChecks[value] = labelChecks[value] + 1; } else { // label is checked vs a dynamic value (like from indirectbr), we cannot do anything @@ -2154,7 +2157,7 @@ void simplifyIfs(Ref ast) { if (node[0] == WHILE) inLoop++; Ref stats = getStatements(node); if (!!stats && stats->size() > 0) { - for (int i = 0; i < stats->size()-1; i++) { + for (int i = 0; i < (int)stats->size()-1; i++) { Ref pre = stats[i]; Ref post = stats[i+1]; if (pre[0] == IF && pre->size() > 3 && !!pre[3] && post[0] == IF && (post->size() <= 3 || !post[3])) { @@ -2164,7 +2167,7 @@ void simplifyIfs(Ref ast) { postCond[2][2][0] == NAME && postCond[2][2][1] == LABEL && postCond[2][3][0] == NUM && postCond[2][3][1]->getNumber() == 0 && postCond[3][0] == NUM) { - double postValue = postCond[3][1]->getNumber(); + int postValue = postCond[3][1]->getInteger(); Ref preElse = pre[3]; if (labelAssigns[postValue] == 1 && labelChecks[postValue] == 1 && preElse[0] == BLOCK && preElse->size() >= 2 && preElse[1]->size() == 1) { Ref preStat = preElse[1][0]; @@ -2223,7 +2226,7 @@ void optimizeFrounds(Ref ast) { const char* getRegPrefix(AsmType type) { switch (type) { - case ASM_INT: return"i"; break; + case ASM_INT: return "i"; break; case ASM_DOUBLE: return "d"; break; case ASM_FLOAT: return "f"; break; case ASM_FLOAT32X4: return "F4"; break; @@ -2236,7 +2239,7 @@ const char* getRegPrefix(AsmType type) { IString getRegName(AsmType type, int num) { const char* str = getRegPrefix(type); - int size = strlen(str) + int(ceil(log10(num))) + 3; + const int size = 256; char temp[size]; int written = sprintf(temp, "%s%d", str, num); assert(written < size); @@ -2277,7 +2280,6 @@ void registerize(Ref ast) { removeAllUselessSubNodes(fun); // vacuum? StringTypeMap regTypes; // reg name -> type auto getNewRegName = [&](int num, IString name) { - const char *str; AsmType type = asmData.getType(name); IString ret = getRegName(type, num); assert(!allVars.has(ret) || asmData.isLocal(ret)); // register must not shadow non-local name @@ -2349,7 +2351,7 @@ void registerize(Ref ast) { } else if (type == SWITCH) { traversePrePostConditional(node[1], possibilifier, [](Ref node){}); Ref cases = node[2]; - for (int i = 0; i < cases->size(); i++) { + for (size_t i = 0; i < cases->size(); i++) { level++; traversePrePostConditional(cases[i][1], possibilifier, [](Ref node){}); purgeLevel(); @@ -2419,7 +2421,7 @@ void registerize(Ref ast) { } else { // when the relevant loop is exited, we will free the register int relevantLoop = optimizables.has(name) ? (optimizableLoops[name] ? optimizableLoops[name] : 1) : 1; - if (loopRegs.size() <= relevantLoop+1) loopRegs.resize(relevantLoop+1); + if ((int)loopRegs.size() <= relevantLoop+1) loopRegs.resize(relevantLoop+1); loopRegs[relevantLoop].push_back(reg); } } @@ -2445,7 +2447,7 @@ void registerize(Ref ast) { Ref type = node[0]; if (LOOP.has(type)) { // Free registers that were locked to this loop - if (loopRegs.size() > loops && loopRegs[loops].size() > 0) { + if ((int)loopRegs.size() > loops && loopRegs[loops].size() > 0) { for (auto loopReg : loopRegs[loops]) { freeRegsClasses[regTypes[loopReg]].push_back(loopReg); } @@ -2596,7 +2598,7 @@ void registerizeHarder(Ref ast) { std::function joinJunction; - auto markJunction = [&](int id=-1) { + auto markJunction = [&](int id) { // Mark current traversal location as a junction. // This makes a new basic block exiting at this position. if (id < 0) { @@ -2688,7 +2690,7 @@ void registerizeHarder(Ref ast) { IString name = node[1]->getIString(); if (asmData.isLocal(name)) { nextBasicBlock->nodes.push_back(node); - nextBasicBlock->isexpr.push_back(isInExpr); + nextBasicBlock->isexpr.push_back(isInExpr != 0); if (nextBasicBlock->kill.count(name) == 0) { nextBasicBlock->use[name] = 1; } @@ -2703,7 +2705,7 @@ void registerizeHarder(Ref ast) { IString name = node[2][1]->getIString(); if (asmData.isLocal(name)) { nextBasicBlock->nodes.push_back(node); - nextBasicBlock->isexpr.push_back(isInExpr); + nextBasicBlock->isexpr.push_back(isInExpr != 0); nextBasicBlock->kill.insert(name); } }; @@ -2721,7 +2723,7 @@ void registerizeHarder(Ref ast) { auto addBlockLabel = [&](Ref node) { assert(nextBasicBlock->nodes.size() == 0); // 'cant add label to an in-progress basic block') if (node[0] == NUM) { - nextBasicBlock->labels.insert(node[1]->getNumber()); + nextBasicBlock->labels.insert(node[1]->getInteger()); } }; @@ -2751,11 +2753,11 @@ void registerizeHarder(Ref ast) { // Traverse each node type according to its particular control-flow semantics. // TODO: switchify this if (type == DEFUN) { - int jEntry = markJunction(); + int jEntry = markJunction(-1); assert(jEntry == ENTRY_JUNCTION); int jExit = addJunction(); assert(jExit == EXIT_JUNCTION); - for (int i = 0; i < node[3]->size(); i++) { + for (size_t i = 0; i < node[3]->size(); i++) { buildFlowGraph(node[3][i]); } joinJunction(jExit, false); @@ -2763,7 +2765,7 @@ void registerizeHarder(Ref ast) { isInExpr++; buildFlowGraph(node[1]); isInExpr--; - int jEnter = markJunction(); + int jEnter = markJunction(-1); int jExit = addJunction(); if (!!node[2]) { // Detect and mark "if (label == N) { }". @@ -2795,7 +2797,7 @@ void registerizeHarder(Ref ast) { } } else { buildFlowGraph(node[1]); - int jEnter = markJunction(); + int jEnter = markJunction(-1); int jExit = addJunction(); if (!!node[2]) { buildFlowGraph(node[2]); @@ -2812,7 +2814,7 @@ void registerizeHarder(Ref ast) { // Special-case "while (1) {}" to use fewer junctions, // since emscripten generates a lot of these. if (isTrueNode(node[1])) { - int jLoop = markJunction(); + int jLoop = markJunction(-1); int jExit = addJunction(); pushActiveLabels(jLoop, jExit); buildFlowGraph(node[2]); @@ -2820,7 +2822,7 @@ void registerizeHarder(Ref ast) { joinJunction(jLoop, false); setJunction(jExit, false); } else { - int jCond = markJunction(); + int jCond = markJunction(-1); int jLoop = addJunction(); int jExit = addJunction(); isInExpr++; @@ -2845,7 +2847,7 @@ void registerizeHarder(Ref ast) { popActiveLabels(); joinJunction(jExit, false); } else if (isTrueNode(node[1])) { - int jLoop = markJunction(); + int jLoop = markJunction(-1); int jExit = addJunction(); pushActiveLabels(jLoop, jExit); buildFlowGraph(node[2]); @@ -2853,7 +2855,7 @@ void registerizeHarder(Ref ast) { joinJunction(jLoop, false); setJunction(jExit, false); } else { - int jLoop = markJunction(); + int jLoop = markJunction(-1); int jCond = addJunction(); int jCondExit = addJunction(); int jExit = addJunction(); @@ -2901,11 +2903,11 @@ void registerizeHarder(Ref ast) { buildFlowGraph(node[1]); isInExpr--; Ref condition = lookThroughCasts(node[1]); - int jCheckExit = markJunction(); + int jCheckExit = markJunction(-1); int jExit = addJunction(); pushActiveLabels(-1, jExit); bool hasDefault = false; - for (int i = 0; i < node[2]->size(); i++) { + for (size_t i = 0; i < node[2]->size(); i++) { setJunction(jCheckExit, false); // All case clauses are either 'default' or a numeric literal. if (!node[2][i][0]) { @@ -2916,7 +2918,7 @@ void registerizeHarder(Ref ast) { addBlockLabel(lookThroughCasts(node[2][i][0])); } } - for (int j = 0; j < node[2][i][1]->size(); j++) { + for (size_t j = 0; j < node[2][i][1]->size(); j++) { buildFlowGraph(node[2][i][1][j]); } // Control flow will never actually reach the end of the case body. @@ -2958,7 +2960,7 @@ void registerizeHarder(Ref ast) { addUseNode(node); } else if (type == BLOCK || type == TOPLEVEL) { if (!!node[1]) { - for (int i = 0; i < node[1]->size(); i++) { + for (size_t i = 0; i < node[1]->size(); i++) { buildFlowGraph(node[1][i]); } } @@ -2977,7 +2979,7 @@ void registerizeHarder(Ref ast) { isInExpr++; buildFlowGraph(node[1]); if (!!node[2]) { - for (int i = 0; i < node[2]->size(); i++) { + for (size_t i = 0; i < node[2]->size(); i++) { buildFlowGraph(node[2][i]); } } @@ -3020,7 +3022,7 @@ void registerizeHarder(Ref ast) { typedef std::pair Jump; std::vector labelledJumps; - for (int i = 0; i < blocks.size(); i++) { + for (size_t i = 0; i < blocks.size(); i++) { Block* block = blocks[i]; // Does it have any labels as preconditions to its entry? for (auto labelVal : block->labels) { @@ -3050,7 +3052,7 @@ void registerizeHarder(Ref ast) { // If label is assigned a non-zero value elsewhere in the block // then all bets are off. This can happen e.g. due to outlining // saving/restoring label to the stack. - for (int j = 0; j < block->nodes.size() - 1; j++) { + for (size_t j = 0; j < block->nodes.size() - 1; j++) { if (block->nodes[j][0] == ASSIGN && block->nodes[j][2][1] == LABEL) { if (block->nodes[j][3][0] != NUM || block->nodes[j][3][1]->getNumber() != 0) { labelledBlocks.clear(); @@ -3077,10 +3079,10 @@ void registerizeHarder(Ref ast) { block->nodes.insert(block->nodes.begin(), makeName(LABEL)); block->isexpr.insert(block->isexpr.begin(), 1); } - for (int i = 0; i < labelledJumps.size(); i++) { + for (size_t i = 0; i < labelledJumps.size(); i++) { auto labelVal = labelledJumps[i].first; auto block = labelledJumps[i].second; - Block* targetBlock = labelledBlocks[labelVal->getNumber()]; + Block* targetBlock = labelledBlocks[labelVal->getInteger()]; if (targetBlock) { // Redirect its exit to entry of the target block. junctions[block->exit].inblocks.erase(block->id); @@ -3302,7 +3304,7 @@ void registerizeHarder(Ref ast) { junctionVariables[name].conf.reserve(asmData.locals.size()); }; - for (int i = 0; i < junctions.size(); i++) { + for (size_t i = 0; i < junctions.size(); i++) { Junction& junc = junctions[i]; for (auto name : junc.live) { if (junctionVariables.count(name) == 0) initializeJunctionVariable(name); @@ -3378,7 +3380,7 @@ void registerizeHarder(Ref ast) { } return true; }; - for (int i = 0; i < sortedJunctionVariables.size(); i++) { + for (size_t i = 0; i < sortedJunctionVariables.size(); i++) { IString name = sortedJunctionVariables[i]; // It may already be assigned due to linked-variable propagation. if (junctionVariables[name].reg > 0) { @@ -3404,7 +3406,7 @@ void registerizeHarder(Ref ast) { // that all inter-block variables are in a good state thanks to // junction variable consistency. - for (int i = 0; i < blocks.size(); i++) { + for (size_t i = 0; i < blocks.size(); i++) { Block* block = blocks[i]; if (block->nodes.size() == 0) continue; Junction& jEnter = junctions[block->entry]; @@ -3450,7 +3452,7 @@ void registerizeHarder(Ref ast) { } std::vector> freeRegsByType; freeRegsByType.resize(freeRegsByTypePre.size()); - for (int j = 0; j < freeRegsByTypePre.size(); j++) { + for (size_t j = 0; j < freeRegsByTypePre.size(); j++) { for (auto pair : freeRegsByTypePre[j]) { freeRegsByType[j].push_back(pair.first); } @@ -3516,7 +3518,7 @@ void registerizeHarder(Ref ast) { } } // If we managed to create any "x=x" assignments, remove them. - for (int j = 0; j < maybeRemoveNodes.size(); j++) { + for (size_t j = 0; j < maybeRemoveNodes.size(); j++) { Ref node = maybeRemoveNodes[j].second; if (node[2][1] == node[3][1]) { if (block->isexpr[maybeRemoveNodes[j].first]) { @@ -3532,7 +3534,7 @@ void registerizeHarder(Ref ast) { StringSet paramRegs; if (!!fun[2]) { - for (int i = 0; i < fun[2]->size(); i++) { + for (size_t i = 0; i < fun[2]->size(); i++) { auto& allRegs = allRegsByType[asmData.getType(fun[2][i]->getIString())]; fun[2][i]->setString(allRegs[junctionVariables[fun[2][i]->getIString()].reg]); paramRegs.insert(fun[2][i]->getIString()); @@ -3546,7 +3548,7 @@ void registerizeHarder(Ref ast) { asmData.params.clear(); asmData.vars.clear(); for (int i = 1; i < nextReg; i++) { - for (int type = 0; type < allRegsByType.size(); type++) { + for (size_t type = 0; type < allRegsByType.size(); type++) { if (allRegsByType[type].count(i) > 0) { IString reg = allRegsByType[type][i]; if (!paramRegs.has(reg)) { @@ -3577,11 +3579,11 @@ void ensureMinifiedNames(int n) { // make sure the nth index in minifiedNames ex static int VALID_MIN_INITS_LEN = strlen(VALID_MIN_INITS); static int VALID_MIN_LATERS_LEN = strlen(VALID_MIN_LATERS); - while (minifiedNames.size() < n+1) { + while ((int)minifiedNames.size() < n+1) { // generate the current name std::string name; name += VALID_MIN_INITS[minifiedState[0]]; - for (int i = 1; i < minifiedState.size(); i++) { + for (size_t i = 1; i < minifiedState.size(); i++) { name += VALID_MIN_LATERS[minifiedState[i]]; } IString str(strdupe(name.c_str())); // leaked! @@ -3666,7 +3668,7 @@ void minifyLocals(Ref ast) { assert(!!fun[1]); } if (!!fun[2]) { - for (int i = 0; i < fun[2]->size(); i++) { + for (size_t i = 0; i < fun[2]->size(); i++) { IString minified = getNextMinifiedName(); newNames[fun[2][i]->getIString()] = minified; fun[2][i]->setString(minified); @@ -3685,7 +3687,7 @@ void minifyLocals(Ref ast) { node[1]->setString(minified); } } else if (type == VAR) { - for (int i = 0; i < node[1]->size(); i++) { + for (size_t i = 0; i < node[1]->size(); i++) { Ref defn = node[1][i]; IString name = defn[0]->getIString(); if (!(newNames.has(name))) { @@ -3765,7 +3767,7 @@ void asmLastOpts(Ref ast) { int me = parent->indexOf(node); if (me < 0) return; // not always directly on a stats, could be in a label for example parent->insert(me+1, lastStats->size()-1); - for (int i = 0; i < lastStats->size()-1; i++) { + for (size_t i = 0; i+1 < lastStats->size(); i++) { parent[me+1+i] = lastStats[i]; } } @@ -3837,9 +3839,12 @@ int main(int argc, char **argv) { char *input = new char[size+1]; rewind(f); int num = fread(input, 1, size, f); - assert(num == size); + // On Windows, ftell() gives the byte position (\r\n counts as two bytes), but when + // reading, fread() returns the number of characters read (\r\n is read as one char \n, and counted as one), + // so return value of fread can be less than size reported by ftell, and that is normal. + assert((num > 0 || size == 0) && num <= size); fclose(f); - input[size] = 0; + input[num] = 0; char *extraInfoStart = strstr(input, "// EXTRA_INFO:"); if (extraInfoStart) { @@ -3891,7 +3896,6 @@ int main(int argc, char **argv) { jser.printAst(); std::cout << jser.buffer << "\n"; } - return 0; } diff --git a/tools/optimizer/parser.cpp b/tools/optimizer/parser.cpp index a650f4bd7bb1e..915ece8363084 100644 --- a/tools/optimizer/parser.cpp +++ b/tools/optimizer/parser.cpp @@ -85,8 +85,7 @@ IString TOPLEVEL("toplevel"), THROW("throw"), SET("="); -IStringSet keywords("var function if else do while for break continue return switch case default throw try catch finally true false null new"), - allOperators(". ! ~ - + * / % + - << >> >>> < <= > >= == != & ^ | ? : = ,"); +IStringSet keywords("var function if else do while for break continue return switch case default throw try catch finally true false null new"); const char *OPERATOR_INITS = "+-*/%<>&^|~=!,?:.", *SEPARATORS = "([;{}"; diff --git a/tools/optimizer/parser.h b/tools/optimizer/parser.h index 4ed8523c104d5..9edbe92256c59 100644 --- a/tools/optimizer/parser.h +++ b/tools/optimizer/parser.h @@ -94,7 +94,7 @@ extern IString TOPLEVEL, THROW, SET; -extern IStringSet keywords, allOperators; +extern IStringSet keywords; extern const char *OPERATOR_INITS, *SEPARATORS; @@ -156,25 +156,38 @@ class Parser { static bool hasChar(const char* list, char x) { while (*list) if (*list++ == x) return true; return false; } + static bool is32Bit(double x) { + return x == (int)x || x == (unsigned int)x; + } + // An atomic fragment of something. Stops at a natural boundary. enum FragType { KEYWORD = 0, OPERATOR = 1, IDENT = 2, STRING = 3, // without quotes - NUMBER = 4, - SEPARATOR = 5 + INT = 4, + DOUBLE = 5, + SEPARATOR = 6 }; struct Frag { +#ifndef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf union { +#endif IString str; double num; +#ifndef _MSC_VER }; +#endif int size; FragType type; - Frag(char* src) { + bool isNumber() const { + return type == INT || type == DOUBLE; + } + + explicit Frag(char* src) { assert(!isSpace(*src)); char *start = src; if (isIdentInit(*src)) { @@ -199,9 +212,24 @@ class Parser { src = end+1; type = STRING; } else if (isDigit(*src) || (src[0] == '.' && isDigit(src[1]))) { - num = strtod(start, &src); + if (src[0] == '0' && (src[1] == 'x' || src[1] == 'X')) { + // Explicitly parse hex numbers of form "0x...", because strtod + // supports hex number strings only in C++11, and Visual Studio 2013 does + // not yet support that functionality. + src += 2; + num = 0; + while (1) { + if (*src >= '0' && *src <= '9') { num *= 16; num += *src - '0'; } + else if (*src >= 'a' && *src <= 'f') { num *= 16; num += *src - 'a' + 10; } + else if (*src >= 'A' && *src <= 'F') { num *= 16; num += *src - 'F' + 10; } + else break; + src++; + } + } else { + num = strtod(start, &src); + } + type = is32Bit(num) ? INT : DOUBLE; assert(src > start); - type = NUMBER; } else if (hasChar(OPERATOR_INITS, *src)) { switch (*src) { case '!': str = src[1] == '=' ? NE : L_NOT; break; @@ -259,7 +287,8 @@ class Parser { } case IDENT: case STRING: - case NUMBER: { + case INT: + case DOUBLE: { src = skipSpace(src); if (frag.type == IDENT) return parseAfterIdent(frag, src, seps); else return parseExpression(parseFrag(frag), src, seps); @@ -282,7 +311,8 @@ class Parser { switch (frag.type) { case IDENT: return Builder::makeName(frag.str); case STRING: return Builder::makeString(frag.str); - case NUMBER: return Builder::makeNumber(frag.num); + case INT: return Builder::makeInt(uint32_t(frag.num)); + case DOUBLE: return Builder::makeDouble(frag.num); default: assert(0); } return nullptr; @@ -438,7 +468,7 @@ class Parser { src = skipSpace(src); NodeRef arg; Frag value(src); - if (value.type == NUMBER) { + if (value.isNumber()) { arg = parseFrag(value); src += value.size; } else { @@ -447,7 +477,7 @@ class Parser { src += value.size; src = skipSpace(src); Frag value2(src); - assert(value2.type == NUMBER); + assert(value2.isNumber()); arg = Builder::makePrefix(MINUS, parseFrag(value2)); src += value2.size; } @@ -598,10 +628,14 @@ class Parser { struct ExpressionElement { bool isNode; - union { +#ifndef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf + union { +#endif NodeRef node; IString op; +#ifndef _MSC_VER }; +#endif ExpressionElement(NodeRef n) : isNode(true), node(n) {} ExpressionElement(IString o) : isNode(false), op(o) {} @@ -672,18 +706,18 @@ class Parser { // we are the toplevel. sort it all out // collapse right to left, highest priority first //dumpParts(parts, 0); - for (auto ops : operatorClasses) { + for (auto& ops : operatorClasses) { if (ops.rtl) { // right to left for (int i = parts.size()-1; i >= 0; i--) { if (parts[i].isNode) continue; IString op = parts[i].getOp(); if (!ops.ops.has(op)) continue; - if (ops.type == OperatorClass::Binary && i > 0 && i < parts.size()-1) { + if (ops.type == OperatorClass::Binary && i > 0 && i < (int)parts.size()-1) { parts[i] = Builder::makeBinary(parts[i-1].getNode(), op, parts[i+1].getNode()); parts.erase(parts.begin() + i + 1); parts.erase(parts.begin() + i - 1); - } else if (ops.type == OperatorClass::Prefix && i < parts.size()-1) { + } else if (ops.type == OperatorClass::Prefix && i < (int)parts.size()-1) { if (i > 0 && parts[i-1].isNode) continue; // cannot apply prefix operator if it would join two nodes parts[i] = Builder::makePrefix(op, parts[i+1].getNode()); parts.erase(parts.begin() + i + 1); @@ -692,7 +726,7 @@ class Parser { // ^ //dumpParts(parts, i); if (op != COLON) continue; - assert(i < parts.size()-1 && i >= 3); + assert(i < (int)parts.size()-1 && i >= 3); if (parts[i-2].getOp() != QUESTION) continue; // e.g. x ? y ? 1 : 0 : 2 parts[i-3] = Builder::makeConditional(parts[i-3].getNode(), parts[i-1].getNode(), parts[i+1].getNode()); parts.erase(parts.begin() + i - 2, parts.begin() + i + 2); @@ -701,16 +735,16 @@ class Parser { } } else { // left to right - for (int i = 0; i < parts.size(); i++) { + for (int i = 0; i < (int)parts.size(); i++) { if (parts[i].isNode) continue; IString op = parts[i].getOp(); if (!ops.ops.has(op)) continue; - if (ops.type == OperatorClass::Binary && i > 0 && i < parts.size()-1) { + if (ops.type == OperatorClass::Binary && i > 0 && i < (int)parts.size()-1) { parts[i] = Builder::makeBinary(parts[i-1].getNode(), op, parts[i+1].getNode()); parts.erase(parts.begin() + i + 1); parts.erase(parts.begin() + i - 1); i--; - } else if (ops.type == OperatorClass::Prefix && i < parts.size()-1) { + } else if (ops.type == OperatorClass::Prefix && i < (int)parts.size()-1) { if (i > 0 && parts[i-1].isNode) continue; // cannot apply prefix operator if it would join two nodes parts[i] = Builder::makePrefix(op, parts[i+1].getNode()); parts.erase(parts.begin() + i + 1); @@ -807,7 +841,7 @@ class Parser { for (int i = 0; i < (curr - allSource); i++) printf(" "); printf("^\n=============\n"); */ - printf("%s:\n==========\n", where); + fprintf(stderr, "%s:\n==========\n", where); int newlinesLeft = 2; int charsLeft = 200; while (*curr) { @@ -817,9 +851,9 @@ class Parser { } charsLeft--; if (charsLeft == 0) break; - printf("%c", *curr++); + fprintf(stderr, "%c", *curr++); } - printf("\n\n"); + fprintf(stderr, "\n\n"); } public: diff --git a/tools/optimizer/simple_ast.cpp b/tools/optimizer/simple_ast.cpp index 8aba0b17594c2..c2170bf3cd9b5 100644 --- a/tools/optimizer/simple_ast.cpp +++ b/tools/optimizer/simple_ast.cpp @@ -16,7 +16,7 @@ bool Ref::operator==(const char *str) { } bool Ref::operator!=(const char *str) { - return get()->isString() ? strcmp(get()->str.str, str) : true; + return get()->isString() ? !!strcmp(get()->str.str, str) : true; } bool Ref::operator==(const IString &str) { @@ -61,6 +61,8 @@ void dump(const char *str, Ref node, bool pretty) { // Traversals struct TraverseInfo { + TraverseInfo() {} + TraverseInfo(Ref node, int index) : node(node), index(index) {} Ref node; int index; }; @@ -110,7 +112,7 @@ struct StackedStack { // a stack, on the stack } }; -#define visitable(node) (node->isArray() and node->size() > 0) +#define visitable(node) (node->isArray() && node->size() > 0) #define TRAV_STACK 40 @@ -119,15 +121,15 @@ void traversePre(Ref node, std::function visit) { if (!visitable(node)) return; visit(node); StackedStack stack; - stack.push_back({ node, 0 }); + stack.push_back(TraverseInfo(node, 0)); while (stack.size() > 0) { TraverseInfo& top = stack.back(); - if (top.index < top.node->size()) { + if (top.index < (int)top.node->size()) { Ref sub = top.node[top.index]; top.index++; if (visitable(sub)) { visit(sub); - stack.push_back({ sub, 0 }); + stack.push_back(TraverseInfo(sub, 0)); } } else { stack.pop_back(); @@ -140,22 +142,21 @@ void traversePrePost(Ref node, std::function visitPre, std::function if (!visitable(node)) return; visitPre(node); StackedStack stack; - stack.push_back({ node, 0 }); + stack.push_back(TraverseInfo(node, 0)); while (stack.size() > 0) { TraverseInfo& top = stack.back(); - if (top.index < top.node->size()) { + if (top.index < (int)top.node->size()) { Ref sub = top.node[top.index]; top.index++; if (visitable(sub)) { visitPre(sub); - stack.push_back({ sub, 0 }); + stack.push_back(TraverseInfo(sub, 0)); } } else { visitPost(top.node); stack.pop_back(); } } - visitPost(node); } // Traverse, calling visitPre before the children and visitPost after. If pre returns false, do not traverse children @@ -163,15 +164,15 @@ void traversePrePostConditional(Ref node, std::function visitPre, st if (!visitable(node)) return; if (!visitPre(node)) return; StackedStack stack; - stack.push_back({ node, 0 }); + stack.push_back(TraverseInfo(node, 0)); while (stack.size() > 0) { TraverseInfo& top = stack.back(); - if (top.index < top.node->size()) { + if (top.index < (int)top.node->size()) { Ref sub = top.node[top.index]; top.index++; if (visitable(sub)) { if (visitPre(sub)) { - stack.push_back({ sub, 0 }); + stack.push_back(TraverseInfo(sub, 0)); } } } else { @@ -179,7 +180,6 @@ void traversePrePostConditional(Ref node, std::function visitPre, st stack.pop_back(); } } - visitPost(node); } // Traverses all the top-level functions in the document @@ -187,7 +187,7 @@ void traverseFunctions(Ref ast, std::function visit) { if (!ast || ast->size() == 0) return; if (ast[0] == TOPLEVEL) { Ref stats = ast[1]; - for (int i = 0; i < stats->size(); i++) { + for (size_t i = 0; i < stats->size(); i++) { Ref curr = stats[i]; if (curr[0] == DEFUN) visit(curr); } diff --git a/tools/optimizer/simple_ast.h b/tools/optimizer/simple_ast.h index b757c1a267d9e..452b5680e9fbd 100644 --- a/tools/optimizer/simple_ast.h +++ b/tools/optimizer/simple_ast.h @@ -10,18 +10,21 @@ #include #include #include +#include #include #include #include "parser.h" +#include "snprintf.h" + #define err(str) fprintf(stderr, str "\n"); #define errv(str, ...) fprintf(stderr, str "\n", __VA_ARGS__); #define printErr err using namespace cashew; -class Ref; +struct Ref; struct Value; void dump(const char *str, Ref node, bool pretty=false); @@ -79,8 +82,13 @@ struct Value { typedef std::vector ArrayStorage; typedef std::unordered_map ObjectStorage; +#ifdef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf + IString str; +#endif union { // TODO: optimize +#ifndef _MSC_VER IString str; +#endif double num; ArrayStorage *arr; bool boo; @@ -191,6 +199,13 @@ struct Value { return boo; } + int getInteger() { // convenience function to get a known integer + assert(fmod(getNumber(), 1) == 0); + int ret = int(getNumber()); + assert(double(ret) == getNumber()); // no loss in conversion + return ret; + } + Value& operator=(const Value& other) { free(); switch (other.type) { @@ -557,7 +572,13 @@ struct JSPrinter { if (!buffer) { buffer = (char*)malloc(size); } else { - buffer = (char*)realloc(buffer, size); + char *buf = (char*)realloc(buffer, size); + if (!buf) { + free(buffer); + printf("Out of memory allocating %d bytes for output buffer!", size); + assert(0); + } + buffer = buf; } } } @@ -695,7 +716,7 @@ struct JSPrinter { void printStats(Ref stats) { bool first = true; - for (int i = 0; i < stats->size(); i++) { + for (size_t i = 0; i < stats->size(); i++) { Ref curr = stats[i]; if (!isNothing(curr)) { if (first) first = false; @@ -728,12 +749,16 @@ struct JSPrinter { emit(node[1]->getCString()); emit('('); Ref args = node[2]; - for (int i = 0; i < args->size(); i++) { + for (size_t i = 0; i < args->size(); i++) { if (i > 0) (pretty ? emit(", ") : emit(',')); emit(args[i]->getCString()); } emit(')'); space(); + if (node->size() == 3 || node[3]->size() == 0) { + emit("{}"); + return; + } emit('{'); indent++; newline(); @@ -804,8 +829,15 @@ struct JSPrinter { assert(d >= 0); unsigned long long uu = (unsigned long long)d; if (uu == d) { - snprintf(buffer, BUFFERSIZE-1, (e && !finalize) ? "0x%llx" : "%llu", uu); - sscanf(buffer, "%lf", &temp); + bool asHex = e && !finalize; + snprintf(buffer, BUFFERSIZE-1, asHex ? "0x%llx" : "%llu", uu); + if (asHex) { + unsigned long long tempULL; + sscanf(buffer, "%llx", &tempULL); + temp = (double)tempULL; + } else { + sscanf(buffer, "%lf", &temp); + } } else { // too large for a machine integer, just use floats snprintf(buffer, BUFFERSIZE-1, e ? "%e" : "%.0f", d); // even on integers, e with a dot is useful, e.g. 1.2e+200 @@ -843,7 +875,8 @@ struct JSPrinter { char *end = strchr(buffer, 0); end--; char *test = end; - while (*test == '0' && test > buffer) test--; + // remove zeros, and also doubles can use at most 24 digits, we can truncate any extras even if not zero + while ((*test == '0' || test - buffer > 24) && test > buffer) test--; int num = end - test; if (num >= 3) { test++; @@ -851,11 +884,16 @@ struct JSPrinter { if (num < 10) { test[1] = '0' + num; test[2] = 0; - } else { - assert(num < 100); + } else if (num < 100) { test[1] = '0' + (num / 10); test[2] = '0' + (num % 10); test[3] = 0; + } else { + assert(num < 1000); + test[1] = '0' + (num / 100); + test[2] = '0' + (num % 100) / 10; + test[3] = '0' + (num % 10); + test[4] = 0; } } } @@ -990,7 +1028,7 @@ struct JSPrinter { printChild(node[1], node, 0); emit('('); Ref args = node[2]; - for (int i = 0; i < args->size(); i++) { + for (size_t i = 0; i < args->size(); i++) { if (i > 0) (pretty ? emit(", ") : emit(',')); printChild(args[i], node, 0); } @@ -1020,7 +1058,7 @@ struct JSPrinter { emit('{'); newline(); Ref cases = node[2]; - for (int i = 0; i < cases->size(); i++) { + for (size_t i = 0; i < cases->size(); i++) { Ref c = cases[i]; if (!c[0]) { emit("default:"); @@ -1054,7 +1092,7 @@ struct JSPrinter { void printVar(Ref node) { emit("var "); Ref args = node[1]; - for (int i = 0; i < args->size(); i++) { + for (size_t i = 0; i < args->size(); i++) { if (i > 0) (pretty ? emit(", ") : emit(',')); emit(args[i][0]->getCString()); if (args[i]->size() > 1) { @@ -1162,7 +1200,7 @@ struct JSPrinter { void printArray(Ref node) { emit('['); Ref args = node[1]; - for (int i = 0; i < args->size(); i++) { + for (size_t i = 0; i < args->size(); i++) { if (i > 0) (pretty ? emit(", ") : emit(',')); print(args[i]); } @@ -1174,7 +1212,7 @@ struct JSPrinter { indent++; newline(); Ref args = node[1]; - for (int i = 0; i < args->size(); i++) { + for (size_t i = 0; i < args->size(); i++) { if (i > 0) { pretty ? emit(", ") : emit(','); newline(); @@ -1257,10 +1295,13 @@ class ValueBuilder { } } - static Ref makeNumber(double num) { + static Ref makeDouble(double num) { return &makeRawArray()->push_back(makeRawString(NUM)) .push_back(&arena.alloc()->setNumber(num)); } + static Ref makeInt(uint32_t num) { + return makeDouble(double(num)); + } static Ref makeBinary(Ref left, IString op, Ref right) { if (op == SET) { @@ -1385,7 +1426,7 @@ class ValueBuilder { assert(switch_[0] == SWITCH); assert(code[0] == BLOCK); if (!explicitBlock) { - for (int i = 0; i < code[1]->size(); i++) { + for (size_t i = 0; i < code[1]->size(); i++) { switch_[2]->back()->back()->push_back(code[1][i]); } } else { diff --git a/tools/optimizer/snprintf.h b/tools/optimizer/snprintf.h new file mode 100644 index 0000000000000..2792a23a23fb7 --- /dev/null +++ b/tools/optimizer/snprintf.h @@ -0,0 +1,32 @@ +#include + +// Visual Studio does not support C99, so emulate snprintf support for it manually. + +#ifdef _MSC_VER + +#define snprintf c99_snprintf + +inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +inline int c99_snprintf(char* str, size_t size, const char* format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(str, size, format, ap); + va_end(ap); + + return count; +} +#endif diff --git a/tools/settings_template_readonly.py b/tools/settings_template_readonly.py index efaec8a3a11f2..e4cb193314d2a 100644 --- a/tools/settings_template_readonly.py +++ b/tools/settings_template_readonly.py @@ -2,6 +2,8 @@ # Note: If you put paths relative to the home directory, do not forget os.path.expanduser +# Note: On Windows, remember to escape backslashes! I.e. EMSCRIPTEN_ROOT='c:\emscripten\' is not valid, but EMSCRIPTEN_ROOT='c:\\emscripten\\' and EMSCRIPTEN_ROOT='c:/emscripten/' are. + import os # this helps projects using emscripten find it @@ -11,6 +13,10 @@ # If not specified, defaults to sys.executable. #PYTHON = 'python' +# Add this if you have manually built the JS optimizer executable (in Emscripten/tools/optimizer) and want to run it from a custom location. +# Alternatively, you can set this as the environment variable EMSCRIPTEN_NATIVE_OPTIMIZER. +# EMSCRIPTEN_NATIVE_OPTIMIZER='/path/to/custom/optimizer(.exe)' + # See below for notes on which JS engine(s) you need NODE_JS = os.path.expanduser(os.getenv('NODE') or '{{{ NODE }}}') # executable SPIDERMONKEY_ENGINE = [os.path.expanduser(os.getenv('SPIDERMONKEY') or 'js')] # executable diff --git a/tools/shared.py b/tools/shared.py index d6c590d2fab84..afeb931956985 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1660,6 +1660,7 @@ def closure_compiler(filename, pretty=True): if pretty: args += ['--formatting', 'PRETTY_PRINT'] if os.environ.get('EMCC_CLOSURE_ARGS'): args += shlex.split(os.environ.get('EMCC_CLOSURE_ARGS')) + logging.debug('closure compiler: ' + ' '.join(args)) process = Popen(args, stdout=PIPE, stderr=STDOUT) cc_output = process.communicate()[0] if process.returncode != 0 or not os.path.exists(filename + '.cc.js'): diff --git a/tools/system_libs.py b/tools/system_libs.py index ca2a6d7ed1608..db0d7f8792360 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -56,8 +56,6 @@ def read_symbols(path, exclude=None): def build_libc(lib_filename, files, lib_opts): o_s = [] - prev_cxx = os.environ.get('EMMAKEN_CXX') - if prev_cxx: os.environ['EMMAKEN_CXX'] = '' musl_internal_includes = ['-I', shared.path_from_root('system', 'lib', 'libc', 'musl', 'src', 'internal'), '-I', shared.path_from_root('system', 'lib', 'libc', 'musl', 'arch', 'js')] commands = [] # Hide several musl warnings that produce a lot of spam to unit test build server logs. @@ -68,7 +66,6 @@ def build_libc(lib_filename, files, lib_opts): commands.append([shared.PYTHON, shared.EMCC, shared.path_from_root('system', 'lib', src), '-o', o] + musl_internal_includes + default_opts + c_opts + lib_opts) o_s.append(o) run_commands(commands) - if prev_cxx: os.environ['EMMAKEN_CXX'] = prev_cxx shared.Building.link(o_s, in_temp(lib_filename)) return in_temp(lib_filename) @@ -107,6 +104,8 @@ def create_libc(): 'shgetc.c', ]], ['math', [ + '__expo2.c', + '__expo2f.c', '__fpclassify.c', '__fpclassifyf.c', '__fpclassifyl.c', @@ -447,6 +446,76 @@ def create_libcextra(): 'wcstod.c', 'wcstol.c', ]], + ['complex', [ + 'cabs.c', + 'cabsf.c', + 'cabsl.c', + 'cacos.c', + 'cacosf.c', + 'cacosh.c', + 'cacoshf.c', + 'cacoshl.c', + 'cacosl.c', + 'carg.c', + 'cargf.c', + 'cargl.c', + 'casin.c', + 'casinf.c', + 'casinh.c', + 'casinhf.c', + 'casinhl.c', + 'casinl.c', + 'catan.c', + 'catanf.c', + 'catanh.c', + 'catanhf.c', + 'catanhl.c', + 'catanl.c', + 'ccos.c', + 'ccosf.c', + 'ccosh.c', + 'ccoshf.c', + 'ccoshl.c', + 'ccosl.c', + '__cexp.c', + 'cexp.c', + '__cexpf.c', + 'cexpf.c', + 'cexpl.c', + 'cimag.c', + 'cimagf.c', + 'cimagl.c', + 'clog.c', + 'clogf.c', + 'clogl.c', + 'conj.c', + 'conjf.c', + 'conjl.c', + 'cpow.c', + 'cpowf.c', + 'cpowl.c', + 'cproj.c', + 'cprojf.c', + 'cprojl.c', + 'creal.c', + 'crealf.c', + 'creall.c', + 'csin.c', + 'csinf.c', + 'csinh.c', + 'csinhf.c', + 'csinhl.c', + 'csinl.c', + 'csqrt.c', + 'csqrtf.c', + 'csqrtl.c', + 'ctan.c', + 'ctanf.c', + 'ctanh.c', + 'ctanhf.c', + 'ctanhl.c', + 'ctanl.c' + ]], ['string', [ 'bcmp.c', 'bcopy.c', @@ -575,11 +644,8 @@ def apply_libcxxabi(need): # gl def create_gl(): - prev_cxx = os.environ.get('EMMAKEN_CXX') - if prev_cxx: os.environ['EMMAKEN_CXX'] = '' o = in_temp('gl.o') check_call([shared.PYTHON, shared.EMCC, shared.path_from_root('system', 'lib', 'gl.c'), '-o', o]) - if prev_cxx: os.environ['EMMAKEN_CXX'] = prev_cxx return o # Setting this in the environment will avoid checking dependencies and make building big projects a little faster @@ -713,7 +779,7 @@ def fetch_project(name, url, expected_version): fullname = os.path.join(Ports.get_dir(), name) if name not in Ports.name_cache: # only mention each port once in log - logging.warning('including port: ' + name) + logging.debug('including port: ' + name) logging.debug(' (at ' + fullname + ')') Ports.name_cache.add(name)