Merge train 209: keepalive anchors as #[used(compiler)] — stop pinning the whole runtime (v0.5.1587) - #10401
Merged
Conversation
added 4 commits
September 17, 2026 06:49
Plain #[used] on Mach-O sets N_NO_DEAD_STRIP, making every KEEP_* anchor an unconditional ld64 root. With codegen-units = 1 perry-runtime is a single archive member, so any runtime reference pulls all anchors and keeps the whole runtime surface alive. #[used(compiler)] keeps the anchor (and its target) in the archive for rustc/LTO without making it a linker root. Left as linker roots: mimalloc __mod_init_func, OHOS .init_array, build stamp. Local experiment branch; not for merge as-is.
The trailing comment on `#![feature(used_with_arg)]` is long enough that rustfmt re-indents the following comment block to align after it, so `cargo fmt --check` fails. Same text, placed above the attribute.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (116)
📝 WalkthroughWalkthroughThe pull request changes keepalive anchors from ChangesKeepalive anchor retention
Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Other · Severity of issue fixed: Low Possibly related PRs
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
This train lands #10382 as v0.5.1587, on
fe0cc15b0f. One source commit, patch-id and authorship preserved.#[used(compiler)]— stop pinning the whole runtime into every binary (−15% typical, −21.7% auto-optimized) #10382 — the ~490 runtime keepalive anchors become#[used(compiler)]instead of plain#[used], so they stop acting as unconditional linker roots.#[used]setsN_NO_DEAD_STRIPon Mach-O andSHF_GNU_RETAINon ELF; withcodegen-units = 1the runtime is a single 17.4 MB object, so pulling in any runtime symbol pulls in all 504KEEP_*statics and each roots whatever it points at.#[used(compiler)]keeps the static through rustc and LLVM — the reason the anchors exist, per size: gate keepalive anchors out of the classic link + opt-in size-optimized runtime rebuild (hello world 5.9→4.6 MB default, 2.3 MB size mode) #6917's revert — while leaving it strippable by the linker.Train repairs
No
changelog.d/fragment — a hard failure of the changeset gate for anycrates/change. Added, keyed to #10382.cargo fmt --check: the trailing comment on#![feature(used_with_arg)]is long enough that rustfmt re-indents the following comment block to align after it. Same text, moved above the attribute.Validation
Validated head
1471c22302. Five-package release build pinned and hash-verified, and re-verified after the gap run.Crate suites cannot see this change — the failure mode is the linker dead-stripping a symbol that is only reached dynamically, which is exactly what #6917 was reverted for. So it was checked two ways that a suite cannot substitute for.
1. The archive stays complete.
llvm-nm --defined-only --extern-onlyonlibperry_runtime.a, restricted to the C-ABI#[no_mangle]surface that generated code actually links against:All six symbols the PR names as reachable only dynamically are present on both arms:
js_box_release,js_bool_box_release,js_i32_box_release,js_closure_set_box_capture_ptr,js_link_path_module_parent(CJSrequire),js_module_dynamic_import_deferred.Two notes on that measurement, because the naive version is worthless. Rust's legacy (
_ZN) and v0 (__RINv…) manglings both carry a per-build crate disambiguator, so diffing raw symbol names reports tens of thousands of spurious missing/added entries — the near-equal counts are the tell. And Apple'snmreturns zero symbols on LTO bitcode archives while exiting 0, so the check asserts a non-zero symbol count before trusting any verdict.2. Programs compile AND run on both auto-optimize paths. A probe exercising boxed closure captures, CJS
requireand dynamicimport():--no-auto-optimizeEach binary's embedded build stamp was read back and required to be this train's, so neither arm can have silently measured a different compiler.
Crate suites for completeness: codegen 1571, runtime 3984, stdlib 139, hir 433, transform 137, cli 1139 — all green except
main's one known runtime failure. All nine preflight gates pass.Gap reds, attributed
Filters
require,import,closure,module,worker,dynamic.closureandworkerare clean.Six fixtures A/B'd against
main's own artifact set — all six identical on both arms (node=1, main=0, train=0) with distinct build stamps per arm:test_issue_1723_require_stdlib_subnamespace,test_dynamic_import_data_10104,test_jwt_sign_dynamic_alg, and the three module-init fixtures. Perry is correct on both arms; node is what fails.Worth recording honestly: those reds clustered on the require/import/module surfaces, which looked like anchor stripping. It was a selection effect — the filters were chosen by those surfaces, so any failure they surfaced was guaranteed to land there.
closureandworkerpassing was the signal against it, and the A/B settled it.test_requireis listed verbatim inrun_parity_tests.sh'sSKIP_TESTSonmain(crypto.randomUUID()differs).test_ramda_user_importis an ext-archive link refusal of the pinned five-package set, not a code difference.Not covered
Windows/COFF is untested, as the PR itself flags. Only macOS arm64 and Linux x86_64 were measured. Worth watching the Windows jobs on this train.
Before merging, the pushed head and unchanged main are checked again. After merging, the rewritten commits are checked for preserved authorship and the main tree must match the validated train exactly.
Summary by CodeRabbit
New Features
Chores