From 576443b287015d07e12c5ba52974d09ff90b5ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 11:53:50 +0200 Subject: [PATCH 01/12] =?UTF-8?q?size:=20PERRY=5FSIZE=5FOPT=3Dz|s=20knob?= =?UTF-8?q?=20=E2=80=94=20rebuild=20auto-opt=20runtime/stdlib=20at=20-C=20?= =?UTF-8?q?opt-level=3Dz/s=20(cache-keyed)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../perry/src/commands/compile/optimized_libs.rs | 2 +- .../commands/compile/optimized_libs/driver.rs | 11 +++++++++++ .../commands/compile/optimized_libs/freshness.rs | 16 +++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/crates/perry/src/commands/compile/optimized_libs.rs b/crates/perry/src/commands/compile/optimized_libs.rs index 18849b2559..22207e4ddf 100644 --- a/crates/perry/src/commands/compile/optimized_libs.rs +++ b/crates/perry/src/commands/compile/optimized_libs.rs @@ -32,7 +32,7 @@ pub(crate) use driver::build_optimized_libs; pub(crate) use freshness::{ auto_optimized_archives_are_fresh, auto_optimized_build_stamp, auto_optimized_cache_key, auto_optimized_cross_features, auto_optimized_source_fingerprint, binding_needs_shared_tokio, - resolve_auto_well_known_libs, + resolve_auto_well_known_libs, size_opt_level, }; pub(crate) use no_auto::{ build_missing_prebuilt_ext_lib, resolve_no_auto_optimized_libs, resolve_prebuilt_ext_libs, diff --git a/crates/perry/src/commands/compile/optimized_libs/driver.rs b/crates/perry/src/commands/compile/optimized_libs/driver.rs index fcb13e09e5..34711a9d35 100644 --- a/crates/perry/src/commands/compile/optimized_libs/driver.rs +++ b/crates/perry/src/commands/compile/optimized_libs/driver.rs @@ -776,6 +776,17 @@ pub(crate) fn build_optimized_libs( // duration of this invocation. rustflags.push("-C panic=abort".to_string()); } + // PERRY_SIZE_OPT=z|s — rebuild the auto-optimized runtime/stdlib at + // `-C opt-level=z`/`s` instead of the profile's `3`. RUSTFLAGS is + // appended after cargo's profile-derived flags, and rustc takes the + // last `-C opt-level`, so this cleanly overrides the per-package + // `opt-level = 3` without a custom profile. Trades some runtime speed + // for a substantially smaller binary; keyed into the auto-opt cache + // dir hash (see `auto_optimized_cache_key`) so size-optimized and + // speed-optimized archives never serve each other's builds. + if let Some(level) = size_opt_level() { + rustflags.push(format!("-C opt-level={level}")); + } // #6125: pin the Rust-side CPU baseline to the same PERRY_TARGET_CPU // knob codegen's clang invocation honors, so the rebuilt runtime/stdlib // (which is what runs before the app's first print — exactly where the diff --git a/crates/perry/src/commands/compile/optimized_libs/freshness.rs b/crates/perry/src/commands/compile/optimized_libs/freshness.rs index f2189cb339..43d5bbe1c0 100644 --- a/crates/perry/src/commands/compile/optimized_libs/freshness.rs +++ b/crates/perry/src/commands/compile/optimized_libs/freshness.rs @@ -55,6 +55,19 @@ pub(crate) fn auto_optimized_archives_are_fresh( true } +/// `PERRY_SIZE_OPT=z|s` — opt-in size-optimized rebuild of the +/// auto-optimized runtime/stdlib archives (`-C opt-level=z`/`s` instead of +/// the profile's `3`). Any other value (or unset) means "off". Read in the +/// rustflags builder AND the cache key so the two can never disagree about +/// which archive a `target/perry-auto-` dir contains. +pub(crate) fn size_opt_level() -> Option<&'static str> { + match std::env::var("PERRY_SIZE_OPT").ok().as_deref() { + Some("z") => Some("z"), + Some("s") => Some("s"), + _ => None, + } +} + /// Cache key for the auto-optimize target dir + build stamp. Hashed into the /// `target/perry-auto-` dir name so each (features, panic-mode, target, /// runtime-gate, version) combination gets its own incremental cache. Kept in @@ -67,7 +80,7 @@ pub(crate) fn auto_optimized_cache_key( ) -> String { let target_str = target.unwrap_or("host"); format!( - "{}|{}|{}|wasm={}|regex={}|temporal={}|ee={}|url={}|norm={}|seg={}|loc={}|diag={}|dgram={}|http2={}|dyneval={}|v={}", + "{}|{}|{}|wasm={}|regex={}|temporal={}|ee={}|url={}|norm={}|seg={}|loc={}|diag={}|dgram={}|http2={}|dyneval={}|sizeopt={}|v={}", feature_arg, panic_abort_safe, target_str, @@ -88,6 +101,7 @@ pub(crate) fn auto_optimized_cache_key( // #6559: dyn-eval presence changes the built archive, so it must // key the freshness stamp like every other runtime feature toggle. perry_hir::has_deferred_dynamic_code_sites(), + size_opt_level().unwrap_or("off"), env!("CARGO_PKG_VERSION"), ) } From 727372fb0f77e57a215f2d65ddb242cfd2a6457e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 11:57:52 +0200 Subject: [PATCH 02/12] =?UTF-8?q?size:=20PERRY=5FSIZE=5FLTO=3Dfat=20?= =?UTF-8?q?=E2=80=94=20fat-LTO=20+=20cu=3D1=20variant=20of=20the=20size-op?= =?UTF-8?q?t=20archive=20rebuild?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/commands/compile/optimized_libs/driver.rs | 10 ++++++++++ .../commands/compile/optimized_libs/freshness.rs | 14 +++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/perry/src/commands/compile/optimized_libs/driver.rs b/crates/perry/src/commands/compile/optimized_libs/driver.rs index 34711a9d35..adb8155e6c 100644 --- a/crates/perry/src/commands/compile/optimized_libs/driver.rs +++ b/crates/perry/src/commands/compile/optimized_libs/driver.rs @@ -787,6 +787,16 @@ pub(crate) fn build_optimized_libs( if let Some(level) = size_opt_level() { rustflags.push(format!("-C opt-level={level}")); } + // PERRY_SIZE_LTO=fat — whole-archive fat LTO + one codegen unit on top + // of the size opt level. The workspace profile's `lto = "thin"` already + // makes every rlib carry bitcode, so the leaf staticlib crates can be + // re-monomorphized as one unit; rustc takes the last `-C lto`/`-C + // codegen-units`, letting these override the profile's thin/16. + if size_lto_fat() { + rustflags.push("-C lto=fat".to_string()); + rustflags.push("-C codegen-units=1".to_string()); + rustflags.push("-C embed-bitcode=yes".to_string()); + } // #6125: pin the Rust-side CPU baseline to the same PERRY_TARGET_CPU // knob codegen's clang invocation honors, so the rebuilt runtime/stdlib // (which is what runs before the app's first print — exactly where the diff --git a/crates/perry/src/commands/compile/optimized_libs/freshness.rs b/crates/perry/src/commands/compile/optimized_libs/freshness.rs index 43d5bbe1c0..99a45c8600 100644 --- a/crates/perry/src/commands/compile/optimized_libs/freshness.rs +++ b/crates/perry/src/commands/compile/optimized_libs/freshness.rs @@ -68,6 +68,14 @@ pub(crate) fn size_opt_level() -> Option<&'static str> { } } +/// `PERRY_SIZE_LTO=fat` — additionally rebuild the archives with fat LTO + +/// a single codegen unit. Slower build, smaller/faster archive; only +/// meaningful together with `size_opt_level`. Keyed into the cache like the +/// opt level. +pub(crate) fn size_lto_fat() -> bool { + std::env::var("PERRY_SIZE_LTO").ok().as_deref() == Some("fat") +} + /// Cache key for the auto-optimize target dir + build stamp. Hashed into the /// `target/perry-auto-` dir name so each (features, panic-mode, target, /// runtime-gate, version) combination gets its own incremental cache. Kept in @@ -101,7 +109,11 @@ pub(crate) fn auto_optimized_cache_key( // #6559: dyn-eval presence changes the built archive, so it must // key the freshness stamp like every other runtime feature toggle. perry_hir::has_deferred_dynamic_code_sites(), - size_opt_level().unwrap_or("off"), + format!( + "{}{}", + size_opt_level().unwrap_or("off"), + if size_lto_fat() { "+fatlto" } else { "" } + ), env!("CARGO_PKG_VERSION"), ) } From b6683f7e8daf2d4839ec57d7f9d6676bfa4ed1be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 11:58:45 +0200 Subject: [PATCH 03/12] size: re-export size_lto_fat --- crates/perry/src/commands/compile/optimized_libs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/perry/src/commands/compile/optimized_libs.rs b/crates/perry/src/commands/compile/optimized_libs.rs index 22207e4ddf..8ce95eeb49 100644 --- a/crates/perry/src/commands/compile/optimized_libs.rs +++ b/crates/perry/src/commands/compile/optimized_libs.rs @@ -32,7 +32,7 @@ pub(crate) use driver::build_optimized_libs; pub(crate) use freshness::{ auto_optimized_archives_are_fresh, auto_optimized_build_stamp, auto_optimized_cache_key, auto_optimized_cross_features, auto_optimized_source_fingerprint, binding_needs_shared_tokio, - resolve_auto_well_known_libs, size_opt_level, + resolve_auto_well_known_libs, size_lto_fat, size_opt_level, }; pub(crate) use no_auto::{ build_missing_prebuilt_ext_lib, resolve_no_auto_optimized_libs, resolve_prebuilt_ext_libs, From ade32626dad9982a18b9eeab18da6637c17e7d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 12:00:39 +0200 Subject: [PATCH 04/12] size: fat LTO via CARGO_PROFILE_RELEASE_LTO env (RUSTFLAGS -C lto breaks rlib deps) --- .../src/commands/compile/optimized_libs/driver.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/perry/src/commands/compile/optimized_libs/driver.rs b/crates/perry/src/commands/compile/optimized_libs/driver.rs index adb8155e6c..f04b8dac7a 100644 --- a/crates/perry/src/commands/compile/optimized_libs/driver.rs +++ b/crates/perry/src/commands/compile/optimized_libs/driver.rs @@ -787,15 +787,14 @@ pub(crate) fn build_optimized_libs( if let Some(level) = size_opt_level() { rustflags.push(format!("-C opt-level={level}")); } - // PERRY_SIZE_LTO=fat — whole-archive fat LTO + one codegen unit on top - // of the size opt level. The workspace profile's `lto = "thin"` already - // makes every rlib carry bitcode, so the leaf staticlib crates can be - // re-monomorphized as one unit; rustc takes the last `-C lto`/`-C - // codegen-units`, letting these override the profile's thin/16. + // PERRY_SIZE_LTO=fat — whole-archive fat LTO on top of the size opt + // level. Must go through cargo's profile override (env form) rather + // than RUSTFLAGS: `-C lto` in RUSTFLAGS reaches every rlib dep too, + // and rustc rejects `-C lto` for rlib crate types. The env override + // lets cargo do it properly (deps keep embedding bitcode, only the + // leaf staticlib runs the fat-LTO pass). if size_lto_fat() { - rustflags.push("-C lto=fat".to_string()); - rustflags.push("-C codegen-units=1".to_string()); - rustflags.push("-C embed-bitcode=yes".to_string()); + cargo_cmd.env("CARGO_PROFILE_RELEASE_LTO", "fat"); } // #6125: pin the Rust-side CPU baseline to the same PERRY_TARGET_CPU // knob codegen's clang invocation honors, so the rebuilt runtime/stdlib From 244976fc687b57b3cb1faf3f34599fb5abad86ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 12:02:18 +0200 Subject: [PATCH 05/12] link: PERRY_EXTRA_LINK_ARGS diagnostic passthrough --- .../perry/src/commands/compile/link/build_and_run.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/perry/src/commands/compile/link/build_and_run.rs b/crates/perry/src/commands/compile/link/build_and_run.rs index e591877906..3274125b31 100644 --- a/crates/perry/src/commands/compile/link/build_and_run.rs +++ b/crates/perry/src/commands/compile/link/build_and_run.rs @@ -1850,6 +1850,16 @@ pub(crate) fn build_and_run_link( // line, so the invocation no longer scales with module count. Only the // native-Windows host is affected (other hosts have a multi-MB `ARG_MAX`); // `PERRY_FORCE_LINK_RESPONSE_FILE=1` forces the path elsewhere for testing. + // PERRY_EXTRA_LINK_ARGS — space-separated args appended verbatim to the + // final link invocation. Diagnostic escape hatch (e.g. + // `-Wl,-why_live,_js_fs_open` to ask ld64 why a symbol survived + // dead-strip); not part of any documented stable surface. + if let Ok(extra) = std::env::var("PERRY_EXTRA_LINK_ARGS") { + for a in extra.split_whitespace() { + cmd.arg(a); + } + } + let mut response_file_to_clean: Option = None; if cfg!(target_os = "windows") || std::env::var_os("PERRY_FORCE_LINK_RESPONSE_FILE").is_some() { // MSVC-style quoting for the native-Windows linker (link.exe/lld-link); From 1529d46aa9b08fb3aa8924730030d071054bebfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 12:07:45 +0200 Subject: [PATCH 06/12] size: gate the 487 #[used] keepalive anchors behind new keepalive-anchors feature (bitcode-LTO only) --- crates/perry-runtime/Cargo.toml | 16 +- crates/perry-runtime/src/array/from_concat.rs | 6 +- crates/perry-runtime/src/array/generic.rs | 16 +- crates/perry-runtime/src/array/header.rs | 14 +- crates/perry-runtime/src/array/indexing.rs | 18 +-- .../perry-runtime/src/array/iter_methods.rs | 10 +- crates/perry-runtime/src/array/push_pop.rs | 4 +- crates/perry-runtime/src/array/sort.rs | 2 +- crates/perry-runtime/src/bigint/convert.rs | 2 +- crates/perry-runtime/src/box.rs | 28 ++-- crates/perry-runtime/src/buffer/access.rs | 4 +- crates/perry-runtime/src/buffer/dataview.rs | 4 +- crates/perry-runtime/src/buffer/encode.rs | 4 +- crates/perry-runtime/src/buffer/query.rs | 2 +- crates/perry-runtime/src/buffer/u8_codec.rs | 14 +- .../perry-runtime/src/builtins/arithmetic.rs | 10 +- .../src/child_process/validate.rs | 6 +- crates/perry-runtime/src/closure/alloc.rs | 4 +- .../src/closure/dispatch/bound.rs | 4 +- crates/perry-runtime/src/closure/registry.rs | 2 +- crates/perry-runtime/src/closure/unbox.rs | 2 +- .../src/collection_iter_object.rs | 12 +- crates/perry-runtime/src/date.rs | 6 +- crates/perry-runtime/src/disposable.rs | 28 ++-- crates/perry-runtime/src/embedded.rs | 8 +- crates/perry-runtime/src/error.rs | 32 ++-- crates/perry-runtime/src/event_target.rs | 2 +- crates/perry-runtime/src/fs/validate.rs | 4 +- crates/perry-runtime/src/gc/barrier.rs | 6 +- crates/perry-runtime/src/iterator_helpers.rs | 2 +- crates/perry-runtime/src/json/parse_api.rs | 2 +- crates/perry-runtime/src/json/raw_json.rs | 4 +- crates/perry-runtime/src/map.rs | 36 ++--- crates/perry-runtime/src/module_require.rs | 8 +- crates/perry-runtime/src/native_abi.rs | 24 +-- crates/perry-runtime/src/node_sea.rs | 10 +- .../src/node_stream_keepalive.rs | 142 +++++++++--------- .../perry-runtime/src/node_submodules/zlib.rs | 10 +- crates/perry-runtime/src/node_v8.rs | 40 ++--- crates/perry-runtime/src/object/alloc.rs | 2 +- .../src/object/class_constructors.rs | 20 +-- .../src/object/class_registry/registration.rs | 10 +- .../perry-runtime/src/object/descriptors.rs | 2 +- .../src/object/global_this/builtin_thunks.rs | 2 +- .../src/object/global_this/fetch_globals.rs | 10 +- crates/perry-runtime/src/object/groupby.rs | 4 +- .../perry-runtime/src/object/native_module.rs | 2 +- .../src/object/native_this_alias.rs | 4 +- .../src/object/object_ops/has_own.rs | 2 +- .../perry-runtime/src/object/this_binding.rs | 6 +- crates/perry-runtime/src/object/with_env.rs | 4 +- crates/perry-runtime/src/os.rs | 2 +- crates/perry-runtime/src/path.rs | 4 +- crates/perry-runtime/src/process/env_misc.rs | 52 +++---- .../perry-runtime/src/promise/async_step.rs | 2 +- .../perry-runtime/src/promise/combinators.rs | 8 +- .../perry-runtime/src/promise/microtasks.rs | 2 +- crates/perry-runtime/src/promise/mod.rs | 18 +-- crates/perry-runtime/src/promise/rejection.rs | 6 +- crates/perry-runtime/src/proxy.rs | 18 +-- crates/perry-runtime/src/proxy/put_value.rs | 2 +- crates/perry-runtime/src/regex/escape.rs | 4 +- crates/perry-runtime/src/set.rs | 54 +++---- crates/perry-runtime/src/string/locale.rs | 8 +- crates/perry-runtime/src/string/pad.rs | 4 +- crates/perry-runtime/src/string/raw.rs | 4 +- crates/perry-runtime/src/string/slice_ops.rs | 8 +- .../perry-runtime/src/symbol/constructors.rs | 4 +- crates/perry-runtime/src/symbol/iterator.rs | 2 +- crates/perry-runtime/src/text.rs | 12 +- crates/perry-runtime/src/tls.rs | 12 +- crates/perry-runtime/src/typed_feedback.rs | 6 +- .../src/typed_feedback/guards.rs | 18 +-- .../perry-runtime/src/typed_feedback/tests.rs | 4 +- .../perry-runtime/src/typed_feedback/trace.rs | 66 ++++---- crates/perry-runtime/src/typedarray/access.rs | 12 +- crates/perry-runtime/src/typedarray_props.rs | 2 +- crates/perry-runtime/src/url/abort.rs | 8 +- crates/perry-runtime/src/url/search_params.rs | 2 +- crates/perry-runtime/src/validators.rs | 8 +- crates/perry-runtime/src/value/dyn_index.rs | 8 +- .../perry-runtime/src/value/dynamic_arith.rs | 10 +- crates/perry-runtime/src/value/nanbox.rs | 2 +- crates/perry-runtime/src/yoga.rs | 2 +- .../compile/optimized_libs/freshness.rs | 12 +- 85 files changed, 513 insertions(+), 489 deletions(-) diff --git a/crates/perry-runtime/Cargo.toml b/crates/perry-runtime/Cargo.toml index 1751368fec..cd28ac3725 100644 --- a/crates/perry-runtime/Cargo.toml +++ b/crates/perry-runtime/Cargo.toml @@ -22,7 +22,21 @@ crate-type = ["rlib"] # actually needs (see optimized_libs.rs), so the heavy subsystems below # (regex engine, Temporal, URL/IDNA, normalize, segmenter) are *opt-in per # app* and absent from binaries that never use them. -default = ["full", "regex-engine", "temporal", "url-engine", "string-normalize", "intl-segmenter", "intl-locale", "intl-datetime", "diagnostics", "mod-dgram", "mod-http2-constants", "dyn-eval"] +default = ["full", "regex-engine", "temporal", "url-engine", "string-normalize", "intl-segmenter", "intl-locale", "intl-datetime", "diagnostics", "mod-dgram", "mod-http2-constants", "dyn-eval", "keepalive-anchors"] +# Compile the ~490 `#[used]` keep-alive anchor statics (`KEEP_*`, `keep!`, +# `K*`/`G*` fn-pointer statics) that pin codegen-facing `#[no_mangle]` entry +# points as dead-strip roots. They exist for the whole-program bitcode-LTO +# link (`PERRY_LLVM_BITCODE_LINK=1`), where the merged module could otherwise +# internalize + drop symbols only referenced from the user's separate `.o`. +# The CLASSIC link path never needs them: every reachable runtime symbol is +# kept by a real undefined reference from the program's objects, and the +# anchors only defeat `-dead_strip` (measured: a hello-world binary carries +# the entire fs/child_process/vm/node_submodules surface through ONE anchored +# chain — KEEP_JS_MODULE_DYNAMIC_IMPORT_DEFERRED → dynamic-import fallback → +# js_nm_install_all → every dispatch bucket). Kept in `default` so the +# prebuilt/full archives and `cargo test` are unchanged; the auto-optimize +# rebuild omits it unless the bitcode link was requested. +keepalive-anchors = [] # Per-module Node-API gate (binary-size): compiles `node:dgram`'s UDP-socket # implementation (`crate::dgram` + `crate::dgram_reactor`, ~2.2k LOC, incl. the # `js_dgram_*` externs codegen emits direct calls to) + its dispatch arm only diff --git a/crates/perry-runtime/src/array/from_concat.rs b/crates/perry-runtime/src/array/from_concat.rs index ce0884edca..bb65ce3600 100644 --- a/crates/perry-runtime/src/array/from_concat.rs +++ b/crates/perry-runtime/src/array/from_concat.rs @@ -140,7 +140,7 @@ pub extern "C" fn js_array_from_value(boxed: f64) -> *mut ArrayHeader { js_array_clone(ptr_bits as *const ArrayHeader) } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_FROM_VALUE: extern "C" fn(f64) -> *mut ArrayHeader = js_array_from_value; /// `Array.from(source, mapFn, thisArg)` — the mapped form. Throws for nullish @@ -165,7 +165,7 @@ pub extern "C" fn js_array_from_mapped( as *mut ArrayHeader } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_FROM_MAPPED: extern "C" fn(f64, f64, f64) -> *mut ArrayHeader = js_array_from_mapped; @@ -282,7 +282,7 @@ pub extern "C" fn js_array_concat_variadic( result } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_CONCAT_VARIADIC: extern "C" fn( *const ArrayHeader, *const f64, diff --git a/crates/perry-runtime/src/array/generic.rs b/crates/perry-runtime/src/array/generic.rs index da28f8cc70..38957fae5b 100644 --- a/crates/perry-runtime/src/array/generic.rs +++ b/crates/perry-runtime/src/array/generic.rs @@ -1120,7 +1120,7 @@ fn clamp_index(v: f64, len: i64) -> i64 { // Keep the generic entry points anchored against dead-strip in the default // (codegen-only reference) compile path (see #3320 — `#[no_mangle]` alone is // not enough once the bitcode is re-linked). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAYLIKE_CB: [extern "C" fn(f64, f64, f64) -> f64; 9] = [ js_arraylike_forEach, js_arraylike_map, @@ -1132,20 +1132,20 @@ static KEEP_ARRAYLIKE_CB: [extern "C" fn(f64, f64, f64) -> f64; 9] = [ js_arraylike_findLast, js_arraylike_findLastIndex, ]; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAYLIKE_REDUCE: [extern "C" fn(f64, f64, i32, f64) -> f64; 2] = [js_arraylike_reduce, js_arraylike_reduceRight]; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAYLIKE_SEARCH: [extern "C" fn(f64, f64, f64, i32) -> f64; 3] = [ js_arraylike_indexOf, js_arraylike_lastIndexOf, js_arraylike_includes, ]; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAYLIKE_AT: extern "C" fn(f64, f64) -> f64 = js_arraylike_at; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAYLIKE_JOIN: extern "C" fn(f64, f64) -> f64 = js_arraylike_join; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAYLIKE_SLICE: extern "C" fn(f64, f64, i32, f64, i32) -> f64 = js_arraylike_slice; // --------------------------------------------------------------------------- @@ -1636,9 +1636,9 @@ pub extern "C" fn js_arraylike_splice(recv: f64, args_ptr: *const f64, count: i3 object_splice(o, args_ptr, count) } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAYLIKE_SORT: extern "C" fn(f64, f64) -> f64 = js_arraylike_sort; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAYLIKE_VARIADIC: [extern "C" fn(f64, *const f64, i32) -> f64; 2] = [js_arraylike_concat, js_arraylike_splice]; diff --git a/crates/perry-runtime/src/array/header.rs b/crates/perry-runtime/src/array/header.rs index 11f35e6f2c..bdeee2921b 100644 --- a/crates/perry-runtime/src/array/header.rs +++ b/crates/perry-runtime/src/array/header.rs @@ -171,7 +171,7 @@ pub extern "C" fn js_tagged_template_get_or_init( cooked_handle.get_raw_mut_ptr::() } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_TAGGED_TEMPLATE_GET_OR_INIT: extern "C" fn( u64, *mut ArrayHeader, @@ -1484,22 +1484,22 @@ pub extern "C" fn js_array_is_numeric_f64_layout(arr: *const ArrayHeader) -> i32 // These raw numeric-array helpers are called from generated code, so release/LTO // builds may otherwise internalize and strip the `#[no_mangle]` exports. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_NUMERIC_VALUE_TO_RAW_F64: extern "C" fn(f64) -> f64 = js_array_numeric_value_to_raw_f64; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_MARK_NUMERIC_F64_LAYOUT: extern "C" fn(*mut ArrayHeader) -> i32 = js_array_mark_numeric_f64_layout; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_CLEAR_NUMERIC_LAYOUT: extern "C" fn(*mut ArrayHeader) = js_array_clear_numeric_layout; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_NOTE_NUMERIC_WRITE: extern "C" fn(*mut ArrayHeader, u64) = js_array_note_numeric_write; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_IS_NUMERIC_F64_LAYOUT: extern "C" fn(*const ArrayHeader) -> i32 = js_array_is_numeric_f64_layout; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_REFRESH_LOCAL_HEAD: extern "C" fn(f64) -> f64 = js_array_refresh_local_head; /// Calculate the byte size for an array with N elements capacity diff --git a/crates/perry-runtime/src/array/indexing.rs b/crates/perry-runtime/src/array/indexing.rs index 39bc53e2f7..565d8356cc 100644 --- a/crates/perry-runtime/src/array/indexing.rs +++ b/crates/perry-runtime/src/array/indexing.rs @@ -440,7 +440,7 @@ pub(crate) unsafe fn keys_array_len_capped_to_capacity(arr: *const ArrayHeader) /// native-region wrappers (`__perry_wrap_*`) and elsewhere, so it must be a /// `#[no_mangle]` C export AND survive dead-stripping even when no Rust caller /// keeps it referenced — mirroring the neighbouring `js_array_push`. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_LENGTH: extern "C" fn(*const ArrayHeader) -> u32 = js_array_length; #[no_mangle] @@ -852,10 +852,10 @@ pub extern "C" fn js_array_numeric_set_f64_unboxed( // These raw numeric-array helpers are called from generated code, so release/LTO // builds may otherwise internalize and strip the `#[no_mangle]` exports. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_NUMERIC_GET_F64_UNBOXED: extern "C" fn(*mut ArrayHeader, u32) -> f64 = js_array_numeric_get_f64_unboxed; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_NUMERIC_SET_F64_UNBOXED: extern "C" fn(*mut ArrayHeader, u32, f64) -> i32 = js_array_numeric_set_f64_unboxed; @@ -1450,27 +1450,27 @@ pub extern "C" fn js_array_numeric_range_add_len(receiver: f64, start: f64, delt array_numeric_range_add_impl(receiver, start, None, delta) } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_FILL_F64_CONST_EXTEND: extern "C" fn( *mut ArrayHeader, u32, f64, ) -> *mut ArrayHeader = js_array_fill_f64_const_extend; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_FILL_F64_IOTA_EXTEND: extern "C" fn(*mut ArrayHeader, u32) -> *mut ArrayHeader = js_array_fill_f64_iota_extend; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_FILL_F64_CONST_LEN_EXTEND: extern "C" fn( *mut ArrayHeader, f64, ) -> *mut ArrayHeader = js_array_fill_f64_const_len_extend; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_FILL_F64_IOTA_LEN_EXTEND: extern "C" fn(*mut ArrayHeader) -> *mut ArrayHeader = js_array_fill_f64_iota_len_extend; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_NUMERIC_RANGE_ADD: extern "C" fn(f64, f64, f64, f64) -> i64 = js_array_numeric_range_add; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_NUMERIC_RANGE_ADD_LEN: extern "C" fn(f64, f64, f64) -> i64 = js_array_numeric_range_add_len; diff --git a/crates/perry-runtime/src/array/iter_methods.rs b/crates/perry-runtime/src/array/iter_methods.rs index c96c988428..aa2c05086d 100644 --- a/crates/perry-runtime/src/array/iter_methods.rs +++ b/crates/perry-runtime/src/array/iter_methods.rs @@ -1041,9 +1041,9 @@ pub extern "C" fn js_array_join_value( // `js_array_join_value`, but its only in-crate caller sits behind a dispatch // path the auto-optimize whole-program-bitcode build can prove unreachable and // dead-strip — which broke the default `perry file.ts -o out` link with -// `undefined _js_array_join_value`. The `#[used]` static pins the symbol so it +// `undefined _js_array_join_value`. The `#[cfg_attr(feature = "keepalive-anchors", used)]` static pins the symbol so it // survives every link mode. Same pattern as `node_stream_keepalive.rs`. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_JOIN_VALUE: extern "C" fn( *const ArrayHeader, f64, @@ -1108,7 +1108,7 @@ pub extern "C" fn js_array_to_locale_string( crate::string::js_string_from_bytes(out.as_ptr(), out.len() as u32) } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_TO_LOCALE_STRING: extern "C" fn( *const ArrayHeader, f64, @@ -1231,7 +1231,7 @@ pub extern "C" fn js_validate_array_callback(cb_boxed: f64) -> i64 { throw_not_a_function(render_callback_typeof(cb_boxed)); } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_VALIDATE_ARRAY_CALLBACK: extern "C" fn(f64) -> i64 = js_validate_array_callback; /// Validate a `map` callback (#4091). Identical to @@ -1252,6 +1252,6 @@ pub extern "C" fn js_validate_array_map_callback(arr: i64, cb_boxed: f64) -> i64 throw_not_a_function(rendered); } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_VALIDATE_ARRAY_MAP_CALLBACK: extern "C" fn(i64, f64) -> i64 = js_validate_array_map_callback; diff --git a/crates/perry-runtime/src/array/push_pop.rs b/crates/perry-runtime/src/array/push_pop.rs index 04efa0ffd0..b286c52f28 100644 --- a/crates/perry-runtime/src/array/push_pop.rs +++ b/crates/perry-runtime/src/array/push_pop.rs @@ -415,7 +415,7 @@ pub extern "C" fn js_array_numeric_push_f64_unboxed( // This raw numeric-array helper is called from generated code, so release/LTO // builds may otherwise internalize and strip the `#[no_mangle]` export. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_NUMERIC_PUSH_F64_UNBOXED: extern "C" fn( *mut ArrayHeader, f64, @@ -820,6 +820,6 @@ pub extern "C" fn js_array_unshift_variadic( } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_UNSHIFT_VARIADIC: extern "C" fn(*mut ArrayHeader, *const f64, u32) -> *mut ArrayHeader = js_array_unshift_variadic; diff --git a/crates/perry-runtime/src/array/sort.rs b/crates/perry-runtime/src/array/sort.rs index 7e40a9822d..8ac2b33ff0 100644 --- a/crates/perry-runtime/src/array/sort.rs +++ b/crates/perry-runtime/src/array/sort.rs @@ -597,7 +597,7 @@ pub extern "C" fn js_validate_array_comparator(cmp_boxed: f64) -> i64 { throw_invalid_comparator(cmp_boxed); } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_VALIDATE_ARRAY_COMPARATOR: extern "C" fn(f64) -> i64 = js_validate_array_comparator; #[cold] diff --git a/crates/perry-runtime/src/bigint/convert.rs b/crates/perry-runtime/src/bigint/convert.rs index d2431bd391..69255958c6 100644 --- a/crates/perry-runtime/src/bigint/convert.rs +++ b/crates/perry-runtime/src/bigint/convert.rs @@ -65,7 +65,7 @@ pub extern "C" fn js_bigint_from_i128_parts(lo: u64, hi: i64) -> *mut BigIntHead bigint_alloc_with_limbs(limbs) } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BIGINT_FROM_I128_PARTS: extern "C" fn(u64, i64) -> *mut BigIntHeader = js_bigint_from_i128_parts; diff --git a/crates/perry-runtime/src/box.rs b/crates/perry-runtime/src/box.rs index a9f1793e77..37ad6d0e95 100644 --- a/crates/perry-runtime/src/box.rs +++ b/crates/perry-runtime/src/box.rs @@ -251,9 +251,9 @@ pub extern "C" fn js_tdz_suppress_end() { /// Keepalive anchors for the auto-optimize whole-program build (generated-code- /// only callees — without these the symbols dead-strip and the app link fails). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TDZ_SUPPRESS_BEGIN: extern "C" fn() = js_tdz_suppress_begin; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TDZ_SUPPRESS_END: extern "C" fn() = js_tdz_suppress_end; /// Compatibility wrapper for legacy f64-lowered boxed locals. @@ -483,29 +483,29 @@ fn is_registered_bool_box_ptr(ptr: *mut BoolBox) -> bool { BOOL_BOX_REGISTRY.with(|r| r.borrow().contains(&(ptr as usize))) } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BOX_ALLOC_BITS: extern "C" fn(i64) -> *mut Box = js_box_alloc_bits; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BOX_GET_BITS: extern "C" fn(*mut Box) -> i64 = js_box_get_bits; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BOX_SET_BITS: extern "C" fn(*mut Box, i64) = js_box_set_bits; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BOX_ALLOC: extern "C" fn(f64) -> *mut Box = js_box_alloc; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BOX_GET: extern "C" fn(*mut Box) -> f64 = js_box_get; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BOX_SET: extern "C" fn(*mut Box, f64) = js_box_set; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_I32_BOX_ALLOC: extern "C" fn(i32) -> *mut I32Box = js_i32_box_alloc; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_I32_BOX_GET: extern "C" fn(*mut I32Box) -> i32 = js_i32_box_get; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_I32_BOX_SET: extern "C" fn(*mut I32Box, i32) = js_i32_box_set; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BOOL_BOX_ALLOC: extern "C" fn(i32) -> *mut BoolBox = js_bool_box_alloc; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BOOL_BOX_GET: extern "C" fn(*mut BoolBox) -> i32 = js_bool_box_get; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BOOL_BOX_SET: extern "C" fn(*mut BoolBox, i32) = js_bool_box_set; #[cfg(test)] diff --git a/crates/perry-runtime/src/buffer/access.rs b/crates/perry-runtime/src/buffer/access.rs index 9e3a5be1c8..71a0c2e478 100644 --- a/crates/perry-runtime/src/buffer/access.rs +++ b/crates/perry-runtime/src/buffer/access.rs @@ -226,9 +226,9 @@ pub extern "C" fn js_buffer_index_get_value(buf_ptr: *const BufferHeader, index: // #6088: force-keep the JS-value buffer index getter under LTO / // auto-optimize. It has zero internal Rust callers — codegen emits the only // call (in `perry-codegen/src/expr/index_get.rs`), so a whole-program bitcode -// link is otherwise free to internalize and dead-strip it. The `#[used]` +// link is otherwise free to internalize and dead-strip it. The `#[cfg_attr(feature = "keepalive-anchors", used)]` // anchor pins it (mirrors `KEEP_JS_TYPED_ARRAY_INDEX_GET_DYNAMIC`). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_BUFFER_INDEX_GET_VALUE: extern "C" fn(*const BufferHeader, i32) -> f64 = js_buffer_index_get_value; diff --git a/crates/perry-runtime/src/buffer/dataview.rs b/crates/perry-runtime/src/buffer/dataview.rs index 2ffc06d09c..8c7e902fc9 100644 --- a/crates/perry-runtime/src/buffer/dataview.rs +++ b/crates/perry-runtime/src/buffer/dataview.rs @@ -453,10 +453,10 @@ fn data_view_method_name<'a>(buf: &'a mut [u8; 16], prefix: &str, kind: DataView } // Called from generated code — keep the exports alive under release/LTO. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_DATA_VIEW_GET_DIRECT: extern "C" fn(f64, f64, f64, i32, i32) -> f64 = js_data_view_get_direct; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_DATA_VIEW_SET_DIRECT: extern "C" fn(f64, f64, f64, f64, i32, i32) -> f64 = js_data_view_set_direct; diff --git a/crates/perry-runtime/src/buffer/encode.rs b/crates/perry-runtime/src/buffer/encode.rs index a4ddeddd3e..e4a3e8eee6 100644 --- a/crates/perry-runtime/src/buffer/encode.rs +++ b/crates/perry-runtime/src/buffer/encode.rs @@ -221,9 +221,9 @@ pub extern "C" fn js_value_to_string_with_encoding_or_radix( /// Keepalive anchor: `js_value_to_string_with_encoding_or_radix` is emitted /// only from generated `.o`, so the auto-optimize whole-program LLVM rebuild -/// would internalize + dead-strip it without a `#[used]` reference (see +/// would internalize + dead-strip it without a `#[cfg_attr(feature = "keepalive-anchors", used)]` reference (see /// project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_VALUE_TO_STRING_ENCODING_OR_RADIX: extern "C" fn(f64, i32, f64) -> *mut StringHeader = js_value_to_string_with_encoding_or_radix; diff --git a/crates/perry-runtime/src/buffer/query.rs b/crates/perry-runtime/src/buffer/query.rs index 5551611f46..13dc043d1d 100644 --- a/crates/perry-runtime/src/buffer/query.rs +++ b/crates/perry-runtime/src/buffer/query.rs @@ -62,7 +62,7 @@ pub unsafe extern "C" fn js_value_buffer_or_typedarray_data( // Referenced only from the prebuilt `perry-ext-http-server` archive, so the // auto-optimize LTO pass would otherwise dead-strip it. Pin it. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_VALUE_BUFFER_OR_TYPEDARRAY_DATA: unsafe extern "C" fn(f64, *mut u32) -> *const u8 = js_value_buffer_or_typedarray_data; diff --git a/crates/perry-runtime/src/buffer/u8_codec.rs b/crates/perry-runtime/src/buffer/u8_codec.rs index bc3849901c..47e80652df 100644 --- a/crates/perry-runtime/src/buffer/u8_codec.rs +++ b/crates/perry-runtime/src/buffer/u8_codec.rs @@ -560,17 +560,17 @@ pub extern "C" fn js_u8_set_from_hex(addr: i64, str_handle: i64) -> f64 { // Keepalive anchors: these `#[no_mangle]` symbols are only referenced from // generated `.o` files, so the auto-optimize whole-program-LLVM bitcode -// rebuild would dead-strip them without an `#[used]` reference. See +// rebuild would dead-strip them without an `#[cfg_attr(feature = "keepalive-anchors", used)]` reference. See // project_auto_optimize_keepalive_3320. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_U8_TO_BASE64: extern "C" fn(i64, f64) -> *mut StringHeader = js_u8_to_base64; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_U8_TO_HEX: extern "C" fn(i64) -> *mut StringHeader = js_u8_to_hex; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_U8_FROM_BASE64: extern "C" fn(i64, f64) -> *mut BufferHeader = js_u8_from_base64; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_U8_FROM_HEX: extern "C" fn(i64) -> *mut BufferHeader = js_u8_from_hex; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_U8_SET_FROM_BASE64: extern "C" fn(i64, i64, f64) -> f64 = js_u8_set_from_base64; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_U8_SET_FROM_HEX: extern "C" fn(i64, i64) -> f64 = js_u8_set_from_hex; diff --git a/crates/perry-runtime/src/builtins/arithmetic.rs b/crates/perry-runtime/src/builtins/arithmetic.rs index c64b0e2d62..a451db9fb3 100644 --- a/crates/perry-runtime/src/builtins/arithmetic.rs +++ b/crates/perry-runtime/src/builtins/arithmetic.rs @@ -410,15 +410,15 @@ pub extern "C" fn js_rel_ge(x: f64, y: f64) -> f64 { // The `js_rel_*` helpers are reached only from Perry-emitted LLVM (the relational // fallthrough in codegen), so a bitcode/auto-optimize link can dead-strip them -// and leave `undefined _js_rel_lt …`. Pin them with `#[used]` statics — same +// and leave `undefined _js_rel_lt …`. Pin them with `#[cfg_attr(feature = "keepalive-anchors", used)]` statics — same // pattern as the write-barrier roots in `gc/barrier.rs`. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REL_LT: extern "C" fn(f64, f64) -> f64 = js_rel_lt; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REL_GT: extern "C" fn(f64, f64) -> f64 = js_rel_gt; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REL_LE: extern "C" fn(f64, f64) -> f64 = js_rel_le; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REL_GE: extern "C" fn(f64, f64) -> f64 = js_rel_ge; #[no_mangle] diff --git a/crates/perry-runtime/src/child_process/validate.rs b/crates/perry-runtime/src/child_process/validate.rs index ec6b20c536..638ca0a06e 100644 --- a/crates/perry-runtime/src/child_process/validate.rs +++ b/crates/perry-runtime/src/child_process/validate.rs @@ -88,13 +88,13 @@ pub extern "C" fn js_child_process_validate_args(value: f64) -> f64 { value } -/// `#[used]` keepalive anchors so the auto-optimize whole-program-LLVM rebuild +/// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive anchors so the auto-optimize whole-program-LLVM rebuild /// does not dead-strip these codegen-invoked `#[no_mangle]` entry points (see /// project_auto_optimize_keepalive_3320). They are referenced only from /// generated `.o`, so without an anchor the bitcode internalizer drops them and /// the default `perry file.ts -o out` link fails. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_CP_VALIDATE_COMMAND: unsafe extern "C" fn(f64, *const u8, u32) -> f64 = js_child_process_validate_command; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_CP_VALIDATE_ARGS: extern "C" fn(f64) -> f64 = js_child_process_validate_args; diff --git a/crates/perry-runtime/src/closure/alloc.rs b/crates/perry-runtime/src/closure/alloc.rs index 91a77f609e..6814d72b0f 100644 --- a/crates/perry-runtime/src/closure/alloc.rs +++ b/crates/perry-runtime/src/closure/alloc.rs @@ -490,9 +490,9 @@ pub extern "C" fn js_closure_set_capture_ptr(closure: *mut ClosureHeader, index: js_closure_set_capture_bits(closure, index, value as u64); } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_CLOSURE_GET_CAPTURE_BITS: extern "C" fn(*const ClosureHeader, u32) -> u64 = js_closure_get_capture_bits; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_CLOSURE_SET_CAPTURE_BITS: extern "C" fn(*mut ClosureHeader, u32, u64) = js_closure_set_capture_bits; diff --git a/crates/perry-runtime/src/closure/dispatch/bound.rs b/crates/perry-runtime/src/closure/dispatch/bound.rs index c568999a7e..4f32a5ed65 100644 --- a/crates/perry-runtime/src/closure/dispatch/bound.rs +++ b/crates/perry-runtime/src/closure/dispatch/bound.rs @@ -485,9 +485,9 @@ pub unsafe extern "C" fn js_function_bind( /// Keepalive anchor for the `js_function_bind` symbol. The auto-optimize /// whole-program LLVM rebuild dead-strips `#[no_mangle]` fns that are only -/// referenced from generated `.o` / other crates; this `#[used]` static +/// referenced from generated `.o` / other crates; this `#[cfg_attr(feature = "keepalive-anchors", used)]` static /// survives the bitcode pipeline. See project_auto_optimize_keepalive_3320. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_FUNCTION_BIND: unsafe extern "C" fn(f64, *const f64, usize) -> f64 = js_function_bind; diff --git a/crates/perry-runtime/src/closure/registry.rs b/crates/perry-runtime/src/closure/registry.rs index 6447998431..d745d35a35 100644 --- a/crates/perry-runtime/src/closure/registry.rs +++ b/crates/perry-runtime/src/closure/registry.rs @@ -380,7 +380,7 @@ pub extern "C" fn js_register_closure_strict_function(func_ptr: *const u8) { /// Keepalive anchor for the auto-optimize whole-program build — the strict /// registration is emitted only from generated module-init code. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_REGISTER_CLOSURE_STRICT_FUNCTION: extern "C" fn(*const u8) = js_register_closure_strict_function; diff --git a/crates/perry-runtime/src/closure/unbox.rs b/crates/perry-runtime/src/closure/unbox.rs index 63bb70c257..ff20a75fa1 100644 --- a/crates/perry-runtime/src/closure/unbox.rs +++ b/crates/perry-runtime/src/closure/unbox.rs @@ -58,6 +58,6 @@ pub extern "C" fn js_closure_unbox_callee_checked_rebind(callee: f64, receiver: } /// Keepalive: generated code is the only caller (#6475). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_CLOSURE_UNBOX_CALLEE_CHECKED_REBIND: extern "C" fn(f64, f64) -> i64 = js_closure_unbox_callee_checked_rebind; diff --git a/crates/perry-runtime/src/collection_iter_object.rs b/crates/perry-runtime/src/collection_iter_object.rs index 1663a60b1f..fd1e658ac4 100644 --- a/crates/perry-runtime/src/collection_iter_object.rs +++ b/crates/perry-runtime/src/collection_iter_object.rs @@ -138,17 +138,17 @@ pub extern "C" fn js_set_entries_iter_obj(set: *const SetHeader) -> i64 { // Rust callers. The whole-program auto-optimize bitcode link would // otherwise internalize + dead-strip the `#[no_mangle]` exports and break // the default compile path (see project_auto_optimize_keepalive). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_MAP_ENTRIES_ITER: extern "C" fn(*const MapHeader) -> i64 = js_map_entries_iter_obj; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_MAP_KEYS_ITER: extern "C" fn(*const MapHeader) -> i64 = js_map_keys_iter_obj; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_MAP_VALUES_ITER: extern "C" fn(*const MapHeader) -> i64 = js_map_values_iter_obj; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_VALUES_ITER: extern "C" fn(*const SetHeader) -> i64 = js_set_values_iter_obj; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_KEYS_ITER: extern "C" fn(*const SetHeader) -> i64 = js_set_keys_iter_obj; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_ENTRIES_ITER: extern "C" fn(*const SetHeader) -> i64 = js_set_entries_iter_obj; /// Build the `{ value, done }` iterator-result object. Mirrors diff --git a/crates/perry-runtime/src/date.rs b/crates/perry-runtime/src/date.rs index 495c6fe3b0..747e2c6d28 100644 --- a/crates/perry-runtime/src/date.rs +++ b/crates/perry-runtime/src/date.rs @@ -790,8 +790,8 @@ pub extern "C" fn js_date_utc(args_ptr: *const f64, argc: i32) -> f64 { /// Keepalive anchor for `js_date_utc` — codegen-only `#[no_mangle]` symbols /// get dead-stripped by the auto-optimize whole-program LLVM bitcode rebuild -/// without a `#[used]` reference (see project_auto_optimize_keepalive_3320). -#[used] +/// without a `#[cfg_attr(feature = "keepalive-anchors", used)]` reference (see project_auto_optimize_keepalive_3320). +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_DATE_UTC: extern "C" fn(*const f64, i32) -> f64 = js_date_utc; /// Coerce a NaN-boxed JS value to a number (ECMAScript ToNumber, restricted @@ -1008,7 +1008,7 @@ pub extern "C" fn js_date_apply_setter( } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_DATE_APPLY_SETTER: extern "C" fn(f64, i32, i32, *const f64, i32) -> f64 = js_date_apply_setter; diff --git a/crates/perry-runtime/src/disposable.rs b/crates/perry-runtime/src/disposable.rs index 289f355c0b..e7907dc3a6 100644 --- a/crates/perry-runtime/src/disposable.rs +++ b/crates/perry-runtime/src/disposable.rs @@ -498,44 +498,44 @@ pub extern "C" fn js_suppressed_error_new(error: f64, suppressed: f64, message: // Keepalive anchors — these `#[no_mangle]` fns are only ever called from // generated code (the codegen `new` arm + the native-module dispatch table), // so the whole-program-LLVM auto-optimize bitcode rebuild would otherwise -// dead-strip them (see project_auto_optimize_keepalive_3320). `#[used]` +// dead-strip them (see project_auto_optimize_keepalive_3320). `#[cfg_attr(feature = "keepalive-anchors", used)]` // survives the bitcode pipeline. // --------------------------------------------------------------------------- -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DISPOSABLE_STACK_NEW: extern "C" fn() -> *mut ObjectHeader = js_disposable_stack_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ASYNC_DISPOSABLE_STACK_NEW: extern "C" fn() -> *mut ObjectHeader = js_async_disposable_stack_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DISPOSABLE_STACK_DISPOSED: extern "C" fn(*mut ObjectHeader) -> f64 = js_disposable_stack_disposed; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DISPOSABLE_STACK_DEFER: extern "C" fn(*mut ObjectHeader, f64) -> f64 = js_disposable_stack_defer; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DISPOSABLE_STACK_USE: extern "C" fn(*mut ObjectHeader, f64) -> f64 = js_disposable_stack_use; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ASYNC_DISPOSABLE_STACK_USE: extern "C" fn(*mut ObjectHeader, f64) -> f64 = js_async_disposable_stack_use; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DISPOSABLE_STACK_ADOPT: extern "C" fn(*mut ObjectHeader, f64, f64) -> f64 = js_disposable_stack_adopt; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DISPOSABLE_STACK_DISPOSE: extern "C" fn(*mut ObjectHeader) -> f64 = js_disposable_stack_dispose; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DISPOSABLE_STACK_SYMBOL_DISPOSE: extern "C" fn(*mut ObjectHeader) -> f64 = js_disposable_stack_symbol_dispose; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DISPOSABLE_STACK_MOVE: extern "C" fn(*mut ObjectHeader) -> f64 = js_disposable_stack_move; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ASYNC_DISPOSABLE_STACK_DISPOSE_ASYNC: extern "C" fn(*mut ObjectHeader) -> f64 = js_async_disposable_stack_dispose_async; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ASYNC_DISPOSABLE_STACK_SYMBOL_ASYNC_DISPOSE: extern "C" fn(*mut ObjectHeader) -> f64 = js_async_disposable_stack_symbol_async_dispose; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SUPPRESSED_ERROR_NEW: extern "C" fn(f64, f64, f64) -> f64 = js_suppressed_error_new; diff --git a/crates/perry-runtime/src/embedded.rs b/crates/perry-runtime/src/embedded.rs index 5de15f94b2..4493904869 100644 --- a/crates/perry-runtime/src/embedded.rs +++ b/crates/perry-runtime/src/embedded.rs @@ -265,16 +265,16 @@ pub extern "C" fn js_perry_read_embedded(path_value: f64) -> *mut crate::buffer: // Keep the FFI symbols external under the thin-LTO + `strip=true` release // profile. A `#[no_mangle] pub extern "C"` alone is internalized and -// dead-stripped; only individual `#[used]` typed fn-pointer statics survive +// dead-stripped; only individual `#[cfg_attr(feature = "keepalive-anchors", used)]` typed fn-pointer statics survive // (see the note in `typed_feedback/trace.rs`). `js_register_embedded_asset` is // called only from the generated C constructor, and `js_perry_read_embedded` // only from codegen-emitted callsites — both are invisible to Rust's reachability. #[rustfmt::skip] mod keep_embedded { use super::*; - #[used] static K0: unsafe extern "C" fn(*const u8, usize, *const u8, usize) = js_register_embedded_asset; - #[used] static K1: extern "C" fn(f64) -> *mut crate::buffer::BufferHeader = js_perry_read_embedded; - #[used] static K2: extern "C" fn() -> *mut crate::array::ArrayHeader = js_perry_embedded_files; + #[cfg_attr(feature = "keepalive-anchors", used)] static K0: unsafe extern "C" fn(*const u8, usize, *const u8, usize) = js_register_embedded_asset; + #[cfg_attr(feature = "keepalive-anchors", used)] static K1: extern "C" fn(f64) -> *mut crate::buffer::BufferHeader = js_perry_read_embedded; + #[cfg_attr(feature = "keepalive-anchors", used)] static K2: extern "C" fn() -> *mut crate::array::ArrayHeader = js_perry_embedded_files; } #[cfg(test)] diff --git a/crates/perry-runtime/src/error.rs b/crates/perry-runtime/src/error.rs index 0366565c4c..881d9373fa 100644 --- a/crates/perry-runtime/src/error.rs +++ b/crates/perry-runtime/src/error.rs @@ -121,7 +121,7 @@ pub unsafe extern "C" fn js_set_call_location(file_ptr: *const u8, file_len: usi // Generated-code-only callee: anchor against the auto-optimize LTO dead-strip // (see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_CALL_LOCATION: unsafe extern "C" fn(*const u8, usize, u32) = js_set_call_location; @@ -436,8 +436,8 @@ pub unsafe extern "C" fn js_node_system_error_value( // These FFI entries are referenced only from extension archives (linked after // the runtime's bitcode is optimized), so the auto-optimize LTO pass would // otherwise dead-strip them (see project_auto_optimize_keepalive_3320). The -// `#[used]` anchors pin them. -#[used] +// `#[cfg_attr(feature = "keepalive-anchors", used)]` anchors pin them. +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ERROR_VALUE_WITH_CODE: unsafe extern "C" fn( *const u8, usize, @@ -446,7 +446,7 @@ static KEEP_JS_ERROR_VALUE_WITH_CODE: unsafe extern "C" fn( i32, ) -> f64 = js_error_value_with_code; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_NODE_SYSTEM_ERROR_VALUE: unsafe extern "C" fn( *const u8, usize, @@ -457,7 +457,7 @@ static KEEP_JS_NODE_SYSTEM_ERROR_VALUE: unsafe extern "C" fn( f64, ) -> f64 = js_node_system_error_value; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_THROW_ERROR_WITH_CODE: unsafe extern "C" fn( *const u8, usize, @@ -855,7 +855,7 @@ pub extern "C" fn js_throw_eval_syntax_error(message: f64) -> f64 { } // #1561-style force-keep: only generated IR calls this. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_THROW_EVAL_SYNTAX_ERROR: extern "C" fn(f64) -> f64 = js_throw_eval_syntax_error; #[no_mangle] @@ -867,7 +867,7 @@ pub extern "C" fn js_throw_restricted_function_property_assignment() -> f64 { } // #1561-style force-keep: only generated IR calls this. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_THROW_RESTRICTED_FN_PROP_ASSIGN: extern "C" fn() -> f64 = js_throw_restricted_function_property_assignment; @@ -955,7 +955,7 @@ pub extern "C" fn js_throw_reference_error_tdz(name: f64) -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code- /// and runtime-only callee). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_THROW_REFERENCE_ERROR_TDZ: extern "C" fn(f64) -> f64 = js_throw_reference_error_tdz; #[no_mangle] @@ -965,7 +965,7 @@ pub extern "C" fn js_throw_reference_error_unresolved_get() -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code ///-only callee; see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_GLOBAL_GET_OR_THROW_UNRESOLVED: extern "C" fn(f64) -> f64 = js_global_get_or_throw_unresolved; @@ -1007,10 +1007,10 @@ pub extern "C" fn js_global_get_or_throw_unresolved(name_value: f64) -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code ///-only callee; see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_GLOBAL_GET_OPTIONAL: extern "C" fn(f64) -> f64 = js_global_get_optional; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_GLOBAL_UPDATE: extern "C" fn(f64, f64, f64) -> f64 = js_global_update; /// `++x` / `x++` / `--x` / `x--` where `x` resolves to no lexical binding — @@ -1068,7 +1068,7 @@ pub extern "C" fn js_global_update(name_value: f64, is_increment: f64, is_prefix /// Keepalive anchor for the auto-optimize whole-program build (generated-code ///-only callee; see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_GLOBAL_ASSIGN_EXISTING_OR_THROW: extern "C" fn(f64, f64) -> f64 = js_global_assign_existing_or_throw; @@ -1670,22 +1670,22 @@ pub(crate) fn throw_immutable_write(kind: u32, key: &str) -> ! { // #2836/#2838/#2904: keep the codegen-emitted error FFIs alive through the // auto-optimize whole-program-bitcode link. These `#[no_mangle]` fns are -// reachable only from generated `.o`; without `#[used]` anchors the +// reachable only from generated `.o`; without `#[cfg_attr(feature = "keepalive-anchors", used)]` anchors the // internalize+dead-strip pass drops them and the default `perry file.ts -o` // link fails (see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ERROR_NEW_KIND_WITH_OPTIONS: extern "C" fn( u32, *mut StringHeader, f64, ) -> *mut ErrorHeader = js_error_new_kind_with_options; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_AGGREGATEERROR_NEW_FULL: extern "C" fn( f64, *mut StringHeader, f64, ) -> *mut ErrorHeader = js_aggregateerror_new_full; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ERROR_IS_ERROR: extern "C" fn(f64) -> f64 = js_error_is_error; #[cfg(test)] diff --git a/crates/perry-runtime/src/event_target.rs b/crates/perry-runtime/src/event_target.rs index d28a1ee206..edec9ad215 100644 --- a/crates/perry-runtime/src/event_target.rs +++ b/crates/perry-runtime/src/event_target.rs @@ -322,7 +322,7 @@ pub extern "C" fn js_event_subclass_init( /// Keepalive anchor for the auto-optimize whole-program build — /// `js_event_subclass_init` is a generated-code-only callee. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_EVENT_SUBCLASS_INIT: extern "C" fn(f64, f64, f64, u32, u32) -> f64 = js_event_subclass_init; diff --git a/crates/perry-runtime/src/fs/validate.rs b/crates/perry-runtime/src/fs/validate.rs index 67c78f0e5f..3013513501 100644 --- a/crates/perry-runtime/src/fs/validate.rs +++ b/crates/perry-runtime/src/fs/validate.rs @@ -369,12 +369,12 @@ pub unsafe extern "C" fn js_validate_event_listener( throw_type_error_with_code(&message, "ERR_INVALID_ARG_TYPE"); } -/// `#[used]` keepalive so the auto-optimize whole-program-LLVM rebuild does +/// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive so the auto-optimize whole-program-LLVM rebuild does /// not dead-strip this codegen-invoked `#[no_mangle]` entry point (see /// project_auto_optimize_keepalive_3320). Called only from generated `.o` /// via the stdlib/ext events validators, so without an anchor the bitcode /// internalizer drops it and the default `perry file.ts -o out` link fails. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_VALIDATE_EVENT_LISTENER: unsafe extern "C" fn(i64, *const u8, u32) -> i64 = js_validate_event_listener; diff --git a/crates/perry-runtime/src/gc/barrier.rs b/crates/perry-runtime/src/gc/barrier.rs index c83276e943..f012098b98 100644 --- a/crates/perry-runtime/src/gc/barrier.rs +++ b/crates/perry-runtime/src/gc/barrier.rs @@ -1514,11 +1514,11 @@ pub extern "C" fn js_write_barrier_root_nanbox(value_bits: u64) { // the runtime through whole-program LLVM bitcode and is free to internalize and // dead-strip an unreferenced `#[no_mangle]` symbol — which broke the default // `perry file.ts -o out` link with `undefined _js_write_barrier_root_*`. The -// `#[used]` statics pin retained reference edges so both survive every link mode. +// `#[cfg_attr(feature = "keepalive-anchors", used)]` statics pin retained reference edges so both survive every link mode. // Same pattern as `node_stream_keepalive.rs` / `typedarray.rs`. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_WRITE_BARRIER_ROOT_HEAP_WORD: extern "C" fn(u64) = js_write_barrier_root_heap_word; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_WRITE_BARRIER_ROOT_NANBOX: extern "C" fn(u64) = js_write_barrier_root_nanbox; #[inline] diff --git a/crates/perry-runtime/src/iterator_helpers.rs b/crates/perry-runtime/src/iterator_helpers.rs index 68d4b104f7..027df66f44 100644 --- a/crates/perry-runtime/src/iterator_helpers.rs +++ b/crates/perry-runtime/src/iterator_helpers.rs @@ -179,7 +179,7 @@ pub extern "C" fn js_iterator_from(val_f64: f64) -> f64 { } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ITERATOR_FROM: extern "C" fn(f64) -> f64 = js_iterator_from; /// `.next()` on a helper iterator object. Pulls lazily from the source per the diff --git a/crates/perry-runtime/src/json/parse_api.rs b/crates/perry-runtime/src/json/parse_api.rs index cbc863f1e4..9da7761e20 100644 --- a/crates/perry-runtime/src/json/parse_api.rs +++ b/crates/perry-runtime/src/json/parse_api.rs @@ -22,7 +22,7 @@ pub extern "C" fn js_json_text_to_string(value: f64) -> *mut StringHeader { // Anchor so the auto-optimize bitcode rebuild doesn't dead-strip this // codegen-only `#[no_mangle]` (see KEEP_RAW_JSON in json/raw_json.rs). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JSON_TEXT_TO_STRING: extern "C" fn(f64) -> *mut StringHeader = js_json_text_to_string; // ─── JSON.parse ─────────────────────────────────────────────────────────────── diff --git a/crates/perry-runtime/src/json/raw_json.rs b/crates/perry-runtime/src/json/raw_json.rs index bbd4e77612..7ac7558c75 100644 --- a/crates/perry-runtime/src/json/raw_json.rs +++ b/crates/perry-runtime/src/json/raw_json.rs @@ -169,7 +169,7 @@ fn throw_raw_json_syntax_error() -> ! { // Keepalive anchors: these `#[no_mangle]` entry points are called only from // generated `.o`; the auto-optimize whole-program bitcode rebuild would // otherwise dead-strip them (see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_RAW_JSON: unsafe extern "C" fn(f64) -> f64 = js_json_raw_json; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_IS_RAW_JSON: unsafe extern "C" fn(f64) -> f64 = js_json_is_raw_json; diff --git a/crates/perry-runtime/src/map.rs b/crates/perry-runtime/src/map.rs index ec19524af4..397359299e 100644 --- a/crates/perry-runtime/src/map.rs +++ b/crates/perry-runtime/src/map.rs @@ -1010,7 +1010,7 @@ const SIDE_TABLE_THRESHOLD: u32 = 8; /// C-ABI: current entries-array index of `key` (SameValueZero), or `-1.0` if /// absent. Used by the delete-safe `for-of` fast path (#6075) to re-derive the /// cursor after a mid-iteration delete compacts the entries array. Only invoked -/// from generated IR, so `#[used]` keeps it linked on the default compile path. +/// from generated IR, so `#[cfg_attr(feature = "keepalive-anchors", used)]` keeps it linked on the default compile path. #[no_mangle] pub extern "C" fn js_map_find_key_index(map_boxed: f64, key: f64) -> f64 { let map = clean_map_ptr(crate::value::js_nanbox_get_pointer(map_boxed) as *const MapHeader); @@ -1019,7 +1019,7 @@ pub extern "C" fn js_map_find_key_index(map_boxed: f64, key: f64) -> f64 { } unsafe { find_key_index(map, normalize_zero(key)) as f64 } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_MAP_FIND_KEY_INDEX: extern "C" fn(f64, f64) -> f64 = js_map_find_key_index; pub(crate) unsafe fn find_key_index(map: *const MapHeader, key: f64) -> i32 { @@ -1608,67 +1608,67 @@ pub extern "C" fn js_map_delete_number_key(map: *mut MapHeader, key: f64) -> i32 // Codegen emits these string-key typed lowering helpers directly from // generated LLVM IR. Keep roots prevent whole-program LTO/dead-strip from // removing the exported symbols when the Rust crate graph has no caller. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_SET_STRING_NUMBER: extern "C" fn( *mut MapHeader, *const StringHeader, f64, ) -> *mut MapHeader = js_map_set_string_number; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_SET_NUMBER_KEY: extern "C" fn(*mut MapHeader, f64, f64) -> *mut MapHeader = js_map_set_number_key; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_SET_STRING_KEY: extern "C" fn( *mut MapHeader, *const StringHeader, f64, ) -> *mut MapHeader = js_map_set_string_key; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_SET_STRING_I32: extern "C" fn( *mut MapHeader, *const StringHeader, i32, ) -> *mut MapHeader = js_map_set_string_i32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_SET_STRING_U32: extern "C" fn( *mut MapHeader, *const StringHeader, u32, ) -> *mut MapHeader = js_map_set_string_u32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_SET_STRING_F32: extern "C" fn( *mut MapHeader, *const StringHeader, f32, ) -> *mut MapHeader = js_map_set_string_f32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_SET_STRING_BOOL: extern "C" fn( *mut MapHeader, *const StringHeader, i32, ) -> *mut MapHeader = js_map_set_string_bool; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_SET_STRING_STRING: extern "C" fn( *mut MapHeader, *const StringHeader, *const StringHeader, ) -> *mut MapHeader = js_map_set_string_string; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_GET_STRING_KEY: extern "C" fn(*const MapHeader, *const StringHeader) -> f64 = js_map_get_string_key; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_GET_NUMBER_KEY: extern "C" fn(*const MapHeader, f64) -> f64 = js_map_get_number_key; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_HAS_STRING_KEY: extern "C" fn(*const MapHeader, *const StringHeader) -> i32 = js_map_has_string_key; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_HAS_NUMBER_KEY: extern "C" fn(*const MapHeader, f64) -> i32 = js_map_has_number_key; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_DELETE_STRING_KEY: extern "C" fn(*mut MapHeader, *const StringHeader) -> i32 = js_map_delete_string_key; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_DELETE_NUMBER_KEY: extern "C" fn(*mut MapHeader, f64) -> i32 = js_map_delete_number_key; @@ -2224,8 +2224,8 @@ pub extern "C" fn js_map_from_iterable(value: f64) -> *mut MapHeader { // `perry-codegen/src/expr/misc_methods.rs`), so it has zero internal Rust // callers. The whole-program auto-optimize bitcode link would otherwise // internalize + dead-strip the `#[no_mangle]` export and break the default -// compile path. The `#[used]` anchor pins it (see project_auto_optimize_keepalive). -#[used] +// compile path. The `#[cfg_attr(feature = "keepalive-anchors", used)]` anchor pins it (see project_auto_optimize_keepalive). +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MAP_FROM_ITERABLE: extern "C" fn(f64) -> *mut MapHeader = js_map_from_iterable; /// `Map.prototype.forEach(callback, thisArg)` — calls `callback` with the diff --git a/crates/perry-runtime/src/module_require.rs b/crates/perry-runtime/src/module_require.rs index 72f1e7bf2e..d4ec3a9321 100644 --- a/crates/perry-runtime/src/module_require.rs +++ b/crates/perry-runtime/src/module_require.rs @@ -492,7 +492,7 @@ pub extern "C" fn js_module_ambient_require() -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code-only /// callee; see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_AMBIENT_REQUIRE: extern "C" fn() -> f64 = js_module_ambient_require; /// Synchronous ambient `require(spec)` resolution for the #5389 Tier 2 codegen @@ -510,7 +510,7 @@ pub extern "C" fn js_module_ambient_require_apply(spec: f64) -> f64 { /// Keepalive anchor for the auto-optimize whole-program build (generated-code-only /// callee; see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_AMBIENT_REQUIRE_APPLY: extern "C" fn(f64) -> f64 = js_module_ambient_require_apply; @@ -571,7 +571,7 @@ pub extern "C" fn js_module_dynamic_import_fallback(spec: f64) -> f64 { } /// Keepalive anchor (same pattern as the ambient-require anchors above). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_DYNAMIC_IMPORT_FALLBACK: extern "C" fn(f64) -> f64 = js_module_dynamic_import_fallback; @@ -592,7 +592,7 @@ pub extern "C" fn js_module_dynamic_import_deferred(spec: f64, msg: f64) -> f64 } /// Keepalive anchor (same pattern as the ambient-require anchors above). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_DYNAMIC_IMPORT_DEFERRED: extern "C" fn(f64, f64) -> f64 = js_module_dynamic_import_deferred; diff --git a/crates/perry-runtime/src/native_abi.rs b/crates/perry-runtime/src/native_abi.rs index 66ebd9db0a..47023275e6 100644 --- a/crates/perry-runtime/src/native_abi.rs +++ b/crates/perry-runtime/src/native_abi.rs @@ -160,36 +160,36 @@ pub extern "C" fn js_typed_string_arg_to_raw(value: f64) -> i64 { // internal typed clone. They have no Rust call sites, so keep explicit // function-pointer references to prevent whole-program LTO/dead-strip from // removing the exported symbols. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_F64_ARG_GUARD: extern "C" fn(f64) -> i32 = js_typed_f64_arg_guard; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_F64_ARG_TO_RAW: extern "C" fn(f64) -> f64 = js_typed_f64_arg_to_raw; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_I32_ARG_GUARD: extern "C" fn(f64) -> i32 = js_typed_i32_arg_guard; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_I32_ARG_TO_RAW: extern "C" fn(f64) -> i32 = js_typed_i32_arg_to_raw; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_I1_ARG_GUARD: extern "C" fn(f64) -> i32 = js_typed_i1_arg_guard; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_I1_ARG_TO_RAW: extern "C" fn(f64) -> i32 = js_typed_i1_arg_to_raw; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_STRING_ARG_GUARD: extern "C" fn(f64) -> i32 = js_typed_string_arg_guard; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_STRING_ARG_TO_RAW: extern "C" fn(f64) -> i64 = js_typed_string_arg_to_raw; // Static-name and static-method lowering emits these by-id wrappers directly // from generated LLVM IR. Keep roots here so LTO cannot strip the symbols just // because the Rust crate graph has no ordinary caller. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_OBJECT_GET_FIELD_BY_PROPERTY_ID_F64: extern "C" fn(*const ObjectHeader, i64) -> f64 = crate::object::js_object_get_field_by_property_id_f64; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_OBJECT_SET_FIELD_BY_PROPERTY_ID: extern "C" fn(*mut ObjectHeader, i64, f64) = crate::object::js_object_set_field_by_property_id; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_NATIVE_CALL_METHOD_BY_ID: unsafe extern "C" fn(f64, i64, *const f64, usize) -> f64 = crate::object::js_native_call_method_by_id; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_NATIVE_CALL_METHOD_APPLY_BY_ID: unsafe extern "C" fn(f64, i64, i64) -> f64 = crate::object::js_native_call_method_apply_by_id; diff --git a/crates/perry-runtime/src/node_sea.rs b/crates/perry-runtime/src/node_sea.rs index 28f2c039f7..206131b4ee 100644 --- a/crates/perry-runtime/src/node_sea.rs +++ b/crates/perry-runtime/src/node_sea.rs @@ -7,15 +7,15 @@ use crate::value::{JSValue, TAG_FALSE}; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SEA_IS_SEA: extern "C" fn() -> f64 = js_sea_is_sea; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SEA_GET_ASSET: extern "C" fn(f64, f64) -> f64 = js_sea_get_asset; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SEA_GET_ASSET_AS_BLOB: extern "C" fn(f64, f64) -> f64 = js_sea_get_asset_as_blob; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SEA_GET_RAW_ASSET: extern "C" fn(f64) -> f64 = js_sea_get_raw_asset; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SEA_GET_ASSET_KEYS: extern "C" fn() -> f64 = js_sea_get_asset_keys; fn false_value() -> f64 { diff --git a/crates/perry-runtime/src/node_stream_keepalive.rs b/crates/perry-runtime/src/node_stream_keepalive.rs index ba035acd27..2b3e9599be 100644 --- a/crates/perry-runtime/src/node_stream_keepalive.rs +++ b/crates/perry-runtime/src/node_stream_keepalive.rs @@ -5,183 +5,183 @@ // by any Rust code in the crate graph. The default `.a` staticlib keeps // them via staticlib-export semantics, but the auto-optimize build round- // trips the runtime through whole-program LLVM bitcode and is free to -// internalize and dead-strip an unreferenced symbol. The `#[used]` statics +// internalize and dead-strip an unreferenced symbol. The `#[cfg_attr(feature = "keepalive-anchors", used)]` statics // below pin retained reference edges so every entry point survives all link // modes. See the same pattern in `value/dyn_index.rs` and `process.rs`. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_EMIT: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_emit; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_EMIT_ARGS: extern "C" fn(i64, f64, i64) -> f64 = super::js_node_stream_method_emit_args; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_READ: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_read; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_PUSH: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_push; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_UNSHIFT: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_unshift; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_READABLE_HWM: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_hwm; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_READABLE_LENGTH: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_length; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_READABLE_OBJECT_MODE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_object_mode; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_READABLE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_READABLE_ENDED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_ended; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_READABLE_ENCODING: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_encoding; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_WRITABLE_HWM: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_hwm; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_WRITABLE_LENGTH: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_length; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_WRITABLE_NEED_DRAIN: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_need_drain; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_WRITABLE_OBJECT_MODE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_object_mode; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_READABLE_ABORTED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_aborted; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_CLOSED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_closed; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_ERRORED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_errored; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_READABLE_DID_READ: extern "C" fn(i64) -> f64 = super::js_node_stream_method_readable_did_read; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_WRITABLE_CORKED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_corked; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_WRITABLE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_WRITABLE_ENDED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_ended; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_WRITABLE_FINISHED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_writable_finished; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_ALLOW_HALF_OPEN: extern "C" fn(i64) -> f64 = super::js_node_stream_method_allow_half_open; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_PAUSE: extern "C" fn(i64) -> f64 = super::js_node_stream_method_pause; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_RESUME: extern "C" fn(i64) -> f64 = super::js_node_stream_method_resume; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_SET_ENCODING: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_set_encoding; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_DESTROY: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_destroy; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_DESTROYED: extern "C" fn(i64) -> f64 = super::js_node_stream_method_destroyed; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_WRITE: extern "C" fn(i64, f64, f64, f64) -> f64 = super::js_node_stream_method_write; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_END: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_end; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_END3: extern "C" fn(i64, f64, f64, f64) -> f64 = super::js_node_stream_method_end3; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_CORK: extern "C" fn(i64) -> f64 = super::js_node_stream_method_cork; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_UNCORK: extern "C" fn(i64) -> f64 = super::js_node_stream_method_uncork; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_SET_MAX_LISTENERS: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_set_max_listeners; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_GET_MAX_LISTENERS: extern "C" fn(i64) -> f64 = super::js_node_stream_method_get_max_listeners; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_ON: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_on; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_ONCE: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_once; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_PREPEND_LISTENER: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_prepend_listener; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_PREPEND_ONCE_LISTENER: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_prepend_once_listener; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_OFF: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_off; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_REMOVE_LISTENER: extern "C" fn(i64, f64, f64) -> f64 = super::js_node_stream_method_remove_listener; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_REMOVE_ALL_LISTENERS: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_remove_all_listeners; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_EVENT_NAMES: extern "C" fn(i64) -> i64 = super::js_node_stream_method_event_names; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_LISTENER_COUNT: extern "C" fn(i64, f64) -> f64 = super::js_node_stream_method_listener_count; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_LISTENERS: extern "C" fn(i64, f64) -> i64 = super::js_node_stream_method_listeners; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_METHOD_RAW_LISTENERS: extern "C" fn(i64, f64) -> i64 = super::js_node_stream_method_raw_listeners; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_READABLE_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_readable_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_WRITABLE_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_writable_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_DUPLEX_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_duplex_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_TRANSFORM_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_transform_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_PASSTHROUGH_NEW: extern "C" fn(f64) -> f64 = super::js_node_stream_passthrough_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_READABLE_FROM: extern "C" fn(f64) -> f64 = super::js_node_stream_readable_from; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_READABLE_FROM_OPTIONS: extern "C" fn(f64, f64) -> f64 = super::js_node_stream_readable_from_options; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_IS_DISTURBED: extern "C" fn(f64) -> f64 = super::js_node_stream_is_disturbed; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_IS_ERRORED: extern "C" fn(f64) -> f64 = super::js_node_stream_is_errored; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_IS_READABLE: extern "C" fn(f64) -> f64 = super::js_node_stream_is_readable; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_IS_WRITABLE: extern "C" fn(f64) -> f64 = super::js_node_stream_is_writable; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_IS_ARRAY_BUFFER_VIEW: extern "C" fn(f64) -> f64 = super::js_node_stream_is_array_buffer_view; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_IS_UINT8_ARRAY: extern "C" fn(f64) -> f64 = super::js_node_stream_is_uint8_array; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_IS_DESTROYED: extern "C" fn(f64) -> f64 = super::js_node_stream_is_destroyed; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_UINT8_ARRAY_TO_BUFFER: extern "C" fn(f64) -> f64 = super::js_node_stream_uint8_array_to_buffer; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_GET_DEFAULT_HWM: extern "C" fn(f64) -> f64 = super::js_node_stream_get_default_hwm; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_SET_DEFAULT_HWM: extern "C" fn(f64, f64) -> f64 = super::js_node_stream_set_default_hwm; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_ADD_ABORT_SIGNAL: extern "C" fn(f64, f64) -> f64 = super::js_node_stream_add_abort_signal; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_COMPOSE: extern "C" fn(*const crate::array::ArrayHeader) -> f64 = super::js_node_stream_compose; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_PIPELINE: extern "C" fn(*const crate::array::ArrayHeader) -> f64 = super::js_node_stream_pipeline; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_DUPLEX_PAIR: extern "C" fn(f64) -> f64 = super::js_node_stream_duplex_pair; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_TO_WEB: extern "C" fn(f64) -> f64 = super::js_node_stream_to_web; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NS_FROM_WEB: extern "C" fn(f64) -> f64 = super::js_node_stream_from_web; diff --git a/crates/perry-runtime/src/node_submodules/zlib.rs b/crates/perry-runtime/src/node_submodules/zlib.rs index 18231fd45f..f05488bf88 100644 --- a/crates/perry-runtime/src/node_submodules/zlib.rs +++ b/crates/perry-runtime/src/node_submodules/zlib.rs @@ -291,13 +291,13 @@ pub extern "C" fn js_zlib_validate_callback(callback: f64) -> i64 { /// bitcode rebuild performed by auto-optimize (see /// `project_auto_optimize_keepalive_3320`). Called only from generated `.o` / /// `perry-ext-zlib`, so without an explicit anchor the dead-stripper drops it. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ZLIB_RESOLVE_LEVEL: extern "C" fn(f64) -> i32 = js_zlib_resolve_level; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ZLIB_VALIDATE_PARAMS: extern "C" fn(f64, f64) -> i32 = js_zlib_validate_params; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ZLIB_VALIDATE_OPTIONS: extern "C" fn(f64, i32) = js_zlib_validate_options; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ZLIB_VALIDATE_BUFFER_ARG: extern "C" fn(i64) = js_zlib_validate_buffer_arg; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ZLIB_VALIDATE_CALLBACK: extern "C" fn(f64) -> i64 = js_zlib_validate_callback; diff --git a/crates/perry-runtime/src/node_v8.rs b/crates/perry-runtime/src/node_v8.rs index b6bdd53065..769e01f24c 100644 --- a/crates/perry-runtime/src/node_v8.rs +++ b/crates/perry-runtime/src/node_v8.rs @@ -33,46 +33,46 @@ use crate::value::JSValue; // Symbol retention: these `#[no_mangle]` entry points are emitted only by // codegen's `node:v8` dispatch — no Rust caller references them, so the // auto-optimize whole-program-LLVM build would dead-strip them without an -// anchor (see node_stream_keepalive.rs). Pin each via a `#[used]` static. -#[used] +// anchor (see node_stream_keepalive.rs). Pin each via a `#[cfg_attr(feature = "keepalive-anchors", used)]` static. +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_SERIALIZE: extern "C" fn(f64) -> f64 = js_v8_serialize; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_DESERIALIZE: extern "C" fn(f64) -> f64 = js_v8_deserialize; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_HEAP_STATS: extern "C" fn() -> f64 = js_v8_get_heap_statistics; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_CODE_STATS: extern "C" fn() -> f64 = js_v8_get_heap_code_statistics; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_SPACE_STATS: extern "C" fn() -> f64 = js_v8_get_heap_space_statistics; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_VERSION_TAG: extern "C" fn() -> f64 = js_v8_cached_data_version_tag; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_GET_HEAP_SNAPSHOT: extern "C" fn(f64) -> f64 = js_v8_get_heap_snapshot; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_WRITE_HEAP_SNAPSHOT: extern "C" fn(f64, f64) -> f64 = js_v8_write_heap_snapshot; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_GC_PROFILER_NEW: extern "C" fn() -> f64 = js_v8_gc_profiler_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_GC_PROFILER_START: extern "C" fn(f64) -> f64 = js_v8_gc_profiler_start; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_GC_PROFILER_STOP: extern "C" fn(f64) -> f64 = js_v8_gc_profiler_stop; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_GC_PROFILER_REPORT: extern "C" fn() -> f64 = js_v8_gc_profiler_report; // #3680: Serializer / Deserializer class constructors. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_SERIALIZER_NEW: extern "C" fn(f64) -> f64 = js_v8_serializer_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_DESERIALIZER_NEW: extern "C" fn(f64) -> f64 = js_v8_deserializer_new; // #3679: lifecycle / diagnostic-control surface. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_NOOP_UNDEFINED: extern "C" fn() -> f64 = js_v8_noop_undefined; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_IS_BUILDING_SNAPSHOT: extern "C" fn() -> f64 = js_v8_is_building_snapshot; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_NAMESPACE: extern "C" fn(*const u8, usize) -> f64 = js_v8_namespace; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_THROW_NOT_BUILDING: extern "C" fn() -> f64 = js_v8_throw_not_building_snapshot; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_V8_PROMISE_HOOK_REGISTER: extern "C" fn() -> f64 = js_v8_promise_hook_register; const TAG_UNDEFINED_BITS: u64 = 0x7FFC_0000_0000_0001; diff --git a/crates/perry-runtime/src/object/alloc.rs b/crates/perry-runtime/src/object/alloc.rs index 5e2a68bf69..425e448881 100644 --- a/crates/perry-runtime/src/object/alloc.rs +++ b/crates/perry-runtime/src/object/alloc.rs @@ -569,7 +569,7 @@ pub extern "C" fn js_object_alloc_class_dynamic_parent( /// Keepalive anchor — `js_object_alloc_class_dynamic_parent` is a /// generated-code-only callee, so the auto-optimize whole-program build would /// otherwise dead-strip it (see the FFI-symbol-link-break class). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_OBJECT_ALLOC_CLASS_DYNAMIC_PARENT: extern "C" fn( u32, u32, diff --git a/crates/perry-runtime/src/object/class_constructors.rs b/crates/perry-runtime/src/object/class_constructors.rs index e6cdb5a52c..ba3deb35ed 100644 --- a/crates/perry-runtime/src/object/class_constructors.rs +++ b/crates/perry-runtime/src/object/class_constructors.rs @@ -108,7 +108,7 @@ pub extern "C" fn js_register_class_constructor_flags( } /// Keepalive anchor (generated-code-only callee). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_REGISTER_CLASS_CONSTRUCTOR_FLAGS: extern "C" fn(i64, i64, i64) = js_register_class_constructor_flags; @@ -162,7 +162,7 @@ pub unsafe extern "C" fn js_class_register_capture_values( /// Keepalive anchor for the auto-optimize whole-program build — /// `js_class_register_capture_values` is a generated-code-only callee. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_CLASS_REGISTER_CAPTURE_VALUES: unsafe extern "C" fn(u32, *const f64, usize) = js_class_register_capture_values; @@ -351,12 +351,12 @@ pub(crate) fn fallback_is_tag_stripped(fallback: f64) -> bool { } /// Keepalive anchors (generated-code-only callees). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_CLASS_CAPTURE_VALUE: extern "C" fn(u32, u32) -> f64 = js_class_capture_value; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_CLASS_CAPTURE_VALUE_OR: extern "C" fn(u32, u32, f64) -> f64 = js_class_capture_value_or; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_PARAM_OR_CLASS_CAPTURE_VALUE: extern "C" fn(f64, u32, u32) -> f64 = js_param_or_class_capture_value; @@ -549,7 +549,7 @@ pub unsafe extern "C" fn js_super_construct_apply( } /// Keepalive anchor (generated-code-only callee). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SUPER_CONSTRUCT_APPLY: unsafe extern "C" fn(u32, f64, f64) -> f64 = js_super_construct_apply; @@ -714,7 +714,7 @@ unsafe fn call_displaced_native_base_method( } /// Keepalive anchor (generated-code-only callee). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SUPER_METHOD_CALL_DYNAMIC: unsafe extern "C" fn( u32, *const u8, @@ -771,7 +771,7 @@ pub unsafe extern "C" fn js_super_method_call_dynamic_apply( } /// Keepalive anchor (generated-code-only callee). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SUPER_METHOD_CALL_DYNAMIC_APPLY: unsafe extern "C" fn( u32, *const u8, @@ -927,7 +927,7 @@ pub unsafe extern "C" fn js_array_push_spread_any( } /// Keepalive anchor (generated-code-only callee). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ARRAY_PUSH_SPREAD_ANY: unsafe extern "C" fn( *mut crate::array::ArrayHeader, f64, @@ -1025,7 +1025,7 @@ pub unsafe extern "C" fn js_error_subclass_default_init( } /// Keepalive: generated code is the only caller (#6469). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ERROR_SUBCLASS_DEFAULT_INIT: unsafe extern "C" fn( f64, f64, diff --git a/crates/perry-runtime/src/object/class_registry/registration.rs b/crates/perry-runtime/src/object/class_registry/registration.rs index 8a938c968a..6c40b6b38d 100644 --- a/crates/perry-runtime/src/object/class_registry/registration.rs +++ b/crates/perry-runtime/src/object/class_registry/registration.rs @@ -291,11 +291,11 @@ pub unsafe extern "C" fn js_register_class_static_setter( // These two are only ever called from codegen-emitted module-init IR (no Rust // caller), so the auto-optimize whole-program-LLVM build would dead-strip them -// without an anchor. Pin each via a `#[used]` static (mirrors node_v8.rs). -#[used] +// without an anchor. Pin each via a `#[cfg_attr(feature = "keepalive-anchors", used)]` static (mirrors node_v8.rs). +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REGISTER_STATIC_GETTER: unsafe extern "C" fn(i64, *const u8, i64, i64) = js_register_class_static_getter; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REGISTER_STATIC_SETTER: unsafe extern "C" fn(i64, *const u8, i64, i64) = js_register_class_static_setter; @@ -328,7 +328,7 @@ pub unsafe extern "C" fn js_register_class_method_bind_length( .insert((class_id as u32, name), length as u32); } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REGISTER_METHOD_BIND_LENGTH: unsafe extern "C" fn(i64, *const u8, i64, i64) = js_register_class_method_bind_length; @@ -361,7 +361,7 @@ pub unsafe extern "C" fn js_register_class_static_method_bind_length( .insert((class_id as u32, name), length as u32); } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REGISTER_STATIC_METHOD_BIND_LENGTH: unsafe extern "C" fn(i64, *const u8, i64, i64) = js_register_class_static_method_bind_length; diff --git a/crates/perry-runtime/src/object/descriptors.rs b/crates/perry-runtime/src/object/descriptors.rs index 36a07dfb1d..b934d5cc87 100644 --- a/crates/perry-runtime/src/object/descriptors.rs +++ b/crates/perry-runtime/src/object/descriptors.rs @@ -1486,5 +1486,5 @@ pub extern "C" fn js_object_create_with_props(proto_value: f64, props_value: f64 result } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_OBJECT_CREATE_WITH_PROPS: extern "C" fn(f64, f64) -> f64 = js_object_create_with_props; diff --git a/crates/perry-runtime/src/object/global_this/builtin_thunks.rs b/crates/perry-runtime/src/object/global_this/builtin_thunks.rs index 01792406d7..d6c80c5163 100644 --- a/crates/perry-runtime/src/object/global_this/builtin_thunks.rs +++ b/crates/perry-runtime/src/object/global_this/builtin_thunks.rs @@ -503,7 +503,7 @@ extern "C" fn depd_wrapfunction_outer_thunk( fn_v } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_FUNCTION_CTOR_FROM_STRINGS: extern "C" fn(*const f64, usize) -> f64 = js_function_ctor_from_strings; diff --git a/crates/perry-runtime/src/object/global_this/fetch_globals.rs b/crates/perry-runtime/src/object/global_this/fetch_globals.rs index 1190b53b34..568f69df6a 100644 --- a/crates/perry-runtime/src/object/global_this/fetch_globals.rs +++ b/crates/perry-runtime/src/object/global_this/fetch_globals.rs @@ -43,9 +43,9 @@ pub extern "C" fn js_module_top_this() -> f64 { /// Keepalive anchor: `js_module_top_this` is referenced only from /// codegen-generated `.o` files, so the auto-optimize whole-program LLVM -/// rebuild would dead-strip it without this `#[used]` pin (see +/// rebuild would dead-strip it without this `#[cfg_attr(feature = "keepalive-anchors", used)]` pin (see /// project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_TOP_THIS: extern "C" fn() -> f64 = js_module_top_this; /// Issue #611: lazily allocate `globalThis` for computed global access. @@ -480,10 +480,10 @@ pub extern "C" fn js_response_subclass_init(this_box: f64, body: f64, init: f64) // `Expr::SuperCall` Request/Response arm); pin them so the auto-optimize // bitcode rebuild's dead-strip can't drop them (see // project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_REQUEST_SUBCLASS_INIT: extern "C" fn(f64, f64, f64) -> f64 = js_request_subclass_init; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_RESPONSE_SUBCLASS_INIT: extern "C" fn(f64, f64, f64) -> f64 = js_response_subclass_init; @@ -828,7 +828,7 @@ pub unsafe extern "C" fn js_fetch_or_value_super( } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_FETCH_OR_VALUE_SUPER: unsafe extern "C" fn(f64, f64, *const f64, usize) -> f64 = js_fetch_or_value_super; diff --git a/crates/perry-runtime/src/object/groupby.rs b/crates/perry-runtime/src/object/groupby.rs index 51a98c915d..411669369c 100644 --- a/crates/perry-runtime/src/object/groupby.rs +++ b/crates/perry-runtime/src/object/groupby.rs @@ -136,9 +136,9 @@ pub extern "C" fn js_map_group_by(items_value: f64, callback: f64) -> f64 { /// Keepalive anchors: these `#[no_mangle]` helpers are only called from /// codegen-emitted `.o`. The auto-optimize whole-program LLVM rebuild /// dead-strips unreferenced `#[no_mangle]` symbols (see #3320), so pin them. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_OBJECT_GROUP_BY: extern "C" fn(f64, f64) -> f64 = js_object_group_by; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_MAP_GROUP_BY: extern "C" fn(f64, f64) -> f64 = js_map_group_by; /// Returns true if `value` is a Symbol (registered SymbolHeader pointer). diff --git a/crates/perry-runtime/src/object/native_module.rs b/crates/perry-runtime/src/object/native_module.rs index 990d151b76..eea75c1d8f 100644 --- a/crates/perry-runtime/src/object/native_module.rs +++ b/crates/perry-runtime/src/object/native_module.rs @@ -1059,7 +1059,7 @@ pub extern "C" fn js_class_method_bind_by_id(instance: f64, method_id: i64) -> f js_class_method_bind(instance, name_ref.ptr, name_ref.len) } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_CLASS_METHOD_BIND_BY_ID: extern "C" fn(f64, i64) -> f64 = js_class_method_bind_by_id; /// Allocate a BOUND_METHOD closure binding `instance` as the receiver for the diff --git a/crates/perry-runtime/src/object/native_this_alias.rs b/crates/perry-runtime/src/object/native_this_alias.rs index 38b7ebacc1..2f8466de5a 100644 --- a/crates/perry-runtime/src/object/native_this_alias.rs +++ b/crates/perry-runtime/src/object/native_this_alias.rs @@ -290,9 +290,9 @@ pub unsafe extern "C" fn js_https_server_construct_with_this( /// Keepalive anchors: the auto-optimize whole-program LLVM rebuild /// dead-strips `#[no_mangle]` fns referenced only from generated `.o` /// files. See the `KEEP_JS_FUNCTION_BIND` precedent in closure/dispatch.rs. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_HTTP_SERVER_CONSTRUCT_WITH_THIS: unsafe extern "C" fn(f64, f64, f64) -> f64 = js_http_server_construct_with_this; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_HTTPS_SERVER_CONSTRUCT_WITH_THIS: unsafe extern "C" fn(f64, f64, f64) -> f64 = js_https_server_construct_with_this; diff --git a/crates/perry-runtime/src/object/object_ops/has_own.rs b/crates/perry-runtime/src/object/object_ops/has_own.rs index a95e829f08..f8982afc82 100644 --- a/crates/perry-runtime/src/object/object_ops/has_own.rs +++ b/crates/perry-runtime/src/object/object_ops/has_own.rs @@ -622,6 +622,6 @@ pub extern "C" fn js_object_property_is_enumerable(obj_value: f64, key_value: f6 } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROPERTY_IS_ENUMERABLE: extern "C" fn(f64, f64) -> f64 = js_object_property_is_enumerable; diff --git a/crates/perry-runtime/src/object/this_binding.rs b/crates/perry-runtime/src/object/this_binding.rs index 3c1bf28c40..6f3c52d9ff 100644 --- a/crates/perry-runtime/src/object/this_binding.rs +++ b/crates/perry-runtime/src/object/this_binding.rs @@ -70,7 +70,7 @@ pub(crate) fn static_this_disarm() { /// checks on subclass receivers throw (test262 static-private-method- /// subclass-receiver). // #1561-style force-keep: only generated IR calls this. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_STATIC_THIS_ARM_CLASSREF: extern "C" fn(u32) = js_static_this_arm_classref; #[no_mangle] @@ -85,7 +85,7 @@ pub extern "C" fn js_static_this_arm_classref(class_id: u32) { /// class-ref expression and the method resolves on a parent class at compile /// time) right before the direct call. // #1561-style force-keep: only generated IR calls this. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_STATIC_THIS_ARM_VALUE: extern "C" fn(f64) = js_static_this_arm_value; #[no_mangle] @@ -96,7 +96,7 @@ pub extern "C" fn js_static_this_arm_value(value: f64) { /// Static-method prologue `this` resolution: take the armed override if any, /// else the lexical class-ref the codegen passes in. // #1561-style force-keep: only generated IR calls this. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_STATIC_THIS_RESOLVE: extern "C" fn(f64) -> f64 = js_static_this_resolve; #[no_mangle] diff --git a/crates/perry-runtime/src/object/with_env.rs b/crates/perry-runtime/src/object/with_env.rs index c154af779a..7baf9db029 100644 --- a/crates/perry-runtime/src/object/with_env.rs +++ b/crates/perry-runtime/src/object/with_env.rs @@ -124,9 +124,9 @@ pub extern "C" fn js_with_implicit_unset() -> f64 { } // #1561-style force-keep: only generated IR calls these. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_WITH_IMPLICIT_UNSET: extern "C" fn() -> f64 = js_with_implicit_unset; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_WITH_IMPLICIT_READ: extern "C" fn(f64, f64) -> f64 = js_with_implicit_read; /// `name` arrives as a NaN-boxed string (codegen lowers `Expr::String` args diff --git a/crates/perry-runtime/src/os.rs b/crates/perry-runtime/src/os.rs index 5a651764f4..0ad783b345 100644 --- a/crates/perry-runtime/src/os.rs +++ b/crates/perry-runtime/src/os.rs @@ -1285,7 +1285,7 @@ fn options_request_buffer(opts_bits: i64) -> bool { read_event_name(enc_ptr).as_deref() == Some("buffer") } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_OS_USER_INFO_OPTIONS: extern "C" fn(i64) -> *mut ObjectHeader = js_os_user_info_options; fn js_os_user_info_impl(buffer_encoding: bool) -> *mut ObjectHeader { diff --git a/crates/perry-runtime/src/path.rs b/crates/perry-runtime/src/path.rs index 349ba09a07..80b3dcd644 100644 --- a/crates/perry-runtime/src/path.rs +++ b/crates/perry-runtime/src/path.rs @@ -829,10 +829,10 @@ pub extern "C" fn js_path_win32_relative_checked(from_f64: f64, to_f64: f64) -> /// Keepalive anchors: these are emitted only from generated code, so the /// whole-program auto-optimize bitcode pass would otherwise dead-strip them. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PATH_RELATIVE_CHECKED: extern "C" fn(f64, f64) -> *mut StringHeader = js_path_relative_checked; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PATH_WIN32_RELATIVE_CHECKED: extern "C" fn(f64, f64) -> *mut StringHeader = js_path_win32_relative_checked; diff --git a/crates/perry-runtime/src/process/env_misc.rs b/crates/perry-runtime/src/process/env_misc.rs index 24e6008667..295fa3444a 100644 --- a/crates/perry-runtime/src/process/env_misc.rs +++ b/crates/perry-runtime/src/process/env_misc.rs @@ -879,7 +879,7 @@ pub extern "C" fn js_process_pending_exit_code() -> i32 { // runtime is `js_process_exit`'s nullish fallback. Anchor the symbol so the // auto-optimize whole-program dead-strip cannot drop it (same guard the // unhandled-rejection reporter uses, #4876). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROCESS_PENDING_EXIT_CODE: extern "C" fn() -> i32 = js_process_pending_exit_code; /// Set an environment variable. Backs `process.env.X = v` (#1344). @@ -957,60 +957,60 @@ pub extern "C" fn js_setenv(name_ptr: *const StringHeader, value: f64) { // trips the runtime through whole-program LLVM bitcode and is free to // internalize + dead-strip an unreferenced symbol — leaving the codegen call // dangling (`Undefined symbols: _js_setenv` at final link, which is exactly -// how #1344's acceptance test still failed on main). The `#[used]` statics +// how #1344's acceptance test still failed on main). The `#[cfg_attr(feature = "keepalive-anchors", used)]` statics // below pin a retained reference edge so both survive every link mode. See // the same pattern in `value/dyn_index.rs`. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SETENV: extern "C" fn(*const StringHeader, f64) = js_setenv; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_REMOVEENV: extern "C" fn(*const StringHeader) = js_removeenv; // #3120: codegen emits `js_module_find_package_json` only from generated `.o`, // so pin a retained reference edge for the auto-optimize whole-program build. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_FIND_PACKAGE_JSON: extern "C" fn(f64, f64) -> f64 = js_module_find_package_json; // node:module helper-state APIs are codegen-emitted from generated `.o`, so pin // retained reference edges for the auto-optimize whole-program build. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_ENABLE_COMPILE_CACHE: extern "C" fn(f64) -> f64 = js_module_enable_compile_cache; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_FLUSH_COMPILE_CACHE: extern "C" fn() -> f64 = js_module_flush_compile_cache; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_GET_COMPILE_CACHE_DIR: extern "C" fn() -> f64 = js_module_get_compile_cache_dir; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_GET_SOURCE_MAPS_SUPPORT: extern "C" fn() -> f64 = js_module_get_source_maps_support; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_SET_SOURCE_MAPS_SUPPORT: extern "C" fn(f64, f64) -> f64 = js_module_set_source_maps_support; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_STRIP_TYPESCRIPT_TYPES: extern "C" fn(f64, f64) -> f64 = js_module_strip_typescript_types; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_REGISTER: extern "C" fn(f64, f64, f64) -> f64 = js_module_register; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_REGISTER_HOOKS: extern "C" fn(f64) -> f64 = js_module_register_hooks; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_DYNAMIC_IMPORT_APPLY_HOOKS: extern "C" fn(f64) -> f64 = js_module_dynamic_import_apply_hooks; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_MODULE_NEW: extern "C" fn(f64) -> f64 = js_module_module_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_FIND_PATH: extern "C" fn(f64, f64, f64) -> f64 = js_module_find_path; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_INIT_PATHS: extern "C" fn() -> f64 = js_module_init_paths; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_LOAD: extern "C" fn(f64, f64, f64) -> f64 = js_module_load; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_NODE_MODULE_PATHS: extern "C" fn(f64) -> f64 = js_module_node_module_paths; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_PRELOAD_MODULES: extern "C" fn(f64) -> f64 = js_module_preload_modules; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_RESOLVE_FILENAME: extern "C" fn(f64, f64, f64, f64) -> f64 = js_module_resolve_filename; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_MODULE_RESOLVE_LOOKUP_PATHS: extern "C" fn(f64, f64) -> f64 = js_module_resolve_lookup_paths; @@ -1759,12 +1759,12 @@ fn read_js_string_lossy(value: f64) -> String { // process native table). Pin retained-reference edges so the auto-optimize // whole-program build doesn't internalize + dead-strip them. Same rationale // as KEEP_JS_SETENV above. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_PROCESS_SOURCE_MAPS_ENABLED: extern "C" fn() -> f64 = js_process_source_maps_enabled; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_PROCESS_SET_SOURCE_MAPS_ENABLED: extern "C" fn(f64) -> f64 = js_process_set_source_maps_enabled; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_PROCESS_REF: extern "C" fn(f64) -> f64 = js_process_ref; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_PROCESS_UNREF: extern "C" fn(f64) -> f64 = js_process_unref; diff --git a/crates/perry-runtime/src/promise/async_step.rs b/crates/perry-runtime/src/promise/async_step.rs index 71d476de11..bba35fb537 100644 --- a/crates/perry-runtime/src/promise/async_step.rs +++ b/crates/perry-runtime/src/promise/async_step.rs @@ -676,7 +676,7 @@ pub extern "C" fn js_async_generator_resume( result } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ASYNC_GENERATOR_RESUME: extern "C" fn(f64, f64, f64) -> f64 = js_async_generator_resume; diff --git a/crates/perry-runtime/src/promise/combinators.rs b/crates/perry-runtime/src/promise/combinators.rs index cd829dbcd2..7f31bfbd2c 100644 --- a/crates/perry-runtime/src/promise/combinators.rs +++ b/crates/perry-runtime/src/promise/combinators.rs @@ -500,14 +500,14 @@ pub extern "C" fn js_promise_any_iterable(value: f64) -> *mut Promise { /// #2822/#3320: keepalive anchors so the whole-program LLVM (auto-optimize) /// build does not dead-strip these codegen-only `#[no_mangle]` entry points. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROMISE_ALL_ITERABLE: extern "C" fn(f64) -> *mut Promise = js_promise_all_iterable; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROMISE_RACE_ITERABLE: extern "C" fn(f64) -> *mut Promise = js_promise_race_iterable; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROMISE_ALL_SETTLED_ITERABLE: extern "C" fn(f64) -> *mut Promise = js_promise_all_settled_iterable; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROMISE_ANY_ITERABLE: extern "C" fn(f64) -> *mut Promise = js_promise_any_iterable; // Queue for scheduled promise resolutions diff --git a/crates/perry-runtime/src/promise/microtasks.rs b/crates/perry-runtime/src/promise/microtasks.rs index d092b97b0a..663899f3ad 100644 --- a/crates/perry-runtime/src/promise/microtasks.rs +++ b/crates/perry-runtime/src/promise/microtasks.rs @@ -75,7 +75,7 @@ pub extern "C" fn js_promise_run_microtasks_event_loop() -> i32 { // The entry event loop is generated code, so nothing in the Rust runtime // references this symbol — anchor it like the other codegen-only hooks so the // auto-optimize internalize+dead-strip pass can't drop it (#4876). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROMISE_RUN_MICROTASKS_EVENT_LOOP: extern "C" fn() -> i32 = js_promise_run_microtasks_event_loop; diff --git a/crates/perry-runtime/src/promise/mod.rs b/crates/perry-runtime/src/promise/mod.rs index 33cfb5e6d3..00808760fc 100644 --- a/crates/perry-runtime/src/promise/mod.rs +++ b/crates/perry-runtime/src/promise/mod.rs @@ -450,23 +450,23 @@ pub fn scan_iter_result_root_mut(visitor: &mut crate::gc::RuntimeRootVisitor<'_> } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITER_RESULT_SET: extern "C" fn(f64, i32) -> f64 = js_iter_result_set; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITER_RESULT_SET_F64: extern "C" fn(f64, i32) -> f64 = js_iter_result_set_f64; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITER_RESULT_SET_I32: extern "C" fn(i32, i32) -> f64 = js_iter_result_set_i32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITER_RESULT_SET_I1: extern "C" fn(i32, i32) -> f64 = js_iter_result_set_i1; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITER_RESULT_GET_VALUE: extern "C" fn() -> f64 = js_iter_result_get_value; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITER_RESULT_GET_VALUE_F64: extern "C" fn() -> f64 = js_iter_result_get_value_f64; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITER_RESULT_GET_VALUE_I32: extern "C" fn() -> i32 = js_iter_result_get_value_i32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITER_RESULT_GET_VALUE_I1: extern "C" fn() -> i32 = js_iter_result_get_value_i1; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITER_RESULT_GET_DONE: extern "C" fn() -> f64 = js_iter_result_get_done; /// Promise state diff --git a/crates/perry-runtime/src/promise/rejection.rs b/crates/perry-runtime/src/promise/rejection.rs index 04cb5b2b6e..5e13f05933 100644 --- a/crates/perry-runtime/src/promise/rejection.rs +++ b/crates/perry-runtime/src/promise/rejection.rs @@ -96,7 +96,7 @@ pub extern "C" fn js_promise_mark_internally_handled(promise: *mut Promise) { /// Keep the stdlib-facing marker alive through the dead-strip pass on the /// PERRY_NO_AUTO_OPTIMIZE prebuilt-lib link (same pattern as the checkpoint /// hook anchors below). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROMISE_MARK_INTERNALLY_HANDLED: extern "C" fn(*mut Promise) = js_promise_mark_internally_handled; @@ -250,11 +250,11 @@ pub extern "C" fn js_promise_report_unhandled_rejections() { // #4876: keep the codegen-emitted hook alive through the auto-optimize // whole-program-bitcode link. It is emitted unconditionally into `_main` but is -// reachable only from generated `.o`; without a `#[used]` anchor the +// reachable only from generated `.o`; without a `#[cfg_attr(feature = "keepalive-anchors", used)]` anchor the // internalize+dead-strip pass drops it and every native link fails with // "undefined symbol" (see the error.rs/combinators.rs anchors for the same // pattern). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROMISE_REPORT_UNHANDLED_REJECTIONS: extern "C" fn() = js_promise_report_unhandled_rejections; diff --git a/crates/perry-runtime/src/proxy.rs b/crates/perry-runtime/src/proxy.rs index 930859cabf..2cd5250dde 100644 --- a/crates/perry-runtime/src/proxy.rs +++ b/crates/perry-runtime/src/proxy.rs @@ -1739,35 +1739,35 @@ pub extern "C" fn js_proxy_revocable(target: f64, handler: f64) -> f64 { } // #2846: retention anchor for `Proxy.revocable` (codegen-only callsite). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROXY_REVOCABLE: extern "C" fn(f64, f64) -> f64 = js_proxy_revocable; // #2762: retention anchors for the Reflect-specific extensibility entry points. // These `#[no_mangle]` fns are emitted only by codegen (no Rust caller in the // crate graph), so the auto-optimize whole-program LLVM bitcode rebuild would // otherwise internalize and dead-strip them. See node_stream_keepalive.rs. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REFLECT_IS_EXTENSIBLE: extern "C" fn(f64) -> f64 = js_reflect_is_extensible; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REFLECT_PREVENT_EXTENSIONS: extern "C" fn(f64) -> f64 = js_reflect_prevent_extensions; // #2761: retention anchor for `Reflect.setPrototypeOf` (codegen-only callsite). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REFLECT_SET_PROTOTYPE_OF: extern "C" fn(f64, f64) -> f64 = js_reflect_set_prototype_of; // #2763/#2764/#2766/#2767: retention anchors for the Reflect entry points // whose only callsites are codegen-emitted. `js_reflect_get` gained a third // `receiver` arg (#2766) and must keep its new signature retained. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REFLECT_GET: extern "C" fn(f64, f64, f64) -> f64 = js_reflect_get; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REFLECT_GET_OWN_PROPERTY_DESCRIPTOR: extern "C" fn(f64, f64) -> f64 = js_reflect_get_own_property_descriptor; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REFLECT_HAS: extern "C" fn(f64, f64) -> f64 = js_reflect_has; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REFLECT_OWN_KEYS: extern "C" fn(f64) -> f64 = js_reflect_own_keys; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REFLECT_APPLY: extern "C" fn(f64, f64, f64) -> f64 = js_reflect_apply; /// Rewrite a `REFLECT_METADATA` key's POINTER-tagged target bits during the diff --git a/crates/perry-runtime/src/proxy/put_value.rs b/crates/perry-runtime/src/proxy/put_value.rs index f9fd397284..c2d40bf14d 100644 --- a/crates/perry-runtime/src/proxy/put_value.rs +++ b/crates/perry-runtime/src/proxy/put_value.rs @@ -492,7 +492,7 @@ unsafe fn dyn_ic_try_store(target: f64, token: u64, slot: u32, value: f64) -> Op // #6088-style keep: codegen emits the only call; a whole-program bitcode // link would otherwise dead-strip the IC entry. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_PUT_VALUE_SET_DYN_IC: extern "C" fn(*mut [i64; 8], f64, f64, f64, i32) -> f64 = js_put_value_set_dyn_ic; diff --git a/crates/perry-runtime/src/regex/escape.rs b/crates/perry-runtime/src/regex/escape.rs index 6c47822377..a2203e544b 100644 --- a/crates/perry-runtime/src/regex/escape.rs +++ b/crates/perry-runtime/src/regex/escape.rs @@ -138,6 +138,6 @@ pub extern "C" fn js_regexp_escape(input: f64) -> f64 { /// Keepalive anchor: `js_regexp_escape` is only called from codegen-emitted /// `.o`, so the auto-optimize whole-program LLVM rebuild would dead-strip it -/// without this `#[used]` reference (see #3320). -#[used] +/// without this `#[cfg_attr(feature = "keepalive-anchors", used)]` reference (see #3320). +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_REGEXP_ESCAPE: extern "C" fn(f64) -> f64 = js_regexp_escape; diff --git a/crates/perry-runtime/src/set.rs b/crates/perry-runtime/src/set.rs index 057287addb..df7412c4e3 100644 --- a/crates/perry-runtime/src/set.rs +++ b/crates/perry-runtime/src/set.rs @@ -716,7 +716,7 @@ fn jsvalue_eq(a: f64, b: f64) -> bool { /// Uses the O(1) hash index side-table. /// C-ABI: current elements-array index of `value` (SameValueZero), or `-1.0` if /// absent. Companion to `js_map_find_key_index` for the delete-safe Set `for-of` -/// fast path (#6075). Only invoked from generated IR, so `#[used]` keeps it. +/// fast path (#6075). Only invoked from generated IR, so `#[cfg_attr(feature = "keepalive-anchors", used)]` keeps it. #[no_mangle] pub extern "C" fn js_set_find_value_index(set_boxed: f64, value: f64) -> f64 { let set = clean_set_ptr(crate::value::js_nanbox_get_pointer(set_boxed) as *const SetHeader); @@ -725,7 +725,7 @@ pub extern "C" fn js_set_find_value_index(set_boxed: f64, value: f64) -> f64 { } unsafe { find_value_index(set, normalize_zero(value)) as f64 } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_FIND_VALUE_INDEX: extern "C" fn(f64, f64) -> f64 = js_set_find_value_index; pub(crate) unsafe fn find_value_index(set: *const SetHeader, value: f64) -> i32 { @@ -1178,47 +1178,47 @@ pub extern "C" fn js_set_delete_bool(set: *mut SetHeader, value: i32) -> i32 { // Codegen emits these string-key typed lowering helpers directly from // generated LLVM IR. Keep roots prevent whole-program LTO/dead-strip from // removing the exported symbols when the Rust crate graph has no caller. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_ADD_STRING: extern "C" fn( *mut SetHeader, *const StringHeader, ) -> *mut SetHeader = js_set_add_string; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_ADD_NUMBER: extern "C" fn(*mut SetHeader, f64) -> *mut SetHeader = js_set_add_number; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_HAS_STRING: extern "C" fn(*const SetHeader, *const StringHeader) -> i32 = js_set_has_string; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_HAS_NUMBER: extern "C" fn(*const SetHeader, f64) -> i32 = js_set_has_number; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_DELETE_STRING: extern "C" fn(*mut SetHeader, *const StringHeader) -> i32 = js_set_delete_string; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_DELETE_NUMBER: extern "C" fn(*mut SetHeader, f64) -> i32 = js_set_delete_number; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_ADD_I32: extern "C" fn(*mut SetHeader, i32) -> *mut SetHeader = js_set_add_i32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_HAS_I32: extern "C" fn(*const SetHeader, i32) -> i32 = js_set_has_i32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_DELETE_I32: extern "C" fn(*mut SetHeader, i32) -> i32 = js_set_delete_i32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_ADD_U32: extern "C" fn(*mut SetHeader, u32) -> *mut SetHeader = js_set_add_u32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_HAS_U32: extern "C" fn(*const SetHeader, u32) -> i32 = js_set_has_u32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_DELETE_U32: extern "C" fn(*mut SetHeader, u32) -> i32 = js_set_delete_u32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_ADD_F32: extern "C" fn(*mut SetHeader, f32) -> *mut SetHeader = js_set_add_f32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_HAS_F32: extern "C" fn(*const SetHeader, f32) -> i32 = js_set_has_f32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_DELETE_F32: extern "C" fn(*mut SetHeader, f32) -> i32 = js_set_delete_f32; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_ADD_BOOL: extern "C" fn(*mut SetHeader, i32) -> *mut SetHeader = js_set_add_bool; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_HAS_BOOL: extern "C" fn(*const SetHeader, i32) -> i32 = js_set_has_bool; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SET_DELETE_BOOL: extern "C" fn(*mut SetHeader, i32) -> i32 = js_set_delete_bool; /// Clear all elements from the set @@ -1803,22 +1803,22 @@ pub extern "C" fn js_set_is_disjoint_from(set: *const SetHeader, other: f64) -> // #2872: keepalive anchors so the auto-optimize whole-program-LLVM-bitcode // rebuild doesn't dead-strip these codegen-only `#[no_mangle]` entry points // (see project_auto_optimize_keepalive_3320 / PR #3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_UNION: extern "C" fn(*const SetHeader, f64) -> *mut SetHeader = js_set_union; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_INTERSECTION: extern "C" fn(*const SetHeader, f64) -> *mut SetHeader = js_set_intersection; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_DIFFERENCE: extern "C" fn(*const SetHeader, f64) -> *mut SetHeader = js_set_difference; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_SYMDIFF: extern "C" fn(*const SetHeader, f64) -> *mut SetHeader = js_set_symmetric_difference; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_IS_SUBSET: extern "C" fn(*const SetHeader, f64) -> i32 = js_set_is_subset_of; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_IS_SUPERSET: extern "C" fn(*const SetHeader, f64) -> i32 = js_set_is_superset_of; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SET_IS_DISJOINT: extern "C" fn(*const SetHeader, f64) -> i32 = js_set_is_disjoint_from; #[cfg(test)] diff --git a/crates/perry-runtime/src/string/locale.rs b/crates/perry-runtime/src/string/locale.rs index 1b01cea0fa..0cbeb7e54c 100644 --- a/crates/perry-runtime/src/string/locale.rs +++ b/crates/perry-runtime/src/string/locale.rs @@ -410,16 +410,16 @@ pub extern "C" fn js_string_validate_collator_args(locales: f64, options: f64) { crate::intl::validate_locale_compare(locales, options); } -// `#[used]` keepalive anchors: these `#[no_mangle]` entry points are reached +// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive anchors: these `#[no_mangle]` entry points are reached // only from generated `.o`, so the whole-program auto-optimize bitcode rebuild // would otherwise dead-strip them (see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_LOCALE_LOWER: extern "C" fn(*const StringHeader, f64) -> *mut StringHeader = js_string_to_locale_lower_case; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_LOCALE_UPPER: extern "C" fn(*const StringHeader, f64) -> *mut StringHeader = js_string_to_locale_upper_case; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_VALIDATE_COLLATOR_ARGS: extern "C" fn(f64, f64) = js_string_validate_collator_args; #[cfg(test)] diff --git a/crates/perry-runtime/src/string/pad.rs b/crates/perry-runtime/src/string/pad.rs index 72fc3c8ed7..c7240e298f 100644 --- a/crates/perry-runtime/src/string/pad.rs +++ b/crates/perry-runtime/src/string/pad.rs @@ -27,10 +27,10 @@ pub extern "C" fn js_string_pad_fill(value: f64) -> *mut StringHeader { crate::builtins::js_string_coerce(value) } -// `#[used]` keepalive: `js_string_pad_fill` is reached only from generated +// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive: `js_string_pad_fill` is reached only from generated // `.o`, so the whole-program auto-optimize bitcode rebuild would dead-strip it // without an anchor (see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PAD_FILL: extern "C" fn(f64) -> *mut StringHeader = js_string_pad_fill; /// Maximum string length Perry/V8 supports as a single `String`. This diff --git a/crates/perry-runtime/src/string/raw.rs b/crates/perry-runtime/src/string/raw.rs index 8f4f6cd1e2..8204beb9fb 100644 --- a/crates/perry-runtime/src/string/raw.rs +++ b/crates/perry-runtime/src/string/raw.rs @@ -114,6 +114,6 @@ fn throw_raw_type_error() -> ! { /// Keepalive anchor — `js_string_raw` is emitted only by generated code /// (the `String.raw(...)` call lowering), so the auto-optimize whole-program /// LLVM rebuild would otherwise dead-strip this `#[no_mangle]` symbol and -/// break linking (see PR #3320 / the `#[used]` keepalive pattern). -#[used] +/// break linking (see PR #3320 / the `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive pattern). +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_STRING_RAW: extern "C" fn(f64, f64) -> *mut StringHeader = js_string_raw; diff --git a/crates/perry-runtime/src/string/slice_ops.rs b/crates/perry-runtime/src/string/slice_ops.rs index 0e7851dc24..960fde3c3a 100644 --- a/crates/perry-runtime/src/string/slice_ops.rs +++ b/crates/perry-runtime/src/string/slice_ops.rs @@ -178,10 +178,10 @@ pub extern "C" fn js_string_substr( ) } -// `#[used]` keepalive: `js_string_substr` is reached only from generated `.o`, +// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive: `js_string_substr` is reached only from generated `.o`, // so the whole-program auto-optimize bitcode rebuild would dead-strip it // without an anchor (see project_auto_optimize_keepalive_3320). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_SUBSTR: extern "C" fn(*const StringHeader, f64, f64) -> *mut StringHeader = js_string_substr; @@ -524,10 +524,10 @@ pub extern "C" fn js_string_position_to_index(pos_f64: f64) -> i32 { } } -// `#[used]` keepalive: `js_string_position_to_index` is reached only from +// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive: `js_string_position_to_index` is reached only from // generated `.o`, so the auto-optimize whole-program bitcode pass would // otherwise dead-strip it. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_POSITION_TO_INDEX: extern "C" fn(f64) -> i32 = js_string_position_to_index; /// Find the last index of a substring (-1 if not found). diff --git a/crates/perry-runtime/src/symbol/constructors.rs b/crates/perry-runtime/src/symbol/constructors.rs index c858b6d3d3..adf6b58a82 100644 --- a/crates/perry-runtime/src/symbol/constructors.rs +++ b/crates/perry-runtime/src/symbol/constructors.rs @@ -179,8 +179,8 @@ fn is_well_known_symbol_member_name(name: &str) -> bool { // #1561-style force-keep: `js_symbol_computed_member` has no internal Rust // callers — only generated IR (perry-hir lowers `Symbol[key]` to a call to it), // so LTO / whole-program-bitcode link modes are free to internalize and -// dead-strip it. The `#[used]` reference edge keeps the export alive. -#[used] +// dead-strip it. The `#[cfg_attr(feature = "keepalive-anchors", used)]` reference edge keeps the export alive. +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SYMBOL_COMPUTED_MEMBER: unsafe extern "C" fn(f64, f64) -> f64 = js_symbol_computed_member; diff --git a/crates/perry-runtime/src/symbol/iterator.rs b/crates/perry-runtime/src/symbol/iterator.rs index ac1830befb..54259d1c67 100644 --- a/crates/perry-runtime/src/symbol/iterator.rs +++ b/crates/perry-runtime/src/symbol/iterator.rs @@ -151,7 +151,7 @@ pub(crate) fn class_ref_resolves_iterator(val_f64: f64) -> bool { /// / guarded `__iter.return()` call in this validator. Returns the result /// unchanged when it is an object. // #1561-style force-keep: only generated IR calls this. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_ITERATOR_RESULT_VALIDATE: extern "C" fn(f64) -> f64 = js_iterator_result_validate; #[no_mangle] diff --git a/crates/perry-runtime/src/text.rs b/crates/perry-runtime/src/text.rs index b513446eff..62f705c9be 100644 --- a/crates/perry-runtime/src/text.rs +++ b/crates/perry-runtime/src/text.rs @@ -602,18 +602,18 @@ fn throw_invalid_encoded_data(encoding: &str) -> ! { /// Keepalive anchors — these `#[no_mangle]` fns are only called from /// generated `.o`, so the auto-optimize whole-program bitcode rebuild -/// would dead-strip them without `#[used]` retention (see +/// would dead-strip them without `#[cfg_attr(feature = "keepalive-anchors", used)]` retention (see /// [[project_auto_optimize_keepalive_3320]]). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_TEXT_DECODER_NEW: extern "C" fn(f64, f64, f64) -> i64 = js_text_decoder_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_TEXT_DECODER_DECODE: extern "C" fn(f64, f64) -> i64 = js_text_decoder_decode_llvm; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_TEXT_DECODER_ENCODING: extern "C" fn(f64) -> *mut StringHeader = js_text_decoder_encoding; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_TEXT_DECODER_FATAL: extern "C" fn(f64) -> f64 = js_text_decoder_fatal; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_TEXT_DECODER_IGNORE_BOM: extern "C" fn(f64) -> f64 = js_text_decoder_ignore_bom; /// TextDecoder / TextEncoder registry-handle property surface for VALUE diff --git a/crates/perry-runtime/src/tls.rs b/crates/perry-runtime/src/tls.rs index 2ae1af1dae..43ee85a141 100644 --- a/crates/perry-runtime/src/tls.rs +++ b/crates/perry-runtime/src/tls.rs @@ -567,18 +567,18 @@ pub extern "C" fn js_tls_check_server_identity(hostname: f64, cert: f64) -> f64 // Keep-alive anchors so the auto-optimize bitcode rebuild does not dead-strip // these codegen-emitted `#[no_mangle]` runtime helpers (referenced from the // native dispatch table in perry-codegen). -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TLS_GET_CIPHERS: extern "C" fn() -> f64 = js_tls_get_ciphers; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TLS_GET_CA_CERTIFICATES: extern "C" fn(f64) -> f64 = js_tls_get_ca_certificates; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TLS_SET_DEFAULT_CA_CERTIFICATES: extern "C" fn(f64) -> f64 = js_tls_set_default_ca_certificates; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TLS_CREATE_SECURE_CONTEXT: extern "C" fn(f64) -> f64 = js_tls_create_secure_context; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TLS_SECURE_CONTEXT_NEW: extern "C" fn(f64) -> f64 = js_tls_secure_context_new; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TLS_CHECK_SERVER_IDENTITY: extern "C" fn(f64, f64) -> f64 = js_tls_check_server_identity; diff --git a/crates/perry-runtime/src/typed_feedback.rs b/crates/perry-runtime/src/typed_feedback.rs index bf26086af2..895aac03aa 100644 --- a/crates/perry-runtime/src/typed_feedback.rs +++ b/crates/perry-runtime/src/typed_feedback.rs @@ -1972,7 +1972,7 @@ pub extern "C" fn js_typed_feedback_packed_f64_range_loop_guard( } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_FEEDBACK_PACKED_F64_RANGE_LOOP_GUARD: extern "C" fn( u64, f64, @@ -2013,7 +2013,7 @@ pub extern "C" fn js_typed_feedback_packed_i32_array_loop_guard( } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_FEEDBACK_PACKED_I32_ARRAY_LOOP_GUARD: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_i32_array_loop_guard; @@ -2050,7 +2050,7 @@ pub extern "C" fn js_typed_feedback_packed_u32_array_loop_guard( } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_FEEDBACK_PACKED_U32_ARRAY_LOOP_GUARD: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_u32_array_loop_guard; diff --git a/crates/perry-runtime/src/typed_feedback/guards.rs b/crates/perry-runtime/src/typed_feedback/guards.rs index 772fdd7a2e..39bbbae1e9 100644 --- a/crates/perry-runtime/src/typed_feedback/guards.rs +++ b/crates/perry-runtime/src/typed_feedback/guards.rs @@ -1036,7 +1036,7 @@ pub extern "C" fn js_typed_feedback_closure_direct_call_guard( // whole-program thin-LTO + `strip=true` build internalizes + dead-strips them // — dangling the codegen call at final link (`Undefined symbols: // _js_typed_feedback_class_field_set_guard` for any class-field program). -// `typed_feedback.rs`'s `#[used]` block covers the helpers defined there; +// `typed_feedback.rs`'s `#[cfg_attr(feature = "keepalive-anchors", used)]` block covers the helpers defined there; // these typed fn-pointer statics extend the same `@llvm.used` retention to the // guard helpers defined here. (A `usize`/`*const()` cast does NOT survive // thin-LTO — only individual typed fn-pointer statics keep the symbol @@ -1045,12 +1045,12 @@ pub extern "C" fn js_typed_feedback_closure_direct_call_guard( #[rustfmt::skip] mod keep_guard_symbols { use super::*; - #[used] static G0: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, i32) -> i32 = js_typed_feedback_class_field_get_guard; - #[used] static G1: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, f64, i32) -> i32 = js_typed_feedback_class_field_set_guard; - #[used] static G1C: extern "C" fn(u64, u64, u64, f64) = js_class_field_set_fallback; - #[used] static G1D: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, f64, i32) = js_class_field_set_ic; - #[used] static G1E: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, i32) -> f64 = js_class_field_get_ic; - #[used] static G2: unsafe extern "C" fn(u64, f64, u32, *const ArrayHeader, *const i8, usize, *const u8) -> i32 = js_typed_feedback_method_direct_call_guard; - #[used] static G3: extern "C" fn(u64, f64, *const u8, u32, u32) -> i32 = js_typed_feedback_closure_direct_call_guard; - #[used] static G4: unsafe extern "C" fn(f64, u32, *const ArrayHeader) -> i32 = js_method_direct_shape_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static G0: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, i32) -> i32 = js_typed_feedback_class_field_get_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static G1: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, f64, i32) -> i32 = js_typed_feedback_class_field_set_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static G1C: extern "C" fn(u64, u64, u64, f64) = js_class_field_set_fallback; + #[cfg_attr(feature = "keepalive-anchors", used)] static G1D: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, f64, i32) = js_class_field_set_ic; + #[cfg_attr(feature = "keepalive-anchors", used)] static G1E: extern "C" fn(u64, f64, u32, *const ArrayHeader, *const crate::StringHeader, u32, i32) -> f64 = js_class_field_get_ic; + #[cfg_attr(feature = "keepalive-anchors", used)] static G2: unsafe extern "C" fn(u64, f64, u32, *const ArrayHeader, *const i8, usize, *const u8) -> i32 = js_typed_feedback_method_direct_call_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static G3: extern "C" fn(u64, f64, *const u8, u32, u32) -> i32 = js_typed_feedback_closure_direct_call_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static G4: unsafe extern "C" fn(f64, u32, *const ArrayHeader) -> i32 = js_method_direct_shape_guard; } diff --git a/crates/perry-runtime/src/typed_feedback/tests.rs b/crates/perry-runtime/src/typed_feedback/tests.rs index b61c2bf1be..c9040c30e3 100644 --- a/crates/perry-runtime/src/typed_feedback/tests.rs +++ b/crates/perry-runtime/src/typed_feedback/tests.rs @@ -999,8 +999,8 @@ fn assert_lto_keepalive_anchor(src: &str, static_name: &str, signature: &str, ta let end = (static_pos + 512).min(src.len()); let window = &src[start..end]; assert!( - window.contains("#[used]"), - "keepalive static {static_name} for {target} is not #[used]" + window.contains("#[cfg_attr(feature = "keepalive-anchors", used)]"), + "keepalive static {static_name} for {target} is not #[cfg_attr(feature = "keepalive-anchors", used)]" ); assert!( window.contains(signature), diff --git a/crates/perry-runtime/src/typed_feedback/trace.rs b/crates/perry-runtime/src/typed_feedback/trace.rs index 82a5a02856..4597834d1a 100644 --- a/crates/perry-runtime/src/typed_feedback/trace.rs +++ b/crates/perry-runtime/src/typed_feedback/trace.rs @@ -352,7 +352,7 @@ pub extern "C" fn js_typed_feedback_maybe_dump_trace() { // symbol, leaving the codegen call dangling (`Undefined symbols: // _js_typed_feedback_native_call_method` etc. at final link — which is exactly // how an instrumented async program failed to link under auto-optimize). The -// `#[used]` typed fn-pointer statics below take the address of each helper, +// `#[cfg_attr(feature = "keepalive-anchors", used)]` typed fn-pointer statics below take the address of each helper, // landing the functions themselves in `@llvm.used` so thin-LTO keeps them // external (not internalized) and the linker's `-dead_strip` honors them — the // same proven retention mechanism as `value/dyn_index.rs` / `process.rs` @@ -368,36 +368,36 @@ mod keep_typed_feedback { js_typed_feedback_native_call_method_apply_by_id, js_typed_feedback_native_call_method_by_id, }; - #[used] static K00: extern "C" fn(u64, u32, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize) = js_typed_feedback_register_site; - #[used] static K01: extern "C" fn(u64) = js_typed_feedback_record_guard_pass; - #[used] static K02: extern "C" fn(u64) = js_typed_feedback_record_guard_fail; - #[used] static K03: extern "C" fn(u64) = js_typed_feedback_record_fallback_call; - #[used] static K04: extern "C" fn(u64, *const ObjectHeader, *const crate::StringHeader) = js_typed_feedback_observe_property_get; - #[used] static K05: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader) = js_typed_feedback_observe_property_set; - #[used] static K06: extern "C" fn(u64, *const ObjectHeader, *const crate::StringHeader) -> f64 = js_typed_feedback_object_get_field_by_name_f64; - #[used] static K07: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader, f64) = js_typed_feedback_object_set_field_by_name; - #[used] static K08: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader, f64) = js_typed_feedback_object_set_field_by_name_fast; - #[used] static K09: unsafe extern "C" fn(u64, f64, *const i8, usize, *const f64, usize) -> f64 = js_typed_feedback_native_call_method; - #[used] static K10: unsafe extern "C" fn(u64, f64, *const i8, usize, i64) -> f64 = js_typed_feedback_native_call_method_apply; - #[used] static K11: extern "C" fn(u64, *const ArrayHeader, u32) -> f64 = js_typed_feedback_array_get_f64; - #[used] static K12: extern "C" fn(u64, f64, i32, i32) -> i32 = js_typed_feedback_plain_array_index_get_guard; - #[used] static K13: extern "C" fn(u64, f64, i32, i32) -> i32 = js_typed_feedback_numeric_array_index_get_guard; - #[used] static K14: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_f64_array_loop_guard; - #[used] static K15: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_u32_array_loop_guard; - #[used] static K16: extern "C" fn(u64, f64, f64) -> f64 = js_typed_feedback_array_index_get_fallback_boxed; - #[used] static K17: extern "C" fn(u64, *mut ArrayHeader, u32, f64) = js_typed_feedback_array_set_f64; - #[used] static K18: extern "C" fn(u64, *mut ArrayHeader, u32, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_f64_extend; - #[used] static K19: extern "C" fn(u64, f64, i32, f64, i32) -> i32 = js_typed_feedback_plain_array_index_set_guard; - #[used] static K20: extern "C" fn(u64, f64, i32, f64, i32) -> i32 = js_typed_feedback_numeric_array_index_set_guard; - #[used] static K21: extern "C" fn(u64, f64, f64) -> i32 = js_typed_feedback_numeric_array_push_guard; - #[used] static K22: extern "C" fn(u64, f64, f64, f64) -> f64 = js_typed_feedback_array_index_set_fallback_boxed; - #[used] static K23: extern "C" fn(u64, *const ArrayHeader, u32) = js_typed_feedback_observe_array_element; - #[used] static K24: extern "C" fn(u64, *mut ArrayHeader, *const crate::StringHeader, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_string_key; - #[used] static K25: extern "C" fn(u64, *mut ArrayHeader, f64, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_index_or_string; - #[used] static K26: extern "C" fn(u64, i64, f64, f64) = js_typed_feedback_object_set_index_polymorphic; - #[used] static K27: extern "C" fn(u64, *mut ObjectHeader, u32, *const crate::StringHeader, f64) = js_typed_feedback_object_set_unboxed_f64_field; - #[used] static K28: extern "C" fn(u64, f64) -> f64 = js_typed_feedback_observe_helper_return; - #[used] static K29: extern "C" fn() = js_typed_feedback_maybe_dump_trace; - #[used] static K30: unsafe extern "C" fn(u64, f64, i64, *const f64, usize) -> f64 = js_typed_feedback_native_call_method_by_id; - #[used] static K31: unsafe extern "C" fn(u64, f64, i64, i64) -> f64 = js_typed_feedback_native_call_method_apply_by_id; + #[cfg_attr(feature = "keepalive-anchors", used)] static K00: extern "C" fn(u64, u32, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize, *const u8, usize) = js_typed_feedback_register_site; + #[cfg_attr(feature = "keepalive-anchors", used)] static K01: extern "C" fn(u64) = js_typed_feedback_record_guard_pass; + #[cfg_attr(feature = "keepalive-anchors", used)] static K02: extern "C" fn(u64) = js_typed_feedback_record_guard_fail; + #[cfg_attr(feature = "keepalive-anchors", used)] static K03: extern "C" fn(u64) = js_typed_feedback_record_fallback_call; + #[cfg_attr(feature = "keepalive-anchors", used)] static K04: extern "C" fn(u64, *const ObjectHeader, *const crate::StringHeader) = js_typed_feedback_observe_property_get; + #[cfg_attr(feature = "keepalive-anchors", used)] static K05: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader) = js_typed_feedback_observe_property_set; + #[cfg_attr(feature = "keepalive-anchors", used)] static K06: extern "C" fn(u64, *const ObjectHeader, *const crate::StringHeader) -> f64 = js_typed_feedback_object_get_field_by_name_f64; + #[cfg_attr(feature = "keepalive-anchors", used)] static K07: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader, f64) = js_typed_feedback_object_set_field_by_name; + #[cfg_attr(feature = "keepalive-anchors", used)] static K08: extern "C" fn(u64, *mut ObjectHeader, *const crate::StringHeader, f64) = js_typed_feedback_object_set_field_by_name_fast; + #[cfg_attr(feature = "keepalive-anchors", used)] static K09: unsafe extern "C" fn(u64, f64, *const i8, usize, *const f64, usize) -> f64 = js_typed_feedback_native_call_method; + #[cfg_attr(feature = "keepalive-anchors", used)] static K10: unsafe extern "C" fn(u64, f64, *const i8, usize, i64) -> f64 = js_typed_feedback_native_call_method_apply; + #[cfg_attr(feature = "keepalive-anchors", used)] static K11: extern "C" fn(u64, *const ArrayHeader, u32) -> f64 = js_typed_feedback_array_get_f64; + #[cfg_attr(feature = "keepalive-anchors", used)] static K12: extern "C" fn(u64, f64, i32, i32) -> i32 = js_typed_feedback_plain_array_index_get_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static K13: extern "C" fn(u64, f64, i32, i32) -> i32 = js_typed_feedback_numeric_array_index_get_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static K14: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_f64_array_loop_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static K15: extern "C" fn(u64, f64) -> i32 = js_typed_feedback_packed_u32_array_loop_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static K16: extern "C" fn(u64, f64, f64) -> f64 = js_typed_feedback_array_index_get_fallback_boxed; + #[cfg_attr(feature = "keepalive-anchors", used)] static K17: extern "C" fn(u64, *mut ArrayHeader, u32, f64) = js_typed_feedback_array_set_f64; + #[cfg_attr(feature = "keepalive-anchors", used)] static K18: extern "C" fn(u64, *mut ArrayHeader, u32, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_f64_extend; + #[cfg_attr(feature = "keepalive-anchors", used)] static K19: extern "C" fn(u64, f64, i32, f64, i32) -> i32 = js_typed_feedback_plain_array_index_set_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static K20: extern "C" fn(u64, f64, i32, f64, i32) -> i32 = js_typed_feedback_numeric_array_index_set_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static K21: extern "C" fn(u64, f64, f64) -> i32 = js_typed_feedback_numeric_array_push_guard; + #[cfg_attr(feature = "keepalive-anchors", used)] static K22: extern "C" fn(u64, f64, f64, f64) -> f64 = js_typed_feedback_array_index_set_fallback_boxed; + #[cfg_attr(feature = "keepalive-anchors", used)] static K23: extern "C" fn(u64, *const ArrayHeader, u32) = js_typed_feedback_observe_array_element; + #[cfg_attr(feature = "keepalive-anchors", used)] static K24: extern "C" fn(u64, *mut ArrayHeader, *const crate::StringHeader, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_string_key; + #[cfg_attr(feature = "keepalive-anchors", used)] static K25: extern "C" fn(u64, *mut ArrayHeader, f64, f64) -> *mut ArrayHeader = js_typed_feedback_array_set_index_or_string; + #[cfg_attr(feature = "keepalive-anchors", used)] static K26: extern "C" fn(u64, i64, f64, f64) = js_typed_feedback_object_set_index_polymorphic; + #[cfg_attr(feature = "keepalive-anchors", used)] static K27: extern "C" fn(u64, *mut ObjectHeader, u32, *const crate::StringHeader, f64) = js_typed_feedback_object_set_unboxed_f64_field; + #[cfg_attr(feature = "keepalive-anchors", used)] static K28: extern "C" fn(u64, f64) -> f64 = js_typed_feedback_observe_helper_return; + #[cfg_attr(feature = "keepalive-anchors", used)] static K29: extern "C" fn() = js_typed_feedback_maybe_dump_trace; + #[cfg_attr(feature = "keepalive-anchors", used)] static K30: unsafe extern "C" fn(u64, f64, i64, *const f64, usize) -> f64 = js_typed_feedback_native_call_method_by_id; + #[cfg_attr(feature = "keepalive-anchors", used)] static K31: unsafe extern "C" fn(u64, f64, i64, i64) -> f64 = js_typed_feedback_native_call_method_apply_by_id; } diff --git a/crates/perry-runtime/src/typedarray/access.rs b/crates/perry-runtime/src/typedarray/access.rs index 08149b8f63..d46968c323 100644 --- a/crates/perry-runtime/src/typedarray/access.rs +++ b/crates/perry-runtime/src/typedarray/access.rs @@ -86,8 +86,8 @@ pub extern "C" fn js_typed_array_read_int32(ta: *const TypedArrayHeader, index: // Codegen-only export: the inline checked-i32 read emits the call in // `perry-codegen/src/expr/i32_fast_path.rs`; a whole-program bitcode link is // otherwise free to internalize and dead-strip it (it has no internal Rust -// caller). The `#[used]` anchor pins it, mirroring the getter above. -#[used] +// caller). The `#[cfg_attr(feature = "keepalive-anchors", used)]` anchor pins it, mirroring the getter above. +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_ARRAY_READ_INT32: extern "C" fn(*const TypedArrayHeader, i32) -> i32 = js_typed_array_read_int32; @@ -120,7 +120,7 @@ pub extern "C" fn js_typed_array_read_f64(ta: *const TypedArrayHeader, index: i3 } // Codegen-only export (see the i32 sibling above): pin under whole-program LTO. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_ARRAY_READ_F64: extern "C" fn(*const TypedArrayHeader, i32) -> f64 = js_typed_array_read_f64; @@ -149,8 +149,8 @@ pub extern "C" fn js_typed_array_index_get_dynamic(ta: *const TypedArrayHeader, // `js_dyn_index_get`, this export has zero internal Rust callers — it is only // invoked from generated LLVM IR (codegen emits the call in // `perry-codegen/src/expr/index_get.rs`), so a whole-program bitcode link is -// free to internalize and dead-strip it. The `#[used]` anchor pins it. -#[used] +// free to internalize and dead-strip it. The `#[cfg_attr(feature = "keepalive-anchors", used)]` anchor pins it. +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_ARRAY_INDEX_GET_DYNAMIC: extern "C" fn(*const TypedArrayHeader, f64) -> f64 = js_typed_array_index_get_dynamic; @@ -561,7 +561,7 @@ pub extern "C" fn js_uint8array_index_get_value( // #6088: force-keep the JS-value Uint8Array index getter under LTO / // auto-optimize — it has zero internal Rust callers (codegen emits the only // call), so a whole-program bitcode link is otherwise free to dead-strip it. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_UINT8ARRAY_INDEX_GET_VALUE: extern "C" fn(*const TypedArrayHeader, i32) -> f64 = js_uint8array_index_get_value; diff --git a/crates/perry-runtime/src/typedarray_props.rs b/crates/perry-runtime/src/typedarray_props.rs index 371c83f2f4..ddbea5d240 100644 --- a/crates/perry-runtime/src/typedarray_props.rs +++ b/crates/perry-runtime/src/typedarray_props.rs @@ -747,7 +747,7 @@ pub extern "C" fn js_typed_array_index_set_dynamic( } } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_TYPED_ARRAY_INDEX_SET_DYNAMIC: extern "C" fn( *mut TypedArrayHeader, f64, diff --git a/crates/perry-runtime/src/url/abort.rs b/crates/perry-runtime/src/url/abort.rs index 486ed0dccd..c5952d7946 100644 --- a/crates/perry-runtime/src/url/abort.rs +++ b/crates/perry-runtime/src/url/abort.rs @@ -635,13 +635,13 @@ pub extern "C" fn js_abort_signal_any( // #2582: keepalive anchors so the auto-optimize whole-program LLVM bitcode // rebuild doesn't internalize + dead-strip these codegen-only `#[no_mangle]` // entry points (see project_auto_optimize_keepalive_3320). These are only -// referenced from generated `.o`, so without `#[used]` they vanish. -#[used] +// referenced from generated `.o`, so without `#[cfg_attr(feature = "keepalive-anchors", used)]` they vanish. +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ABORT_SIGNAL_ABORT: extern "C" fn(f64) -> *mut ObjectHeader = js_abort_signal_abort; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ABORT_SIGNAL_ANY: extern "C" fn(*mut crate::array::ArrayHeader) -> *mut ObjectHeader = js_abort_signal_any; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ABORT_SIGNAL_THROW_IF_ABORTED: extern "C" fn(*mut ObjectHeader) -> f64 = js_abort_signal_throw_if_aborted; diff --git a/crates/perry-runtime/src/url/search_params.rs b/crates/perry-runtime/src/url/search_params.rs index b81b658b08..193372df58 100644 --- a/crates/perry-runtime/src/url/search_params.rs +++ b/crates/perry-runtime/src/url/search_params.rs @@ -127,7 +127,7 @@ pub extern "C" fn js_url_search_params_subclass_init(this: f64, init: f64) -> f6 /// Reached only from codegen-emitted IR (the `Expr::SuperCall` URLSearchParams /// arm); pin it so the auto-optimize bitcode rebuild's dead-strip can't drop it. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_URL_SEARCH_PARAMS_SUBCLASS_INIT: extern "C" fn(f64, f64) -> f64 = js_url_search_params_subclass_init; diff --git a/crates/perry-runtime/src/validators.rs b/crates/perry-runtime/src/validators.rs index 027fa6bdb2..9bb5cb3ff6 100644 --- a/crates/perry-runtime/src/validators.rs +++ b/crates/perry-runtime/src/validators.rs @@ -338,7 +338,7 @@ pub fn validate_port(value: f64, name: &str) -> u16 { // hard segfault rather than node's catchable `TypeError`. These helpers take // the *original* NaN-boxed value (as `f64`) plus the argument name, so codegen // can emit a `call void` validation BEFORE the unbox, throwing node's typed -// error instead of crashing. The `#[used]` anchors below keep them alive +// error instead of crashing. The `#[cfg_attr(feature = "keepalive-anchors", used)]` anchors below keep them alive // through the auto-optimize whole-program rebuild (the bitcode internalizer // drops `#[no_mangle]` symbols only referenced from generated `.o`). // ============================================================================ @@ -423,12 +423,12 @@ pub unsafe extern "C" fn js_runtime_validate_integer_arg( validate_integer(value, &name, min, max); } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_VALIDATE_STRING_ARG: unsafe extern "C" fn(f64, *const u8, u32) = js_runtime_validate_string_arg; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_VALIDATE_CRYPTO_KEY_ARG: unsafe extern "C" fn(f64, *const u8, u32) = js_runtime_validate_crypto_key_arg; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_VALIDATE_INTEGER_ARG: unsafe extern "C" fn(f64, *const u8, u32, f64, f64) = js_runtime_validate_integer_arg; diff --git a/crates/perry-runtime/src/value/dyn_index.rs b/crates/perry-runtime/src/value/dyn_index.rs index 33c9ef3f8d..a108eb47cd 100644 --- a/crates/perry-runtime/src/value/dyn_index.rs +++ b/crates/perry-runtime/src/value/dyn_index.rs @@ -591,14 +591,14 @@ pub extern "C" fn js_is_undefined_or_bare_nan(value: f64) -> i32 { // `#[no_mangle]` symbol and dead-strip it, leaving the codegen-emitted call // dangling: `Undefined symbols: _js_dyn_index_get` at final link. // -// The `#[used]` statics below take the address of each export, creating a +// The `#[cfg_attr(feature = "keepalive-anchors", used)]` statics below take the address of each export, creating a // retained reference edge that LTO and the linker's `-dead_strip` must // honor (the entries land in `@llvm.used` / a `no_dead_strip` section). This // guarantees the symbols survive auto-optimize regardless of feature set or // link mode. Function-pointer types are `Sync`, so no wrapper is needed. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_DYN_INDEX_GET: extern "C" fn(f64, f64) -> f64 = js_dyn_index_get; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_DYN_INDEX_SET: extern "C" fn(f64, f64, f64) -> f64 = js_dyn_index_set; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_IS_UNDEFINED_OR_BARE_NAN: extern "C" fn(f64) -> i32 = js_is_undefined_or_bare_nan; diff --git a/crates/perry-runtime/src/value/dynamic_arith.rs b/crates/perry-runtime/src/value/dynamic_arith.rs index 5e36535548..ae502b1732 100644 --- a/crates/perry-runtime/src/value/dynamic_arith.rs +++ b/crates/perry-runtime/src/value/dynamic_arith.rs @@ -750,15 +750,15 @@ pub unsafe extern "C" fn js_dynamic_ushr(a: f64, b: f64) -> f64 { (ai >> bi) as f64 } -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DYNAMIC_POW: unsafe extern "C" fn(f64, f64) -> f64 = js_dynamic_pow; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DYNAMIC_USHR: unsafe extern "C" fn(f64, f64) -> f64 = js_dynamic_ushr; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_DYNAMIC_BITNOT: unsafe extern "C" fn(f64) -> f64 = js_dynamic_bitnot; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_TO_NUMERIC: unsafe extern "C" fn(f64) -> f64 = js_to_numeric; -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_NUMERIC_STEP: unsafe extern "C" fn(f64, i32) -> f64 = js_numeric_step; #[cfg(test)] diff --git a/crates/perry-runtime/src/value/nanbox.rs b/crates/perry-runtime/src/value/nanbox.rs index 0568ad5795..95e873c6b4 100644 --- a/crates/perry-runtime/src/value/nanbox.rs +++ b/crates/perry-runtime/src/value/nanbox.rs @@ -351,7 +351,7 @@ pub extern "C" fn js_switch_strict_equals(a: f64, b: f64) -> i32 { // #1561-style force-keep: only generated IR calls this — see // value/dyn_index.rs for the rationale. -#[used] +#[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_SWITCH_STRICT_EQUALS: extern "C" fn(f64, f64) -> i32 = js_switch_strict_equals; /// Check if a NaN-boxed f64 value represents a string. diff --git a/crates/perry-runtime/src/yoga.rs b/crates/perry-runtime/src/yoga.rs index af55fd40af..52b48cd4cc 100644 --- a/crates/perry-runtime/src/yoga.rs +++ b/crates/perry-runtime/src/yoga.rs @@ -719,7 +719,7 @@ pub extern "C" fn js_yoga_get_computed_edge(id: f64, kind: f64, edge: f64) -> f6 // Typed statics (coercion, not a const ptr→int cast). macro_rules! keep { ($n:ident : $t:ty = $f:ident) => { - #[used] + #[cfg_attr(feature = "keepalive-anchors", used)] static $n: $t = $f; }; } diff --git a/crates/perry/src/commands/compile/optimized_libs/freshness.rs b/crates/perry/src/commands/compile/optimized_libs/freshness.rs index 99a45c8600..68848cebf2 100644 --- a/crates/perry/src/commands/compile/optimized_libs/freshness.rs +++ b/crates/perry/src/commands/compile/optimized_libs/freshness.rs @@ -88,7 +88,7 @@ pub(crate) fn auto_optimized_cache_key( ) -> String { let target_str = target.unwrap_or("host"); format!( - "{}|{}|{}|wasm={}|regex={}|temporal={}|ee={}|url={}|norm={}|seg={}|loc={}|diag={}|dgram={}|http2={}|dyneval={}|sizeopt={}|v={}", + "{}|{}|{}|wasm={}|regex={}|temporal={}|ee={}|url={}|norm={}|seg={}|loc={}|diag={}|dgram={}|http2={}|dyneval={}|sizeopt={}|anchors={}|v={}", feature_arg, panic_abort_safe, target_str, @@ -114,6 +114,7 @@ pub(crate) fn auto_optimized_cache_key( size_opt_level().unwrap_or("off"), if size_lto_fat() { "+fatlto" } else { "" } ), + std::env::var("PERRY_LLVM_BITCODE_LINK").ok().as_deref() == Some("1"), env!("CARGO_PKG_VERSION"), ) } @@ -203,6 +204,15 @@ pub(crate) fn auto_optimized_cross_features( cross_features.push("perry-runtime/regex-engine".to_string()); } } + // The `#[used]` keep-alive anchors exist for the whole-program bitcode + // LTO path only (see perry-runtime's `keepalive-anchors` feature docs). + // The classic link keeps every reachable symbol via real undefined + // references, so the anchors are omitted there — that is what lets + // `-dead_strip` drop the never-imported node-module surface from small + // programs. Re-enable them whenever the bitcode link was requested. + if std::env::var("PERRY_LLVM_BITCODE_LINK").ok().as_deref() == Some("1") { + cross_features.push("perry-runtime/keepalive-anchors".to_string()); + } // Compile OUT perry-runtime's no-op fetch stubs (`js_fetch_with_options` / // `js_headers_new` / `js_request_new`, gated `#[cfg(not(feature = // "external-fetch-symbols"))]`) whenever the program uses fetch — perry-stdlib's From d8adf5d2935dd2789ef949c5a3672e1010af2511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 12:16:19 +0200 Subject: [PATCH 07/12] size: feature-gate mimalloc global allocator (alloc-mimalloc, default-on; dropped in PERRY_SIZE_OPT rebuilds) --- crates/perry-runtime/Cargo.toml | 13 ++++++++++--- crates/perry-runtime/src/gc/mod.rs | 6 +++++- crates/perry-runtime/src/gc/tests/os_tag.rs | 6 +++++- crates/perry-runtime/src/lib.rs | 6 ++++-- .../commands/compile/optimized_libs/freshness.rs | 6 ++++++ 5 files changed, 30 insertions(+), 7 deletions(-) diff --git a/crates/perry-runtime/Cargo.toml b/crates/perry-runtime/Cargo.toml index cd28ac3725..55fbd574eb 100644 --- a/crates/perry-runtime/Cargo.toml +++ b/crates/perry-runtime/Cargo.toml @@ -22,7 +22,7 @@ crate-type = ["rlib"] # actually needs (see optimized_libs.rs), so the heavy subsystems below # (regex engine, Temporal, URL/IDNA, normalize, segmenter) are *opt-in per # app* and absent from binaries that never use them. -default = ["full", "regex-engine", "temporal", "url-engine", "string-normalize", "intl-segmenter", "intl-locale", "intl-datetime", "diagnostics", "mod-dgram", "mod-http2-constants", "dyn-eval", "keepalive-anchors"] +default = ["full", "regex-engine", "temporal", "url-engine", "string-normalize", "intl-segmenter", "intl-locale", "intl-datetime", "diagnostics", "mod-dgram", "mod-http2-constants", "dyn-eval", "keepalive-anchors", "alloc-mimalloc"] # Compile the ~490 `#[used]` keep-alive anchor statics (`KEEP_*`, `keep!`, # `K*`/`G*` fn-pointer statics) that pin codegen-facing `#[no_mangle]` entry # points as dead-strip roots. They exist for the whole-program bitcode-LTO @@ -37,6 +37,13 @@ default = ["full", "regex-engine", "temporal", "url-engine", "string-normalize", # prebuilt/full archives and `cargo test` are unchanged; the auto-optimize # rebuild omits it unless the bitcode link was requested. keepalive-anchors = [] +# Route every Rust heap allocation through mimalloc (#62) — ~3-5× faster +# per-alloc than macOS `malloc` on hot paths, at ~140 KB of binary. In +# `default` (and force-added by the auto-optimize rebuild) so behavior is +# unchanged everywhere; a size-optimized rebuild (`PERRY_SIZE_OPT=z|s`) +# leaves it off and falls back to the system allocator. The #6882 VM-tag +# retag rides the same gate (it only exists to label mimalloc's mappings). +alloc-mimalloc = ["dep:mimalloc", "dep:libmimalloc-sys"] # Per-module Node-API gate (binary-size): compiles `node:dgram`'s UDP-socket # implementation (`crate::dgram` + `crate::dgram_reactor`, ~2.2k LOC, incl. the # `js_dgram_*` externs codegen emits direct calls to) + its dispatch arm only @@ -257,7 +264,7 @@ socket2 = { version = "0.6", features = ["all"] } # a corruption suspect, so lib.rs falls back to std::alloc::System there and we # avoid compiling mimalloc's C on that target entirely. [target.'cfg(target_pointer_width = "64")'.dependencies] -mimalloc = { version = "0.1", default-features = false } +mimalloc = { version = "0.1", default-features = false, optional = true } # #6882: mimalloc tags its OS mappings with VM tag 100, which macOS tooling # (vmmap, Instruments' VM Tracker, `footprint`) decodes as `IOAccelerator` — @@ -266,7 +273,7 @@ mimalloc = { version = "0.1", default-features = false } # which lives behind libmimalloc-sys's `extended` API. Apple-only: the tag is # only consulted by Apple kernels, and mimalloc itself is 64-bit-only (above). [target.'cfg(all(target_pointer_width = "64", target_vendor = "apple"))'.dependencies] -libmimalloc-sys = { version = "0.1", features = ["extended"] } +libmimalloc-sys = { version = "0.1", features = ["extended"], optional = true } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.61", features = [ diff --git a/crates/perry-runtime/src/gc/mod.rs b/crates/perry-runtime/src/gc/mod.rs index 1b051fac94..d80e6dcd05 100644 --- a/crates/perry-runtime/src/gc/mod.rs +++ b/crates/perry-runtime/src/gc/mod.rs @@ -566,7 +566,11 @@ pub extern "C" fn js_gc_init() { // mapped before this call (early Rust startup) keep tag 100; the bulk // of the heap (arena blocks, GC metadata) maps afterwards. Idempotent, // like the rest of this function. - #[cfg(all(target_pointer_width = "64", target_vendor = "apple"))] + #[cfg(all( + target_pointer_width = "64", + target_vendor = "apple", + feature = "alloc-mimalloc" + ))] if std::env::var_os("MIMALLOC_OS_TAG").is_none() { unsafe { libmimalloc_sys::mi_option_set(libmimalloc_sys::mi_option_os_tag, 240) }; } diff --git a/crates/perry-runtime/src/gc/tests/os_tag.rs b/crates/perry-runtime/src/gc/tests/os_tag.rs index ccb892bf85..9958539bd1 100644 --- a/crates/perry-runtime/src/gc/tests/os_tag.rs +++ b/crates/perry-runtime/src/gc/tests/os_tag.rs @@ -2,7 +2,11 @@ //! macOS tooling decodes tag 100 as `IOAccelerator`, so the whole JS heap //! shows up as GPU-driver memory in vmmap/Instruments/footprint. -#[cfg(all(target_pointer_width = "64", target_vendor = "apple"))] +#[cfg(all( + target_pointer_width = "64", + target_vendor = "apple", + feature = "alloc-mimalloc" +))] #[test] fn js_gc_init_retags_mimalloc_os_mappings() { // Only asserts when the profiler override isn't steering the tag — diff --git a/crates/perry-runtime/src/lib.rs b/crates/perry-runtime/src/lib.rs index d0c6f360a1..5e0277674e 100644 --- a/crates/perry-runtime/src/lib.rs +++ b/crates/perry-runtime/src/lib.rs @@ -23,10 +23,12 @@ // arm64_32: mimalloc on a 32-bit-pointer (ILP32) tier-3 target is unproven and // a corruption suspect; use the system allocator (libsystem_malloc, solid on // watchOS) on 32-bit. Keep mimalloc's speed on 64-bit. -#[cfg(target_pointer_width = "64")] +// `alloc-mimalloc` (default-on) can be dropped by a size-optimized rebuild +// (`PERRY_SIZE_OPT`), trading the faster allocator for ~140 KB of binary. +#[cfg(all(target_pointer_width = "64", feature = "alloc-mimalloc"))] #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; -#[cfg(not(target_pointer_width = "64"))] +#[cfg(not(all(target_pointer_width = "64", feature = "alloc-mimalloc")))] #[global_allocator] static GLOBAL: std::alloc::System = std::alloc::System; diff --git a/crates/perry/src/commands/compile/optimized_libs/freshness.rs b/crates/perry/src/commands/compile/optimized_libs/freshness.rs index 68848cebf2..f5ce23a63c 100644 --- a/crates/perry/src/commands/compile/optimized_libs/freshness.rs +++ b/crates/perry/src/commands/compile/optimized_libs/freshness.rs @@ -204,6 +204,12 @@ pub(crate) fn auto_optimized_cross_features( cross_features.push("perry-runtime/regex-engine".to_string()); } } + // mimalloc global allocator (#62): keep the default-fast behavior on the + // normal auto-optimize path; only an explicit size-optimized rebuild + // (`PERRY_SIZE_OPT=z|s`) trades it for the system allocator (~140 KB). + if size_opt_level().is_none() { + cross_features.push("perry-runtime/alloc-mimalloc".to_string()); + } // The `#[used]` keep-alive anchors exist for the whole-program bitcode // LTO path only (see perry-runtime's `keepalive-anchors` feature docs). // The classic link keeps every reachable symbol via real undefined From b97db0aadda3f5279d78c15c9e7a0b8618705e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 12:19:20 +0200 Subject: [PATCH 08/12] docs+changelog: size-optimized build knobs --- changelog.d/0000-size-optimized-binaries.md | 7 +++++++ docs/src/cli/flags.md | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 changelog.d/0000-size-optimized-binaries.md diff --git a/changelog.d/0000-size-optimized-binaries.md b/changelog.d/0000-size-optimized-binaries.md new file mode 100644 index 0000000000..77df5f7ef4 --- /dev/null +++ b/changelog.d/0000-size-optimized-binaries.md @@ -0,0 +1,7 @@ +**Binary size — small hello-world binaries (5.9 MB → 2.2 MB class):** three independent levers, all default-off or behavior-preserving. + +- `PERRY_SIZE_OPT=z|s` rebuilds the auto-optimized runtime/stdlib archives at `-C opt-level=z`/`s` (cache-keyed alongside the existing feature/panic keys), and `PERRY_SIZE_LTO=fat` layers whole-archive fat LTO on top via `CARGO_PROFILE_RELEASE_LTO`. Explicitly opt-in: trades runtime speed for size. +- The ~490 `#[used]` keep-alive anchor statics in perry-runtime now sit behind a new default-on `keepalive-anchors` feature. They exist for the whole-program bitcode-LTO link (`PERRY_LLVM_BITCODE_LINK=1`), which re-enables them; the classic link keeps every reachable symbol via real undefined references, so the normal auto-optimize rebuild omits them. Measured: one anchored chain (`KEEP_JS_MODULE_DYNAMIC_IMPORT_DEFERRED` → dynamic-import fallback → `js_nm_install_all` → every dispatch bucket) was pinning the entire fs/child_process/node_vm/node_submodules surface into a `console.log` hello world. Differential probe (dynamic `import()`, `createRequire`, `getBuiltinModule`, fs sync/promises, streams, `child_process`) stays byte-identical to Node. +- The mimalloc global allocator is now the default-on `alloc-mimalloc` feature; the normal auto-optimize path force-adds it (no behavior change), while `PERRY_SIZE_OPT` rebuilds fall back to the system allocator (~140 KB). + +Also adds `PERRY_EXTRA_LINK_ARGS` (verbatim extra link args — diagnostic escape hatch, e.g. `-Wl,-why_live,_sym`). diff --git a/docs/src/cli/flags.md b/docs/src/cli/flags.md index f479228316..cc977db108 100644 --- a/docs/src/cli/flags.md +++ b/docs/src/cli/flags.md @@ -118,6 +118,22 @@ unchanged modules, leaving the trace dir empty). Minification strips comments, collapses whitespace, and mangles local variable/parameter/non-exported function names for smaller output. +### Size-optimized builds + +For the smallest possible native binaries, two opt-in environment variables +rebuild the auto-optimized runtime/stdlib archives tuned for size instead of +speed (they require a Perry workspace checkout, like the rest of +auto-optimize): + +| Variable | Description | +|----------|-------------| +| `PERRY_SIZE_OPT=z` (or `s`) | Rebuild the runtime/stdlib at `-C opt-level=z`/`s` instead of `3`, and use the system allocator instead of mimalloc. Roughly halves a small program's binary at some runtime-speed cost. Size-optimized and normal archives are cached independently. | +| `PERRY_SIZE_LTO=fat` | Additionally run whole-archive fat LTO over the rebuilt archives (slower rebuild, smaller binary). Only meaningful together with `PERRY_SIZE_OPT`. | + +A `console.log` hello world on macOS arm64: 5.9 MB default → ~2.2 MB with +`PERRY_SIZE_OPT=z PERRY_SIZE_LTO=fat`. Programs that use more of the runtime +shrink less, proportionally. + ## Testing Flags | Flag | Description | From abb96aeec133f19ff58be6edf2945c4c89d558d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 12:58:41 +0200 Subject: [PATCH 09/12] test: update keepalive-anchor audit for cfg_attr-gated form --- crates/perry-runtime/src/typed_feedback/tests.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/perry-runtime/src/typed_feedback/tests.rs b/crates/perry-runtime/src/typed_feedback/tests.rs index c9040c30e3..5892582201 100644 --- a/crates/perry-runtime/src/typed_feedback/tests.rs +++ b/crates/perry-runtime/src/typed_feedback/tests.rs @@ -995,12 +995,15 @@ fn assert_lto_keepalive_anchor(src: &str, static_name: &str, signature: &str, ta let static_pos = src .find(static_name) .unwrap_or_else(|| panic!("missing keepalive static {static_name} for {target}")); - let start = static_pos.saturating_sub(32); + // Lookback must cover the full gated attribute line above the static + // (`#[cfg_attr(feature = "keepalive-anchors", used)]` + newline). + let start = static_pos.saturating_sub(96); let end = (static_pos + 512).min(src.len()); let window = &src[start..end]; assert!( - window.contains("#[cfg_attr(feature = "keepalive-anchors", used)]"), - "keepalive static {static_name} for {target} is not #[cfg_attr(feature = "keepalive-anchors", used)]" + window.contains(r#"#[cfg_attr(feature = "keepalive-anchors", used)]"#), + "keepalive static {static_name} for {target} lacks the feature-gated #[used] \ + (cfg_attr keepalive-anchors) attribute" ); assert!( window.contains(signature), From 8248536cec19a1ceeedfa2468ddb620247413dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 13:00:28 +0200 Subject: [PATCH 10/12] changelog: name fragment after PR (#6917) --- ...size-optimized-binaries.md => 6917-size-optimized-binaries.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{0000-size-optimized-binaries.md => 6917-size-optimized-binaries.md} (100%) diff --git a/changelog.d/0000-size-optimized-binaries.md b/changelog.d/6917-size-optimized-binaries.md similarity index 100% rename from changelog.d/0000-size-optimized-binaries.md rename to changelog.d/6917-size-optimized-binaries.md From 62b32dba9d10fa7920a495fb38dab0d172f405fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 13:28:38 +0200 Subject: [PATCH 11/12] =?UTF-8?q?fix(size):=20keep=20mimalloc=20in=20size-?= =?UTF-8?q?mode=20rebuilds=20(system-alloc=20misclassifies=20heap=20bands?= =?UTF-8?q?=20=E2=80=94=20silent=20output=20loss);=20key=20PERRY=5FEXTRA?= =?UTF-8?q?=5FLINK=5FARGS=20into=20link=20cache;=20mirror=20size=20opt-lev?= =?UTF-8?q?el=20into=20bitcode=20emission;=20correct=20anchor=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/perry-runtime/src/array/iter_methods.rs | 7 ++++--- .../perry-runtime/src/child_process/validate.rs | 11 ++++++----- crates/perry-runtime/src/promise/rejection.rs | 12 ++++++------ crates/perry-runtime/src/value/dyn_index.rs | 12 +++++++----- .../src/commands/compile/link/link_cache.rs | 4 ++++ .../commands/compile/optimized_libs/driver.rs | 7 +++++++ .../commands/compile/optimized_libs/freshness.rs | 16 ++++++++++------ 7 files changed, 44 insertions(+), 25 deletions(-) diff --git a/crates/perry-runtime/src/array/iter_methods.rs b/crates/perry-runtime/src/array/iter_methods.rs index aa2c05086d..19ce7ef947 100644 --- a/crates/perry-runtime/src/array/iter_methods.rs +++ b/crates/perry-runtime/src/array/iter_methods.rs @@ -1040,9 +1040,10 @@ pub extern "C" fn js_array_join_value( // Symbol retention: codegen lowers `arr.join(sep)` to a call to // `js_array_join_value`, but its only in-crate caller sits behind a dispatch // path the auto-optimize whole-program-bitcode build can prove unreachable and -// dead-strip — which broke the default `perry file.ts -o out` link with -// `undefined _js_array_join_value`. The `#[cfg_attr(feature = "keepalive-anchors", used)]` static pins the symbol so it -// survives every link mode. Same pattern as `node_stream_keepalive.rs`. +// dead-strip — which broke that link with `undefined _js_array_join_value`. +// The feature-gated `#[used]` static pins the symbol for the bitcode-LTO +// link (`keepalive-anchors`); the classic link keeps it via the program's +// own undefined reference. Same pattern as `node_stream_keepalive.rs`. #[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_ARRAY_JOIN_VALUE: extern "C" fn( *const ArrayHeader, diff --git a/crates/perry-runtime/src/child_process/validate.rs b/crates/perry-runtime/src/child_process/validate.rs index 638ca0a06e..4c21286860 100644 --- a/crates/perry-runtime/src/child_process/validate.rs +++ b/crates/perry-runtime/src/child_process/validate.rs @@ -88,11 +88,12 @@ pub extern "C" fn js_child_process_validate_args(value: f64) -> f64 { value } -/// `#[cfg_attr(feature = "keepalive-anchors", used)]` keepalive anchors so the auto-optimize whole-program-LLVM rebuild -/// does not dead-strip these codegen-invoked `#[no_mangle]` entry points (see -/// project_auto_optimize_keepalive_3320). They are referenced only from -/// generated `.o`, so without an anchor the bitcode internalizer drops them and -/// the default `perry file.ts -o out` link fails. +/// Feature-gated (`keepalive-anchors`) `#[used]` anchors so the whole-program +/// bitcode-LTO link does not dead-strip these codegen-invoked `#[no_mangle]` +/// entry points (see project_auto_optimize_keepalive_3320). They are +/// referenced only from generated `.o`, so the bitcode internalizer would +/// otherwise drop them there; the classic link keeps them via the program's +/// own undefined references and builds without the anchors. #[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_CP_VALIDATE_COMMAND: unsafe extern "C" fn(f64, *const u8, u32) -> f64 = js_child_process_validate_command; diff --git a/crates/perry-runtime/src/promise/rejection.rs b/crates/perry-runtime/src/promise/rejection.rs index 5e13f05933..16dae3886c 100644 --- a/crates/perry-runtime/src/promise/rejection.rs +++ b/crates/perry-runtime/src/promise/rejection.rs @@ -248,12 +248,12 @@ pub extern "C" fn js_promise_report_unhandled_rejections() { process_rejections(); } -// #4876: keep the codegen-emitted hook alive through the auto-optimize -// whole-program-bitcode link. It is emitted unconditionally into `_main` but is -// reachable only from generated `.o`; without a `#[cfg_attr(feature = "keepalive-anchors", used)]` anchor the -// internalize+dead-strip pass drops it and every native link fails with -// "undefined symbol" (see the error.rs/combinators.rs anchors for the same -// pattern). +// #4876: keep the codegen-emitted hook alive through the whole-program +// bitcode-LTO link (`keepalive-anchors`). It is emitted unconditionally into +// `_main` but is reachable only from generated `.o`; the bitcode +// internalize+dead-strip pass would otherwise drop it and that link mode +// fails with "undefined symbol". The classic link needs no anchor (see the +// error.rs/combinators.rs anchors for the same pattern). #[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_PROMISE_REPORT_UNHANDLED_REJECTIONS: extern "C" fn() = js_promise_report_unhandled_rejections; diff --git a/crates/perry-runtime/src/value/dyn_index.rs b/crates/perry-runtime/src/value/dyn_index.rs index a108eb47cd..4ae5307861 100644 --- a/crates/perry-runtime/src/value/dyn_index.rs +++ b/crates/perry-runtime/src/value/dyn_index.rs @@ -591,11 +591,13 @@ pub extern "C" fn js_is_undefined_or_bare_nan(value: f64) -> i32 { // `#[no_mangle]` symbol and dead-strip it, leaving the codegen-emitted call // dangling: `Undefined symbols: _js_dyn_index_get` at final link. // -// The `#[cfg_attr(feature = "keepalive-anchors", used)]` statics below take the address of each export, creating a -// retained reference edge that LTO and the linker's `-dead_strip` must -// honor (the entries land in `@llvm.used` / a `no_dead_strip` section). This -// guarantees the symbols survive auto-optimize regardless of feature set or -// link mode. Function-pointer types are `Sync`, so no wrapper is needed. +// The feature-gated (`keepalive-anchors`) `#[used]` statics below take the +// address of each export, creating a retained reference edge that LTO and +// the linker's `-dead_strip` must honor (the entries land in `@llvm.used` / +// a `no_dead_strip` section) whenever that link mode is in play. The classic +// link keeps these exports via the program's own undefined references, so +// the anchors compile out there. Function-pointer types are `Sync`, so no +// wrapper is needed. #[cfg_attr(feature = "keepalive-anchors", used)] static KEEP_JS_DYN_INDEX_GET: extern "C" fn(f64, f64) -> f64 = js_dyn_index_get; #[cfg_attr(feature = "keepalive-anchors", used)] diff --git a/crates/perry/src/commands/compile/link/link_cache.rs b/crates/perry/src/commands/compile/link/link_cache.rs index bfa308efd9..1a11e47765 100644 --- a/crates/perry/src/commands/compile/link/link_cache.rs +++ b/crates/perry/src/commands/compile/link/link_cache.rs @@ -30,6 +30,10 @@ const LINK_ENV_VARS: &[&str] = &[ "ANDROID_NDK_HOME", "OHOS_SDK_HOME", "HARMONYOS_SDK_HOME", + // Appended verbatim to the link line after cache-status is prepared, so a + // changed value must miss the cache or diagnostic flags (`-why_live`) get + // a stale cached binary back with no linker run at all. + "PERRY_EXTRA_LINK_ARGS", ]; #[derive(Debug, Clone)] diff --git a/crates/perry/src/commands/compile/optimized_libs/driver.rs b/crates/perry/src/commands/compile/optimized_libs/driver.rs index f04b8dac7a..98ca0ac87b 100644 --- a/crates/perry/src/commands/compile/optimized_libs/driver.rs +++ b/crates/perry/src/commands/compile/optimized_libs/driver.rs @@ -962,6 +962,13 @@ pub(crate) fn build_optimized_libs( if panic_abort_safe { bc_rustflags.push_str("-C panic=abort "); } + // Mirror the size-optimized opt level into the bitcode emission so + // the merged whole-program module is built from the same tuning as + // the staticlib archives (last `-C opt-level` wins over the + // profile's, same as the archive rebuild above). + if let Some(level) = size_opt_level() { + bc_rustflags.push_str(&format!("-C opt-level={level} ")); + } bc_rustflags.push_str("-C codegen-units=1"); // #6125: the bitcode-LTO path must obey the same CPU baseline as the // staticlib build above, or the LTO'd binary re-imports the build diff --git a/crates/perry/src/commands/compile/optimized_libs/freshness.rs b/crates/perry/src/commands/compile/optimized_libs/freshness.rs index f5ce23a63c..15662efb64 100644 --- a/crates/perry/src/commands/compile/optimized_libs/freshness.rs +++ b/crates/perry/src/commands/compile/optimized_libs/freshness.rs @@ -204,12 +204,16 @@ pub(crate) fn auto_optimized_cross_features( cross_features.push("perry-runtime/regex-engine".to_string()); } } - // mimalloc global allocator (#62): keep the default-fast behavior on the - // normal auto-optimize path; only an explicit size-optimized rebuild - // (`PERRY_SIZE_OPT=z|s`) trades it for the system allocator (~140 KB). - if size_opt_level().is_none() { - cross_features.push("perry-runtime/alloc-mimalloc".to_string()); - } + // mimalloc global allocator (#62): force-added on EVERY auto-optimize + // rebuild, including size-optimized ones. Dropping it for size (~140 KB) + // produces binaries whose console output silently vanishes / that throw + // spurious TypeErrors: runtime pointer-classification paths assume the + // allocator's address bands (the macOS mimalloc heap lands in a high + // window; system-malloc allocations land elsewhere and get misread as + // handles/non-pointers). Until value/addr_class.rs is audited for + // system-allocator ranges, the feature stays force-on; the cfg gate in + // perry-runtime remains for that future audit. + cross_features.push("perry-runtime/alloc-mimalloc".to_string()); // The `#[used]` keep-alive anchors exist for the whole-program bitcode // LTO path only (see perry-runtime's `keepalive-anchors` feature docs). // The classic link keeps every reachable symbol via real undefined From 35540deeeef77e6dc46fdf38f5f006527c127676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 28 Jul 2026 13:40:25 +0200 Subject: [PATCH 12/12] docs+changelog: corrected size-mode numbers (2.4 MB, mimalloc retained) --- changelog.d/6917-size-optimized-binaries.md | 4 ++-- docs/src/cli/flags.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/changelog.d/6917-size-optimized-binaries.md b/changelog.d/6917-size-optimized-binaries.md index 77df5f7ef4..a921bf0a03 100644 --- a/changelog.d/6917-size-optimized-binaries.md +++ b/changelog.d/6917-size-optimized-binaries.md @@ -1,7 +1,7 @@ -**Binary size — small hello-world binaries (5.9 MB → 2.2 MB class):** three independent levers, all default-off or behavior-preserving. +**Binary size — small hello-world binaries (5.9 MB → 2.4 MB class):** independent levers, all default-off or behavior-preserving. - `PERRY_SIZE_OPT=z|s` rebuilds the auto-optimized runtime/stdlib archives at `-C opt-level=z`/`s` (cache-keyed alongside the existing feature/panic keys), and `PERRY_SIZE_LTO=fat` layers whole-archive fat LTO on top via `CARGO_PROFILE_RELEASE_LTO`. Explicitly opt-in: trades runtime speed for size. - The ~490 `#[used]` keep-alive anchor statics in perry-runtime now sit behind a new default-on `keepalive-anchors` feature. They exist for the whole-program bitcode-LTO link (`PERRY_LLVM_BITCODE_LINK=1`), which re-enables them; the classic link keeps every reachable symbol via real undefined references, so the normal auto-optimize rebuild omits them. Measured: one anchored chain (`KEEP_JS_MODULE_DYNAMIC_IMPORT_DEFERRED` → dynamic-import fallback → `js_nm_install_all` → every dispatch bucket) was pinning the entire fs/child_process/node_vm/node_submodules surface into a `console.log` hello world. Differential probe (dynamic `import()`, `createRequire`, `getBuiltinModule`, fs sync/promises, streams, `child_process`) stays byte-identical to Node. -- The mimalloc global allocator is now the default-on `alloc-mimalloc` feature; the normal auto-optimize path force-adds it (no behavior change), while `PERRY_SIZE_OPT` rebuilds fall back to the system allocator (~140 KB). +- The mimalloc global allocator is now the default-on `alloc-mimalloc` feature. Every auto-optimize rebuild (including size mode) force-adds it, so nothing changes at runtime; the gate exists for a future system-allocator option, which today is blocked on `value/addr_class.rs`'s heap-address window ([2 TB, 128 TB)) assuming mimalloc's mapping range — a system-malloc build misclassifies every allocation as a non-pointer (silent output loss / spurious TypeErrors). Also adds `PERRY_EXTRA_LINK_ARGS` (verbatim extra link args — diagnostic escape hatch, e.g. `-Wl,-why_live,_sym`). diff --git a/docs/src/cli/flags.md b/docs/src/cli/flags.md index cc977db108..8f79ff5d53 100644 --- a/docs/src/cli/flags.md +++ b/docs/src/cli/flags.md @@ -127,10 +127,10 @@ auto-optimize): | Variable | Description | |----------|-------------| -| `PERRY_SIZE_OPT=z` (or `s`) | Rebuild the runtime/stdlib at `-C opt-level=z`/`s` instead of `3`, and use the system allocator instead of mimalloc. Roughly halves a small program's binary at some runtime-speed cost. Size-optimized and normal archives are cached independently. | +| `PERRY_SIZE_OPT=z` (or `s`) | Rebuild the runtime/stdlib at `-C opt-level=z`/`s` instead of `3`. Roughly halves a small program's binary at some runtime-speed cost (compute-heavy inner loops can run ~2-3× slower). Size-optimized and normal archives are cached independently. | | `PERRY_SIZE_LTO=fat` | Additionally run whole-archive fat LTO over the rebuilt archives (slower rebuild, smaller binary). Only meaningful together with `PERRY_SIZE_OPT`. | -A `console.log` hello world on macOS arm64: 5.9 MB default → ~2.2 MB with +A `console.log` hello world on macOS arm64: 5.9 MB default → 2.4 MB with `PERRY_SIZE_OPT=z PERRY_SIZE_LTO=fat`. Programs that use more of the runtime shrink less, proportionally.