Loading
Commits on Source 15
-
Josh Bleecher Snyder authored
TLS.Free delivers pending signals as a side effect of unwinding a C stack frame. Splitting the check out into its own method lets libc routines with no frame to unwind reach it directly, rather than allocating a frame just to get the signal delivered.
-
Josh Bleecher Snyder authored
The transpiled musl mem* routines move at most four bytes per load/store, through a double pointer indirection. Go's copy, clear and bytes.Compare lower to the runtime's vectorized memmove and memclr and to bytes.Compare's SIMD scan. In the JSON-heavy speedtest1 workloads of github.com/josharian/benchmosql, where SQLite shuttles blob payloads around, that takes Xmemcpy from 41% of CPU to under 20%; Xmemcmp is 47-98% faster across the measured sizes. Only linux/amd64 and linux/arm64, the two arches the measurements cover. The other musl arches keep the transpiled routines; the non-musl ports have always had hand-written ones in libc.go. Xmemcmp returns -1/0/+1 rather than musl's difference of the first mismatched bytes. C99 specifies only the sign, and every caller in the transpiled musl code and in modernc.org/sqlite's generated code (including its vec and pcache variants) compares the result against zero. Xmemset's nonzero fill seeds 512 bytes with word stores and then doubles that prefix with copy. Transpiled musl's unrolled word stores are genuinely fast from 512B to 4KiB, and this is the only pure-Go shape measured that keeps up with them there; a plain byte loop and a doubling fill from a single byte both lose badly somewhere. Xfabs allocated a 16-byte C stack frame, with a deferred release, to clear one sign bit, and Xstrcspn built its byte set on the C stack. Both now work in Go locals and check for pending signals directly.
-
Josh Bleecher Snyder authored
This helper walked the string a byte at a time. It is the implementation behind Xstrlen on every port: the musl translation is generated with -hide strlen exactly so that the generated code reaches it too, both directly and through Xstrlen. Scanning with bytes.IndexByte instead, over chunks that stop at 4096-byte boundaries so that the scan cannot fault, is 10-83% faster from 16 bytes up on linux/amd64, linux/arm64 and darwin/arm64 (benchstat, n=10, strings at an odd offset). Strings shorter than 8 bytes pay ~1.2ns of call overhead on amd64; a hybrid that prescans a word or two inline recovers only a fraction of that while giving back half the mid-size win, so it is not worth its complexity. strlen_test.go keeps the classic word-at-a-time scan as a benchmark baseline. strlen_guard_test.go checks the no-overread property against a PROT_NONE guard page.
-
Josh Bleecher Snyder authored
Every ccgo-generated function brackets its body with TLS.Alloc and TLS.Free, so their call overhead is paid on every C function call. Free's common path is now a stack pointer decrement and a bool test, which inlines into every generated function; the signal delivery it guards stays behind a call. When a signal is pending it is still delivered at the same point. Alloc is split the same way but still does not inline: any useful fast path plus the call to the grow path exceeds the inlining budget, the call alone costing 57 of the 80 available. The fast path does at least stop loading the stack slot twice. It is possible to make Alloc mid-stack inline, but it is a very disruptive change with moderate perf impact.
-
cznic authored
native_musl.go now serves every musl-derived target, not just linux/amd64 and linux/arm64. generator.go applies the ___musl_ rename unconditionally and the transpiled originals in the six remaining ccgo_linux_*.go files are renamed accordingly. On linux/arm musl's memcpy is assembly and was never transpiled; the hand-written memcpy in libc_musl_linux_arm.go takes the ___musl_memcpy role, while the generated arm code keeps calling its unchanged _memcpy helper. The 32-bit targets exposed three spots that leaned on 64-bit operations, expensive there: - Xmemset seeds with aligned native-word stores instead of unaligned PutUint64: parity on amd64, -39% to -77% vs the transpiled original on 386 from 32 bytes up, where the 64-bit seed had measured up to +140%. The doubling loop no longer computes i *= 2, which could overflow int on 32-bit. - Xstrcspn's byte set uses native words: -40% to -48% vs transpiled on 386 (previously up to +81%), and -10% to -25% on amd64 too. - strlen splits per arch (strlen.go, strlen_simd.go, strlen_nosimd.go): the chunked bytes.IndexByte scan stays where IndexByte has real assembly (amd64, arm64, ppc64le, s390x, riscv64, loong64); the rest use the classic word-at-a-time scan, because on 386 IndexByte is a byte loop and the chunked scan measured ~2x slower than even the plain byte loop it had replaced. Verified: go test -short and TestLibc natively on linux/amd64 and linux/386; the native_musl and strlen tests on arm, arm64, loong64, ppc64le, riscv64 and s390x under qemu-user; a 1.6M-case equivalence run against the ___musl_ originals on amd64, 386, arm and s390x; -race; build_all_targets.sh; and downstream modernc.org/sqlite -short, a 36k-test SQLite TCL subset via libsqlite3, libz and libquickjs. Co-Authored-By:
Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B7y1NexgTgnEx8S6uFHoqw
-
cznic authored
Let every builder regenerate its ccgo_linux_<goarch>.go after 886cba32 extended the native musl routines to all linux targets; the autogen runs double-check that generator.go reproduces the renames committed there. Co-Authored-By:
Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B7y1NexgTgnEx8S6uFHoqw
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored