Skip to content

Commit 88fd2a2

Browse files
committed
hir: speculative cross-branch pure-call elimination
Recursive self-inlining leaves a redundancy dominator-CSE can't see: the depth-1 inline of fib makes the fib(n-1) and fib(n-2) expansions each call fib(n-3), but on separate base-case-guarded branches, so neither call dominates the other. pure_call_pre hoists the shared call to the nearest block dominating both sites and rewrites both results to it, collapsing the recurrence base from phi (1.618) to ~1.466 — the same transform LLVM's GVN performs, now on every backend. Because the hoist target runs on more paths than the original sites, the move is gated on speculation-safety: the callee must be pure, non-faulting (no int div/rem), loop-free, and — if recursive — structurally decreasing under a range base-guard, so it terminates on every input including the speculated ones. speculation_safe_module certifies this; anything unproven is left alone. Cranelift-tier fib: 346ms -> 10.7ms (~32x), result unchanged. All other kernels unchanged and correct. Gate off with ZYNTAX_DISABLE_PURE_CALL_PRE=1.
1 parent 9248631 commit 88fd2a2

3 files changed

Lines changed: 996 additions & 5 deletions

File tree

‎crates/compiler/src/lib.rs‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub mod memory_pass;
5858
pub mod monomorphize;
5959
pub mod optimization;
6060
pub mod pattern_matching;
61+
pub mod pure_call_pre;
6162
pub mod purity;
6263
pub mod reduction_vectorize;
6364
pub mod runtime;
@@ -1698,6 +1699,7 @@ pub struct InterpOptStats {
16981699
pub drop_insert: drop_insert::DropStats,
16991700
pub tco: tco::TcoStats,
17001701
pub recursive_inline: inline::RecursiveInlineStats,
1702+
pub pure_call_pre: pure_call_pre::PureCallPreStats,
17011703
}
17021704

17031705
/// Run the subset of HIR optimization passes that are safe for the
@@ -1964,11 +1966,16 @@ pub fn run_interp_safe_opts(module: &mut HirModule) -> InterpOptStats {
19641966
// Recursive self-inlining above can leave two inlined copies of a
19651967
// body sharing a pure sub-call — e.g. the depth-1 inline of `fib`
19661968
// makes both the `fib(n-1)` and `fib(n-2)` expansions call
1967-
// `fib(n-3)`. Re-infer purity (inlining rewrote bodies) and re-run
1968-
// CSE so any such duplicate that sits in a dominance relationship
1969-
// collapses. (The cross-branch case is handled by a dedicated pass
1970-
// added next.)
1969+
// `fib(n-3)`. Re-infer purity (inlining rewrote bodies), then:
1970+
// * `pure_call_pre` hoists the shared call across the two base-case
1971+
// branches to their common dominator (the cross-branch case
1972+
// dominator-CSE can't reach), gated on speculation-safety, and
1973+
// * `cse` collapses any remaining duplicate that already sits in a
1974+
// dominance relationship.
19711975
purity::infer_module(module);
1976+
let pcp = pure_call_pre::run_module(module);
1977+
stats.pure_call_pre.hoisted += pcp.hoisted;
1978+
stats.pure_call_pre.groups_visited += pcp.groups_visited;
19721979
let post_ri_cse = cse::eliminate_module(module);
19731980
stats.cse.eliminated += post_ri_cse.eliminated;
19741981
stats.cse.rewrites += post_ri_cse.rewrites;

0 commit comments

Comments
 (0)