From a36be01d81b982ff35a938073fec0db6dd10a9ca Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Wed, 2 Sep 2026 15:11:45 -0700 Subject: [PATCH] [libc] Always use __builtin_wasm_memory_fill in memset Followup to #27653 and #27656. Replace Musl's `memset.c` and the `-Oz` logic in `emscripten_memset.c` with `__builtin_wasm_memory_fill`. Under ASan, keep `__attribute__((no_sanitize("address")))` on the naive scalar loop because ASan's shadow memory poisoner calls `REAL(memset)` directly on shadow memory itself, which would otherwise trigger an immediate shadow-on-shadow fault. Microbenchmark results (100k iterations across sizes 1B to 16KB): Aligned sets (sizes 1B to 16KB): - V8: 30.36ms vs 70.52ms (56.9% faster, 2.3x) - SpiderMonkey: 31.91ms vs 67.01ms (52.4% faster, 2.1x) - JavaScriptCore: 26.70ms vs 70.44ms (62.1% faster, 2.6x) Unaligned sets (offsets 1..3 across sizes 1B to 16KB): - V8: 30.61ms vs 65.28ms (53.1% faster, 2.1x) - SpiderMonkey: 32.94ms vs 64.28ms (48.8% faster, 2.0x) - JavaScriptCore: 26.90ms vs 63.24ms (57.5% faster, 2.4x) --- system/lib/libc/emscripten_memset.c | 47 +++++++++++-------- system/lib/libc/musl/src/string/memcmp.c | 2 +- test/codesize/test_codesize_cxx_ctors1.json | 8 ++-- test/codesize/test_codesize_cxx_ctors2.json | 8 ++-- test/codesize/test_codesize_cxx_except.json | 8 ++-- .../test_codesize_cxx_except_wasm.json | 8 ++-- .../test_codesize_cxx_except_wasm_legacy.json | 8 ++-- test/codesize/test_codesize_cxx_mangle.json | 8 ++-- test/codesize/test_codesize_cxx_noexcept.json | 8 ++-- test/codesize/test_codesize_cxx_wasmfs.json | 8 ++-- test/codesize/test_codesize_hello_O0.json | 8 ++-- test/codesize/test_codesize_hello_dylink.json | 9 ++-- .../test_codesize_hello_dylink_all.json | 5 +- test/codesize/test_unoptimized_code_size.json | 16 +++---- tools/native_sigs.py | 1 - tools/system_libs.py | 11 +---- 16 files changed, 79 insertions(+), 84 deletions(-) diff --git a/system/lib/libc/emscripten_memset.c b/system/lib/libc/emscripten_memset.c index 2593cb4f3e481..7b77e8bada9c6 100644 --- a/system/lib/libc/emscripten_memset.c +++ b/system/lib/libc/emscripten_memset.c @@ -1,30 +1,37 @@ -#include "emscripten_internal.h" // for emscripten_memset_big - -#if defined(__has_feature) && __has_feature(address_sanitizer) -// build an uninstrumented version of memset -__attribute__((no_sanitize("address"))) void *__musl_memset(void *str, int c, size_t n); -__attribute__((no_sanitize("address"))) void *__memset(void *str, int c, size_t n); -#endif - -__attribute__((__weak__)) void *__musl_memset(void *str, int c, size_t n); -__attribute__((__weak__)) void *__memset(void *str, int c, size_t n); +/* + * Copyright 2019 The Emscripten Authors. All rights reserved. + * Emscripten is available under two separate licenses, the MIT license and the + * University of Illinois/NCSA Open Source License. Both these licenses can be + * found in the LICENSE file. + */ + + +#include +#include +#include "libc.h" + +// Use the simple/naive version of memset when building with asan. +// Note: ASan's shadow memory poisoner calls REAL(memset) directly on shadow +// memory, so this must remain uninstrumented to prevent ASan from checking +// the shadow memory of the shadow memory itself. +#if __has_feature(address_sanitizer) + +static __attribute__((no_sanitize("address"))) void *__memset(void *dest, int c, size_t n) { + unsigned char *d = (unsigned char *)dest; + while (n--) *d++ = (unsigned char)c; + return dest; +} -#if defined(EMSCRIPTEN_OPTIMIZE_FOR_OZ) +#else -void *__memset(void *str, int c, size_t n) { +static void *__memset(void *dest, int c, size_t n) { // memory.fill traps on OOB zero-length sets, but memset must not. if (n) { - __builtin_wasm_memory_fill(0, str, c, n); + __builtin_wasm_memory_fill(0, dest, c, n); } - return str; + return dest; } -#else - -#define memset __memset -#include "musl/src/string/memset.c" -#undef memset - #endif weak_alias(__memset, emscripten_builtin_memset); diff --git a/system/lib/libc/musl/src/string/memcmp.c b/system/lib/libc/musl/src/string/memcmp.c index c93cf8cc97330..ba9ab6d41d3c9 100644 --- a/system/lib/libc/musl/src/string/memcmp.c +++ b/system/lib/libc/musl/src/string/memcmp.c @@ -1,4 +1,4 @@ -#if __EMSCRIPTEN__ +#ifdef __EMSCRIPTEN__ #include #endif #include diff --git a/test/codesize/test_codesize_cxx_ctors1.json b/test/codesize/test_codesize_cxx_ctors1.json index 6b3623a9cac8d..bf0ec566af858 100644 --- a/test/codesize/test_codesize_cxx_ctors1.json +++ b/test/codesize/test_codesize_cxx_ctors1.json @@ -1,10 +1,10 @@ { "a.out.js": 19224, "a.out.js.gz": 8124, - "a.out.nodebug.wasm": 134250, - "a.out.nodebug.wasm.gz": 51364, - "total": 153474, - "total_gz": 59488, + "a.out.nodebug.wasm": 133897, + "a.out.nodebug.wasm.gz": 51215, + "total": 153121, + "total_gz": 59339, "sent": [ "__cxa_throw", "_abort_js", diff --git a/test/codesize/test_codesize_cxx_ctors2.json b/test/codesize/test_codesize_cxx_ctors2.json index f626c23da11de..fade99f9481f1 100644 --- a/test/codesize/test_codesize_cxx_ctors2.json +++ b/test/codesize/test_codesize_cxx_ctors2.json @@ -1,10 +1,10 @@ { "a.out.js": 19201, "a.out.js.gz": 8107, - "a.out.nodebug.wasm": 133679, - "a.out.nodebug.wasm.gz": 51045, - "total": 152880, - "total_gz": 59152, + "a.out.nodebug.wasm": 133326, + "a.out.nodebug.wasm.gz": 50903, + "total": 152527, + "total_gz": 59010, "sent": [ "__cxa_throw", "_abort_js", diff --git a/test/codesize/test_codesize_cxx_except.json b/test/codesize/test_codesize_cxx_except.json index 71d97754fdd7a..fc27f2e981edf 100644 --- a/test/codesize/test_codesize_cxx_except.json +++ b/test/codesize/test_codesize_cxx_except.json @@ -1,10 +1,10 @@ { "a.out.js": 22917, "a.out.js.gz": 9078, - "a.out.nodebug.wasm": 176707, - "a.out.nodebug.wasm.gz": 58892, - "total": 199624, - "total_gz": 67970, + "a.out.nodebug.wasm": 176354, + "a.out.nodebug.wasm.gz": 58741, + "total": 199271, + "total_gz": 67819, "sent": [ "__cxa_begin_catch", "__cxa_end_catch", diff --git a/test/codesize/test_codesize_cxx_except_wasm.json b/test/codesize/test_codesize_cxx_except_wasm.json index 06f54595c4ee5..a5c197a3c0032 100644 --- a/test/codesize/test_codesize_cxx_except_wasm.json +++ b/test/codesize/test_codesize_cxx_except_wasm.json @@ -1,10 +1,10 @@ { "a.out.js": 19023, "a.out.js.gz": 8039, - "a.out.nodebug.wasm": 149967, - "a.out.nodebug.wasm.gz": 56437, - "total": 168990, - "total_gz": 64476, + "a.out.nodebug.wasm": 149614, + "a.out.nodebug.wasm.gz": 56302, + "total": 168637, + "total_gz": 64341, "sent": [ "_abort_js", "_tzset_js", diff --git a/test/codesize/test_codesize_cxx_except_wasm_legacy.json b/test/codesize/test_codesize_cxx_except_wasm_legacy.json index aa0e0f17c0a7d..51ee5373016f2 100644 --- a/test/codesize/test_codesize_cxx_except_wasm_legacy.json +++ b/test/codesize/test_codesize_cxx_except_wasm_legacy.json @@ -1,10 +1,10 @@ { "a.out.js": 19101, "a.out.js.gz": 8065, - "a.out.nodebug.wasm": 147749, - "a.out.nodebug.wasm.gz": 56113, - "total": 166850, - "total_gz": 64178, + "a.out.nodebug.wasm": 147396, + "a.out.nodebug.wasm.gz": 55982, + "total": 166497, + "total_gz": 64047, "sent": [ "_abort_js", "_tzset_js", diff --git a/test/codesize/test_codesize_cxx_mangle.json b/test/codesize/test_codesize_cxx_mangle.json index bf11ca1b1f52b..81f267b07078d 100644 --- a/test/codesize/test_codesize_cxx_mangle.json +++ b/test/codesize/test_codesize_cxx_mangle.json @@ -1,10 +1,10 @@ { "a.out.js": 22967, "a.out.js.gz": 9098, - "a.out.nodebug.wasm": 242987, - "a.out.nodebug.wasm.gz": 81112, - "total": 265954, - "total_gz": 90210, + "a.out.nodebug.wasm": 242634, + "a.out.nodebug.wasm.gz": 80968, + "total": 265601, + "total_gz": 90066, "sent": [ "__cxa_begin_catch", "__cxa_end_catch", diff --git a/test/codesize/test_codesize_cxx_noexcept.json b/test/codesize/test_codesize_cxx_noexcept.json index 7f2ab2bd72d62..fabf7f33335f8 100644 --- a/test/codesize/test_codesize_cxx_noexcept.json +++ b/test/codesize/test_codesize_cxx_noexcept.json @@ -1,10 +1,10 @@ { "a.out.js": 19224, "a.out.js.gz": 8124, - "a.out.nodebug.wasm": 136160, - "a.out.nodebug.wasm.gz": 51987, - "total": 155384, - "total_gz": 60111, + "a.out.nodebug.wasm": 135807, + "a.out.nodebug.wasm.gz": 51835, + "total": 155031, + "total_gz": 59959, "sent": [ "__cxa_throw", "_abort_js", diff --git a/test/codesize/test_codesize_cxx_wasmfs.json b/test/codesize/test_codesize_cxx_wasmfs.json index 14a2b868a37a7..d2ea1ec4f48c3 100644 --- a/test/codesize/test_codesize_cxx_wasmfs.json +++ b/test/codesize/test_codesize_cxx_wasmfs.json @@ -1,10 +1,10 @@ { "a.out.js": 6604, "a.out.js.gz": 3154, - "a.out.nodebug.wasm": 173886, - "a.out.nodebug.wasm.gz": 64740, - "total": 180490, - "total_gz": 67894, + "a.out.nodebug.wasm": 173533, + "a.out.nodebug.wasm.gz": 64593, + "total": 180137, + "total_gz": 67747, "sent": [ "__cxa_throw", "_abort_js", diff --git a/test/codesize/test_codesize_hello_O0.json b/test/codesize/test_codesize_hello_O0.json index d6ee2dca50ce3..ded16df663886 100644 --- a/test/codesize/test_codesize_hello_O0.json +++ b/test/codesize/test_codesize_hello_O0.json @@ -1,10 +1,10 @@ { "a.out.js": 23471, "a.out.js.gz": 8555, - "a.out.nodebug.wasm": 14578, - "a.out.nodebug.wasm.gz": 7228, - "total": 38049, - "total_gz": 15783, + "a.out.nodebug.wasm": 14228, + "a.out.nodebug.wasm.gz": 7062, + "total": 37699, + "total_gz": 15617, "sent": [ "fd_write" ], diff --git a/test/codesize/test_codesize_hello_dylink.json b/test/codesize/test_codesize_hello_dylink.json index fc68c69a68441..5f68ff9d01cfd 100644 --- a/test/codesize/test_codesize_hello_dylink.json +++ b/test/codesize/test_codesize_hello_dylink.json @@ -1,10 +1,10 @@ { "a.out.js": 26258, "a.out.js.gz": 11211, - "a.out.nodebug.wasm": 17365, - "a.out.nodebug.wasm.gz": 8831, - "total": 43623, - "total_gz": 20042, + "a.out.nodebug.wasm": 17009, + "a.out.nodebug.wasm.gz": 8683, + "total": 43267, + "total_gz": 19894, "sent": [ "__syscall_stat64", "emscripten_resize_heap", @@ -37,7 +37,6 @@ "$__emscripten_stdout_close", "$__emscripten_stdout_seek", "$__fwritex", - "$__memset", "$__stdio_write", "$__strchrnul", "$__towrite", diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index e884ba00f9b9c..99fa97702becc 100644 --- a/test/codesize/test_codesize_hello_dylink_all.json +++ b/test/codesize/test_codesize_hello_dylink_all.json @@ -1,7 +1,7 @@ { "a.out.js": 270584, - "a.out.nodebug.wasm": 588564, - "total": 859148, + "a.out.nodebug.wasm": 588324, + "total": 858908, "sent": [ "IMG_Init", "IMG_Load", @@ -2134,7 +2134,6 @@ "__ltsf2", "__lttf2", "__lxstat", - "__memset", "__mo_lookup", "__moddi3", "__modsi3", diff --git a/test/codesize/test_unoptimized_code_size.json b/test/codesize/test_unoptimized_code_size.json index f9fdd5dc2de9a..4af6d347e0f1a 100644 --- a/test/codesize/test_unoptimized_code_size.json +++ b/test/codesize/test_unoptimized_code_size.json @@ -1,16 +1,16 @@ { "hello_world.js": 54465, "hello_world.js.gz": 17297, - "hello_world.wasm": 14578, - "hello_world.wasm.gz": 7228, + "hello_world.wasm": 14228, + "hello_world.wasm.gz": 7062, "no_asserts.js": 23544, "no_asserts.js.gz": 8261, - "no_asserts.wasm": 11692, - "no_asserts.wasm.gz": 5768, + "no_asserts.wasm": 11342, + "no_asserts.wasm.gz": 5611, "strict.js": 51616, "strict.js.gz": 16308, - "strict.wasm": 14578, - "strict.wasm.gz": 7223, - "total": 170473, - "total_gz": 62085 + "strict.wasm": 14228, + "strict.wasm.gz": 7061, + "total": 169423, + "total_gz": 61600 } diff --git a/tools/native_sigs.py b/tools/native_sigs.py index 9e6fedb00eea5..9878ea4ef2b9e 100644 --- a/tools/native_sigs.py +++ b/tools/native_sigs.py @@ -303,7 +303,6 @@ '__map_file': 'ppp', '__math_invalidl': '_p__', '__memrchr': 'pp_p', - '__memset': 'pp_p', '__mkostemps': '_p__', '__mmap': 'ppp____', '__mo_lookup': 'pppp', diff --git a/tools/system_libs.py b/tools/system_libs.py index bbdf3944b8983..02851cae36dff 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -1442,19 +1442,10 @@ def __init__(self, **kwargs): def get_libcall_files(self): # see comments in libc.customize_build_cmd - - # some files also appear in libc, and a #define affects them - mem_files = files_in_path( - path='system/lib/libc', - filenames=['emscripten_memset.c']) - - # some functions have separate files - math_files = files_in_path( + return files_in_path( path='system/lib/libc/musl/src/math', filenames=['pow_small.c', 'log_small.c', 'log2_small.c']) - return mem_files + math_files - def get_files(self): libcall_files = self.get_libcall_files()