Loading
Commits on Source 76
-
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...
-
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
-
cznic authored
-
cznic authored
cc v4.29.5 evaluates -eval-all-macros replacement lists as real C expressions, so musl's '#define SIG_DFL ((void (*)(int)) 0)' now yields a Go const in the generated ccgo_linux_*.go. The hand-written copies in libc_musl.go became redeclarations and broke the nuc64 builder. Blank internal/autogen/*.mod so that every linux arch is regenerated. Co-Authored-By:
Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wEqtpjA83G2VUumniLSje
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
ccgo emits X__builtin_<name> for 23 <math.h> functions libc did not define and falls back to the iqlibc qualifier, so the generated Go did not compile. Add the forwarders in libc_all.go, plus __flt_rounds/__builtin_flt_rounds, __builtin___strncat_chk, __builtin___vsprintf_chk and a correctly rounded fmaf (round to odd), which musl's fmaf.c cannot provide because the generator drops fenv. Hand-written ports: add erf, erfc, exp2, fdim, fma, ilogb, lgamma, logb, nextafter, nextafterf, nexttoward, remainder, remquo (transliterated from musl) and tgamma, the 8/16-bit AtomicLoadP*/AtomicStoreP* helpers, scalbn on windows, and make __builtin_clzl honour a 32-bit unsigned long. musl ports: add the missing AtomicStorePInt16/AtomicStorePUint16. Co-Authored-By:
Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FgRyDF4AcfkxXi4EEnkMB
-
cznic authored
Closes #56. The missing libm __builtin_ names, the 8/16-bit AtomicLoadP/AtomicStoreP helpers, __builtin_flt_rounds, the fortify strncat/vsprintf variants and fmaf are provided since afc4c418. Reopen if the darwin or windows/arm64 builders find a problem with the hand-written port implementations. Co-Authored-By:
Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FgRyDF4AcfkxXi4EEnkMB
-
cznic authored
The inline assembly in musl's atomic_arch.h was turned into assertion failures by ccgo's -ignore-asm-errors, so on every 64 bit musl port ftrylockfile, the locking getc/putc helpers, __libc_sigaction, ffsll, the generic fma, timer_delete, bindtextdomain, dcngettext, __asctime_r, __stack_chk_fail and __do_orphaned_stdio_locks aborted when reached. On arm the ll/sc pair was a panicking stub. flockfile/funlockfile therefore aborted everywhere, and __lockfile/__unlockfile were TODO panics. - internal/overlay/musl/arch/*/atomic_arch.h: keep the assembly for real compilers and, under __CCGO__, only declare a_cas, a_cas_p, a_swap, a_fetch_add, a_and, a_or, a_and_64, a_or_64, a_store, a_barrier, a_spin, a_crash, a_ctz_64 and a_clz_64 (a_cas, a_barrier, a_crash and a_clz_32 on arm), the same pattern i386 already used. atomic64.go and libc_musl_linux_arm.go implement them with sync/atomic. - pthread_impl.h overlay: __wake and __futexwait are declared only and implemented on the Go parking lot that ___lock/___unlock already use, so a raw futex can no longer miss a parked goroutine. - libc_musl.go: real __lockfile/__unlockfile transliterated from musl, the same lock word protocol as the transpiled ftrylockfile and funlockfile. - generator.go hides __libc_sigaction; libc_musl.go provides it on top of the Xsignal machinery, with SA_SIGINFO and SA_RESETHAND, instead of the raw rt_sigaction syscall that would have installed Go function values as kernel handler and restorer. - syscall_musl.go: tkill/tgkill directed at the calling emulated thread deliver to it directly, which makes raise() work instead of signalling whatever real thread has that tid. - ccgo_linux_amd64.go regenerated; the other ports regenerate on their builders. libc-test on linux/amd64 goes from pass=200 to pass=204 with no new failures: flockfile-list and sigreturn now pass, sigaltstack is blacklisted since alternate signal stacks cannot be emulated. Co-Authored-By:
Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FgRyDF4AcfkxXi4EEnkMB
-
cznic authored
The generator deleted all of src/sched and src/thread because pthreads are hand-written in pthread_musl.go. Now that the atomics translate to Go (0ae842f8) most of what #55 lists transpiles as is: - generator.go keeps sem_*.c, pthread_kill.c, pthread_testcancel.c and the sched sources except sched_getcpu.c (vDSO) and sched_yield.c (hand-written). ccgo_linux_amd64.go regenerated. - The goroutine parking lot behind ___lock/__wake gets per-waiter channels and a timeout, and libc_musl.go provides musl's __timedwait_cp on it, so sem_timedwait and friends work with CLOCK_REALTIME deadlines. - A tid registry lets tkill/tgkill (hence pthread_kill and raise) reach other emulated threads: the signal is queued for the target's next check point. - pthread_condattr_{init,destroy,setclock,getclock,setpshared,getpshared} in Go; pthread_cond_init records the clock in musl's _c_clock slot and pthread_cond_timedwait ...
-
cznic authored
The builders regenerate a target only when go.mod differs from the target's snapshot. The overlays of 0ae842f8 and the generator change of f6b7b410 must reach every arch, so the snapshots are emptied; a successful generation writes them back. Co-Authored-By:
Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FgRyDF4AcfkxXi4EEnkMB
-
cznic authored
darwin: fix sched_yield and wcschr signatures, implement the *at family, pty, kqueue and other missing or TODO functions, updates #54 Signatures on the hand-written ports: sched_yield now returns int and wcschr returns the pointer to the match (or to the terminator for L'\0') instead of a wchar_t. darwin, previously missing: openat, fstatat, mkdirat, unlinkat, renameat, readlinkat, symlinkat, linkat, fchmodat, fchownat, faccessat, utimensat, futimens, lchmod, fdopendir, getloadavg, posix_openpt, grantpt, unlockpt, ptsname, ptsname_r, getlogin_r, pthread_setname_np, pthread_getname_np, kqueue, kevent, fpathconf. darwin, previously TODO panics: link (amd64), dup2 (arm64), chown, mknod, truncate (also had a variadic signature), getrusage, accept, raise, issetugid, inet_ntoa, gettimeofday with a timezone, sysconf and pathconf for unknown names (EINVAL instead of a panic, more names answered), poll with no descriptors, getgid, getegid. kill, readv, setsid, confstr and openpty were shared TODO stubs in libc.go; they are implemented for the unix ports in libc_unix.go and libc_darwin.go and stay stubs on windows and, for openpty, on the other ports. Not covered: sigaltstack, waitid, posix_spawn, mkfifoat, mknodat, and the darwin sigaction, which hands Go function values to the kernel. Co-Authored-By:
Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FgRyDF4AcfkxXi4EEnkMB
-
cznic authored
windows: fix the environment, _wopen, wcschr, setlocale, CloseHandle, feof and ungetc, add dup2, strncat, strtoll and three Win32 calls, updates #57 Environment: bootWinEnviron did not terminate _wenviron, so the array overran and _wgetenv, which relies on the terminator, never found the last variable. _wgetenv now also finds variables the Go side set after startup, and _wputenv updates the narrow environment getenv reads and os.Setenv, so all three views agree. _wopen passed its UTF-16 path through the narrow GoString. wcschr returns the terminator for L'\0'. setlocale reports the "C" locale instead of NULL. CloseHandle returns FALSE on failure instead of EINVAL. TODO panics: SetHandleInformation, GetOverlappedResult and OutputDebugStringW (which also had variadic Go signatures) are Win32 calls now; dup2 duplicates the handle under the requested descriptor in the descriptor table; ungetc keeps one pushed back byte in the FILE, which fgetc and fread consume first. feof and clearerr are added, with an EOF flag set by reads that return nothing and cleared by fseek, rewind and ungetc. Missing on windows: strncat, strtoll, strtoull, _strtoi64, _strtoui64. scanf.go: the %c conversion, shared by all hand-written ports, was a TODO panic; CPython's only sscanf ends with it. Co-Authored-By:
Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FgRyDF4AcfkxXi4EEnkMB
-
cznic authored
mirror setenv/putenv/unsetenv/clearenv to the Go environment on musl, export the windows descriptor table, updates #58 The transpiled musl environment functions only touched the C environment, so os.Getenv disagreed with getenv after a C setenv and consumers kept the two in step by hand. generator.go now renames the transpiled functions to ___musl_* and appends wrappers to the generated file that mirror a successful change to the Go environment; keeping them in the generated file leaves every arch consistent until it is regenerated. Changes made with os.Setenv remain invisible to getenv, there is no hook for that direction. ccgo_linux_amd64.go regenerated. windows: WindowsHandle, WrapWindowsHandle, WrapWindowsHandleAt and ReleaseWindowsHandle expose the descriptor table so that UCRT replacements need no go:linkname into unexported functions. TLS.StackSlots already exists on both ports as the supported way to read the emulated stack depth; a test covers the hand-written one. Co-Authored-By:
Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FgRyDF4AcfkxXi4EEnkMB
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
Since f6b7b410 the generator keeps src/thread/sem_timedwait.c, which uses a_inc, a_dec and a_spin. The i386 atomic_arch.h overlay declares all three under __CCGO__, so atomic.h does not derive them from a_fetch_add and a_barrier as it does on the other ports, and the e5-1650 autogen failed with ccgo_linux_386.go: undefined: _a_dec, _a_spin, _a_inc a_inc and a_dec use sync/atomic, a_spin yields like atomic64.go does. Verified by regenerating linux/386 locally with an i686 compiler wrapper, which reproduces the builder's errors exactly without this change, and running the full test with it: libc-test files=471 buildFails=0 skip=261 execFails=0 pass=212. Co-Authored-By:
Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012mK8VBVmKLRjyDFqYsR7uV
-
cznic authored
src/regression/malloc-oom fills the address space with PROT_NONE mappings, probing the largest free gap with mmap+munmap and then mapping it. Other threads of the Go process (glibc thread stacks and malloc arenas, runtime allocations) map memory in between, so under load the test fails with "vmfill failed: Out of memory". Measured standalone: 0-0.1% idle, 6-9% with the CPUs saturated, the same before and after the 2026-09-10 changes. It failed the nuc64 run on 2026-09-14. Both variants compile to the same Go code. arm, arm64, loong64, ppc64le and riscv64 already list both; amd64 had only -static, s390x only the dynamic one and 386 neither. Co-Authored-By:
Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012mK8VBVmKLRjyDFqYsR7uV
-
cznic authored
windows: build _wenviron from the process environment, fix TestIssue57Handles expectations, updates #57 bootWinEnviron copied the narrow environment, which X__imp__environ takes during package initialization, so _wenviron never listed variables the Go side set later. TestIssue57Environ failed that way on every Windows target, "_wenviron: 46 entries, os has 47, variable seen false" on windows/arm64. It now reads os.Environ(). TestIssue57Handles, hidden behind the builder's -failfast, expected CloseHandle(INVALID_HANDLE_VALUE) to fail, but that value is also the current process pseudo handle, which CloseHandle accepts. It also expected GetOverlappedResult on a synchronous pipe to fail, but the call reports the status recorded in OVERLAPPED.Internal, whatever the handle. The test now closes NULL and checks both a completed and a failed request. Verified on windows/arm64 (Windows 11) and under wine for windows/amd64 and windows/386: all tests pass. Co-Authored-By:
Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012mK8VBVmKLRjyDFqYsR7uV
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
Since 0ae842f8 (v1.76.0) ___libc_sigaction is a Go function that records dispositions for delivery through os/signal and never touches the kernel; Xabort's ___libc_sigaction(SIGABRT, SIG_DFL) therefore leaves the Go runtime's SIGABRT handler installed, and the kill(getpid(), SIGABRT) that follows prints "SIGABRT: abort" and a goroutine dump to stderr before the runtime re-raises the signal and dies. In v1.75.7 the transpiled musl __libc_sigaction issued the rt_sigaction syscall, so the process died silently, as a C abort() does. The difference is observable: Tcl's exec reports a child that dies by a signal as "child killed: SIGABRT" only when its stderr stayed empty, and sqlite's writecrash.test (test_devsym.c calls abort() on the n-th write) matches on that, so every linux target of modernc.org/libsqlite3 fails writecrash-1.1.1 with v1.76.0 and passes with v1.75.7 - verified A/B on linux/amd64 with everything else equal. resetSigDfl (abort_linux.go, the musl linux ports) installs SIG_DFL with rt_sigaction and unblocks the signal with rt_sigprocmask, replacing the runtime's handler for that one signal; Xabort calls it after the bookkeeping call to ___libc_sigaction and then raises SIGABRT thread-directed with tgkill, as the Go runtime's raise() and the netbsd Xabort do, because a process-directed kill(2) is asynchronous and the calling thread can run on before the signal lands. The kill and the panic that follow are unreachable fallbacks. With this writecrash.test passes again (0 errors out of 997 tests), and libsqlite3's full Tcl permutation on linux/amd64 is back to its waived failures only. Builds on all eight linux ports. Co-Authored-By:
Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dq5kdSFJ1eyaaH2cjpMAKk
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
ccgo v4.36.0 linked musl's __builtin_nanf("") to nanf itself, so in v1.77.0 Xnanf calls itself on every musl target. nan(), nanf(), nanl() and strtod(), strtof(), strtold() or sscanf() of "nan" die with a fatal stack overflow. ccgo fixes it in "v4/lib: link a builtin to the plain function only when linking with libc". TestNaN checks those, and "inf", in a child process, so the fatal error fails only this test. It fails with the ccgo_linux_*.go files of v1.77.0 and passes once they are regenerated by the fixed ccgo. The hand-written ports do not have the bug and most of them lack nan and nanl, so the test is musl only. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
cznic authored
Its Xnanf calls itself on the musl targets. See the parent commit. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored
-
cznic authored