From 49f5e7aa15bcc5d3ff0f80c24e95c7a0d7072042 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:12:27 +0000 Subject: [PATCH 1/4] Pin a real simdutf kernel when CPUID dispatch picks the unsupported stub simdutf selects its kernel by CPUID on first use. When no compiled-in kernel matches the advertised feature set (QEMU's default qemu64 model hides SSE4.2), it installs a stub whose every method returns 0 or false. validate_ascii then reports ASCII input as non-ASCII, and allocate_latin1_into_utf8_with_list never makes progress, so bun spins at 100% CPU on the entry path before it opens the entry file. The scalar fallback kernel is not compiled into WTF's simdutf. At process start, if the active implementation is the stub, select the least demanding compiled-in kernel. On x64 that is westmere, which needs only SSE4.2, the same baseline the whole binary is compiled for. --- src/bun_core/lib.rs | 4 ++++ src/runtime/bin_entry/mod.rs | 5 +++++ src/simdutf_sys/bun-simdutf.cpp | 24 ++++++++++++++++++++++++ src/simdutf_sys/simdutf.rs | 9 +++++++++ test/regression/issue/41361.test.ts | 26 ++++++++++++++++++++++++++ 5 files changed, 68 insertions(+) create mode 100644 test/regression/issue/41361.test.ts diff --git a/src/bun_core/lib.rs b/src/bun_core/lib.rs index c5b8d48c6220..15551ae5ee62 100644 --- a/src/bun_core/lib.rs +++ b/src/bun_core/lib.rs @@ -1607,6 +1607,10 @@ pub(crate) mod strings_impl { break; } Some(i) => { + debug_assert!( + rest[i] >= 0x80, + "first_non_ascii_usize pointed at an ASCII byte" + ); list.extend_from_slice(&rest[..i]); rest = &rest[i..]; while let Some(&c) = rest.first() { diff --git a/src/runtime/bin_entry/mod.rs b/src/runtime/bin_entry/mod.rs index 098d8c73ceaa..586e27970ee3 100644 --- a/src/runtime/bin_entry/mod.rs +++ b/src/runtime/bin_entry/mod.rs @@ -157,6 +157,11 @@ pub(crate) unsafe extern "C" fn main(argc: c_int, argv: *const *const c_char) -> // 1. Crash handler first so anything below gets a usable trace. bun_crash_handler::init(); + // simdutf's CPUID dispatch may land on a stub that returns 0 for every + // call (QEMU's default CPU model hides SSE4.2). Pin a real kernel before + // the first string conversion. + bun_simdutf_sys::simdutf::init(); + use_mimalloc_in_dependencies(); // SIGPIPE/SIGXFSZ → SIG_IGN. diff --git a/src/simdutf_sys/bun-simdutf.cpp b/src/simdutf_sys/bun-simdutf.cpp index 6afd6862ddc1..a28202a37cc8 100644 --- a/src/simdutf_sys/bun-simdutf.cpp +++ b/src/simdutf_sys/bun-simdutf.cpp @@ -7,6 +7,30 @@ typedef struct SIMDUTFResult { extern "C" { +// simdutf picks its kernel by CPUID. When no compiled-in kernel matches the +// advertised feature set (QEMU's default qemu64 model hides SSE4.2 even though +// the host executes it; or SIMDUTF_FORCE_IMPLEMENTATION names an unknown +// kernel), it installs a stub whose every method returns 0 / false / +// error_code::OTHER. Callers cannot tell that from a real result: validate_ascii +// says "not ASCII" for ASCII input, length queries return 0, and transcoding +// loops that rely on forward progress spin forever. +// +// The scalar fallback kernel is not compiled into WTF's simdutf, so the least +// demanding kernel in the list is what the binary's -march baseline already +// assumes everywhere else. If the process reached main(), the CPU runs it. +void simdutf__init() +{ + if (simdutf::get_active_implementation()->name() != "unsupported") + return; + + const simdutf::implementation* least_demanding = nullptr; + for (const simdutf::implementation* impl : simdutf::get_available_implementations()) + least_demanding = impl; + + if (least_demanding) + simdutf::get_active_implementation() = least_demanding; +} + bool simdutf__validate_utf8(const char* buf, size_t len) { return simdutf::validate_utf8(buf, len); diff --git a/src/simdutf_sys/simdutf.rs b/src/simdutf_sys/simdutf.rs index 47be59bcb46d..43c892e9462c 100644 --- a/src/simdutf_sys/simdutf.rs +++ b/src/simdutf_sys/simdutf.rs @@ -31,6 +31,7 @@ impl Status { } unsafe extern "C" { + safe fn simdutf__init(); pub(crate) fn simdutf__validate_utf8(buf: *const u8, len: usize) -> bool; pub(crate) fn simdutf__validate_utf8_with_errors(buf: *const u8, len: usize) -> SIMDUTFResult; pub fn simdutf__validate_ascii(buf: *const u8, len: usize) -> bool; @@ -60,6 +61,14 @@ unsafe extern "C" { pub fn simdutf__utf8_length_from_latin1(input: *const u8, length: usize) -> usize; } +/// Replaces simdutf's "unsupported" stub (every call returns 0 / false) with the +/// least demanding compiled-in kernel. simdutf installs the stub when CPUID +/// advertises none of the compiled-in feature sets, which happens under QEMU's +/// default CPU model. Call once at process start, before any thread. +pub fn init() { + simdutf__init(); +} + pub mod validate { use super::*; diff --git a/test/regression/issue/41361.test.ts b/test/regression/issue/41361.test.ts new file mode 100644 index 000000000000..4d00a44dac05 --- /dev/null +++ b/test/regression/issue/41361.test.ts @@ -0,0 +1,26 @@ +import { expect, test } from "bun:test"; +import { bunEnv, bunExe, tempDir } from "harness"; + +// simdutf picks its SIMD kernel by CPUID at first use. When no compiled-in +// kernel matches (QEMU's default CPU model hides SSE4.2), it installs a stub +// whose every call returns 0 or false. SIMDUTF_FORCE_IMPLEMENTATION with an +// unknown name installs the same stub on any machine. Bun used to spin forever +// in a Latin-1 to UTF-8 loop on the first ASCII string longer than 32 bytes, +// which is the entry point path, before it opened the entry file. +test("bun starts when simdutf finds no supported kernel", async () => { + using dir = tempDir("simdutf-no-supported-kernel", { + "entry-file-with-a-name-longer-than-thirty-two-bytes.mjs": `console.log("ok");`, + }); + await using proc = Bun.spawn({ + cmd: [bunExe(), `${dir}/entry-file-with-a-name-longer-than-thirty-two-bytes.mjs`], + env: { ...bunEnv, SIMDUTF_FORCE_IMPLEMENTATION: "bogus" }, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + timeout: 10_000, + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stdout).toBe("ok\n"); + expect(stderr).toBe(""); + expect(exitCode).toBe(0); +}); From 00f71716fb8abd58fb70ba7f3fdaf1f49d225071 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:28:42 +0000 Subject: [PATCH 2/4] test: rely on the runner timeout instead of a spawn timeout --- test/regression/issue/41361.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/regression/issue/41361.test.ts b/test/regression/issue/41361.test.ts index 4d00a44dac05..d64a2d488ab4 100644 --- a/test/regression/issue/41361.test.ts +++ b/test/regression/issue/41361.test.ts @@ -17,7 +17,6 @@ test("bun starts when simdutf finds no supported kernel", async () => { cwd: String(dir), stdout: "pipe", stderr: "pipe", - timeout: 10_000, }); const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect(stdout).toBe("ok\n"); From 8346d311d7776823537ee1a8579a57156ab0e98d Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:41:16 +0000 Subject: [PATCH 3/4] Shorten the simdutf init comments, assert the child exited on its own --- src/runtime/bin_entry/mod.rs | 5 ++--- src/simdutf_sys/bun-simdutf.cpp | 16 +++++----------- src/simdutf_sys/simdutf.rs | 6 ++---- test/regression/issue/41361.test.ts | 1 + 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/runtime/bin_entry/mod.rs b/src/runtime/bin_entry/mod.rs index 586e27970ee3..44dac7781e91 100644 --- a/src/runtime/bin_entry/mod.rs +++ b/src/runtime/bin_entry/mod.rs @@ -157,9 +157,8 @@ pub(crate) unsafe extern "C" fn main(argc: c_int, argv: *const *const c_char) -> // 1. Crash handler first so anything below gets a usable trace. bun_crash_handler::init(); - // simdutf's CPUID dispatch may land on a stub that returns 0 for every - // call (QEMU's default CPU model hides SSE4.2). Pin a real kernel before - // the first string conversion. + // Before the first string conversion: simdutf's CPUID dispatch may have + // picked a stub that returns 0 for every call. bun_simdutf_sys::simdutf::init(); use_mimalloc_in_dependencies(); diff --git a/src/simdutf_sys/bun-simdutf.cpp b/src/simdutf_sys/bun-simdutf.cpp index a28202a37cc8..7c912eb76f12 100644 --- a/src/simdutf_sys/bun-simdutf.cpp +++ b/src/simdutf_sys/bun-simdutf.cpp @@ -7,17 +7,11 @@ typedef struct SIMDUTFResult { extern "C" { -// simdutf picks its kernel by CPUID. When no compiled-in kernel matches the -// advertised feature set (QEMU's default qemu64 model hides SSE4.2 even though -// the host executes it; or SIMDUTF_FORCE_IMPLEMENTATION names an unknown -// kernel), it installs a stub whose every method returns 0 / false / -// error_code::OTHER. Callers cannot tell that from a real result: validate_ascii -// says "not ASCII" for ASCII input, length queries return 0, and transcoding -// loops that rely on forward progress spin forever. -// -// The scalar fallback kernel is not compiled into WTF's simdutf, so the least -// demanding kernel in the list is what the binary's -march baseline already -// assumes everywhere else. If the process reached main(), the CPU runs it. +// When CPUID advertises none of the compiled-in kernels (QEMU's default CPU +// model hides SSE4.2 that the host executes), simdutf installs a stub whose +// every method returns 0 / false. WTF builds simdutf without the scalar +// fallback, so pick the last kernel in the priority-ordered list: on x64 that +// is westmere, the same -march=nehalem baseline the whole binary runs. void simdutf__init() { if (simdutf::get_active_implementation()->name() != "unsupported") diff --git a/src/simdutf_sys/simdutf.rs b/src/simdutf_sys/simdutf.rs index 43c892e9462c..74ec55a5eae1 100644 --- a/src/simdutf_sys/simdutf.rs +++ b/src/simdutf_sys/simdutf.rs @@ -61,10 +61,8 @@ unsafe extern "C" { pub fn simdutf__utf8_length_from_latin1(input: *const u8, length: usize) -> usize; } -/// Replaces simdutf's "unsupported" stub (every call returns 0 / false) with the -/// least demanding compiled-in kernel. simdutf installs the stub when CPUID -/// advertises none of the compiled-in feature sets, which happens under QEMU's -/// default CPU model. Call once at process start, before any thread. +/// Replaces simdutf's "unsupported" stub (every call returns 0 / false) with a +/// compiled-in kernel. Call once at process start, before any thread. pub fn init() { simdutf__init(); } diff --git a/test/regression/issue/41361.test.ts b/test/regression/issue/41361.test.ts index d64a2d488ab4..751fd8e6fbe3 100644 --- a/test/regression/issue/41361.test.ts +++ b/test/regression/issue/41361.test.ts @@ -21,5 +21,6 @@ test("bun starts when simdutf finds no supported kernel", async () => { const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect(stdout).toBe("ok\n"); expect(stderr).toBe(""); + expect(proc.signalCode).toBeNull(); expect(exitCode).toBe(0); }); From fefc6d292016228ec5023bb974a29fbf16c6c3bf Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:53:03 +0000 Subject: [PATCH 4/4] ci: retrigger