Skip to content

Merge train 209: keepalive anchors as #[used(compiler)] — stop pinning the whole runtime (v0.5.1587) - #10401

Merged
proggeramlug merged 4 commits into
mainfrom
train209r
Sep 17, 2026
Merged

proggeramlug merged 4 commits into
mainfrom
train209r

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

This train lands #10382 as v0.5.1587, on fe0cc15b0f. One source commit, patch-id and authorship preserved.

Train repairs

No changelog.d/ fragment — a hard failure of the changeset gate for any crates/ 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-only on libperry_runtime.a, restricted to the C-ABI #[no_mangle] surface that generated code actually links against:

main this train
C-ABI defined symbols 4470 4470 (0 missing, 0 added)

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 (CJS require), 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's nm returns 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 require and dynamic import():

path compile run binary vs node
auto-optimize (shipping path) ok ok 8.93 MB byte-identical
--no-auto-optimize ok ok 15.6 MB byte-identical

Each 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. closure and worker are 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. closure and worker passing was the signal against it, and the A/B settled it.

test_require is listed verbatim in run_parity_tests.sh's SKIP_TESTS on main (crypto.randomUUID() differs). test_ramda_user_import is 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

    • Reduced compiled application and compiler binary sizes by retaining only runtime components required by each program.
    • Preserved availability of compiler-generated runtime functionality while allowing unused components to be removed.
  • Chores

    • Updated the project version to 0.5.1587.

Ralph Küpper 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.
@proggeramlug
proggeramlug merged commit e6dcb62 into main Sep 17, 2026
20 of 21 checks passed
@proggeramlug
proggeramlug deleted the train209r branch September 17, 2026 05:39
@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: d48575f1-b7fa-49b6-a47b-e2f80733262d

📥 Commits

Reviewing files that changed from the base of the PR and between fe0cc15 and 1471c22.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (116)
  • CLAUDE.md
  • Cargo.toml
  • changelog.d/10382-used-compiler-keepalive-anchors.md
  • crates/perry-runtime/src/array/element_shape.rs
  • crates/perry-runtime/src/array/fill_extend.rs
  • crates/perry-runtime/src/array/from_concat.rs
  • crates/perry-runtime/src/array/generic.rs
  • crates/perry-runtime/src/array/generic_object.rs
  • crates/perry-runtime/src/array/header.rs
  • crates/perry-runtime/src/array/immutable.rs
  • crates/perry-runtime/src/array/indexing.rs
  • crates/perry-runtime/src/array/iter_methods.rs
  • crates/perry-runtime/src/array/join.rs
  • crates/perry-runtime/src/array/numeric_range.rs
  • crates/perry-runtime/src/array/push_pop.rs
  • crates/perry-runtime/src/array/sort.rs
  • crates/perry-runtime/src/array/subclass_loop_guard.rs
  • crates/perry-runtime/src/array/subclass_packed_index.rs
  • crates/perry-runtime/src/bigint/convert.rs
  • crates/perry-runtime/src/box.rs
  • crates/perry-runtime/src/buffer/access.rs
  • crates/perry-runtime/src/buffer/dataview.rs
  • crates/perry-runtime/src/buffer/encode.rs
  • crates/perry-runtime/src/buffer/query.rs
  • crates/perry-runtime/src/buffer/u8_codec.rs
  • crates/perry-runtime/src/builtins/arithmetic.rs
  • crates/perry-runtime/src/bun_compat/jsc.rs
  • crates/perry-runtime/src/child_process/validate.rs
  • crates/perry-runtime/src/closure/alloc.rs
  • crates/perry-runtime/src/closure/dispatch/bound.rs
  • crates/perry-runtime/src/closure/registry.rs
  • crates/perry-runtime/src/closure/unbox.rs
  • crates/perry-runtime/src/collection_iter_object.rs
  • crates/perry-runtime/src/date.rs
  • crates/perry-runtime/src/disposable.rs
  • crates/perry-runtime/src/eh.rs
  • crates/perry-runtime/src/embedded.rs
  • crates/perry-runtime/src/embedded/compressed.rs
  • crates/perry-runtime/src/error.rs
  • crates/perry-runtime/src/error_subclass_stack.rs
  • crates/perry-runtime/src/event_target.rs
  • crates/perry-runtime/src/fs/validate.rs
  • crates/perry-runtime/src/gc/barrier/mod.rs
  • crates/perry-runtime/src/gc/layout/typed_shape.rs
  • crates/perry-runtime/src/intl/segments_view.rs
  • crates/perry-runtime/src/iterator_helpers.rs
  • crates/perry-runtime/src/json/parse_api.rs
  • crates/perry-runtime/src/json/raw_json.rs
  • crates/perry-runtime/src/lib.rs
  • crates/perry-runtime/src/map.rs
  • crates/perry-runtime/src/module_require.rs
  • crates/perry-runtime/src/module_require/import_meta_resolve.rs
  • crates/perry-runtime/src/native_abi.rs
  • crates/perry-runtime/src/node_sea.rs
  • crates/perry-runtime/src/node_stream_keepalive.rs
  • crates/perry-runtime/src/node_submodules/zlib.rs
  • crates/perry-runtime/src/node_v8.rs
  • crates/perry-runtime/src/object/alloc.rs
  • crates/perry-runtime/src/object/class_constructors.rs
  • crates/perry-runtime/src/object/class_meta_registry.rs
  • crates/perry-runtime/src/object/class_registry/construct/class_return.rs
  • crates/perry-runtime/src/object/class_registry/registration.rs
  • crates/perry-runtime/src/object/descriptors.rs
  • crates/perry-runtime/src/object/global_this/builtin_thunks.rs
  • crates/perry-runtime/src/object/global_this/fetch_globals.rs
  • crates/perry-runtime/src/object/groupby.rs
  • crates/perry-runtime/src/object/native_module.rs
  • crates/perry-runtime/src/object/native_this_alias.rs
  • crates/perry-runtime/src/object/object_ops/has_own.rs
  • crates/perry-runtime/src/object/shapes.rs
  • crates/perry-runtime/src/object/this_binding.rs
  • crates/perry-runtime/src/object/with_env.rs
  • crates/perry-runtime/src/os.rs
  • crates/perry-runtime/src/os/os_process_emitter.rs
  • crates/perry-runtime/src/param_type_guard.rs
  • crates/perry-runtime/src/path.rs
  • crates/perry-runtime/src/path/value_args.rs
  • crates/perry-runtime/src/process/env_misc.rs
  • crates/perry-runtime/src/promise/async_step.rs
  • crates/perry-runtime/src/promise/combinators.rs
  • crates/perry-runtime/src/promise/microtasks.rs
  • crates/perry-runtime/src/promise/mod.rs
  • crates/perry-runtime/src/promise/rejection.rs
  • crates/perry-runtime/src/proxy.rs
  • crates/perry-runtime/src/proxy/put_value.rs
  • crates/perry-runtime/src/regex/escape.rs
  • crates/perry-runtime/src/set.rs
  • crates/perry-runtime/src/string/locale.rs
  • crates/perry-runtime/src/string/pad.rs
  • crates/perry-runtime/src/string/raw.rs
  • crates/perry-runtime/src/string/slice_ops.rs
  • crates/perry-runtime/src/string/suffix_cursor.rs
  • crates/perry-runtime/src/symbol/constructors.rs
  • crates/perry-runtime/src/symbol/iterator.rs
  • crates/perry-runtime/src/text.rs
  • crates/perry-runtime/src/tls.rs
  • crates/perry-runtime/src/typed_feedback.rs
  • crates/perry-runtime/src/typed_feedback/guards.rs
  • crates/perry-runtime/src/typed_feedback/tests.rs
  • crates/perry-runtime/src/typed_feedback/trace.rs
  • crates/perry-runtime/src/typedarray/access.rs
  • crates/perry-runtime/src/typedarray_props.rs
  • crates/perry-runtime/src/url/abort.rs
  • crates/perry-runtime/src/url/search_params.rs
  • crates/perry-runtime/src/validators.rs
  • crates/perry-runtime/src/value/dyn_index.rs
  • crates/perry-runtime/src/value/dynamic_arith.rs
  • crates/perry-runtime/src/value/nanbox.rs
  • crates/perry-runtime/src/yoga.rs
  • crates/perry-stdlib/src/events/constructors.rs
  • crates/perry-stdlib/src/lib.rs
  • crates/perry-stdlib/src/sqlite/better.rs
  • crates/perry-stdlib/src/tls.rs
  • crates/perry-stdlib/src/worker_threads.rs
  • crates/perry-stdlib/src/worker_threads/direct_message.rs
  • crates/perry-stdlib/src/zlib.rs

📝 Walkthrough

Walkthrough

The pull request changes keepalive anchors from #[used] to #[used(compiler)] across the runtime and standard library. It adds the required nightly feature gates, aligns selected feature guards, updates documentation, and bumps the version to 0.5.1587.

Changes

Keepalive anchor retention

Layer / File(s) Summary
Compiler retention support and release metadata
CLAUDE.md, Cargo.toml, crates/perry-runtime/src/lib.rs, crates/perry-stdlib/src/lib.rs, changelog.d/*
The version changes to 0.5.1587. Both crates enable used_with_arg. The changelog records the retention and binary-size results.
Runtime keepalive anchors
crates/perry-runtime/src/**
Runtime KEEP_* statics change from #[used] to #[used(compiler)]. Several previously unconditional anchors now use the keepalive-anchors feature gate.
Typed-feedback validation
crates/perry-runtime/src/typed_feedback/*
Typed-feedback anchors use compiler-scoped retention. The source assertion accepts either #[used] or #[used(compiler)].
Standard-library keepalive anchors
crates/perry-stdlib/src/**
Event, SQLite, TLS, worker-thread, and zlib anchors change to #[used(compiler)].

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Other · Severity of issue fixed: Low

Possibly related PRs

  • PerryTS/perry#6917: Introduced the keepalive-anchors feature and the anchor statics changed by this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch train209r

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant