diff options
64 files changed, 1483 insertions, 392 deletions
diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index b60921cd..00000000 --- a/ChangeLog +++ /dev/null @@ -1,137 +0,0 @@ -2011-??-??: Version 2.0 - - * Bundled headers - * TODO: try to not need all the -h in tests/runner.py, and for user scripts too - -2011-07-31: Version 1.5 - - * eSpeak text-to-speech demo - * Filesystem emulation API - * Parse metadata type info for easier interaction with llvm structures - * Bindings generator improvements - * Many library improvements and fixes - * Various bug fixes - -2011-07-10: Version 1.4 - - * Compiling and loading of dynamic libraries - * Automatic bindings generation using CppHeaderParser - * Much improved emscripten.py - * Many library improvements and fixes - * Various bug fixes - -2011-06-22: Version 1.3 - - * Optional typed arrays with single shared buffer (TA2) - * Optional support for nonportable optimizations with TA2 - * Relooper optimizations - * Reading from stdin on the web opens window.prompt - * Various bug fixes - -2011-05-29: Version 1.2 - - * Doom demo - * Major SDL improvements: color palettes, input events, audio - * Various improvements for CHECK_* and CORRECT_*, and new AUTO_OPTIMIZE - * Experimental work towards supporting OpenGL in WebGL - * Various bug fixes - -2011-05-01: Version 1.1 - - * Much improved Bullet demo (llvm opts, quantum = 1, CubicVR.js) - * QUANTUM == 1 a.k.a memory compression option - * Dead function elimination tool - * Various performance improvements in generated code - * Various bug fixes - -2011-04-09: Version 1.0 - - * Poppler test and web demo - * Optimize compiler memory usage very significantly - * Support for LLVM 2.9 - * Better interaction with closure compiler - * Finish docs/paper.pdf - * Many bug fixes - -2011-03-05: Version 0.9 - - * OpenJPEG test and web demo - * Line number debugging info and autodebugger tool - * CORRECT_ROUNDINGS option - * Line-specific CORRECT_* options - * 20% faster compilation - * Generate strict mode JavaScript - * Many bug fixes, additional tests and library implementations - -2011-02-06: Version 0.8 - - * Freetype web demo (2011-02-08, right after 0.8) - * Freetype and zlib tests (including the entire build procedure) - * File emulation - * CHECK_OVERFLOWS & CORRECT_OVERFLOWS options to handle numerical size issues - * CHECK_SIGNS option to find whether GUARD_SIGNS is necessary - * Improvements to usage of llvm optimizations, and testing - * Many bug fixes, additional tests and library implementations - -2010-12-18: Version 0.7 - - * Python web demo - * Support for essentially all LLVM optimizations - * Proper support for C bitfields - * Many bug fixes, additional tests and library implementations - -2010-11-25: Version 0.6 - - * Web demos: Lua and Bullet/WebGL - * SAFE_HEAP checks for invalid reads/writes, nonportable .ll - * Basic C++ exceptions support (|catch(...)|) - * Optimize compilation of very large projects (memory and speed) - * Support for frontend-optimized .ll input, plus tests - * Integration (combining scripts with compiled C++) tools and tests - * Many bug fixes, additional tests and library implementations - -2010-11-02: Version 0.5 - - * Much faster compilation, in particular of large projects - * Bullet physics library test - * GCC name demangling test - * Module-ization of generated code (optional) - * Name demangler and namespace generator tools - * Many code cleanups, bug fixes, additional tests and library implementations - -2010-10-17: Version 0.4 - - * Much faster optimized code, now 10X the speed of our unoptimized code - * Support for the recently-released LLVM 2.8 - * Support for typed arrays - * Integration with the Closure Compiler - * Benchmarking framework in test runner - * Many code cleanups, bug fixes, and additional tests - -2010-10-05: Version 0.3 - - * Much faster compilation (but still slow with relooper) - * Clang support - * Optional memory alignment that precisely matches C/C++ - * Proper memory management, including stack and (optional) dlmalloc - * Rewritten relooper; no emulated blocks in any test - * Initial support for SDL - * Raytracing test + web demo - * Many code cleanups, bug fixes, additional tests and library implementations - -2010-09-11: Version 0.2 - - * sauer (cubescript) test passes (without RELOOPing), + web demo - * ES_SIZEOF - safe&portable sizeof replacement - * emscripten.py tool for easy compiling - * Better debugging support, using SAFE_HEAP and LABEL_DEBUG, using internal preprocessor - * Compiler can now run all tests in both SpiderMonkey and V8 - * Various compiler optimizations (still barely scratched the surface though) - * Many code cleanups, bug fixes, additional tests and library implementations - -2010-08-28: Version 0.1 - - * All tests pass, including fannkuch and fasta, but constglobalstructs - * Relooping of Fannkuch is complete, fasta has one left - * Emscriptened Fannkuch is 19X slower than gcc -O0, 37X than gcc -O2 - @@ -1108,10 +1108,45 @@ try: # Note that we assume a single symbol is enough to know if we have/do not have dlmalloc etc. If you # include just a few symbols but want the rest, this will not work. + def read_symbols(path, exclude=None): + symbols = map(lambda line: line.strip().split(' ')[1], open(path).readlines()) + if exclude: + symbols = filter(lambda symbol: symbol not in exclude, symbols) + return set(symbols) + + # XXX We also need to add libc symbols that use malloc, for example strdup. It's very rare to use just them and not + # a normal malloc symbol (like free, after calling strdup), so we haven't hit this yet, but it is possible. + libc_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libc.symbols')) + libcextra_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libcextra.symbols')) + libcxx_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libcxx', 'symbols'), exclude=libc_symbols) + libcxxabi_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libcxxabi', 'symbols'), exclude=libc_symbols) + + def build_libc(lib_filename, files): + o_s = [] + prev_cxx = os.environ.get('EMMAKEN_CXX') + if prev_cxx: os.environ['EMMAKEN_CXX'] = '' + musl_internal_includes = shared.path_from_root('system', 'lib', 'libc', 'musl', 'src', 'internal') + for src in files: + o = in_temp(os.path.basename(src) + '.o') + execute([shared.PYTHON, shared.EMCC, shared.path_from_root('system', 'lib', src), '-o', o, '-I', musl_internal_includes], stdout=stdout, stderr=stderr) + o_s.append(o) + if prev_cxx: os.environ['EMMAKEN_CXX'] = prev_cxx + shared.Building.link(o_s, in_temp(lib_filename)) + return in_temp(lib_filename) + + def build_libcxx(src_dirname, lib_filename, files): + o_s = [] + for src in files: + o = in_temp(src + '.o') + srcfile = shared.path_from_root(src_dirname, src) + execute([shared.PYTHON, shared.EMXX, srcfile, '-o', o, '-std=c++11'], stdout=stdout, stderr=stderr) + o_s.append(o) + shared.Building.link(o_s, in_temp(lib_filename)) + return in_temp(lib_filename) + # libc def create_libc(): if DEBUG: print >> sys.stderr, 'emcc: building libc for cache' - o_s = [] libc_files = [ 'dlmalloc.c', os.path.join('libcxx', 'new.cpp'), @@ -1126,17 +1161,7 @@ try: os.path.join('libc', 'gen', 'vwarnx.c'), os.path.join('libc', 'stdlib', 'strtod.c'), ]; - - prev_cxx = os.environ.get('EMMAKEN_CXX') - if prev_cxx: os.environ['EMMAKEN_CXX'] = '' - for src in libc_files: - o = in_temp(os.path.basename(src) + '.o') - execute([shared.PYTHON, shared.EMCC, shared.path_from_root('system', 'lib', src), '-o', o], stdout=stdout, stderr=stderr) - o_s.append(o) - if prev_cxx: os.environ['EMMAKEN_CXX'] = prev_cxx - - shared.Building.link(o_s, in_temp('libc.bc')) - return in_temp('libc.bc') + return build_libc('libc.bc', libc_files) def fix_libc(need): # libc needs some sign correction. # If we are in mode 0, switch to 2. We will add our lines @@ -1148,14 +1173,70 @@ try: shared.Settings.CORRECT_SIGNS_LINES = [shared.path_from_root('src', 'dlmalloc.c') + ':' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]] # If we are in mode 1, we are correcting everything anyhow. If we are in mode 3, we will be corrected # so all is well anyhow too. - # XXX We also need to add libc symbols that use malloc, for example strdup. It's very rare to use just them and not - # a normal malloc symbol (like free, after calling strdup), so we haven't hit this yet, but it is possible. - libc_symbols = open(shared.path_from_root('system', 'lib', 'libc.symbols')).read().split('\n') + + # libcextra + def create_libcextra(): + if DEBUG: print >> sys.stderr, 'emcc: building libcextra for cache' + musl_files = [ + ['multibyte', [ + 'btowc.c', + 'mblen.c', + 'mbrlen.c', + 'mbrtowc.c', + 'mbsinit.c', + 'mbsnrtowcs.c', + 'mbsrtowcs.c', + 'mbstowcs.c', + 'mbtowc.c', + 'wcrtomb.c', + 'wcsnrtombs.c', + 'wcsrtombs.c', + 'wcstombs.c', + 'wctob.c', + 'wctomb.c', + ]], + ['string', [ + 'wcpcpy.c', + 'wcpncpy.c', + 'wcscasecmp.c', + # 'wcscasecmp_l.c', # XXX: alltypes.h issue + 'wcscat.c', + 'wcschr.c', + 'wcscmp.c', + 'wcscpy.c', + 'wcscspn.c', + 'wcsdup.c', + 'wcslen.c', + 'wcsncasecmp.c', + # 'wcsncasecmp_l.c', # XXX: alltypes.h issue + 'wcsncat.c', + 'wcsncmp.c', + 'wcsncpy.c', + 'wcsnlen.c', + 'wcspbrk.c', + 'wcsrchr.c', + 'wcsspn.c', + 'wcsstr.c', + 'wcstok.c', + 'wcswcs.c', + 'wmemchr.c', + 'wmemcmp.c', + 'wmemcpy.c', + 'wmemmove.c', + 'wmemset.c', + ]] + ] + libcextra_files = [] + for directory, sources in musl_files: + libcextra_files += [os.path.join('libc', 'musl', 'src', directory, source) for source in sources] + return build_libc('libcextra.bc', libcextra_files) + + def fix_libcextra(need): + pass # libcxx def create_libcxx(): if DEBUG: print >> sys.stderr, 'emcc: building libcxx for cache' - os = [] libcxx_files = [ 'algorithm.cpp', 'condition_variable.cpp', @@ -1180,50 +1261,34 @@ try: 'regex.cpp', 'strstream.cpp' ] - for src in libcxx_files: - o = in_temp(src + '.o') - srcfile = shared.path_from_root('system', 'lib', 'libcxx', src) - execute([shared.PYTHON, shared.EMXX, srcfile, '-o', o, '-std=c++11'], stdout=stdout, stderr=stderr) - os.append(o) - shared.Building.link(os, in_temp('libcxx.bc')) - return in_temp('libcxx.bc') + return build_libcxx(os.path.join('system', 'lib', 'libcxx'), 'libcxx.bc', libcxx_files) + def fix_libcxx(need): assert shared.Settings.QUANTUM_SIZE == 4, 'We do not support libc++ with QUANTUM_SIZE == 1' # libcxx might need corrections, so turn them all on. TODO: check which are actually needed shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1 #print >> sys.stderr, 'emcc: info: using libcxx turns on CORRECT_* options' - libcxx_symbols = map(lambda line: line.strip().split(' ')[1], open(shared.path_from_root('system', 'lib', 'libcxx', 'symbols')).readlines()) - libcxx_symbols = filter(lambda symbol: symbol not in libc_symbols, libcxx_symbols) - libcxx_symbols = set(libcxx_symbols) # libcxxabi - just for dynamic_cast for now def create_libcxxabi(): if DEBUG: print >> sys.stderr, 'emcc: building libcxxabi for cache' - os = [] libcxxabi_files = [ 'typeinfo.cpp', 'private_typeinfo.cpp' ] - for src in libcxxabi_files: - o = in_temp(src + '.o') - srcfile = shared.path_from_root('system', 'lib', 'libcxxabi', 'src', src) - execute([shared.PYTHON, shared.EMXX, srcfile, '-o', o, '-std=c++11'], stdout=stdout, stderr=stderr) - os.append(o) - shared.Building.link(os, in_temp('libcxxabi.bc')) - return in_temp('libcxxabi.bc') + return build_libcxx(os.path.join('system', 'lib', 'libcxxabi', 'src'), 'libcxxabi.bc', libcxxabi_files) + def fix_libcxxabi(need): assert shared.Settings.QUANTUM_SIZE == 4, 'We do not support libc++abi with QUANTUM_SIZE == 1' #print >> sys.stderr, 'emcc: info: using libcxxabi, this may need CORRECT_* options' #shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1 - libcxxabi_symbols = map(lambda line: line.strip().split(' ')[1], open(shared.path_from_root('system', 'lib', 'libcxxabi', 'symbols')).readlines()) - libcxxabi_symbols = filter(lambda symbol: symbol not in libc_symbols, libcxxabi_symbols) - libcxxabi_symbols = set(libcxxabi_symbols) # If we have libcxx, we must force inclusion of libc, since libcxx uses new internally. Note: this is kind of hacky # Settings this in the environment will avoid checking dependencies and make building big projects a little faster force = os.environ.get('EMCC_FORCE_STDLIBS') has = need = None for name, create, fix, library_symbols in [('libcxx', create_libcxx, fix_libcxx, libcxx_symbols), + ('libcextra', create_libcextra, fix_libcextra, libcextra_symbols), ('libcxxabi', create_libcxxabi, fix_libcxxabi, libcxxabi_symbols), ('libc', create_libc, fix_libc, libc_symbols)]: if not force: diff --git a/src/analyzer.js b/src/analyzer.js index 3278139b..7fbdf24d 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -660,10 +660,12 @@ function analyzer(data, sidePass) { assert(PRECISE_I64_MATH, 'Must have precise i64 math for non-constant 64-bit shifts'); Types.preciseI64MathUsed = 1; value.intertype = 'value'; - value.ident = 'var ' + value.assignTo + '$0 = _bitshift64' + value.op[0].toUpperCase() + value.op.substr(1) + '(' + - asmCoercion(sourceElements[0].ident, 'i32') + ',' + - asmCoercion(sourceElements[1].ident, 'i32') + ',' + - asmCoercion(value.params[1].ident + '$0', 'i32') + ');' + + value.ident = 'var ' + value.assignTo + '$0 = ' + + asmCoercion('_bitshift64' + value.op[0].toUpperCase() + value.op.substr(1) + '(' + + asmCoercion(sourceElements[0].ident, 'i32') + ',' + + asmCoercion(sourceElements[1].ident, 'i32') + ',' + + asmCoercion(value.params[1].ident + '$0', 'i32') + ')', 'i32' + ) + ';' + 'var ' + value.assignTo + '$1 = tempRet0;'; value.assignTo = null; i++; diff --git a/src/compiler.js b/src/compiler.js index 9c19aeb0..313fd5f7 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -168,6 +168,10 @@ if (SAFE_HEAP >= 2) { EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS); EXPORTED_GLOBALS = set(EXPORTED_GLOBALS); EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST); + +DEAD_FUNCTIONS.forEach(function(dead) { + DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push(dead.substr(1)); +}); DEAD_FUNCTIONS = numberedSet(DEAD_FUNCTIONS); RUNTIME_DEBUG = LIBRARY_DEBUG || GL_DEBUG; diff --git a/src/fastLong.js b/src/fastLong.js index 95f398db..d1ce5d39 100644 --- a/src/fastLong.js +++ b/src/fastLong.js @@ -22,13 +22,13 @@ function ___divdi3($a$0, $a$1, $b$0, $b$1) { $1$1 = (($a$1 | 0) < 0 ? -1 : 0) >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; $2$0 = $b$1 >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; $2$1 = (($b$1 | 0) < 0 ? -1 : 0) >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; - $4$0 = _i64Subtract($1$0 ^ $a$0, $1$1 ^ $a$1, $1$0, $1$1); + $4$0 = _i64Subtract($1$0 ^ $a$0, $1$1 ^ $a$1, $1$0, $1$1) | 0; $4$1 = tempRet0; - $6$0 = _i64Subtract($2$0 ^ $b$0, $2$1 ^ $b$1, $2$0, $2$1); + $6$0 = _i64Subtract($2$0 ^ $b$0, $2$1 ^ $b$1, $2$0, $2$1) | 0; $7$0 = $2$0 ^ $1$0; $7$1 = $2$1 ^ $1$1; $8$0 = ___udivmoddi4($4$0, $4$1, $6$0, tempRet0, 0) | 0; - $10$0 = _i64Subtract($8$0 ^ $7$0, tempRet0 ^ $7$1, $7$0, $7$1); + $10$0 = _i64Subtract($8$0 ^ $7$0, tempRet0 ^ $7$1, $7$0, $7$1) | 0; return (tempRet0 = tempRet0, $10$0) | 0; } function ___remdi3($a$0, $a$1, $b$0, $b$1) { @@ -44,11 +44,11 @@ function ___remdi3($a$0, $a$1, $b$0, $b$1) { $1$1 = (($a$1 | 0) < 0 ? -1 : 0) >> 31 | (($a$1 | 0) < 0 ? -1 : 0) << 1; $2$0 = $b$1 >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; $2$1 = (($b$1 | 0) < 0 ? -1 : 0) >> 31 | (($b$1 | 0) < 0 ? -1 : 0) << 1; - $4$0 = _i64Subtract($1$0 ^ $a$0, $1$1 ^ $a$1, $1$0, $1$1); + $4$0 = _i64Subtract($1$0 ^ $a$0, $1$1 ^ $a$1, $1$0, $1$1) | 0; $4$1 = tempRet0; - $6$0 = _i64Subtract($2$0 ^ $b$0, $2$1 ^ $b$1, $2$0, $2$1); + $6$0 = _i64Subtract($2$0 ^ $b$0, $2$1 ^ $b$1, $2$0, $2$1) | 0; ___udivmoddi4($4$0, $4$1, $6$0, tempRet0, $rem); - $10$0 = _i64Subtract(HEAP32[$rem >> 2] ^ $1$0, HEAP32[$rem + 4 >> 2] ^ $1$1, $1$0, $1$1); + $10$0 = _i64Subtract(HEAP32[$rem >> 2] ^ $1$0, HEAP32[$rem + 4 >> 2] ^ $1$1, $1$0, $1$1) | 0; $10$1 = tempRet0; STACKTOP = __stackBase__; return (tempRet0 = $10$1, $10$0) | 0; @@ -245,7 +245,7 @@ function ___udivmoddi4($a$0, $a$1, $b$0, $b$1, $rem) { } else { $d_sroa_0_0_insert_insert99$0 = 0 | $b$0 & -1; $d_sroa_0_0_insert_insert99$1 = $d_sroa_1_4_extract_shift$0 | $b$1 & 0; - $137$0 = _i64Add($d_sroa_0_0_insert_insert99$0, $d_sroa_0_0_insert_insert99$1, -1, -1); + $137$0 = _i64Add($d_sroa_0_0_insert_insert99$0, $d_sroa_0_0_insert_insert99$1, -1, -1) | 0; $137$1 = tempRet0; $q_sroa_1_1198 = $q_sroa_1_1_ph; $q_sroa_0_1199 = $q_sroa_0_1_ph; @@ -262,7 +262,7 @@ function ___udivmoddi4($a$0, $a$1, $b$0, $b$1, $rem) { $150$1 = tempRet0; $151$0 = $150$1 >> 31 | (($150$1 | 0) < 0 ? -1 : 0) << 1; $152 = $151$0 & 1; - $154$0 = _i64Subtract($r_sroa_0_0_insert_insert42$0, $r_sroa_0_0_insert_insert42$1, $151$0 & $d_sroa_0_0_insert_insert99$0, ((($150$1 | 0) < 0 ? -1 : 0) >> 31 | (($150$1 | 0) < 0 ? -1 : 0) << 1) & $d_sroa_0_0_insert_insert99$1); + $154$0 = _i64Subtract($r_sroa_0_0_insert_insert42$0, $r_sroa_0_0_insert_insert42$1, $151$0 & $d_sroa_0_0_insert_insert99$0, ((($150$1 | 0) < 0 ? -1 : 0) >> 31 | (($150$1 | 0) < 0 ? -1 : 0) << 1) & $d_sroa_0_0_insert_insert99$1) | 0; $r_sroa_0_0_extract_trunc = $154$0; $r_sroa_1_4_extract_trunc = tempRet0; $155 = $sr_1202 - 1 | 0; @@ -296,3 +296,4 @@ function ___udivmoddi4($a$0, $a$1, $b$0, $b$1, $rem) { return (tempRet0 = $_0$1, $_0$0) | 0; } // ======================================================================= + diff --git a/src/jsifier.js b/src/jsifier.js index a01b2655..aab21eea 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -484,17 +484,12 @@ function JSify(data, functionsOnly, givenFunctions) { if (BUILD_AS_SHARED_LIB) { // Shared libraries reuse the runtime of their parents. item.JS = ''; - } else if (LibraryManager.library.hasOwnProperty(shortident)) { - item.JS = addFromLibrary(shortident); - } else if (!LibraryManager.library.hasOwnProperty(shortident + '__inline')) { - if (!(item.ident in DEAD_FUNCTIONS) && !UNRESOLVED_AS_DEAD) { - item.JS = 'var ' + item.ident + '; // stub for ' + item.ident; - if (ASM_JS) { - error('Unresolved symbol: ' + item.ident + ', this must be corrected for asm.js validation to succeed. Consider adding it to DEAD_FUNCTIONS.'); - } else if (WARN_ON_UNDEFINED_SYMBOLS) { - warn('Unresolved symbol: ' + item.ident); - } + } else { + if (!LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) { + if (ASSERTIONS) printErr('warning: missing function: ' + shortident); + LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);"); } + item.JS = addFromLibrary(shortident); } return ret; } @@ -713,6 +708,7 @@ function JSify(data, functionsOnly, givenFunctions) { } else { ret += 'var setjmpLabel = 0;\n'; ret += 'var setjmpTable = ' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n'; + ret += makeSetValue('setjmpTable', '0', '0', 'i32') + ';'; // initialize first entry to 0 } } ret += indent + 'while(1) '; @@ -1370,7 +1366,7 @@ function JSify(data, functionsOnly, givenFunctions) { args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) }); if (ASM_JS) { - if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced) { + if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || funcData.setjmpTable) { args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) }); } else { args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) }); @@ -1438,30 +1434,25 @@ function JSify(data, functionsOnly, givenFunctions) { returnType = getReturnType(type); } - if (callIdent in DEAD_FUNCTIONS) { - var ret = 'abort(' + DEAD_FUNCTIONS[callIdent] + ')'; - if (ASM_JS) ret = asmCoercion(ret, returnType); - return ret; - } - if (byPointer) { var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs); if (ASM_JS) { assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out) - if (!byPointerForced) { + if (!byPointerForced && !funcData.setjmpTable) { + // normal asm function pointer call callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py } else { - // This is a forced call, through an invoke_*. + // This is a call through an invoke_*, either a forced one, or a setjmp-required one // note: no need to update argsTypes at this point - Functions.unimplementedFunctions[callIdent] = sig; - args.unshift(byPointerForced ? Functions.getIndex(callIdent) : callIdent); + if (byPointerForced) Functions.unimplementedFunctions[callIdent] = sig; + args.unshift(byPointerForced ? Functions.getIndex(callIdent) : asmCoercion(callIdent, 'i32')); callIdent = 'invoke_' + sig; } } else if (SAFE_DYNCALLS) { assert(!ASM_JS, 'cannot emit safe dyncalls in asm'); callIdent = '(tempInt=' + callIdent + ',tempInt < 0 || tempInt >= FUNCTION_TABLE.length-1 || !FUNCTION_TABLE[tempInt] ? abort("dyncall error: ' + sig + ' " + FUNCTION_TABLE_NAMES[tempInt]) : tempInt)'; } - if (!byPointerForced) callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; + if (!ASM_JS || (!byPointerForced && !funcData.setjmpTable)) callIdent = Functions.getTable(sig) + '[' + callIdent + ']'; } var ret = callIdent + '(' + args.join(', ') + ')'; @@ -1475,7 +1466,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (ASM_JS && funcData.setjmpTable) { // check if a longjmp was done. If a setjmp happened, check if ours. If ours, go to -111 to handle it. // otherwise, just return - the call to us must also have been an invoke, so the setjmp propagates that way - ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) > 0)) { setjmpLabel = _testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable); if ((setjmpLabel|0) > 0) { label = -1111; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;\n'; + ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) > 0)) { setjmpLabel = ' + asmCoercion('_testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable)', 'i32') + '; if ((setjmpLabel|0) > 0) { label = -1111; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;\n'; } return ret; diff --git a/src/library.js b/src/library.js index cd416599..5bbf4204 100644 --- a/src/library.js +++ b/src/library.js @@ -4168,32 +4168,6 @@ LibraryManager.library = { } }, - mbtowc: function(pwc, pmb, maxx) { - // XXX doesn't really handle multibyte at all - if (!pmb) return 0; - maxx = Math.min({{{ cDefine('_NL_CTYPE_MB_CUR_MAX') }}}, maxx); - var i; - for (i = 0; i < maxx; i++) { - var curr = {{{ makeGetValue('pmb', 0, 'i8') }}}; - if (pwc) { - {{{ makeSetValue('pwc', '0', 'curr', 'i8') }}}; - {{{ makeSetValue('pwc', '1', '0', 'i8') }}}; - pwc += 2; - } - pmb++; - if (!curr) break; - } - return i; - }, - - wcrtomb: function(s, wc, ps) { - // XXX doesn't really handle multibyte at all - if (s) { - {{{ makeSetValue('s', '0', 'wc', 'i8') }}}; - } - return 1; - }, - arc4random: 'rand', // ========================================================================== @@ -4243,8 +4217,6 @@ LibraryManager.library = { return ret|0; }, - wmemcpy: function() { throw 'wmemcpy not implemented' }, - llvm_memcpy_i32: 'memcpy', llvm_memcpy_i64: 'memcpy', llvm_memcpy_p0i8_p0i8_i32: 'memcpy', @@ -4274,8 +4246,6 @@ LibraryManager.library = { llvm_memmove_p0i8_p0i8_i32: 'memmove', llvm_memmove_p0i8_p0i8_i64: 'memmove', - wmemmove: function() { throw 'wmemmove not implemented' }, - memset__inline: function(ptr, value, num, align) { return makeSetValues(ptr, 0, value, 'null', num, align); }, @@ -4316,32 +4286,18 @@ LibraryManager.library = { llvm_memset_p0i8_i32: 'memset', llvm_memset_p0i8_i64: 'memset', - wmemset: function() { throw 'wmemset not implemented' }, - strlen__sig: 'ii', strlen__asm: true, strlen: function(ptr) { ptr = ptr|0; var curr = 0; curr = ptr; - while ({{{ makeGetValueAsm('curr', '0', 'i8') }}}|0 != 0) { + while ({{{ makeGetValueAsm('curr', '0', 'i8') }}}) { curr = (curr + 1)|0; } return (curr - ptr)|0; }, - // TODO: Implement when we have real unicode support. - mblen: function() { - return 1; - }, - - wcslen: function() { throw 'wcslen not implemented' }, - mbrlen: function() { throw 'mbrlen not implemented' }, - mbsrtowcs: function() { throw 'mbsrtowcs not implemented' }, - wcsnrtombs: function() { throw 'wcsnrtombs not implemented' }, - mbsnrtowcs: function() { throw 'mbsnrtowcs not implemented' }, - mbrtowc: function() { throw 'mbrtowc not implemented' }, - strspn: function(pstr, pset) { var str = pstr, set, strcurr, setcurr; while (1) { @@ -4382,7 +4338,7 @@ LibraryManager.library = { do { {{{ makeCopyValues('(pdest+i)|0', '(psrc+i)|0', 1, 'i8', null, 1) }}}; i = (i+1)|0; - } while (({{{ makeGetValue('psrc', 'i-1', 'i8') }}})|0 != 0); + } while ({{{ makeGetValueAsm('psrc', 'i-1', 'i8') }}}); return pdest|0; }, @@ -4437,11 +4393,11 @@ LibraryManager.library = { strcat: function(pdest, psrc) { pdest = pdest|0; psrc = psrc|0; var i = 0; - pdest = (pdest + _strlen(pdest))|0; + pdest = (pdest + (_strlen(pdest)|0))|0; do { {{{ makeCopyValues('pdest+i', 'psrc+i', 1, 'i8', null, 1) }}}; i = (i+1)|0; - } while ({{{ makeGetValueAsm('psrc', 'i-1', 'i8') }}} != 0); + } while ({{{ makeGetValueAsm('psrc', 'i-1', 'i8') }}}); return pdest|0; }, @@ -4501,8 +4457,8 @@ LibraryManager.library = { px = px|0; py = py|0; n = n|0; var i = 0, x = 0, y = 0; while ((i>>>0) < (n>>>0)) { - x = _tolower({{{ makeGetValueAsm('px', 'i', 'i8', 0, 1) }}}); - y = _tolower({{{ makeGetValueAsm('py', 'i', 'i8', 0, 1) }}}); + x = _tolower({{{ makeGetValueAsm('px', 'i', 'i8', 0, 1) }}})|0; + y = _tolower({{{ makeGetValueAsm('py', 'i', 'i8', 0, 1) }}})|0; if (((x|0) == (y|0)) & ((x|0) == 0)) return 0; if ((x|0) == 0) return -1; if ((y|0) == 0) return 1; @@ -5355,6 +5311,8 @@ LibraryManager.library = { llvm_objectsize_i32: function() { return -1 }, // TODO: support this + llvm_dbg_declare__inline: function() { throw 'llvm_debug_declare' }, // avoid warning + // ========================================================================== // llvm-mono integration // ========================================================================== @@ -6220,6 +6178,7 @@ LibraryManager.library = { saveSetjmp__asm: true, saveSetjmp__sig: 'iii', + saveSetjmp__deps: ['putchar'], saveSetjmp: function(env, label, table) { // Not particularly fast: slow table lookup of setjmpId to label. But setjmp // prevents relooping anyhow, so slowness is to be expected. And typical case @@ -6233,7 +6192,7 @@ LibraryManager.library = { #endif setjmpId = (setjmpId+1)|0; {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; - while ((i|0) < {{{ MAX_SETJMPS }}}) { + while ((i|0) < {{{ 2*MAX_SETJMPS }}}) { if ({{{ makeGetValueAsm('table', 'i*4', 'i32') }}} == 0) { {{{ makeSetValueAsm('table', 'i*4', 'setjmpId', 'i32') }}}; {{{ makeSetValueAsm('table', 'i*4+4', 'label', 'i32') }}}; @@ -6243,7 +6202,8 @@ LibraryManager.library = { } i = (i+2)|0; } - abort(987); // if you hit this, adjust MAX_SETJMPS + {{{ makePrintChars('too many setjmps in a function call, build with a higher value for MAX_SETJMPS') }}}; + abort(0); return 0; }, @@ -6270,7 +6230,7 @@ LibraryManager.library = { setjmp__inline: function(env) { // Save the label #if ASM_JS - return '_saveSetjmp(' + env + ', label, setjmpTable)'; + return '_saveSetjmp(' + env + ', label, setjmpTable)|0'; #else return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32', undefined, undefined, undefined, undefined, ',') + ', 0)'; #endif diff --git a/src/modules.js b/src/modules.js index 7a769846..ce162ac1 100644 --- a/src/modules.js +++ b/src/modules.js @@ -291,7 +291,7 @@ var Functions = { var sig = ASM_JS ? Functions.implementedFunctions[ident] || Functions.unimplementedFunctions[ident] || LibraryManager.library[ident.substr(1) + '__sig'] : 'x'; assert(sig, ident); if (!tables[sig]) tables[sig] = emptyTable(sig); // TODO: make them compact - tables[sig][this.indexedFunctions[ident]] = ident in DEAD_FUNCTIONS ? '0' : ident; + tables[sig][this.indexedFunctions[ident]] = ident; } var generated = false; var wrapped = {}; diff --git a/src/parseTools.js b/src/parseTools.js index 2eb456f1..f4ce12d5 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -2000,7 +2000,7 @@ function processMathop(item) { } function preciseCall(name) { Types.preciseI64MathUsed = true; - return finish([name + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 + ')', 'tempRet0']); + return finish([asmCoercion(name + '(' + low1 + ',' + high1 + ',' + low2 + ',' + high2 + ')', 'i32'), 'tempRet0']); } function i64PreciseLib(type) { return preciseCall('_i64' + type[0].toUpperCase() + type.substr(1)); @@ -2363,3 +2363,14 @@ function getTypeFromHeap(suffix) { } } +// Generates code that prints without printf(), but just putchar (so can be directly inline in asm.js) +function makePrintChars(s, sep) { + sep = sep || ';'; + var ret = ''; + for (var i = 0; i < s.length; i++) { + ret += '_putchar(' + s.charCodeAt(i) + ')' + sep; + } + ret += '_putchar(10)'; + return ret; +} + diff --git a/src/settings.js b/src/settings.js index 7cd0c02e..c878be92 100644 --- a/src/settings.js +++ b/src/settings.js @@ -55,7 +55,7 @@ var ALLOW_MEMORY_GROWTH = 0; // If false, we abort with an error if we try to al // that case we must be careful about optimizations, in particular the // eliminator). Note that memory growth is only supported with typed // arrays. -var MAX_SETJMPS = 10; // size of setjmp table allocated in each function invocation (that has setjmp) +var MAX_SETJMPS = 20; // size of setjmp table allocated in each function invocation (that has setjmp) // Code embetterments var MICRO_OPTS = 1; // Various micro-optimizations, like nativizing variables @@ -342,19 +342,10 @@ var PGO = 0; // Enables profile-guided optimization in the form of runtime check // calling PGOMonitor.dump()); var DEAD_FUNCTIONS = []; // Functions on this list are not converted to JS, and calls to // them are turned into abort()s. This is potentially useful for - // (1) reducing code size, if you know some function will never - // be called (see PGO), and also (2) ASM.js requires all declared - // functions to have a corresponding implementation (even if the - // function is never called) and will emit an error during linking if no - // implementation can be found; with this option, asm.js validation will - // succeed for that function and calls to it. + // reducing code size. // If a dead function is actually called, you will get a runtime // error. // TODO: options to lazily load such functions -var UNRESOLVED_AS_DEAD = 0; // Handle all unresolved functions as if they were in the - // list of dead functions. This is a quick way to turn - // all unresolved references into runtime aborts (and not - // get compile-time warnings or errors on them). var EXPLICIT_ZEXT = 0; // If 1, generate an explicit conversion of zext i1 to i32, using ?: diff --git a/system/lib/libc.symbols b/system/lib/libc.symbols index 96e995cf..1b126abf 100644 --- a/system/lib/libc.symbols +++ b/system/lib/libc.symbols @@ -1,81 +1,81 @@ -_ZNKSt20bad_array_new_length4whatEv -_ZNKSt9bad_alloc4whatEv -_ZNSt20bad_array_new_lengthC1Ev -_ZNSt20bad_array_new_lengthC2Ev -_ZNSt20bad_array_new_lengthD0Ev -_ZNSt20bad_array_new_lengthD1Ev -_ZNSt20bad_array_new_lengthD2Ev -_ZNSt9bad_allocC1Ev -_ZNSt9bad_allocC2Ev -_ZNSt9bad_allocD0Ev -_ZNSt9bad_allocD1Ev -_ZNSt9bad_allocD2Ev -_ZSt15get_new_handlerv -_ZSt15set_new_handlerPFvvE -_ZSt17__throw_bad_allocv -_ZSt7nothrow -_ZTISt20bad_array_new_length -_ZTISt9bad_alloc -_ZTSSt20bad_array_new_length -_ZTSSt9bad_alloc -_ZTVSt20bad_array_new_length -_ZTVSt9bad_alloc -_ZdaPv -_ZdaPvRKSt9nothrow_t -_ZdlPv -_ZdlPvRKSt9nothrow_t -_Znaj -_ZnajRKSt9nothrow_t -_Znwj -_ZnwjRKSt9nothrow_t -_err -_errx -_verr -_verrx -_vwarn -_vwarnx -_warn -_warnx -atof -bulk_free -calloc -err -errx -free -getopt -getopt_long -getopt_long_only -independent_calloc -independent_comalloc -mallinfo -malloc -malloc_footprint -malloc_footprint_limit -malloc_max_footprint -malloc_set_footprint_limit -malloc_stats -malloc_trim -malloc_usable_size -mallopt -memalign -optarg -opterr -optind -optopt -optreset -posix_memalign -pvalloc -realloc -realloc_in_place -strtod -strtod_l -strtof -strtold -strtold_l -valloc -verr -verrx -vwarn -vwarnx -warn1 -warnx + T _ZNKSt20bad_array_new_length4whatEv + T _ZNKSt9bad_alloc4whatEv + T _ZNSt20bad_array_new_lengthC1Ev + T _ZNSt20bad_array_new_lengthC2Ev + T _ZNSt20bad_array_new_lengthD0Ev + ? _ZNSt20bad_array_new_lengthD1Ev + T _ZNSt20bad_array_new_lengthD2Ev + T _ZNSt9bad_allocC1Ev + T _ZNSt9bad_allocC2Ev + T _ZNSt9bad_allocD0Ev + T _ZNSt9bad_allocD1Ev + T _ZNSt9bad_allocD2Ev + T _ZSt15get_new_handlerv + T _ZSt15set_new_handlerPFvvE + T _ZSt17__throw_bad_allocv + D _ZSt7nothrow + D _ZTISt20bad_array_new_length + D _ZTISt9bad_alloc + D _ZTSSt20bad_array_new_length + D _ZTSSt9bad_alloc + D _ZTVSt20bad_array_new_length + D _ZTVSt9bad_alloc + W _ZdaPv + W _ZdaPvRKSt9nothrow_t + W _ZdlPv + W _ZdlPvRKSt9nothrow_t + W _Znaj + W _ZnajRKSt9nothrow_t + W _Znwj + W _ZnwjRKSt9nothrow_t + T _err + T _errx + T _verr + T _verrx + T _vwarn + T _vwarnx + T _warn + T _warnx + T atof + W bulk_free + W calloc + W err + W errx + W free + T getopt + T getopt_long + T getopt_long_only + W independent_calloc + W independent_comalloc + W mallinfo + W malloc + W malloc_footprint + W malloc_footprint_limit + W malloc_max_footprint + W malloc_set_footprint_limit + W malloc_stats + W malloc_trim + T malloc_usable_size + W mallopt + W memalign + C optarg + D opterr + D optind + D optopt + C optreset + W posix_memalign + W pvalloc + W realloc + W realloc_in_place + T strtod + T strtod_l + T strtof + T strtold + T strtold_l + W valloc + W verr + W verrx + W vwarn + W vwarnx + W warn1 + W warnx diff --git a/system/lib/libc/musl/memcpy.c b/system/lib/libc/musl/memcpy.c deleted file mode 100644 index 8e98302f..00000000 --- a/system/lib/libc/musl/memcpy.c +++ /dev/null @@ -1,29 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <stdint.h> - -#define SS (sizeof(size_t)) -#define ALIGN (sizeof(size_t)-1) -#define ONES ((size_t)-1/UCHAR_MAX) - -void *memcpy(void *restrict dest, const void *restrict src, size_t n) -{ - unsigned char *d = dest; - const unsigned char *s = src; - - if (((uintptr_t)d & ALIGN) != ((uintptr_t)s & ALIGN)) - goto misaligned; - - for (; ((uintptr_t)d & ALIGN) && n; n--) *d++ = *s++; - if (n) { - size_t *wd = (void *)d; - const size_t *ws = (const void *)s; - - for (; n>=SS; n-=SS) *wd++ = *ws++; - d = (void *)wd; - s = (const void *)ws; -misaligned: - for (; n; n--) *d++ = *s++; - } - return dest; -} diff --git a/system/lib/libc/musl/readme.txt b/system/lib/libc/musl/readme.txt new file mode 100644 index 00000000..0df3429d --- /dev/null +++ b/system/lib/libc/musl/readme.txt @@ -0,0 +1 @@ +These sources were downloaded from the musl-0.9.10 release on April 14, 2003. diff --git a/system/lib/libc/musl/src/internal/libc.c b/system/lib/libc/musl/src/internal/libc.c new file mode 100644 index 00000000..942f6b44 --- /dev/null +++ b/system/lib/libc/musl/src/internal/libc.c @@ -0,0 +1,22 @@ +#include "libc.h" + +#ifdef USE_LIBC_ACCESSOR +struct __libc *__libc_loc() +{ + static struct __libc __libc; + return &__libc; +} +#else +struct __libc __libc; +#endif + +#ifdef BROKEN_VISIBILITY +__asm__(".hidden __libc"); +#endif + +size_t __hwcap; +size_t __sysinfo; +char *__progname=0, *__progname_full=0; + +weak_alias(__progname, program_invocation_short_name); +weak_alias(__progname_full, program_invocation_name); diff --git a/system/lib/libc/musl/src/internal/libc.h b/system/lib/libc/musl/src/internal/libc.h new file mode 100644 index 00000000..c9416f07 --- /dev/null +++ b/system/lib/libc/musl/src/internal/libc.h @@ -0,0 +1,71 @@ +#ifndef LIBC_H +#define LIBC_H + +#include <stdlib.h> +#include <stdio.h> + +struct __libc { + void *main_thread; + int threaded; + int secure; + size_t *auxv; + int (*atexit)(void (*)(void)); + void (*fini)(void); + void (*ldso_fini)(void); + volatile int threads_minus_1; + int canceldisable; + FILE *ofl_head; + int ofl_lock[2]; + size_t tls_size; +}; + +extern size_t __hwcap; + +#if !defined(__PIC__) || (100*__GNUC__+__GNUC_MINOR__ >= 303 && !defined(__PCC__)) + +#ifdef __PIC__ +#if __GNUC__ < 4 +#define BROKEN_VISIBILITY 1 +#endif +#define ATTR_LIBC_VISIBILITY __attribute__((visibility("hidden"))) +#else +#define ATTR_LIBC_VISIBILITY +#endif + +extern struct __libc __libc ATTR_LIBC_VISIBILITY; +#define libc __libc + +#else + +#define USE_LIBC_ACCESSOR +#define ATTR_LIBC_VISIBILITY +extern struct __libc *__libc_loc(void) __attribute__((const)); +#define libc (*__libc_loc()) + +#endif + + +/* Designed to avoid any overhead in non-threaded processes */ +void __lock(volatile int *) ATTR_LIBC_VISIBILITY; +void __unlock(volatile int *) ATTR_LIBC_VISIBILITY; +int __lockfile(FILE *) ATTR_LIBC_VISIBILITY; +void __unlockfile(FILE *) ATTR_LIBC_VISIBILITY; +#define LOCK(x) (libc.threads_minus_1 ? (__lock(x),1) : ((void)(x),1)) +#define UNLOCK(x) (libc.threads_minus_1 ? (__unlock(x),1) : ((void)(x),1)) + +void __synccall(void (*)(void *), void *); +int __setxid(int, int, int, int); + +extern char **__environ; + +#undef weak_alias +#define weak_alias(old, new) \ + extern __typeof(old) new __attribute__((weak, alias(#old))) + +#undef LFS64_2 +#define LFS64_2(x, y) weak_alias(x, y) + +#undef LFS64 +#define LFS64(x) LFS64_2(x, x##64) + +#endif diff --git a/system/lib/libc/musl/src/multibyte/btowc.c b/system/lib/libc/musl/src/multibyte/btowc.c new file mode 100644 index 00000000..9d2c3b16 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/btowc.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <wchar.h> + +wint_t btowc(int c) +{ + return c<128U ? c : EOF; +} diff --git a/system/lib/libc/musl/src/multibyte/internal.c b/system/lib/libc/musl/src/multibyte/internal.c new file mode 100644 index 00000000..ab22806e --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/internal.c @@ -0,0 +1,38 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <inttypes.h> + +#include "internal.h" + +#define C(x) ( x<2 ? -1 : ( R(0x80,0xc0) | x ) ) +#define D(x) C((x+16)) +#define E(x) ( ( x==0 ? R(0xa0,0xc0) : \ + x==0xd ? R(0x80,0xa0) : \ + R(0x80,0xc0) ) \ + | ( R(0x80,0xc0) >> 6 ) \ + | x ) +#define F(x) ( ( x>=5 ? 0 : \ + x==0 ? R(0x90,0xc0) : \ + x==4 ? R(0x80,0xa0) : \ + R(0x80,0xc0) ) \ + | ( R(0x80,0xc0) >> 6 ) \ + | ( R(0x80,0xc0) >> 12 ) \ + | x ) + +const uint32_t bittab[] = { + C(0x2),C(0x3),C(0x4),C(0x5),C(0x6),C(0x7), + C(0x8),C(0x9),C(0xa),C(0xb),C(0xc),C(0xd),C(0xe),C(0xf), + D(0x0),D(0x1),D(0x2),D(0x3),D(0x4),D(0x5),D(0x6),D(0x7), + D(0x8),D(0x9),D(0xa),D(0xb),D(0xc),D(0xd),D(0xe),D(0xf), + E(0x0),E(0x1),E(0x2),E(0x3),E(0x4),E(0x5),E(0x6),E(0x7), + E(0x8),E(0x9),E(0xa),E(0xb),E(0xc),E(0xd),E(0xe),E(0xf), + F(0x0),F(0x1),F(0x2),F(0x3),F(0x4) +}; + +#ifdef BROKEN_VISIBILITY +__asm__(".hidden __fsmu8"); +#endif diff --git a/system/lib/libc/musl/src/multibyte/internal.h b/system/lib/libc/musl/src/multibyte/internal.h new file mode 100644 index 00000000..25ba240e --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/internal.h @@ -0,0 +1,22 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#define bittab __fsmu8 + +#include "libc.h" + +extern const uint32_t bittab[] ATTR_LIBC_VISIBILITY; + +/* Upper 6 state bits are a negative integer offset to bound-check next byte */ +/* equivalent to: ( (b-0x80) | (b+offset) ) & ~0x3f */ +#define OOB(c,b) (((((b)>>3)-0x10)|(((b)>>3)+((int32_t)(c)>>26))) & ~7) + +/* Interval [a,b). Either a must be 80 or b must be c0, lower 3 bits clear. */ +#define R(a,b) ((uint32_t)((a==0x80 ? 0x40-b : -a) << 23)) +#define FAILSTATE R(0x80,0x80) + +#define SA 0xc2u +#define SB 0xf4u diff --git a/system/lib/libc/musl/src/multibyte/mblen.c b/system/lib/libc/musl/src/multibyte/mblen.c new file mode 100644 index 00000000..26d35649 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/mblen.c @@ -0,0 +1,17 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +int mblen(const char *s, size_t n) +{ + return mbtowc(0, s, n); +} diff --git a/system/lib/libc/musl/src/multibyte/mbrlen.c b/system/lib/libc/musl/src/multibyte/mbrlen.c new file mode 100644 index 00000000..c9714ef8 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/mbrlen.c @@ -0,0 +1,18 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t mbrlen(const char *restrict s, size_t n, mbstate_t *restrict st) +{ + static unsigned internal; + return mbrtowc(0, s, n, st ? st : (mbstate_t *)&internal); +} diff --git a/system/lib/libc/musl/src/multibyte/mbrtowc.c b/system/lib/libc/musl/src/multibyte/mbrtowc.c new file mode 100644 index 00000000..db803661 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/mbrtowc.c @@ -0,0 +1,57 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t mbrtowc(wchar_t *restrict wc, const char *restrict src, size_t n, mbstate_t *restrict st) +{ + static unsigned internal_state; + unsigned c; + const unsigned char *s = (const void *)src; + const unsigned N = n; + + if (!st) st = (void *)&internal_state; + c = *(unsigned *)st; + + if (!s) { + if (c) goto ilseq; + return 0; + } else if (!wc) wc = (void *)&wc; + + if (!n) return -2; + if (!c) { + if (*s < 0x80) return !!(*wc = *s); + if (*s-SA > SB-SA) goto ilseq; + c = bittab[*s++-SA]; n--; + } + + if (n) { + if (OOB(c,*s)) goto ilseq; +loop: + c = c<<6 | *s++-0x80; n--; + if (!(c&(1U<<31))) { + *(unsigned *)st = 0; + *wc = c; + return N-n; + } + if (n) { + if (*s-0x80u >= 0x40) goto ilseq; + goto loop; + } + } + + *(unsigned *)st = c; + return -2; +ilseq: + *(unsigned *)st = 0; + errno = EILSEQ; + return -1; +} diff --git a/system/lib/libc/musl/src/multibyte/mbsinit.c b/system/lib/libc/musl/src/multibyte/mbsinit.c new file mode 100644 index 00000000..c0e7e494 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/mbsinit.c @@ -0,0 +1,17 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +int mbsinit(const mbstate_t *st) +{ + return !st || !*(unsigned *)st; +} diff --git a/system/lib/libc/musl/src/multibyte/mbsnrtowcs.c b/system/lib/libc/musl/src/multibyte/mbsnrtowcs.c new file mode 100644 index 00000000..33457f95 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/mbsnrtowcs.c @@ -0,0 +1,65 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> +#include <stdio.h> + +#include "internal.h" + +size_t mbsnrtowcs(wchar_t *restrict wcs, const char **restrict src, size_t n, size_t wn, mbstate_t *restrict st) +{ + size_t l, cnt=0, n2; + wchar_t *ws, wbuf[256]; + const char *s = *src; + + if (!wcs) ws = wbuf, wn = sizeof wbuf / sizeof *wbuf; + else ws = wcs; + + /* making sure output buffer size is at most n/4 will ensure + * that mbsrtowcs never reads more than n input bytes. thus + * we can use mbsrtowcs as long as it's practical.. */ + + while ( s && wn && ( (n2=n/4)>=wn || n2>32 ) ) { + if (n2>=wn) n2=wn; + n -= n2; + l = mbsrtowcs(ws, &s, n2, st); + if (!(l+1)) { + cnt = l; + wn = 0; + break; + } + if (ws != wbuf) { + ws += l; + wn -= l; + } + cnt += l; + } + if (s) while (wn && n) { + l = mbrtowc(ws, s, n, st); + if (l+2<=2) { + if (!(l+1)) { + cnt = l; + break; + } + if (!l) { + s = 0; + break; + } + /* have to roll back partial character */ + *(unsigned *)st = 0; + break; + } + s += l; n -= l; + /* safe - this loop runs fewer than sizeof(wbuf)/8 times */ + ws++; wn--; + cnt++; + } + if (wcs) *src = s; + return cnt; +} diff --git a/system/lib/libc/musl/src/multibyte/mbsrtowcs.c b/system/lib/libc/musl/src/multibyte/mbsrtowcs.c new file mode 100644 index 00000000..75a493c4 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/mbsrtowcs.c @@ -0,0 +1,100 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t mbsrtowcs(wchar_t *restrict ws, const char **restrict src, size_t wn, mbstate_t *restrict st) +{ + const unsigned char *s = (const void *)*src; + size_t wn0 = wn; + unsigned c = 0; + + if (st && (c = *(unsigned *)st)) { + if (ws) { + *(unsigned *)st = 0; + goto resume; + } else { + goto resume0; + } + } + + if (!ws) for (;;) { + if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) { + while (!(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) { + s += 4; + wn -= 4; + } + } + if (*s-1u < 0x7f) { + s++; + wn--; + continue; + } + if (*s-SA > SB-SA) break; + c = bittab[*s++-SA]; +resume0: + if (OOB(c,*s)) { s--; break; } + s++; + if (c&(1U<<25)) { + if (*s-0x80u >= 0x40) { s-=2; break; } + s++; + if (c&(1U<<19)) { + if (*s-0x80u >= 0x40) { s-=3; break; } + s++; + } + } + wn--; + c = 0; + } else for (;;) { + if (!wn) return wn0; + if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) { + while (wn>=4 && !(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) { + *ws++ = *s++; + *ws++ = *s++; + *ws++ = *s++; + *ws++ = *s++; + wn -= 4; + } + } + if (*s-1u < 0x7f) { + *ws++ = *s++; + wn--; + continue; + } + if (*s-SA > SB-SA) break; + c = bittab[*s++-SA]; +resume: + if (OOB(c,*s)) { s--; break; } + c = (c<<6) | *s++-0x80; + if (c&(1U<<31)) { + if (*s-0x80u >= 0x40) { s-=2; break; } + c = (c<<6) | *s++-0x80; + if (c&(1U<<31)) { + if (*s-0x80u >= 0x40) { s-=3; break; } + c = (c<<6) | *s++-0x80; + } + } + *ws++ = c; + wn--; + c = 0; + } + + if (!c && !*s) { + if (ws) { + *ws = 0; + *src = 0; + } + return wn0-wn; + } + errno = EILSEQ; + if (ws) *src = (const void *)s; + return -1; +} diff --git a/system/lib/libc/musl/src/multibyte/mbstowcs.c b/system/lib/libc/musl/src/multibyte/mbstowcs.c new file mode 100644 index 00000000..dc0d4594 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/mbstowcs.c @@ -0,0 +1,7 @@ +#include <stdlib.h> +#include <wchar.h> + +size_t mbstowcs(wchar_t *restrict ws, const char *restrict s, size_t wn) +{ + return mbsrtowcs(ws, (void*)&s, wn, 0); +} diff --git a/system/lib/libc/musl/src/multibyte/mbtowc.c b/system/lib/libc/musl/src/multibyte/mbtowc.c new file mode 100644 index 00000000..ec9e54ad --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/mbtowc.c @@ -0,0 +1,53 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" +#include <stdio.h> +int mbtowc(wchar_t *restrict wc, const char *restrict src, size_t n) +{ + unsigned c; + const unsigned char *s = (const void *)src; + + if (!s) return 0; + if (!n) goto ilseq; + if (!wc) wc = (void *)&wc; + + if (*s < 0x80) return !!(*wc = *s); + if (*s-SA > SB-SA) goto ilseq; + c = bittab[*s++-SA]; + + /* Avoid excessive checks against n: If shifting the state n-1 + * times does not clear the high bit, then the value of n is + * insufficient to read a character */ + if (n<4 && ((c<<(6*n-6)) & (1U<<31))) goto ilseq; + + if (OOB(c,*s)) goto ilseq; + c = c<<6 | *s++-0x80; + if (!(c&(1U<<31))) { + *wc = c; + return 2; + } + + if (*s-0x80u >= 0x40) goto ilseq; + c = c<<6 | *s++-0x80; + if (!(c&(1U<<31))) { + *wc = c; + return 3; + } + + if (*s-0x80u >= 0x40) goto ilseq; + *wc = c<<6 | *s++-0x80; + return 4; + +ilseq: + errno = EILSEQ; + return -1; +} diff --git a/system/lib/libc/musl/src/multibyte/wcrtomb.c b/system/lib/libc/musl/src/multibyte/wcrtomb.c new file mode 100644 index 00000000..250649f5 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/wcrtomb.c @@ -0,0 +1,38 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t wcrtomb(char *restrict s, wchar_t wc, mbstate_t *restrict st) +{ + if (!s) return 1; + if ((unsigned)wc < 0x80) { + *s = wc; + return 1; + } else if ((unsigned)wc < 0x800) { + *s++ = 0xc0 | (wc>>6); + *s = 0x80 | (wc&0x3f); + return 2; + } else if ((unsigned)wc < 0xd800 || (unsigned)wc-0xe000 < 0x2000) { + *s++ = 0xe0 | (wc>>12); + *s++ = 0x80 | ((wc>>6)&0x3f); + *s = 0x80 | (wc&0x3f); + return 3; + } else if ((unsigned)wc-0x10000 < 0x100000) { + *s++ = 0xf0 | (wc>>18); + *s++ = 0x80 | ((wc>>12)&0x3f); + *s++ = 0x80 | ((wc>>6)&0x3f); + *s = 0x80 | (wc&0x3f); + return 4; + } + errno = EILSEQ; + return -1; +} diff --git a/system/lib/libc/musl/src/multibyte/wcsnrtombs.c b/system/lib/libc/musl/src/multibyte/wcsnrtombs.c new file mode 100644 index 00000000..a2e308b3 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/wcsnrtombs.c @@ -0,0 +1,52 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t wcsnrtombs(char *restrict dst, const wchar_t **restrict wcs, size_t wn, size_t n, mbstate_t *restrict st) +{ + size_t l, cnt=0, n2; + char *s, buf[256]; + const wchar_t *ws = *wcs; + + if (!dst) s = buf, n = sizeof buf; + else s = dst; + + while ( ws && n && ( (n2=wn)>=n || n2>32 ) ) { + if (n2>=n) n2=n; + wn -= n2; + l = wcsrtombs(s, &ws, n2, 0); + if (!(l+1)) { + cnt = l; + n = 0; + break; + } + if (s != buf) { + s += l; + n -= l; + } + cnt += l; + } + if (ws) while (n && wn) { + l = wcrtomb(s, *ws, 0); + if ((l+1)<=1) { + if (!l) ws = 0; + else cnt = l; + break; + } + ws++; wn--; + /* safe - this loop runs fewer than sizeof(buf) times */ + s+=l; n-=l; + cnt++; + } + if (dst) *wcs = ws; + return cnt; +} diff --git a/system/lib/libc/musl/src/multibyte/wcsrtombs.c b/system/lib/libc/musl/src/multibyte/wcsrtombs.c new file mode 100644 index 00000000..d48a65e7 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/wcsrtombs.c @@ -0,0 +1,58 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t wcsrtombs(char *restrict s, const wchar_t **restrict ws, size_t n, mbstate_t *restrict st) +{ + const wchar_t *ws2; + char buf[4]; + size_t N = n, l; + if (!s) { + for (n=0, ws2=*ws; *ws2; ws2++) { + if (*ws2 >= 0x80u) { + l = wcrtomb(buf, *ws2, 0); + if (!(l+1)) return -1; + n += l; + } else n++; + } + return n; + } + while (n>=4 && **ws) { + if (**ws >= 0x80u) { + l = wcrtomb(s, **ws, 0); + if (!(l+1)) return -1; + s += l; + n -= l; + } else { + *s++ = **ws; + n--; + } + (*ws)++; + } + while (n && **ws) { + if (**ws >= 0x80u) { + l = wcrtomb(buf, **ws, 0); + if (!(l+1)) return -1; + if (l>n) return N-n; + wcrtomb(s, **ws, 0); + s += l; + n -= l; + } else { + *s++ = **ws; + n--; + } + (*ws)++; + } + if (n) *s = 0; + *ws = 0; + return N-n; +} diff --git a/system/lib/libc/musl/src/multibyte/wcstombs.c b/system/lib/libc/musl/src/multibyte/wcstombs.c new file mode 100644 index 00000000..ab152874 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/wcstombs.c @@ -0,0 +1,7 @@ +#include <stdlib.h> +#include <wchar.h> + +size_t wcstombs(char *restrict s, const wchar_t *restrict ws, size_t n) +{ + return wcsrtombs(s, &(const wchar_t *){ws}, n, 0); +} diff --git a/system/lib/libc/musl/src/multibyte/wctob.c b/system/lib/libc/musl/src/multibyte/wctob.c new file mode 100644 index 00000000..d6353ee1 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/wctob.c @@ -0,0 +1,8 @@ +#include <stdio.h> +#include <wchar.h> + +int wctob(wint_t c) +{ + if (c < 128U) return c; + return EOF; +} diff --git a/system/lib/libc/musl/src/multibyte/wctomb.c b/system/lib/libc/musl/src/multibyte/wctomb.c new file mode 100644 index 00000000..6910ef37 --- /dev/null +++ b/system/lib/libc/musl/src/multibyte/wctomb.c @@ -0,0 +1,18 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +int wctomb(char *s, wchar_t wc) +{ + if (!s) return 0; + return wcrtomb(s, wc, 0); +} diff --git a/system/lib/libc/musl/src/string/wcpcpy.c b/system/lib/libc/musl/src/string/wcpcpy.c new file mode 100644 index 00000000..ef401343 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcpcpy.c @@ -0,0 +1,6 @@ +#include <wchar.h> + +wchar_t *wcpcpy(wchar_t *restrict d, const wchar_t *restrict s) +{ + return wcscpy(d, s) + wcslen(s); +} diff --git a/system/lib/libc/musl/src/string/wcpncpy.c b/system/lib/libc/musl/src/string/wcpncpy.c new file mode 100644 index 00000000..b667f6d6 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcpncpy.c @@ -0,0 +1,6 @@ +#include <wchar.h> + +wchar_t *wcpncpy(wchar_t *restrict d, const wchar_t *restrict s, size_t n) +{ + return wcsncpy(d, s, n) + wcsnlen(s, n); +} diff --git a/system/lib/libc/musl/src/string/wcscasecmp.c b/system/lib/libc/musl/src/string/wcscasecmp.c new file mode 100644 index 00000000..3edeec7d --- /dev/null +++ b/system/lib/libc/musl/src/string/wcscasecmp.c @@ -0,0 +1,7 @@ +#include <wchar.h> +#include <wctype.h> + +int wcscasecmp(const wchar_t *l, const wchar_t *r) +{ + return wcsncasecmp(l, r, -1); +} diff --git a/system/lib/libc/musl/src/string/wcscasecmp_l.c b/system/lib/libc/musl/src/string/wcscasecmp_l.c new file mode 100644 index 00000000..065dd0aa --- /dev/null +++ b/system/lib/libc/musl/src/string/wcscasecmp_l.c @@ -0,0 +1,6 @@ +#include <wchar.h> + +int wcscasecmp_l(const wchar_t *l, const wchar_t *r, locale_t locale) +{ + return wcscasecmp(l, r); +} diff --git a/system/lib/libc/musl/src/string/wcscat.c b/system/lib/libc/musl/src/string/wcscat.c new file mode 100644 index 00000000..d4f00ebd --- /dev/null +++ b/system/lib/libc/musl/src/string/wcscat.c @@ -0,0 +1,7 @@ +#include <wchar.h> + +wchar_t *wcscat(wchar_t *restrict dest, const wchar_t *restrict src) +{ + wcscpy(dest + wcslen(dest), src); + return dest; +} diff --git a/system/lib/libc/musl/src/string/wcschr.c b/system/lib/libc/musl/src/string/wcschr.c new file mode 100644 index 00000000..8dfc2f31 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcschr.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +wchar_t *wcschr(const wchar_t *s, wchar_t c) +{ + if (!c) return (wchar_t *)s + wcslen(s); + for (; *s && *s != c; s++); + return *s ? (wchar_t *)s : 0; +} diff --git a/system/lib/libc/musl/src/string/wcscmp.c b/system/lib/libc/musl/src/string/wcscmp.c new file mode 100644 index 00000000..26eeee70 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcscmp.c @@ -0,0 +1,7 @@ +#include <wchar.h> + +int wcscmp(const wchar_t *l, const wchar_t *r) +{ + for (; *l==*r && *l && *r; l++, r++); + return *l - *r; +} diff --git a/system/lib/libc/musl/src/string/wcscpy.c b/system/lib/libc/musl/src/string/wcscpy.c new file mode 100644 index 00000000..625bf53d --- /dev/null +++ b/system/lib/libc/musl/src/string/wcscpy.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +wchar_t *wcscpy(wchar_t *restrict d, const wchar_t *restrict s) +{ + wchar_t *a = d; + while ((*d++ = *s++)); + return a; +} diff --git a/system/lib/libc/musl/src/string/wcscspn.c b/system/lib/libc/musl/src/string/wcscspn.c new file mode 100644 index 00000000..c4e52722 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcscspn.c @@ -0,0 +1,10 @@ +#include <wchar.h> + +size_t wcscspn(const wchar_t *s, const wchar_t *c) +{ + const wchar_t *a; + if (!c[0]) return wcslen(s); + if (!c[1]) return (s=wcschr(a=s, *c)) ? s-a : wcslen(a); + for (a=s; *s && !wcschr(c, *s); s++); + return s-a; +} diff --git a/system/lib/libc/musl/src/string/wcsdup.c b/system/lib/libc/musl/src/string/wcsdup.c new file mode 100644 index 00000000..dd49c1b6 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsdup.c @@ -0,0 +1,11 @@ +#include <stdlib.h> +#include <wchar.h> +#include "libc.h" + +wchar_t *wcsdup(const wchar_t *s) +{ + size_t l = wcslen(s); + wchar_t *d = malloc((l+1)*sizeof(wchar_t)); + if (!d) return NULL; + return wmemcpy(d, s, l+1); +} diff --git a/system/lib/libc/musl/src/string/wcslen.c b/system/lib/libc/musl/src/string/wcslen.c new file mode 100644 index 00000000..1b7b6655 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcslen.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +size_t wcslen(const wchar_t *s) +{ + const wchar_t *a; + for (a=s; *s; s++); + return s-a; +} diff --git a/system/lib/libc/musl/src/string/wcsncasecmp.c b/system/lib/libc/musl/src/string/wcsncasecmp.c new file mode 100644 index 00000000..8fefe799 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsncasecmp.c @@ -0,0 +1,9 @@ +#include <wchar.h> +#include <wctype.h> + +int wcsncasecmp(const wchar_t *l, const wchar_t *r, size_t n) +{ + if (!n--) return 0; + for (; *l && *r && n && (*l == *r || towlower(*l) == towlower(*r)); l++, r++, n--); + return towlower(*l) - towlower(*r); +} diff --git a/system/lib/libc/musl/src/string/wcsncasecmp_l.c b/system/lib/libc/musl/src/string/wcsncasecmp_l.c new file mode 100644 index 00000000..63872481 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsncasecmp_l.c @@ -0,0 +1,6 @@ +#include <wchar.h> + +int wcsncasecmp_l(const wchar_t *l, const wchar_t *r, size_t n, locale_t locale) +{ + return wcsncasecmp(l, r, n); +} diff --git a/system/lib/libc/musl/src/string/wcsncat.c b/system/lib/libc/musl/src/string/wcsncat.c new file mode 100644 index 00000000..8563f1a2 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsncat.c @@ -0,0 +1,10 @@ +#include <wchar.h> + +wchar_t *wcsncat(wchar_t *restrict d, const wchar_t *restrict s, size_t n) +{ + wchar_t *a = d; + d += wcslen(d); + while (n && *s) n--, *d++ = *s++; + *d++ = 0; + return a; +} diff --git a/system/lib/libc/musl/src/string/wcsncmp.c b/system/lib/libc/musl/src/string/wcsncmp.c new file mode 100644 index 00000000..4ab32a92 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsncmp.c @@ -0,0 +1,7 @@ +#include <wchar.h> + +int wcsncmp(const wchar_t *l, const wchar_t *r, size_t n) +{ + for (; n && *l==*r && *l && *r; n--, l++, r++); + return n ? *l - *r : 0; +} diff --git a/system/lib/libc/musl/src/string/wcsncpy.c b/system/lib/libc/musl/src/string/wcsncpy.c new file mode 100644 index 00000000..4bede04d --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsncpy.c @@ -0,0 +1,9 @@ +#include <wchar.h> + +wchar_t *wcsncpy(wchar_t *restrict d, const wchar_t *restrict s, size_t n) +{ + wchar_t *a = d; + while (n && *s) n--, *d++ = *s++; + wmemset(d, 0, n); + return a; +} diff --git a/system/lib/libc/musl/src/string/wcsnlen.c b/system/lib/libc/musl/src/string/wcsnlen.c new file mode 100644 index 00000000..a7763373 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsnlen.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +size_t wcsnlen(const wchar_t *s, size_t n) +{ + const wchar_t *z = wmemchr(s, 0, n); + if (z) n = z-s; + return n; +} diff --git a/system/lib/libc/musl/src/string/wcspbrk.c b/system/lib/libc/musl/src/string/wcspbrk.c new file mode 100644 index 00000000..0c72c197 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcspbrk.c @@ -0,0 +1,7 @@ +#include <wchar.h> + +wchar_t *wcspbrk(const wchar_t *s, const wchar_t *b) +{ + s += wcscspn(s, b); + return *s ? (wchar_t *)s : NULL; +} diff --git a/system/lib/libc/musl/src/string/wcsrchr.c b/system/lib/libc/musl/src/string/wcsrchr.c new file mode 100644 index 00000000..8961b9e2 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsrchr.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +wchar_t *wcsrchr(const wchar_t *s, wchar_t c) +{ + const wchar_t *p; + for (p=s+wcslen(s); p>=s && *p!=c; p--); + return p>=s ? (wchar_t *)p : 0; +} diff --git a/system/lib/libc/musl/src/string/wcsspn.c b/system/lib/libc/musl/src/string/wcsspn.c new file mode 100644 index 00000000..4320d8f6 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsspn.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +size_t wcsspn(const wchar_t *s, const wchar_t *c) +{ + const wchar_t *a; + for (a=s; *s && wcschr(c, *s); s++); + return s-a; +} diff --git a/system/lib/libc/musl/src/string/wcsstr.c b/system/lib/libc/musl/src/string/wcsstr.c new file mode 100644 index 00000000..037d0965 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcsstr.c @@ -0,0 +1,108 @@ +#include <wchar.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#define MAX(a,b) ((a)>(b)?(a):(b)) +#define MIN(a,b) ((a)<(b)?(a):(b)) + +static wchar_t *twoway_wcsstr(const wchar_t *h, const wchar_t *n) +{ + const wchar_t *z; + size_t l, ip, jp, k, p, ms, p0, mem, mem0; + + /* Computing length of needle */ + for (l=0; n[l] && h[l]; l++); + if (n[l]) return 0; /* hit the end of h */ + + /* Compute maximal suffix */ + ip = -1; jp = 0; k = p = 1; + while (jp+k<l) { + if (n[ip+k] == n[jp+k]) { + if (k == p) { + jp += p; + k = 1; + } else k++; + } else if (n[ip+k] > n[jp+k]) { + jp += k; + k = 1; + p = jp - ip; + } else { + ip = jp++; + k = p = 1; + } + } + ms = ip; + p0 = p; + + /* And with the opposite comparison */ + ip = -1; jp = 0; k = p = 1; + while (jp+k<l) { + if (n[ip+k] == n[jp+k]) { + if (k == p) { + jp += p; + k = 1; + } else k++; + } else if (n[ip+k] < n[jp+k]) { + jp += k; + k = 1; + p = jp - ip; + } else { + ip = jp++; + k = p = 1; + } + } + if (ip+1 > ms+1) ms = ip; + else p = p0; + + /* Periodic needle? */ + if (wmemcmp(n, n+p, ms+1)) { + mem0 = 0; + p = MAX(ms, l-ms-1) + 1; + } else mem0 = l-p; + mem = 0; + + /* Initialize incremental end-of-haystack pointer */ + z = h; + + /* Search loop */ + for (;;) { + /* Update incremental end-of-haystack pointer */ + if (z-h < l) { + /* Fast estimate for MIN(l,63) */ + size_t grow = l | 63; + const wchar_t *z2 = wmemchr(z, 0, grow); + if (z2) { + z = z2; + if (z-h < l) return 0; + } else z += grow; + } + + /* Compare right half */ + for (k=MAX(ms+1,mem); n[k] && n[k] == h[k]; k++); + if (n[k]) { + h += k-ms; + mem = 0; + continue; + } + /* Compare left half */ + for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--); + if (k == mem) return (wchar_t *)h; + h += p; + mem = mem0; + } +} + +wchar_t *wcsstr(const wchar_t *restrict h, const wchar_t *restrict n) +{ + /* Return immediately on empty needle or haystack */ + if (!n[0]) return (wchar_t *)h; + if (!h[0]) return 0; + + /* Use faster algorithms for short needles */ + h = wcschr(h, *n); + if (!h || !n[1]) return (wchar_t *)h; + if (!h[1]) return 0; + + return twoway_wcsstr(h, n); +} diff --git a/system/lib/libc/musl/src/string/wcstok.c b/system/lib/libc/musl/src/string/wcstok.c new file mode 100644 index 00000000..ecc80331 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcstok.c @@ -0,0 +1,12 @@ +#include <wchar.h> + +wchar_t *wcstok(wchar_t *restrict s, const wchar_t *restrict sep, wchar_t **restrict p) +{ + if (!s && !(s = *p)) return NULL; + s += wcsspn(s, sep); + if (!*s) return *p = 0; + *p = s + wcscspn(s, sep); + if (**p) *(*p)++ = 0; + else *p = 0; + return s; +} diff --git a/system/lib/libc/musl/src/string/wcswcs.c b/system/lib/libc/musl/src/string/wcswcs.c new file mode 100644 index 00000000..9cfe4ac4 --- /dev/null +++ b/system/lib/libc/musl/src/string/wcswcs.c @@ -0,0 +1,6 @@ +#include <wchar.h> + +wchar_t *wcswcs(const wchar_t *haystack, const wchar_t *needle) +{ + return wcsstr(haystack, needle); +} diff --git a/system/lib/libc/musl/src/string/wmemchr.c b/system/lib/libc/musl/src/string/wmemchr.c new file mode 100644 index 00000000..37d69629 --- /dev/null +++ b/system/lib/libc/musl/src/string/wmemchr.c @@ -0,0 +1,8 @@ +#include <string.h> +#include <wchar.h> + +wchar_t *wmemchr(const wchar_t *s, wchar_t c, size_t n) +{ + for (; n && *s != c; n--, s++); + return n ? (wchar_t *)s : 0; +} diff --git a/system/lib/libc/musl/src/string/wmemcmp.c b/system/lib/libc/musl/src/string/wmemcmp.c new file mode 100644 index 00000000..6788a383 --- /dev/null +++ b/system/lib/libc/musl/src/string/wmemcmp.c @@ -0,0 +1,8 @@ +#include <string.h> +#include <wchar.h> + +int wmemcmp(const wchar_t *l, const wchar_t *r, size_t n) +{ + for (; n && *l==*r; n--, l++, r++); + return n ? *l-*r : 0; +} diff --git a/system/lib/libc/musl/src/string/wmemcpy.c b/system/lib/libc/musl/src/string/wmemcpy.c new file mode 100644 index 00000000..55a8e1d8 --- /dev/null +++ b/system/lib/libc/musl/src/string/wmemcpy.c @@ -0,0 +1,9 @@ +#include <string.h> +#include <wchar.h> + +wchar_t *wmemcpy(wchar_t *restrict d, const wchar_t *restrict s, size_t n) +{ + wchar_t *a = d; + while (n--) *d++ = *s++; + return a; +} diff --git a/system/lib/libc/musl/src/string/wmemmove.c b/system/lib/libc/musl/src/string/wmemmove.c new file mode 100644 index 00000000..cde4feec --- /dev/null +++ b/system/lib/libc/musl/src/string/wmemmove.c @@ -0,0 +1,12 @@ +#include <string.h> +#include <wchar.h> + +wchar_t *wmemmove(wchar_t *d, const wchar_t *s, size_t n) +{ + wchar_t *d0 = d; + if ((size_t)(d-s) < n) + while (n--) d[n] = s[n]; + else + while (n--) *d++ = *s++; + return d0; +} diff --git a/system/lib/libc/musl/src/string/wmemset.c b/system/lib/libc/musl/src/string/wmemset.c new file mode 100644 index 00000000..1a2a8618 --- /dev/null +++ b/system/lib/libc/musl/src/string/wmemset.c @@ -0,0 +1,9 @@ +#include <string.h> +#include <wchar.h> + +wchar_t *wmemset(wchar_t *d, wchar_t c, size_t n) +{ + wchar_t *ret = d; + while (n--) *d++ = c; + return ret; +} diff --git a/system/lib/libcextra.symbols b/system/lib/libcextra.symbols new file mode 100644 index 00000000..97805c96 --- /dev/null +++ b/system/lib/libcextra.symbols @@ -0,0 +1,48 @@ + U __errno + U __fsmu8 + T btowc + U malloc + T mblen + T mbrlen + d mbrlen.internal + T mbrtowc + d mbrtowc.internal_state + T mbsinit + T mbsnrtowcs + T mbsrtowcs + T mbstowcs + T mbtowc + U towlower + t twoway_wcsstr + T wcpcpy + T wcpncpy + T wcrtomb + T wcscasecmp + T wcscat + T wcschr + T wcscmp + T wcscpy + T wcscspn + T wcsdup + T wcslen + T wcsncasecmp + T wcsncat + T wcsncmp + T wcsncpy + T wcsnlen + T wcsnrtombs + T wcspbrk + T wcsrchr + T wcsrtombs + T wcsspn + T wcsstr + T wcstok + T wcstombs + T wcswcs + T wctob + T wctomb + T wmemchr + T wmemcmp + T wmemcpy + T wmemmove + T wmemset diff --git a/tests/runner.py b/tests/runner.py index 811d32a5..5602fd94 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -2309,6 +2309,23 @@ cat |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO|''', ['wowie', 'too' ''' self.do_run(src, 'Assertion failed: 1 == false') + def test_libcextra(self): + if self.emcc_args is None: return self.skip('needs emcc for libcextra') + src = r''' + #include <stdio.h> + #include <wchar.h> + + int main() + { + const wchar_t* wstr = L"Hello"; + + printf("wcslen: %d\n", wcslen(wstr)); + + return 0; + } + ''' + self.do_run(src, 'wcslen: 5') + def test_longjmp(self): src = r''' #include <stdio.h> @@ -2489,6 +2506,145 @@ Calling longjmp the second time! Exception execution path of first function! 1 ''') + def test_longjmp_funcptr(self): + src = r''' + #include <stdio.h> + #include <setjmp.h> + + static jmp_buf buf; + + void (*fp)() = NULL; + + void second(void) { + printf("second\n"); // prints + longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 + } + + void first(void) { + fp(); + printf("first\n"); // does not print + } + + int main(int argc, char **argv) { + fp = argc == 200 ? NULL : second; + + volatile int x = 0; + if ( ! setjmp(buf) ) { + x++; + first(); // when executed, setjmp returns 0 + } else { // when longjmp jumps back, setjmp returns 1 + printf("main: %d\n", x); // prints + } + + return 0; + } + ''' + self.do_run(src, 'second\nmain: 1\n') + + def test_longjmp_repeat(self): + Settings.MAX_SETJMPS = 1 + + src = r''' + #include <stdio.h> + #include <setjmp.h> + + static jmp_buf buf; + + int main() { + volatile int x = 0; + printf("setjmp:%d\n", setjmp(buf)); + x++; + printf("x:%d\n", x); + if (x < 4) longjmp(buf, x*2); + return 0; + } + ''' + self.do_run(src, '''setjmp:0 +x:1 +setjmp:2 +x:2 +setjmp:4 +x:3 +setjmp:6 +x:4 +''') + + def test_longjmp_stacked(self): + src = r''' + #include <stdio.h> + #include <setjmp.h> + #include <stdlib.h> + #include <string.h> + + int bottom, top; + + int run(int y) { + // confuse stack + char *s = (char*)alloca(100); + memset(s, 1, 100); + s[y] = y; + s[y/2] = y*2; + volatile int x = s[y]; + top = (int)alloca(4); + if (x <= 2) return x; + jmp_buf buf; + printf("setjmp of %d\n", x); + if (setjmp(buf) == 0) { + printf("going\n"); + x += run(x/2); + longjmp(buf, 1); + } + printf("back\n"); + return x/2; + } + + int main(int argc, char **argv) { + int sum = 0; + for (int i = 0; i < argc*2; i++) { + bottom = (int)alloca(4); + sum += run(10); + // scorch the earth + if (bottom < top) { + memset((void*)bottom, 1, top - bottom); + } else { + memset((void*)top, 1, bottom - top); + } + } + printf("%d\n", sum); + return sum; + } + ''' + self.do_run(src, '''setjmp of 10 +going +setjmp of 5 +going +back +back +setjmp of 10 +going +setjmp of 5 +going +back +back +12 +''') + + def test_setjmp_many(self): + src = r''' + #include <stdio.h> + #include <setjmp.h> + + int main(int argc) { + jmp_buf buf; + for (int i = 0; i < NUM; i++) printf("%d\n", setjmp(buf)); + if (argc-- == 1131) longjmp(buf, 11); + return 0; + } + ''' + for num in [Settings.MAX_SETJMPS, Settings.MAX_SETJMPS+1]: + print num + self.do_run(src.replace('NUM', str(num)), '0\n' * num if num <= Settings.MAX_SETJMPS or not Settings.ASM_JS else 'build with a higher value for MAX_SETJMPS') + def test_exceptions(self): if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') @@ -8015,11 +8171,12 @@ def process(filename): # Kill off the dead function, still works and it is not emitted Settings.DEAD_FUNCTIONS = ['_unused'] js = test('*9*') - assert 'function _unused(' not in js + assert 'function _unused($' not in js # no compiled code + assert 'function _unused(' in js # lib-generated stub Settings.DEAD_FUNCTIONS = [] # Run the same code with argc that uses the dead function, see abort - test(('abort', 'is not a function'), args=['a', 'b'], no_build=True) + test(('missing function: unused'), args=['a', 'b'], no_build=True) # Normal stuff run_all('normal', r''' @@ -8094,7 +8251,7 @@ def process(filename): before = len(open('normal.js').read()) after = len(open('pgoed.js').read()) - assert after < 0.66 * before, [before, after] # expect a big size reduction + assert after < 0.90 * before, [before, after] # expect a size reduction # with response in settings element itself @@ -10201,7 +10358,7 @@ f.close() (1, 0, 3, 2), (1, 1, 3, 4) ]: print asm, linkable, chunks, js_chunks - output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm, '-s', 'UNRESOLVED_AS_DEAD=1'] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate() + output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1', '-s', 'LINKABLE=%d' % linkable, '-s', 'ASM_JS=%d' % asm] + (['-O2'] if asm else []), stdout=PIPE, stderr=PIPE).communicate() ok = False for c in range(chunks, chunks+2): ok = ok or ('phase 2 working on %d chunks' % c in err) diff --git a/tools/find_bigfuncs.py b/tools/find_bigfuncs.py new file mode 100644 index 00000000..ebff8b6e --- /dev/null +++ b/tools/find_bigfuncs.py @@ -0,0 +1,23 @@ +''' +Simple tool to find big functions in an .ll file. Anything over i64 is of interest. +''' + +import os, sys, re + +filename = sys.argv[1] +i = 0 +maxx = -1 +maxxest = '?' +start = -1 +curr = '?' +for line in open(filename): + i += 1 + if line.startswith('function '): + start = i + curr = line + elif line.startswith('}'): + size = i - start + if size > maxx: + maxx = size + maxxest = curr +print maxx, 'lines in', maxxest diff --git a/tools/shared.py b/tools/shared.py index 6b5ad835..33b0273e 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -111,7 +111,7 @@ else: config_file = config_file.replace('{{{ EMSCRIPTEN_ROOT }}}', __rootpath__) llvm_root = '/usr/bin' try: - llvm_root = os.path.dirname(Popen(['which', 'clang'], stdout=PIPE).communicate()[0].replace('\n', '')) + llvm_root = os.path.dirname(Popen(['which', 'llvm-dis'], stdout=PIPE).communicate()[0].replace('\n', '')) except: pass config_file = config_file.replace('{{{ LLVM_ROOT }}}', llvm_root) @@ -199,7 +199,7 @@ def check_node_version(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.3.6' +EMSCRIPTEN_VERSION = '1.3.7' def check_sanity(force=False): try: @@ -401,7 +401,7 @@ except: # Force a simple, standard target as much as possible: target 32-bit linux, and disable various flags that hint at other platforms COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__i386', '-Ui386', '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__', - '-DEMSCRIPTEN', '-U__STRICT_ANSI__', + '-DEMSCRIPTEN', '-D__EMSCRIPTEN__', '-U__STRICT_ANSI__', '-target', 'i386-pc-linux-gnu', '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno'] USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK') |