Repository navigation
Conversation
musl's memcmp and bcmp are byte loops, and its memcpy and memmove use rep movs. They took 4.4% of the cycles of a VS Code check and 9.2% on TypeORM (relmusl1). The linux-x64 PGO tsc now links lust's memfast.c (MIT, Theo Browne; byte-equal below its new header) through -Wl,--wrap for memcpy, memmove, memcmp, bcmp and memchr. - memfast.sh compiles it with musl-gcc, checks the object (no undefined symbol, no AVX or BMI) and prints the link flags. - build-pgo.sh: PGO_RUSTFLAGS adds rustc flags to both builds. - release.yml: the flags go to the PGO build only, so the plain tsc (musl's functions) stays the reference of the PGO check. The tsc check reads the five __wrap_ symbols. - NOTICE.md, licenses/memfast-LICENSE.txt and pack.mjs carry the notice into NOTICE.txt. Other targets do not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The linux-x64 tsc had no BOLT: build-bolt.sh refuses static bins, because BOLT breaks static glibc. Static musl works (relmusl1) with two changes against build-release.sh's recipe: - A static bin runs no BOLT code at exit, so the counting copy writes its counts from a watcher every second (-instrumentation-sleep-time=1 -instrumentation-no-counters-clear). Some watchers spin after their last write; the script kills them. - No -split-eh. It gives moved landing pads an LPStart, and LLVM libunwind (the musl target's unwinder) aborts on such an LSDA, so a caught panic would end the tsc. bolt-musl.sh trains a counting copy on pgo-train.sh's set and rewrites the PGO tsc. check-unwind.py then checks the BOLT tsc: a real caught panic (API transpileDeclaration of a .d.ts, which Go answers with a recovered assert) must answer and the process must go on, and every LSDA must start with 0xFF. build-pgo.sh keeps --emit-relocs for the static link with PGO_EMIT_RELOCS=1. release.yml installs LLVM 23 BOLT on linux-x64 and runs the step before the PGO check, which then compares the BOLT tsc with the plain tsc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With pipefail, `cmd | grep -q` can fail when grep stops early and cmd gets SIGPIPE, so a check could miss AVX code or BOLT runtime symbols. Count the matches instead (grep -c reads all input). The built bins do not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
`tsc --listFilesOnly | grep -qx` failed on darwin-x64 with exit 141: grep -q ended at the first match, and the tsc died of SIGPIPE under pipefail. grep without -q reads all of the output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check-unwind.py check 1 now runs a second caught panic: --callbacks readFile:error, then createSnapshot. The tsconfig read panics inside the config load, and in the CI BOLT tsc this unwind crosses 3 frames that BOLT moved, each with a landing pad. Check 1 also fails when stderr has a "libunwind:" line. LLVM libunwind only logs "lsda at ... does not start with 0xFF". It does not always abort. The texts of check-unwind.py, bolt-musl.sh, release.yml and npm/README.md now say that -split-eh can break the unwind, and that check 2 is a cautious guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
t3dotgg
added a commit
that referenced
this pull request
Oct 25, 2026
… tsc 50.3 MB to 49.3 MB llvm-bolt writes its new .gcc_except_table in place of the old one only when it fits. Block reordering splits call-site ranges, so the new table is about 2.6% bigger (R236: 25.8 KB over 1.0 MB). It went to the new segment, 1.0 MB more, and the old table stayed in the file with no FDE that points to it. Now `bolt-musl.sh pad` writes a 64 KiB object of zeros in a .gcc_except_table section, and release.yml links it into the linux-x64 PGO tsc (PGO_RUSTFLAGS; -u keeps it from --gc-sections). llvm-bolt then writes the new table in place (25,974 bytes of the pad used). The script prints the pad left, and warns when the table does not fit. R236 rebuild of this recipe on a testbox: BOLT tsc 49,334,752 bytes, its PGO tsc 48,290,992 (48,225,456 without the pad); without the pad the BOLT tsc was 50,299,600. gzip -9: 18.80 MB, PGO tsc 18.76 MB. The 1.04 MB left: the moved jump tables (0.73 MB; the PIC jump tables of musl, jemalloc and libunwind force -jump-tables=move, and with -use-old-text -skip-funcs does not stop that) and the file gap before the new segment (0.32 MB). Testbox st, query, hono, zod, VS Code and Sentry: instructions:u -0.21% against its PGO tsc, +0.05% against the PR #106 release tsc. cycles:u on VS Code and Sentry -2.5% and -2.5% against its PGO tsc (without the pad -2.4% and -2.5% in the same run); the 5-cell geomean -2.1% (-2.9%), the difference is query, the smallest cell. Output byte-equal to the PR #106 release tsc and main's R236 tsc on the out-check cells; check-unwind.py, pgo-train.sh compare and the release.yml tsc check pass. The header also says not to strip the BOLT tsc: llvm-strip moves the new segment back over the gap, and Linux before 5.18 then passes a wrong AT_PHDR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The linux-x64 tsc that release.yml publishes is static musl with no BOLT. On the same source it needs about 7.5% more cycles than our full release recipe (glibc, BOLT; relmusl1 study). Two causes:
rep movs. They took 4.4% of the cycles of a VS Code check and 9.2% on TypeORM.Change
memfast (first commit). lust's
memfast.creplaces musl's memcpy, memmove, memcmp, bcmp and memchr through-Wl,--wrap.memfast.shcompiles it with musl-gcc and checks the object (no undefined symbol, no AVX or BMI). Only the PGO build gets it. The plain tsc keeps musl's functions, so the existing PGO check also checks memfast.BOLT (second commit).
bolt-musl.shmakes a counting copy (llvm-bolt -instrument), runs it on pgo-train.sh's set, and rewrites the PGO tsc. A static bin needs-instrumentation-sleep-time=1 -instrumentation-no-counters-clear, and the script kills watchers that keep spinning. No-split-eh(see the unwind check). LLVM 23 BOLT comes from apt.llvm.org. build-pgo.sh keeps--emit-relocsfor this build (PGO_EMIT_RELOCS=1).A fix in "Check the tsc".
tsc --listFilesOnly | grep -qxfailed darwin-x64 on this PR's first run with exit 141:grep -qended at the first match, and the tsc died of SIGPIPE under pipefail. grep without-qreads all of the output.A stronger unwind check (last commit). See below.
Other targets do not change. linux-arm64 keeps musl's functions and has no BOLT (not tried).
Numbers
cycles:u with
--noEmit, median of 5 runs, as a ratio to the tsc that main's release.yml builds. 3 passes on one Blacksmith testbox, all bins in each pass. Each cell gives the range over the 3 passes. Query is not in the table: it is too short (0.24 G cycles, 5 to 13% spread)..text. The other 79% stay in the old text (.bolt.org.text) and run from there. Only the old copies of the moved functions (about 15.6 MB) are dead.Unwind check
The port catches panics where Go recovers them, so the BOLT tsc must still unwind. With
-split-eh, BOLT gives moved landing pads an LPStart in their LSDA. LLVM libunwind (the musl target's unwinder) then logs "lsda at ... does not start with 0xFF". That line is only a log. In some cases the unwind then fails ("failed to initiate panic", abort). In other cases it works. So the BOLT step runscheck-unwind.pyon the BOLT tsc:tsc --api. After each one, the tsc must send the error response, answer the next request, exit 0, and write nolibunwind:line to stderr.transpileDeclarationof a.d.tsfile. Go asserts there ("Output generation failed").--callbacks readFile:error,createSnapshotof a project. The read of its tsconfig panics inside the config load. In the CI BOLT tsc, this unwind crosses 3 frames that BOLT moved, each with a landing pad.The CI BOLT tsc (Release run 39967282137) and a non-BOLT tsc pass both (15,411 and 12,177 LSDAs). The test BOLT tscs made with
-split-ehfail both: the second panic is still caught, but stderr has 6libunwind:lines, and 2,180 LSDAs have an LPStart. In a small test program, the BOLT copies with LPStart LSDAs aborted on a caught panic (exit 134), and the copies without them passed.License
crates/ts_goport/scripts/memfast.cissrc/runtime/memfast.cof t3dotgg/lust-lang at 6ced2b86fd (MIT, Copyright (c) 2026 Theo Browne). It is byte-equal below a new header that names the source and the license. NOTICE.md has a new "memfast" section, and npm/pack.mjs addslicenses/memfast-LICENSE.txtto each package's NOTICE.txt. BOLT is a build tool: the shipped bin has no BOLT code (no__boltsymbols).How it was tested
A testbox ran the linux-x64 steps of release.yml from the yml text itself, for main, the first commit and the second commit: Build environment, Build, PGO build, BOLT, the PGO check and "Check the tsc" (with the Alpine run). All passed.
This PR's Release runs: all six
buildjobs andpackagepass.build (linux-x64)takes 15 to 17 minutes (BOLT tools 5 s, BOLT 42 s, the PGO check "same output: 1993 files", the five__wrap_functions, the Alpine run). Inverify (linux-x64), the tsc-rs install and check of the BOLT tsc passes.All six
verifyjobs fail in "npm install and check (@tsc-rs/typescript as typescript)". This failure is not from this PR. That step importstypescript/unstable/sync, but at main's Go pin 3e9236361fa9 (typescript@7.1.0-beta, since 2026-10-22) the package exports it astypescript/sync. The last green Release run was before the pin moved. A release from main fails the same way. The fix is #108, not merged yet.Not in this PR
-split-eh.Changes by Claude Opus 5.5 in Claude Code.
🤖 Generated with Claude Code