Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
362 changes: 3 additions & 359 deletions .github/workflows/gc-native-roots.yml

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions changelog.d/7345-delete-statepoint-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
### Delete the explicit statepoint bridge — one native-root backend, not two

Perry carried two statepoint backends. The **explicit bridge** rewrote Perry's
own IR text into `gc.statepoint` calls with hand-emitted relocations; **RS4GC**
retypes root allocas and lets LLVM's `RewriteStatepointsForGC` insert every
statepoint and relocation itself. The bridge is gone.

They were never peers. RS4GC does strictly more: the bridge **cannot root an
`invoke`**, so since #7330 it refused try-carrying functions outright, and CI
had to skip `09_try_catch_roots` on that arm. Keeping a mode that cannot
compile what its sibling compiles — plus its textual emitter, its call parser,
and its knob — is the permanent hybrid this project keeps paying for.

The bridge was also RS4GC's fallback: a bail in the RS4GC recognizer silently
downgraded the whole function to it. **Measured before removing it: 1,574
functions across `test-drizzle-pg` (1,543) and the gc-ratchet probes (31) all
lowered as `rs4gc`, none fell back.** A fallback nothing takes is an untested
configuration, which is what the GC knob kill-policy exists to prevent — so a
bail is now a hard failure naming the function, not a silent downgrade.

Deleted with it, because only the bridge used them: the CFG-based root-liveness
analysis (RS4GC gets liveness from LLVM's SSA form), the direct-call parser and
statepoint emitter, the `PreciseRootBackend` enum, and the `PERRY_STATEPOINTS`
knob — `PERRY_RS4GC=1` is now the single switch, and one fewer GC knob is one
less kill-policy debt. `native_stack_roots_enabled()` is just `rs4gc_enabled()`.

Net **−1,216 lines**. The default shadow-stack path is untouched.

Verified: all ten gc-ratchet probes byte-match the pinned Node oracle on the
sole backend under `PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1
PERRY_STACKMAP_WALKER=verify`, the default arm is 10/10, `test-drizzle-pg`
still builds, and 593 codegen unit tests pass.
22 changes: 1 addition & 21 deletions crates/perry-codegen/src/codegen/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,26 +105,6 @@ pub(crate) fn precise_root_analysis_enabled() -> bool {
shadow_stack_enabled() || native_stack_roots_enabled()
}

/// Research-only moving-GC backend using LLVM's explicit statepoint
/// relocation sequence (`PERRY_STATEPOINTS=1`).
///
/// The standalone plain-stack-map mode (`PERRY_STACK_MAPS`) was deleted per
/// the GC knob kill-policy after the quiet-host matrix: statepoints matched
/// it within timer quantization, and it is structurally unsound — LLVM's
/// stackmap intrinsic can record a root slot's address as `Register R#N`
/// (caller-saved, unrecoverable at collection time), making those roots
/// invisible to the collector by construction. The plain-map LOWERING
/// survives only as this mode's internal fallback for `try`/setjmp
/// functions and unsupported call forms. The Register hazard exists there
/// too, which is why shrinking the fallback set is the remaining
/// correctness work for this backend, tracked in the experiment doc.
pub(crate) fn statepoints_enabled() -> bool {
matches!(
std::env::var("PERRY_STATEPOINTS").as_deref(),
Ok("1") | Ok("on") | Ok("true")
)
}

/// `PERRY_RS4GC=1` — research pipeline for #7174: root allocas become
/// `ptr addrspace(1)`, functions are tagged `gc "statepoint-example"`, and
/// each module is piped through `opt -passes='function(mem2reg),
Expand All @@ -146,7 +126,7 @@ pub(crate) fn rs4gc_enabled() -> bool {
/// Whether precise roots should use a native-stack metadata backend rather
/// than Perry's heap-backed shadow frame.
pub(crate) fn native_stack_roots_enabled() -> bool {
statepoints_enabled() || rs4gc_enabled()
rs4gc_enabled()
}

/// `PERRY_GC_SAFEPOINT_ONLY=1` — the explicit-safepoint collection contract
Expand Down
30 changes: 8 additions & 22 deletions crates/perry-codegen/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ use crate::types::LlvmType;
/// #7173 / #7174). A sibling file only because of the 2,000-line cap.
mod precise_roots;

use precise_roots::{
lower_precise_roots_to_native_stack, retype_landing_pads_for_statepoints, PreciseRootBackend,
};
use precise_roots::{lower_precise_roots_to_native_stack, retype_landing_pads_for_statepoints};

pub struct LlFunction {
pub name: String,
Expand Down Expand Up @@ -685,14 +683,12 @@ impl LlFunction {
// #7174: the `!has_try` exclusion is gone with the field. Try/catch no
// longer lowers to setjmp/longjmp (#7302), so nothing can jump past a
// `gc.relocate` any more and statepoints cover every function.
let gc_strategy = if self.stack_map_requested
&& (crate::codegen::helpers::statepoints_enabled()
|| crate::codegen::helpers::rs4gc_enabled())
{
" gc \"statepoint-example\""
} else {
""
};
let gc_strategy =
if self.stack_map_requested && crate::codegen::helpers::native_stack_roots_enabled() {
" gc \"statepoint-example\""
} else {
""
};
// Invoke-EH (#7302): functions containing landing/funclet pads name
// their personality on the define line. LLVM's grammar orders these
// `[fn attrs] [gc] [personality]`, so the strategy precedes it.
Expand Down Expand Up @@ -730,17 +726,7 @@ impl LlFunction {
// lazily-reserved scalar root and every call site is visible.
//
let ir = if self.stack_map_requested {
let backend = if crate::codegen::helpers::rs4gc_enabled() {
PreciseRootBackend::Rs4gc
} else {
// Not `StackMap`: that variant is gone. Both sites that set
// `stack_map_requested` are guarded by
// `native_stack_roots_enabled()`, which is exactly
// `statepoints_enabled() || rs4gc_enabled()`, so this branch
// is only reachable with statepoints on.
PreciseRootBackend::Statepoint
};
lower_precise_roots_to_native_stack(&ir, &self.name, self.stack_map_slot_count, backend)
lower_precise_roots_to_native_stack(&ir, &self.name, self.stack_map_slot_count)
} else {
ir
};
Expand Down
Loading
Loading