Skip to main content

ffai_core/
fastmath.rs

1//! Transcendentals without libm calls — the shared kernel every engine uses.
2//!
3//! # Why this module exists
4//!
5//! `exp`, `ln`, `tanh`, `sin`, `cos` and `powf` **have no SIMD instruction at
6//! any width**. Every call is a scalar libm call, and — worse than its own cost
7//! — it is a hard barrier to vectorising the loop it sits in. Replacing them
8//! with polynomials is the highest-yield mechanical change available in this
9//! workspace (`docs/plans/turbocharger.md`).
10//!
11//! **`sqrt`, `min`, `max`, `mul`, `add` are the opposite**: SSE2 baseline,
12//! already vectorised by the compiler. Rewriting those is how a campaign wastes
13//! a week — mp3's `xrpow` hand-AVX2 measured 0.97x and was reverted.
14//!
15//! # Why ONE module and not three
16//!
17//! Three implementations of this idea already existed, all wired to production,
18//! none aware of the others: `ffai-diana`'s `exp_fast`, `ffai-mercury`'s
19//! `fast_exp`, `ffai-argus`'s `exp_poly`. They had **drifted in the one detail
20//! that decides whether the win happens at all** (see below), and two of the
21//! three had the bug. Consolidating is the point, not a tidy-up.
22//!
23//! # ★ The rounding step is the whole trick
24//!
25//! `exp(x) = 2^(x*log2 e)` splits into an integer power (written straight into
26//! the f32 exponent field) and a fractional part (a degree-5 polynomial). That
27//! split needs a round-to-integer — and **that is where two of the three
28//! implementations reintroduced the libm call they had just removed.**
29//!
30//! Rust's `f32::round` is ties-**away-from-zero**, which no x86 instruction
31//! implements (`vroundps` is ties-to-even). So it lowers to a call, or to a
32//! long branchy sequence, sitting in the middle of the loop. `floor` is no
33//! better: it needs SSE4.1, above the portable x86-64 baseline.
34//!
35//! Adding `1.5 * 2^23` forces any value below `2^22` to round into the
36//! mantissa's last bit; subtracting it back leaves the value rounded to
37//! nearest-even. Pure float arithmetic — no call, no branch, **and it needs no
38//! `target_feature`**, so it vectorises on aarch64 (NEON is baseline) exactly
39//! as it does on x86.
40//!
41//! Measured by `ffai-diana` over 16 M elements, best of 7, single thread:
42//!
43//! | | time | rate |
44//! |---|---:|---:|
45//! | `memcpy` (the roofline) | 5.58 ms | 24.04 GB/s |
46//! | with `f32::round` | 60.84 ms | 2.21 GB/s |
47//! | with `round_ties_even` | 38.85 ms | 3.45 GB/s |
48//! | **with the magic number** | **12.91 ms** | **10.40 GB/s** |
49//!
50//! 4.71x, and **bit-identical** to the `round()` version over the activation
51//! range. A transcendental within 2.3x of pure memory traffic is a
52//! transcendental that vectorised.
53//!
54//! # Accuracy and how it is gated
55//!
56//! These are float approximations: the gate is a tolerance against libm plus
57//! the caller's own end-to-end oracle, never bit-identity against `std`. Each
58//! function documents its measured worst case. The tests here check the
59//! tolerance, the landmarks, and the shape (monotonicity where it holds,
60//! saturation, exact values at 0) — the last of which catches an approximation
61//! that is accurate on average and wrong somewhere specific.
62
63/// `1.5 * 2^23` — the round-to-nearest-even trick. See the module docs.
64const MAGIC: f32 = 12_582_912.0;
65
66/// Round to nearest even, without a libm call or an SSE4.1 instruction.
67///
68/// Valid for `|x| < 2^22`, which every use here guarantees by clamping first.
69#[inline(always)]
70#[must_use]
71pub fn round_ties_even_fast(x: f32) -> f32 {
72    (x + MAGIC) - MAGIC
73}
74
75/// `2^x`, for `x` already clamped to a sane exponent range.
76#[inline(always)]
77fn exp2_unchecked(x: f32) -> f32 {
78    let n = round_ties_even_fast(x);
79    let f = x - n; // in [-0.5, 0.5]
80    // 2^f = exp(f * ln2), so the coefficients are ln2^k / k!.
81    //
82    // DERIVED, not transcribed. Hand-typed decimals here are a real hazard:
83    // trimming one digit to satisfy a lint silently selects a different f32
84    // and breaks the oracle. Let the compiler compute them.
85    const L1: f32 = std::f32::consts::LN_2;
86    const L2: f32 = L1 * L1 / 2.0;
87    const L3: f32 = L1 * L1 * L1 / 6.0;
88    const L4: f32 = L1 * L1 * L1 * L1 / 24.0;
89    const L5: f32 = L1 * L1 * L1 * L1 * L1 / 120.0;
90    let p = 1.0 + f * (L1 + f * (L2 + f * (L3 + f * (L4 + f * L5))));
91    // 2^n straight into the exponent field.
92    // SITE-REVIEWED cast allows. This function is `exp2_unchecked`: its
93    // contract, stated above, is that the caller has ALREADY clamped `x` to a
94    // sane exponent range, and every caller does. So `n` is a small integral
95    // f32 -- the `as i32` cannot truncate anything that was there -- and
96    // `n + 127` is then in [2, 252], so the `as u32` has no sign to lose.
97    // Allowed here rather than crate-wide precisely so these two lints keep
98    // firing on code that has not been read.
99    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
100    let scale = f32::from_bits((((n as i32) + 127) as u32) << 23);
101    p * scale
102}
103
104/// `e^x`, accurate to **4.2e-6 relative** over `[-20, 20]` (measured, not
105/// claimed — the degree-5 polynomial is the limit, and the two implementations
106/// this replaced both documented ~1e-7, which was optimistic).
107///
108/// Clamped to `[-87, 88]`: outside that the f32 result is 0 or infinite
109/// anyway, and an unclamped exponent write would produce a denormal or garbage
110/// rather than a saturated value.
111#[inline(always)]
112#[must_use]
113pub fn exp(x: f32) -> f32 {
114    exp2_unchecked((x * std::f32::consts::LOG2_E).clamp(-125.0, 125.0))
115}
116
117/// `2^x`.
118#[inline(always)]
119#[must_use]
120pub fn exp2(x: f32) -> f32 {
121    exp2_unchecked(x.clamp(-125.0, 125.0))
122}
123
124/// `1 / (1 + e^-x)`.
125#[inline(always)]
126#[must_use]
127pub fn sigmoid(x: f32) -> f32 {
128    1.0 / (1.0 + exp(-x))
129}
130
131/// `tanh(x)`, via `1 - 2/(e^{2x} + 1)`.
132///
133/// Chosen over a Padé rational form deliberately. A Padé `tanh` was tried in
134/// two campaigns and failed both: it is accurate near zero and degrades to
135/// ~1.2e-3 by `|x| = 6`, while saturation is only safe from `|x| >= 7` — so
136/// **no crossover threshold exists**. In Mercury it compounded 1.8e-4 into
137/// 7e-2 over 16 coupled gates. Range reduction has no such range problem.
138#[inline(always)]
139#[must_use]
140pub fn tanh(x: f32) -> f32 {
141    // Small-|x| branch, and it is not optional. `1 - 2/(e^{2x}+1)` computes
142    // `1 - (something within an ulp of 1)` as x -> 0, so every significant
143    // digit cancels: measured **2.5e-4 relative at x = 2.4e-4**, which is a
144    // 100 % error on a value the caller thinks is exact. The Maclaurin series
145    // has no such problem there, and the crossover is where the two agree.
146    if x.abs() < 0.02 {
147        return x * (1.0 - x * x * (1.0 / 3.0));
148    }
149    1.0 - 2.0 / (exp(2.0 * x) + 1.0)
150}
151
152/// `gelu_pytorch_tanh` — `0.5x(1 + tanh(sqrt(2/pi)(x + 0.044715 x^3)))`.
153///
154/// Written as `x * sigmoid(2z)` because `tanh(z) = 2*sigmoid(2z) - 1` exactly,
155/// which saves one operation over computing the tanh and folding it back.
156#[inline(always)]
157#[must_use]
158pub fn gelu_tanh(x: f32) -> f32 {
159    const SQRT_2_OVER_PI: f32 = 0.797_884_56;
160    let z = SQRT_2_OVER_PI * x * (1.0 + 0.044_715 * x * x);
161    x / (1.0 + exp(-2.0 * z))
162}
163
164/// `SiLU`/swish — `x * sigmoid(x)`.
165#[inline(always)]
166#[must_use]
167pub fn silu(x: f32) -> f32 {
168    x / (1.0 + exp(-x))
169}
170
171/// `erf(x)`, Abramowitz & Stegun 7.1.26 — **1.6e-6 measured in f32**.
172///
173/// Needed because `gelu_erf` and `gelu_tanh` are **different functions**, not
174/// two spellings of one. They differ by up to ~1e-3, which is far above the
175/// tolerance any of these engines gate at, so a site calling `.gelu_erf()`
176/// cannot be handed the tanh form as an optimisation. Six of the seventeen
177/// activation sites in this workspace are `gelu_erf`.
178#[inline(always)]
179#[must_use]
180pub fn erf(x: f32) -> f32 {
181    const P: f32 = 0.327_591_1;
182    const A1: f32 = 0.254_829_59;
183    const A2: f32 = -0.284_496_74;
184    const A3: f32 = 1.421_413_7;
185    const A4: f32 = -1.453_152_1;
186    const A5: f32 = 1.061_405_4;
187    let sign = if x < 0.0 { -1.0 } else { 1.0 };
188    let ax = x.abs();
189    let t = 1.0 / P.mul_add(ax, 1.0);
190    let poly = t * (A1 + t * (A2 + t * (A3 + t * (A4 + t * A5))));
191    sign * (1.0 - poly * exp(-ax * ax))
192}
193
194/// `gelu` in its **exact** form — `0.5x(1 + erf(x/sqrt 2))`.
195///
196/// This is what candle's `.gelu_erf()` computes, and what `.gelu()` only
197/// approximates. Keep them apart.
198#[inline(always)]
199#[must_use]
200pub fn gelu_erf(x: f32) -> f32 {
201    const INV_SQRT_2: f32 = std::f32::consts::FRAC_1_SQRT_2;
202    0.5 * x * (1.0 + erf(x * INV_SQRT_2))
203}
204
205/// Natural log, accurate to ~1e-6 absolute over the positive range.
206///
207/// The mirror of [`exp`]: pull the exponent out of the bit pattern, and take a
208/// polynomial in the mantissa. `x <= 0` returns `-inf`/`NaN` as `f32::ln`
209/// does, so callers that clamp (every log-mel does) behave identically.
210#[inline(always)]
211#[must_use]
212#[allow(clippy::many_single_char_names)]
213// Pre-existing: `x`, `e`, `m`, `s`, `p` are the atanh series' own names.
214pub fn ln(x: f32) -> f32 {
215    if x <= 0.0 {
216        return if x == 0.0 {
217            f32::NEG_INFINITY
218        } else {
219            f32::NAN
220        };
221    }
222    let bits = x.to_bits();
223    // Exponent field, unbiased.
224    // SITE-REVIEWED. `& 0xff` bounds this at 255 before the cast, so `as i32`
225    // cannot wrap; it is an 8-bit IEEE-754 exponent field by construction.
226    #[allow(clippy::cast_possible_wrap)]
227    let e = ((bits >> 23) & 0xff) as i32 - 127;
228    // Mantissa forced into [1, 2), then centred on [-1/3, 1/3] by the
229    // `m > sqrt(2)` split so the polynomial converges fast.
230    let m = f32::from_bits((bits & 0x007f_ffff) | 0x3f80_0000);
231    let (m, e) = if m > std::f32::consts::SQRT_2 {
232        (m * 0.5, e + 1)
233    } else {
234        (m, e)
235    };
236    let s = (m - 1.0) / (m + 1.0);
237    let s2 = s * s;
238    // atanh series: ln(m) = 2s(1 + s^2/3 + s^4/5 + s^6/7 + s^8/9).
239    let p = 2.0
240        * s
241        * (1.0 + s2 * (0.333_333_34 + s2 * (0.2 + s2 * (0.142_857_15 + s2 * 0.111_111_11))));
242    (e as f32).mul_add(std::f32::consts::LN_2, p)
243}
244
245/// `log10(x)`, for log-mel and decibel work.
246#[inline(always)]
247#[must_use]
248pub fn log10(x: f32) -> f32 {
249    ln(x) * std::f32::consts::LOG10_E
250}
251
252#[cfg(test)]
253mod exp_sub_sum_tests {
254    use super::*;
255
256    /// Every vector twin against the scalar oracle, on the shape the caller
257    /// actually uses (1024-wide attention rows) plus the tail lengths that
258    /// exercise the scalar remainder in both the 8-lane and 4-lane kernels.
259    #[test]
260    fn vector_twins_match_the_scalar_oracle() {
261        for &n in &[0usize, 1, 3, 4, 7, 8, 9, 15, 16, 31, 33, 64, 1024, 1031] {
262            // Deterministic, and spanning the range attention scores occupy —
263            // including values far below the max, where exp underflows.
264            let src: Vec<f32> = (0..n)
265                .map(|i| ((i * 37 % 211) as f32 - 105.0) * 0.15)
266                .collect();
267            let max = src.iter().copied().fold(f32::NEG_INFINITY, f32::max);
268
269            let mut want = src.clone();
270            let want_sum = exp_sub_sum_scalar(&mut want, if n == 0 { 0.0 } else { max });
271            let mut got = src.clone();
272            let got_sum = exp_sub_sum_inplace(&mut got, if n == 0 { 0.0 } else { max });
273
274            for (i, (a, b)) in want.iter().zip(got.iter()).enumerate() {
275                let err = (a - b).abs() / a.abs().max(1e-30);
276                assert!(err < 1e-5, "n={n} i={i}: {a} vs {b} (rel {err:e})");
277            }
278            let serr = (want_sum - got_sum).abs() / want_sum.abs().max(1e-30);
279            // Lane splitting reassociates the sum, so this is a tolerance and
280            // not an equality — by construction, not by accident.
281            assert!(
282                serr < 1e-5,
283                "n={n} sum {want_sum} vs {got_sum} (rel {serr:e})"
284            );
285        }
286    }
287
288    /// `max_f32`'s twins are EXACT — max is associative on non-NaN floats — so
289    /// this is `assert_eq!`, not a tolerance.
290    #[test]
291    fn max_twins_are_exact() {
292        for &n in &[0usize, 1, 3, 4, 7, 8, 9, 15, 31, 33, 1024, 1031] {
293            let xs: Vec<f32> = (0..n)
294                .map(|i| ((i * 89 % 401) as f32 - 200.0) * 0.37)
295                .collect();
296            assert_eq!(max_f32_scalar(&xs), max_f32(&xs), "n={n}");
297        }
298        assert_eq!(max_f32(&[]), f32::NEG_INFINITY);
299    }
300
301    /// The GELU twins against the scalar oracle, across the range an MLP
302    /// activation actually sees plus both saturating tails.
303    #[test]
304    fn gelu_twins_match_the_scalar_oracle() {
305        for &n in &[0usize, 1, 3, 4, 7, 8, 9, 15, 33, 4096, 4099] {
306            let src: Vec<f32> = (0..n)
307                .map(|i| ((i * 53 % 601) as f32 - 300.0) * 0.09)
308                .collect();
309            let mut want = src.clone();
310            for v in &mut want {
311                *v = gelu_tanh(*v);
312            }
313            let mut got = src.clone();
314            gelu_tanh_inplace(&mut got);
315            for (i, (a, b)) in want.iter().zip(got.iter()).enumerate() {
316                let err = (a - b).abs() / a.abs().max(1e-6);
317                assert!(
318                    err < 1e-5,
319                    "n={n} i={i} x={}: {a} vs {b} (rel {err:e})",
320                    src[i]
321                );
322            }
323        }
324    }
325
326    /// A softmax built on it sums to 1 — the property the caller depends on.
327    #[test]
328    fn normalises_to_one() {
329        let mut row: Vec<f32> = (0..1024).map(|i| ((i % 97) as f32) * 0.11 - 5.0).collect();
330        let max = row.iter().copied().fold(f32::NEG_INFINITY, f32::max);
331        let sum = exp_sub_sum_inplace(&mut row, max);
332        let total: f32 = row.iter().map(|v| v / sum).sum();
333        assert!((total - 1.0).abs() < 1e-4, "sums to {total}");
334    }
335}
336
337#[cfg(test)]
338mod tests {
339    use super::*;
340
341    /// Worst relative error of `f` against `oracle` over a dense sweep.
342    fn sweep(lo: f32, hi: f32, f: impl Fn(f32) -> f32, oracle: impl Fn(f32) -> f32) -> (f32, f32) {
343        let n = 200_001;
344        let mut worst_rel = 0.0f32;
345        let mut at = lo;
346        for i in 0..n {
347            let x = lo + (hi - lo) * (i as f32 / (n - 1) as f32);
348            let (a, b) = (f(x), oracle(x));
349            // A point passes if EITHER the relative or the absolute error is
350            // small, and that is not a loosened gate — it is the only honest
351            // one across this dynamic range.
352            //
353            // The oracle runs out of precision before we do. GELU at
354            // x = -4.744 is ~1.2e-6, and the reference computes it as
355            // `0.5*x*(1 + tanh z)` where `1 + tanh z` cancels down to a few
356            // f32 epsilons: the ORACLE's own uncertainty there is ~23 %
357            // relative. Judging us by relative error against it would be
358            // measuring its cancellation, not our accuracy — our absolute
359            // error at that point is 7.6e-8. Further out, `f32::tanh` returns
360            // exactly -1.0 and the oracle produces a hard zero.
361            let abs = (a - b).abs();
362            let rel = if b.abs() > f32::MIN_POSITIVE {
363                (abs / b.abs()).min(abs)
364            } else {
365                abs
366            };
367            if rel > worst_rel {
368                worst_rel = rel;
369                at = x;
370            }
371        }
372        (worst_rel, at)
373    }
374
375    #[test]
376    fn exp_tracks_libm() {
377        let (rel, at) = sweep(-20.0, 20.0, exp, f32::exp);
378        eprintln!("exp: worst rel {rel:.3e} at x = {at}");
379        assert!(rel < 1e-5, "exp worst rel {rel:.3e} at {at}");
380    }
381
382    #[test]
383    fn exp_saturates_rather_than_producing_garbage() {
384        // The clamp is load-bearing: an unclamped exponent write produces a
385        // denormal or a wrapped exponent, which is a plausible-looking wrong
386        // number rather than an infinity anyone would notice.
387        assert!(exp(200.0).is_finite(), "exp(200) must saturate, not wrap");
388        assert!(exp(200.0) > 1e30, "exp(200) should be very large");
389        // NOT zero: the clamp is in log2 space, so exp(-200) saturates to
390        // 2^-125 ~= 2.4e-38. Tiny and finite is the correct behaviour — the
391        // thing the clamp exists to prevent is a WRAPPED exponent field, which
392        // would be a large plausible number instead.
393        assert!(
394            exp(-200.0) < 1e-30 && exp(-200.0) >= 0.0,
395            "exp(-200) = {} should saturate tiny, not wrap",
396            exp(-200.0)
397        );
398        assert!((exp(0.0) - 1.0).abs() < 1e-7, "exp(0) = {}", exp(0.0));
399    }
400
401    #[test]
402    fn the_magic_rounding_is_round_ties_even() {
403        // The property the whole module depends on. Ties go to EVEN, unlike
404        // `f32::round` which goes away from zero — that difference is the
405        // reason this exists.
406        for (x, want) in [
407            (0.5f32, 0.0f32),
408            (1.5, 2.0),
409            (2.5, 2.0),
410            (-0.5, -0.0),
411            (-1.5, -2.0),
412            (-2.5, -2.0),
413            (3.2, 3.0),
414            (-3.7, -4.0),
415        ] {
416            let got = round_ties_even_fast(x);
417            assert_eq!(got, want, "round_ties_even_fast({x}) = {got}, want {want}");
418        }
419        // And it agrees with std's ties-even over a range.
420        for i in -1000..1000 {
421            let x = i as f32 / 7.0;
422            assert_eq!(round_ties_even_fast(x), x.round_ties_even(), "at {x}");
423        }
424    }
425
426    #[test]
427    fn tanh_tracks_libm_and_saturates() {
428        let (rel, at) = sweep(-8.0, 8.0, tanh, f32::tanh);
429        eprintln!("tanh: worst rel {rel:.3e} at x = {at}");
430        assert!(rel < 1e-5, "tanh worst rel {rel:.3e} at {at}");
431        assert!((tanh(0.0)).abs() < 1e-7);
432        assert!((tanh(20.0) - 1.0).abs() < 1e-6);
433        assert!((tanh(-20.0) + 1.0).abs() < 1e-6);
434    }
435
436    #[test]
437    fn sigmoid_and_silu_track_libm() {
438        let (rel, at) = sweep(-15.0, 15.0, sigmoid, |x| 1.0 / (1.0 + (-x).exp()));
439        assert!(rel < 1e-5, "sigmoid worst rel {rel:.3e} at {at}");
440        let (rel, at) = sweep(-15.0, 15.0, silu, |x| x / (1.0 + (-x).exp()));
441        assert!(rel < 1e-5, "silu worst rel {rel:.3e} at {at}");
442        assert_eq!(silu(0.0), 0.0, "silu(0) must be exactly 0");
443    }
444
445    #[test]
446    fn gelu_tracks_libm_and_keeps_its_dip() {
447        let oracle =
448            |x: f32| 0.5 * x * (1.0 + (0.797_884_56 * x * (1.0 + 0.044_715 * x * x)).tanh());
449        let (rel, at) = sweep(-10.0, 10.0, gelu_tanh, oracle);
450        eprintln!("gelu: worst rel {rel:.3e} at x = {at}");
451        assert!(rel < 1e-5, "gelu worst rel {rel:.3e} at {at}");
452        // GELU is NOT monotone — it dips to about -0.17 near x = -0.75, and an
453        // approximation that smooths that away is wrong in the one place the
454        // curve has any shape.
455        assert!(
456            (-0.18..-0.15).contains(&gelu_tanh(-0.75)),
457            "gelu(-0.75) = {} should sit near the -0.17 minimum",
458            gelu_tanh(-0.75)
459        );
460        assert_eq!(gelu_tanh(0.0), 0.0, "gelu(0) must be exactly 0");
461        assert!((gelu_tanh(10.0) - 10.0).abs() < 1e-4);
462        assert!(gelu_tanh(-10.0).abs() < 1e-5);
463    }
464
465    #[test]
466    fn erf_and_gelu_erf_track_a_reference() {
467        // erf has no `std` implementation, so the oracle is a high-order
468        // series evaluated in f64 — an independent route to the same value
469        // rather than a rearrangement of the same approximation.
470        let oracle = |x: f32| -> f32 {
471            let x = f64::from(x);
472            // Taylor out to |x| = 4, where `1 - erf(4) = 1.5e-8` is already
473            // below f32 epsilon at 1.0 — so saturating past it costs nothing.
474            //
475            // The first version cut over at 3.0 and FAILED, reporting a
476            // 2.2e-5 error that was exactly `1 - erf(3)`: the oracle was
477            // returning a hard 1.0 where the true value still had digits. The
478            // magnitude naming the cutoff is what identified it as the
479            // oracle's fault rather than the kernel's.
480            if x.abs() < 4.0 {
481                let mut term = x;
482                let mut sum = x;
483                for n in 1..90 {
484                    term *= -x * x / f64::from(n);
485                    sum += term / f64::from(2 * n + 1);
486                }
487                (sum * 2.0 / std::f64::consts::PI.sqrt()) as f32
488            } else if x > 0.0 {
489                1.0
490            } else {
491                -1.0
492            }
493        };
494        let (rel, at) = sweep(-3.0, 3.0, erf, oracle);
495        eprintln!("erf: worst {rel:.3e} at x = {at}");
496        // 1.6e-6 measured. A&S 7.1.26 claims 1.5e-7 ABSOLUTE for the formula;
497        // the extra comes from evaluating it in f32, where `1 - poly*exp` is a
498        // cancellation that amplifies the rounding. Not a coefficient error —
499        // the worst point is mid-range, not at a cutoff.
500        assert!(rel < 5e-6, "erf worst {rel:.3e} at {at}");
501        // NOT exactly zero, and forcing it would cost a branch on a hot path
502        // for no benefit. A&S 7.1.26's five coefficients sum to 0.999999999,
503        // not 1, so `1 - poly*exp(0)` leaves ~1.8e-7 — inside the 1.6e-6 the
504        // formula is good for anyway.
505        assert!(erf(0.0).abs() < 3e-7, "erf(0) = {}", erf(0.0));
506        // gelu_erf(0) IS exact, structurally: x multiplies the whole thing.
507        assert_eq!(gelu_erf(0.0), 0.0, "gelu_erf(0) must be exactly 0");
508        assert!((erf(4.0) - 1.0).abs() < 1e-6);
509        assert!((erf(-4.0) + 1.0).abs() < 1e-6);
510
511        // gelu_erf and gelu_tanh must NOT be interchangeable, and the test
512        // says so — if they ever agree to 1e-5 someone has quietly aliased one
513        // to the other.
514        let worst = (-40..40)
515            .map(|i| {
516                let x = i as f32 / 10.0;
517                (gelu_erf(x) - gelu_tanh(x)).abs()
518            })
519            .fold(0.0f32, f32::max);
520        assert!(
521            worst > 1e-4,
522            "gelu_erf and gelu_tanh differ by only {worst:.3e} — are they aliased?"
523        );
524    }
525
526    #[test]
527    fn ln_and_log10_track_libm() {
528        let (rel, at) = sweep(1e-6, 1e6, ln, f32::ln);
529        eprintln!("ln: worst rel {rel:.3e} at x = {at}");
530        assert!(rel < 1e-5, "ln worst rel {rel:.3e} at {at}");
531        let (rel, at) = sweep(1e-6, 1e6, log10, f32::log10);
532        assert!(rel < 1e-5, "log10 worst rel {rel:.3e} at {at}");
533        // The landmarks a log-mel actually hits.
534        assert!((log10(1.0)).abs() < 1e-6, "log10(1) = {}", log10(1.0));
535        assert!((log10(10.0) - 1.0).abs() < 1e-5);
536        assert!((log10(1e-10) + 10.0).abs() < 1e-4);
537    }
538
539    #[test]
540    fn ln_matches_std_at_the_edges() {
541        assert_eq!(ln(0.0), f32::NEG_INFINITY);
542        assert!(ln(-1.0).is_nan());
543        assert!((ln(std::f32::consts::E) - 1.0).abs() < 1e-6);
544    }
545
546    #[test]
547    fn exp_and_ln_round_trip() {
548        // A property neither sweep catches on its own: the two must be
549        // inverses, so an error in one that happens to cancel in a sweep
550        // against the same libm still shows up here.
551        for i in -60..60 {
552            let x = i as f32 / 6.0;
553            let back = ln(exp(x));
554            assert!((back - x).abs() < 1e-4, "ln(exp({x})) = {back}");
555        }
556    }
557}
558
559// ---------------------------------------------------------------------------
560// Vectorised softmax inner loop
561// ---------------------------------------------------------------------------
562
563/// `row[i] = exp(row[i] - max)`, returning the sum — the softmax inner loop.
564///
565/// # Why this exists as one function rather than `map` + `sum`
566///
567/// Measured on Argus's `SoftmaxExpInplace`: 151 M elements per caption at
568/// **228 M elem/s**, 22.7 % of the whole vision tower and 2.7x the `q.k^T`
569/// matmul that produces its input. An elementwise pass beating the O(n^3)
570/// matmul that feeds it is the tell.
571///
572/// Two costs, and neither yields to a scalar rewrite:
573///
574/// * `(x - max).exp()` is a libm `expf` CALL per element, ~75 M/s.
575/// * `sum += e` is a loop-carried dependency on a non-associative add, so LLVM
576///   will not lane the loop — and a scalar polynomial `exp` with eight split
577///   accumulators was **measured slower** than libm here (2410 ms vs 2090 ms),
578///   because it removes the call but still will not vectorise.
579///
580/// So the fix has to be explicit lanes, which is what this is. The scalar body
581/// below stays as the oracle and as the fallback for targets with neither ISA.
582///
583/// Accuracy: `exp` here is the degree-5 `exp2` polynomial, ~4.2e-6 relative.
584/// Lane splitting reassociates the sum, so it differs from a sequential fold in
585/// the last bits — gate on tolerance, never bit-identity.
586#[must_use]
587pub fn exp_sub_sum_inplace(row: &mut [f32], max: f32) -> f32 {
588    #[cfg(target_arch = "x86_64")]
589    {
590        if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
591            // SAFETY: both features probed immediately above.
592            return unsafe { exp_sub_sum_avx2(row, max) };
593        }
594    }
595    #[cfg(target_arch = "aarch64")]
596    {
597        // Unconditional, like the wasm arm and unlike x86's runtime probe:
598        // NEON is BASELINE on aarch64, so there is nothing to detect. That is
599        // also why this arm is easy to forget -- the scalar body below already
600        // auto-vectorises for the ELEMENTWISE primitives, so the gap only bites
601        // the two REDUCTIONS, where a loop-carried non-associative accumulator
602        // stops LLVM cold no matter what the baseline is.
603        //
604        // SAFETY: `neon` is a compile-time guarantee on this target.
605        return unsafe { exp_sub_sum_neon(row, max) };
606    }
607    #[cfg(target_arch = "wasm32")]
608    {
609        // Unconditional, unlike the x86 arm's runtime check: wasm validates a
610        // whole module ahead of time, so a v128 instruction anywhere makes the
611        // MODULE require SIMD. There is no `is_wasm_feature_detected!` and no
612        // per-call dispatch. The workspace already sets `+simd128` for this
613        // target in `.cargo/config.toml`, which makes SIMD a baseline
614        // requirement rather than an upgrade.
615        //
616        // SAFETY: `simd128` is a compile-time guarantee on this target.
617        return unsafe { exp_sub_sum_simd128(row, max) };
618    }
619    #[allow(unreachable_code)]
620    exp_sub_sum_scalar(row, max)
621}
622
623/// The oracle. Every vector twin is gated against this.
624#[must_use]
625pub fn exp_sub_sum_scalar(row: &mut [f32], max: f32) -> f32 {
626    let mut sum = 0.0f32;
627    for v in row.iter_mut() {
628        let e = exp(*v - max);
629        *v = e;
630        sum += e;
631    }
632    sum
633}
634
635/// AVX2 + FMA, eight lanes.
636///
637/// # Safety
638/// Caller must have verified `avx2` and `fma`.
639#[allow(clippy::many_single_char_names)]
640// The names are the polynomial's own: `n`, `i`, `x`, `t`, `r`, `f`, `p`, `e`,
641// `k`, `z`. Expanding them into prose makes the kernel harder to read against
642// its twins and the scalar oracle, which is the only way this code is verified.
643#[allow(clippy::wildcard_imports)]
644// A SIMD kernel names 15 intrinsics; importing them one by one is a list that
645// goes stale the moment the arithmetic changes, and hides nothing.
646#[cfg(target_arch = "x86_64")]
647#[target_feature(enable = "avx2,fma")]
648unsafe fn exp_sub_sum_avx2(row: &mut [f32], max: f32) -> f32 {
649    // SAFETY: whole-body wrapper inserted by the edition-2024
650    // `unsafe_op_in_unsafe_fn` migration. This block adds no new obligation:
651    // the contract is stated on the `unsafe fn` signature above.
652    unsafe {
653        use std::arch::x86_64::*;
654        const L1: f32 = std::f32::consts::LN_2;
655        const L2: f32 = L1 * L1 / 2.0;
656        const L3: f32 = L1 * L1 * L1 / 6.0;
657        const L4: f32 = L1 * L1 * L1 * L1 / 24.0;
658        const L5: f32 = L1 * L1 * L1 * L1 * L1 / 120.0;
659
660        let vmax = _mm256_set1_ps(max);
661        let log2e = _mm256_set1_ps(std::f32::consts::LOG2_E);
662        let magic = _mm256_set1_ps(MAGIC);
663        let lo = _mm256_set1_ps(-125.0);
664        let hi = _mm256_set1_ps(125.0);
665        let c1 = _mm256_set1_ps(L1);
666        let c2 = _mm256_set1_ps(L2);
667        let c3 = _mm256_set1_ps(L3);
668        let c4 = _mm256_set1_ps(L4);
669        let c5 = _mm256_set1_ps(L5);
670        let one = _mm256_set1_ps(1.0);
671        let bias = _mm256_set1_epi32(127);
672
673        let mut acc = _mm256_setzero_ps();
674        let n = row.len();
675        let mut i = 0;
676        while i + 8 <= n {
677            let x = _mm256_loadu_ps(row.as_ptr().add(i));
678            // exp(x - max) == exp2((x - max) * log2(e)), clamped like the scalar.
679            let t = _mm256_mul_ps(_mm256_sub_ps(x, vmax), log2e);
680            let t = _mm256_min_ps(_mm256_max_ps(t, lo), hi);
681            // round-to-nearest-even without an SSE4.1 instruction: (t+M)-M.
682            let r = _mm256_sub_ps(_mm256_add_ps(t, magic), magic);
683            let f = _mm256_sub_ps(t, r);
684            // Horner, FMA-fused.
685            let p = _mm256_fmadd_ps(f, c5, c4);
686            let p = _mm256_fmadd_ps(f, p, c3);
687            let p = _mm256_fmadd_ps(f, p, c2);
688            let p = _mm256_fmadd_ps(f, p, c1);
689            let p = _mm256_fmadd_ps(f, p, one);
690            // 2^r straight into the exponent field.
691            let e = _mm256_cvtps_epi32(r);
692            let e = _mm256_slli_epi32::<23>(_mm256_add_epi32(e, bias));
693            let out = _mm256_mul_ps(p, _mm256_castsi256_ps(e));
694            _mm256_storeu_ps(row.as_mut_ptr().add(i), out);
695            acc = _mm256_add_ps(acc, out);
696            i += 8;
697        }
698        // Horizontal reduce, then the scalar tail through the SAME polynomial.
699        let mut lanes = [0f32; 8];
700        _mm256_storeu_ps(lanes.as_mut_ptr(), acc);
701        let mut sum = ((lanes[0] + lanes[1]) + (lanes[2] + lanes[3]))
702            + ((lanes[4] + lanes[5]) + (lanes[6] + lanes[7]));
703        while i < n {
704            let e = exp(row[i] - max);
705            row[i] = e;
706            sum += e;
707            i += 1;
708        }
709        sum
710    }
711}
712
713/// NEON, four lanes, FMA-fused.
714///
715/// **Why this exists even though NEON is baseline.** The scalar oracle below
716/// auto-vectorises on aarch64 for elementwise work, so the obvious reading is
717/// that this arm is redundant. It is not: `sum += e` is a loop-carried
718/// dependency on a NON-ASSOCIATIVE add, and LLVM may not reassociate it
719/// without fast-math. So the oracle stays a scalar dependency chain on every
720/// target regardless of the baseline, and only explicit lanes break it. The
721/// same argument covers [`max_f32_neon`]; it does NOT cover
722/// [`gelu_tanh_neon`], which is elementwise and would likely have vectorised
723/// unaided -- that one is here for symmetry and to pin the numerics.
724///
725/// Mirrors the AVX2 arm rather than the wasm one, because NEON HAS an FMA
726/// (`vfmaq_f32`) and wasm's base SIMD does not.
727///
728/// # Safety
729/// `neon` is a compile-time guarantee on `aarch64` here.
730#[allow(clippy::many_single_char_names)]
731// The names are the polynomial's own: `n`, `i`, `x`, `t`, `r`, `f`, `p`, `e`,
732// `k`, `z`. Expanding them into prose makes the kernel harder to read against
733// its twins and the scalar oracle, which is the only way this code is verified.
734#[allow(clippy::wildcard_imports)]
735// A SIMD kernel names 15 intrinsics; importing them one by one is a list that
736// goes stale the moment the arithmetic changes, and hides nothing.
737#[cfg(target_arch = "aarch64")]
738#[target_feature(enable = "neon")]
739unsafe fn exp_sub_sum_neon(row: &mut [f32], max: f32) -> f32 {
740    // SAFETY: whole-body wrapper inserted by the edition-2024
741    // `unsafe_op_in_unsafe_fn` migration. This block adds no new obligation:
742    // the contract is stated on the `unsafe fn` signature above.
743    unsafe {
744        use core::arch::aarch64::*;
745        const L1: f32 = core::f32::consts::LN_2;
746        const L2: f32 = L1 * L1 / 2.0;
747        const L3: f32 = L1 * L1 * L1 / 6.0;
748        const L4: f32 = L1 * L1 * L1 * L1 / 24.0;
749        const L5: f32 = L1 * L1 * L1 * L1 * L1 / 120.0;
750
751        let vmax = vdupq_n_f32(max);
752        let log2e = vdupq_n_f32(core::f32::consts::LOG2_E);
753        let magic = vdupq_n_f32(MAGIC);
754        let lo = vdupq_n_f32(-125.0);
755        let hi = vdupq_n_f32(125.0);
756        let c1 = vdupq_n_f32(L1);
757        let c2 = vdupq_n_f32(L2);
758        let c3 = vdupq_n_f32(L3);
759        let c4 = vdupq_n_f32(L4);
760        let c5 = vdupq_n_f32(L5);
761        let one = vdupq_n_f32(1.0);
762        let bias = vdupq_n_s32(127);
763
764        let mut acc = vdupq_n_f32(0.0);
765        let n = row.len();
766        let mut i = 0;
767        while i + 4 <= n {
768            let x = vld1q_f32(row.as_ptr().add(i));
769            let t = vmulq_f32(vsubq_f32(x, vmax), log2e);
770            let t = vminq_f32(vmaxq_f32(t, lo), hi);
771            // Round-to-nearest-even via the magic constant, exactly as the other
772            // twins do -- NEON has `vrndnq_f32`, but using it here would make this
773            // arm disagree with them in the last bit at the ties.
774            let r = vsubq_f32(vaddq_f32(t, magic), magic);
775            let f = vsubq_f32(t, r);
776            // `vfmaq_f32(a, b, c)` is `a + b * c`.
777            let p = vfmaq_f32(c4, f, c5);
778            let p = vfmaq_f32(c3, f, p);
779            let p = vfmaq_f32(c2, f, p);
780            let p = vfmaq_f32(c1, f, p);
781            let p = vfmaq_f32(one, f, p);
782            let e = vshlq_n_s32::<23>(vaddq_s32(vcvtq_s32_f32(r), bias));
783            let out = vmulq_f32(p, vreinterpretq_f32_s32(e));
784            vst1q_f32(row.as_mut_ptr().add(i), out);
785            acc = vaddq_f32(acc, out);
786            i += 4;
787        }
788        // Same pairwise order as the wasm twin, so the two four-lane arms agree.
789        let mut sum = (vgetq_lane_f32::<0>(acc) + vgetq_lane_f32::<1>(acc))
790            + (vgetq_lane_f32::<2>(acc) + vgetq_lane_f32::<3>(acc));
791        while i < n {
792            let e = exp(row[i] - max);
793            row[i] = e;
794            sum += e;
795            i += 1;
796        }
797        sum
798    }
799}
800
801/// wasm SIMD128, four lanes.
802///
803/// **No FMA.** Base wasm SIMD has no fused multiply-add, so each Horner step is
804/// a separate multiply and add — which is half of why this target runs ~6.5x
805/// behind native single-thread even with SIMD on.
806///
807/// # Safety
808/// `simd128` is a compile-time guarantee on `wasm32` here.
809#[allow(clippy::many_single_char_names)]
810// The names are the polynomial's own: `n`, `i`, `x`, `t`, `r`, `f`, `p`, `e`,
811// `k`, `z`. Expanding them into prose makes the kernel harder to read against
812// its twins and the scalar oracle, which is the only way this code is verified.
813#[allow(clippy::wildcard_imports)]
814// A SIMD kernel names 15 intrinsics; importing them one by one is a list that
815// goes stale the moment the arithmetic changes, and hides nothing.
816#[cfg(target_arch = "wasm32")]
817#[target_feature(enable = "simd128")]
818unsafe fn exp_sub_sum_simd128(row: &mut [f32], max: f32) -> f32 {
819    // SAFETY: whole-body wrapper inserted by the edition-2024
820    // `unsafe_op_in_unsafe_fn` migration. This block adds no new obligation:
821    // the contract is stated on the `unsafe fn` signature above.
822    unsafe {
823        use core::arch::wasm32::*;
824        const L1: f32 = core::f32::consts::LN_2;
825        const L2: f32 = L1 * L1 / 2.0;
826        const L3: f32 = L1 * L1 * L1 / 6.0;
827        const L4: f32 = L1 * L1 * L1 * L1 / 24.0;
828        const L5: f32 = L1 * L1 * L1 * L1 * L1 / 120.0;
829
830        let vmax = f32x4_splat(max);
831        let log2e = f32x4_splat(core::f32::consts::LOG2_E);
832        let magic = f32x4_splat(MAGIC);
833        let lo = f32x4_splat(-125.0);
834        let hi = f32x4_splat(125.0);
835        let c1 = f32x4_splat(L1);
836        let c2 = f32x4_splat(L2);
837        let c3 = f32x4_splat(L3);
838        let c4 = f32x4_splat(L4);
839        let c5 = f32x4_splat(L5);
840        let one = f32x4_splat(1.0);
841        let bias = i32x4_splat(127);
842
843        let mut acc = f32x4_splat(0.0);
844        let n = row.len();
845        let mut i = 0;
846        while i + 4 <= n {
847            let x = v128_load(row.as_ptr().add(i).cast());
848            let t = f32x4_mul(f32x4_sub(x, vmax), log2e);
849            let t = f32x4_pmin(hi, f32x4_pmax(lo, t));
850            let r = f32x4_sub(f32x4_add(t, magic), magic);
851            let f = f32x4_sub(t, r);
852            let p = f32x4_add(f32x4_mul(f, c5), c4);
853            let p = f32x4_add(f32x4_mul(f, p), c3);
854            let p = f32x4_add(f32x4_mul(f, p), c2);
855            let p = f32x4_add(f32x4_mul(f, p), c1);
856            let p = f32x4_add(f32x4_mul(f, p), one);
857            let e = i32x4_trunc_sat_f32x4(r);
858            let e = i32x4_shl(i32x4_add(e, bias), 23);
859            let out = f32x4_mul(p, e);
860            v128_store(row.as_mut_ptr().add(i).cast(), out);
861            acc = f32x4_add(acc, out);
862            i += 4;
863        }
864        let mut sum = (f32x4_extract_lane::<0>(acc) + f32x4_extract_lane::<1>(acc))
865            + (f32x4_extract_lane::<2>(acc) + f32x4_extract_lane::<3>(acc));
866        while i < n {
867            let e = exp(row[i] - max);
868            row[i] = e;
869            sum += e;
870            i += 1;
871        }
872        sum
873    }
874}
875
876/// Maximum of a slice — vectorised.
877///
878/// The other half of the softmax row. `for &v in row { max = max.max(v) }` is a
879/// loop-carried reduction on a function with NaN semantics, so LLVM will not
880/// lane it any more than it lanes the sum. It is a full pass over the same
881/// 50 MB-per-layer score tensor that `exp_sub_sum_inplace` then walks again.
882///
883/// Returns `f32::NEG_INFINITY` for an empty slice, matching a fold from that
884/// identity. Lane-splitting a MAX is exact — `max` is associative and
885/// commutative on non-NaN floats — so unlike the sum this twin is gated by
886/// `assert_eq!`, not by tolerance.
887#[must_use]
888pub fn max_f32(xs: &[f32]) -> f32 {
889    #[cfg(target_arch = "x86_64")]
890    {
891        if is_x86_feature_detected!("avx2") {
892            // SAFETY: probed immediately above.
893            return unsafe { max_f32_avx2(xs) };
894        }
895    }
896    #[cfg(target_arch = "aarch64")]
897    {
898        // SAFETY: `neon` is a compile-time guarantee on this target.
899        return unsafe { max_f32_neon(xs) };
900    }
901    #[cfg(target_arch = "wasm32")]
902    {
903        // SAFETY: `simd128` is a compile-time guarantee on this target.
904        return unsafe { max_f32_simd128(xs) };
905    }
906    #[allow(unreachable_code)]
907    max_f32_scalar(xs)
908}
909
910/// The oracle.
911#[must_use]
912pub fn max_f32_scalar(xs: &[f32]) -> f32 {
913    let mut m = f32::NEG_INFINITY;
914    for &v in xs {
915        m = m.max(v);
916    }
917    m
918}
919
920/// # Safety
921/// Caller must have verified `avx2`.
922#[cfg(target_arch = "x86_64")]
923#[target_feature(enable = "avx2")]
924unsafe fn max_f32_avx2(xs: &[f32]) -> f32 {
925    // SAFETY: whole-body wrapper inserted by the edition-2024
926    // `unsafe_op_in_unsafe_fn` migration. This block adds no new obligation:
927    // the contract is stated on the `unsafe fn` signature above.
928    unsafe {
929        use std::arch::x86_64::{_mm256_loadu_ps, _mm256_max_ps, _mm256_storeu_ps};
930        let n = xs.len();
931        if n < 8 {
932            return max_f32_scalar(xs);
933        }
934        let mut acc = _mm256_loadu_ps(xs.as_ptr());
935        let mut i = 8;
936        while i + 8 <= n {
937            acc = _mm256_max_ps(acc, _mm256_loadu_ps(xs.as_ptr().add(i)));
938            i += 8;
939        }
940        let mut lanes = [0f32; 8];
941        _mm256_storeu_ps(lanes.as_mut_ptr(), acc);
942        let mut m = lanes[0];
943        for &v in &lanes[1..] {
944            m = m.max(v);
945        }
946        while i < n {
947            m = m.max(xs[i]);
948            i += 1;
949        }
950        m
951    }
952}
953
954/// NEON, four lanes. Exact, like the other max twins.
955///
956/// # Safety
957/// `neon` is a compile-time guarantee on `aarch64` here.
958#[cfg(target_arch = "aarch64")]
959#[target_feature(enable = "neon")]
960unsafe fn max_f32_neon(xs: &[f32]) -> f32 {
961    // SAFETY: whole-body wrapper inserted by the edition-2024
962    // `unsafe_op_in_unsafe_fn` migration. This block adds no new obligation:
963    // the contract is stated on the `unsafe fn` signature above.
964    unsafe {
965        use core::arch::aarch64::{vld1q_f32, vmaxq_f32, vmaxvq_f32};
966        let n = xs.len();
967        if n < 4 {
968            return max_f32_scalar(xs);
969        }
970        let mut acc = vld1q_f32(xs.as_ptr());
971        let mut i = 4;
972        while i + 4 <= n {
973            acc = vmaxq_f32(acc, vld1q_f32(xs.as_ptr().add(i)));
974            i += 4;
975        }
976        let mut m = vmaxvq_f32(acc);
977        while i < n {
978            m = m.max(xs[i]);
979            i += 1;
980        }
981        m
982    }
983}
984
985/// # Safety
986/// `simd128` is a compile-time guarantee on `wasm32` here.
987#[cfg(target_arch = "wasm32")]
988#[target_feature(enable = "simd128")]
989unsafe fn max_f32_simd128(xs: &[f32]) -> f32 {
990    // SAFETY: whole-body wrapper inserted by the edition-2024
991    // `unsafe_op_in_unsafe_fn` migration. This block adds no new obligation:
992    // the contract is stated on the `unsafe fn` signature above.
993    unsafe {
994        use core::arch::wasm32::{f32x4_extract_lane, f32x4_pmax, v128_load};
995        let n = xs.len();
996        if n < 4 {
997            return max_f32_scalar(xs);
998        }
999        // `f32x4_pmax` is the raw max: it returns the second operand when either is
1000        // NaN, which matches nothing in particular — but these are attention
1001        // scores, never NaN, and the scalar oracle gate covers the range we feed.
1002        let mut acc = v128_load(xs.as_ptr().cast());
1003        let mut i = 4;
1004        while i + 4 <= n {
1005            acc = f32x4_pmax(acc, v128_load(xs.as_ptr().add(i).cast()));
1006            i += 4;
1007        }
1008        let mut m = f32x4_extract_lane::<0>(acc);
1009        m = m.max(f32x4_extract_lane::<1>(acc));
1010        m = m.max(f32x4_extract_lane::<2>(acc));
1011        m = m.max(f32x4_extract_lane::<3>(acc));
1012        while i < n {
1013            m = m.max(xs[i]);
1014            i += 1;
1015        }
1016        m
1017    }
1018}
1019
1020/// `xs[i] = gelu_tanh(xs[i])`, vectorised.
1021///
1022/// The activation between a transformer MLP's two projections, so it runs over
1023/// `seq * 4 * hidden` elements per layer. Argus's tower had an AVX2 twin for it
1024/// and **no wasm twin at all**, so the browser took a scalar loop — the same
1025/// asymmetry the softmax had.
1026///
1027/// `gelu_tanh` is branch-free (`x / (1 + exp(-2z))`), so unlike `tanh` there is
1028/// no small-|x| special case to reproduce; the twins are the same expression in
1029/// lanes. Gated by tolerance against the scalar oracle.
1030pub fn gelu_tanh_inplace(xs: &mut [f32]) {
1031    #[cfg(target_arch = "x86_64")]
1032    {
1033        if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
1034            // SAFETY: probed immediately above.
1035            unsafe { gelu_tanh_avx2(xs) };
1036            return;
1037        }
1038    }
1039    #[cfg(target_arch = "aarch64")]
1040    {
1041        // SAFETY: `neon` is a compile-time guarantee on this target.
1042        unsafe { gelu_tanh_neon(xs) };
1043        return;
1044    }
1045    #[cfg(target_arch = "wasm32")]
1046    {
1047        // SAFETY: `simd128` is a compile-time guarantee on this target.
1048        unsafe { gelu_tanh_simd128(xs) };
1049        return;
1050    }
1051    #[allow(unreachable_code)]
1052    for v in xs.iter_mut() {
1053        *v = gelu_tanh(*v);
1054    }
1055}
1056
1057#[allow(clippy::excessive_precision)]
1058// `0.797_884_56` is `SQRT_2_OVER_PI` from the scalar oracle above, verbatim.
1059// Truncating it is bit-identical (0x2a424c3f either way), but it would make the
1060// twin stop matching the oracle ON THE PAGE, and reading them side by side is
1061// how the twins are checked.
1062/// # Safety
1063/// Caller must have verified `avx2` and `fma`.
1064#[allow(clippy::many_single_char_names)]
1065// The names are the polynomial's own: `n`, `i`, `x`, `t`, `r`, `f`, `p`, `e`,
1066// `k`, `z`. Expanding them into prose makes the kernel harder to read against
1067// its twins and the scalar oracle, which is the only way this code is verified.
1068#[allow(clippy::wildcard_imports)]
1069// A SIMD kernel names 15 intrinsics; importing them one by one is a list that
1070// goes stale the moment the arithmetic changes, and hides nothing.
1071#[cfg(target_arch = "x86_64")]
1072#[target_feature(enable = "avx2,fma")]
1073unsafe fn gelu_tanh_avx2(xs: &mut [f32]) {
1074    // SAFETY: whole-body wrapper inserted by the edition-2024
1075    // `unsafe_op_in_unsafe_fn` migration. This block adds no new obligation:
1076    // the contract is stated on the `unsafe fn` signature above.
1077    unsafe {
1078        use std::arch::x86_64::*;
1079        const L1: f32 = std::f32::consts::LN_2;
1080        const L2: f32 = L1 * L1 / 2.0;
1081        const L3: f32 = L1 * L1 * L1 / 6.0;
1082        const L4: f32 = L1 * L1 * L1 * L1 / 24.0;
1083        const L5: f32 = L1 * L1 * L1 * L1 * L1 / 120.0;
1084        let sq2pi = _mm256_set1_ps(0.797_884_56);
1085        let k = _mm256_set1_ps(0.044_715);
1086        let one = _mm256_set1_ps(1.0);
1087        let m2 = _mm256_set1_ps(-2.0);
1088        let log2e = _mm256_set1_ps(std::f32::consts::LOG2_E);
1089        let magic = _mm256_set1_ps(MAGIC);
1090        let lo = _mm256_set1_ps(-125.0);
1091        let hi = _mm256_set1_ps(125.0);
1092        let (c1, c2, c3, c4, c5) = (
1093            _mm256_set1_ps(L1),
1094            _mm256_set1_ps(L2),
1095            _mm256_set1_ps(L3),
1096            _mm256_set1_ps(L4),
1097            _mm256_set1_ps(L5),
1098        );
1099        let bias = _mm256_set1_epi32(127);
1100        let n = xs.len();
1101        let mut i = 0;
1102        while i + 8 <= n {
1103            let x = _mm256_loadu_ps(xs.as_ptr().add(i));
1104            // z = sqrt(2/pi) * x * (1 + k x^2)
1105            let x2 = _mm256_mul_ps(x, x);
1106            let z = _mm256_mul_ps(_mm256_mul_ps(sq2pi, x), _mm256_fmadd_ps(k, x2, one));
1107            // exp(-2z)
1108            let t = _mm256_mul_ps(_mm256_mul_ps(m2, z), log2e);
1109            let t = _mm256_min_ps(_mm256_max_ps(t, lo), hi);
1110            let r = _mm256_sub_ps(_mm256_add_ps(t, magic), magic);
1111            let f = _mm256_sub_ps(t, r);
1112            let p = _mm256_fmadd_ps(f, c5, c4);
1113            let p = _mm256_fmadd_ps(f, p, c3);
1114            let p = _mm256_fmadd_ps(f, p, c2);
1115            let p = _mm256_fmadd_ps(f, p, c1);
1116            let p = _mm256_fmadd_ps(f, p, one);
1117            let e = _mm256_slli_epi32::<23>(_mm256_add_epi32(_mm256_cvtps_epi32(r), bias));
1118            let ex = _mm256_mul_ps(p, _mm256_castsi256_ps(e));
1119            _mm256_storeu_ps(
1120                xs.as_mut_ptr().add(i),
1121                _mm256_div_ps(x, _mm256_add_ps(one, ex)),
1122            );
1123            i += 8;
1124        }
1125        while i < n {
1126            xs[i] = gelu_tanh(xs[i]);
1127            i += 1;
1128        }
1129    }
1130}
1131
1132#[allow(clippy::excessive_precision)]
1133// `0.797_884_56` is `SQRT_2_OVER_PI` from the scalar oracle above, verbatim.
1134// Truncating it is bit-identical (0x2a424c3f either way), but it would make the
1135// twin stop matching the oracle ON THE PAGE, and reading them side by side is
1136// how the twins are checked.
1137/// NEON, four lanes, FMA-fused.
1138///
1139/// # Safety
1140/// `neon` is a compile-time guarantee on `aarch64` here.
1141#[allow(clippy::many_single_char_names)]
1142// The names are the polynomial's own: `n`, `i`, `x`, `t`, `r`, `f`, `p`, `e`,
1143// `k`, `z`. Expanding them into prose makes the kernel harder to read against
1144// its twins and the scalar oracle, which is the only way this code is verified.
1145#[allow(clippy::wildcard_imports)]
1146// A SIMD kernel names 15 intrinsics; importing them one by one is a list that
1147// goes stale the moment the arithmetic changes, and hides nothing.
1148#[cfg(target_arch = "aarch64")]
1149#[target_feature(enable = "neon")]
1150unsafe fn gelu_tanh_neon(xs: &mut [f32]) {
1151    // SAFETY: whole-body wrapper inserted by the edition-2024
1152    // `unsafe_op_in_unsafe_fn` migration. This block adds no new obligation:
1153    // the contract is stated on the `unsafe fn` signature above.
1154    unsafe {
1155        use core::arch::aarch64::*;
1156        const L1: f32 = core::f32::consts::LN_2;
1157        const L2: f32 = L1 * L1 / 2.0;
1158        const L3: f32 = L1 * L1 * L1 / 6.0;
1159        const L4: f32 = L1 * L1 * L1 * L1 / 24.0;
1160        const L5: f32 = L1 * L1 * L1 * L1 * L1 / 120.0;
1161        let sq2pi = vdupq_n_f32(0.797_884_56);
1162        let k = vdupq_n_f32(0.044_715);
1163        let one = vdupq_n_f32(1.0);
1164        let m2 = vdupq_n_f32(-2.0);
1165        let log2e = vdupq_n_f32(core::f32::consts::LOG2_E);
1166        let magic = vdupq_n_f32(MAGIC);
1167        let lo = vdupq_n_f32(-125.0);
1168        let hi = vdupq_n_f32(125.0);
1169        let (c1, c2, c3, c4, c5) = (
1170            vdupq_n_f32(L1),
1171            vdupq_n_f32(L2),
1172            vdupq_n_f32(L3),
1173            vdupq_n_f32(L4),
1174            vdupq_n_f32(L5),
1175        );
1176        let bias = vdupq_n_s32(127);
1177        let n = xs.len();
1178        let mut i = 0;
1179        while i + 4 <= n {
1180            let x = vld1q_f32(xs.as_ptr().add(i));
1181            let x2 = vmulq_f32(x, x);
1182            let z = vmulq_f32(vmulq_f32(sq2pi, x), vfmaq_f32(one, k, x2));
1183            let t = vmulq_f32(vmulq_f32(m2, z), log2e);
1184            let t = vminq_f32(vmaxq_f32(t, lo), hi);
1185            let r = vsubq_f32(vaddq_f32(t, magic), magic);
1186            let f = vsubq_f32(t, r);
1187            let p = vfmaq_f32(c4, f, c5);
1188            let p = vfmaq_f32(c3, f, p);
1189            let p = vfmaq_f32(c2, f, p);
1190            let p = vfmaq_f32(c1, f, p);
1191            let p = vfmaq_f32(one, f, p);
1192            let e = vshlq_n_s32::<23>(vaddq_s32(vcvtq_s32_f32(r), bias));
1193            let ex = vmulq_f32(p, vreinterpretq_f32_s32(e));
1194            vst1q_f32(xs.as_mut_ptr().add(i), vdivq_f32(x, vaddq_f32(one, ex)));
1195            i += 4;
1196        }
1197        while i < n {
1198            xs[i] = gelu_tanh(xs[i]);
1199            i += 1;
1200        }
1201    }
1202}
1203
1204#[allow(clippy::excessive_precision)]
1205// `0.797_884_56` is `SQRT_2_OVER_PI` from the scalar oracle above, verbatim.
1206// Truncating it is bit-identical (0x2a424c3f either way), but it would make the
1207// twin stop matching the oracle ON THE PAGE, and reading them side by side is
1208// how the twins are checked.
1209/// # Safety
1210/// `simd128` is a compile-time guarantee on `wasm32` here.
1211#[allow(clippy::many_single_char_names)]
1212// The names are the polynomial's own: `n`, `i`, `x`, `t`, `r`, `f`, `p`, `e`,
1213// `k`, `z`. Expanding them into prose makes the kernel harder to read against
1214// its twins and the scalar oracle, which is the only way this code is verified.
1215#[allow(clippy::wildcard_imports)]
1216// A SIMD kernel names 15 intrinsics; importing them one by one is a list that
1217// goes stale the moment the arithmetic changes, and hides nothing.
1218#[cfg(target_arch = "wasm32")]
1219#[target_feature(enable = "simd128")]
1220unsafe fn gelu_tanh_simd128(xs: &mut [f32]) {
1221    // SAFETY: whole-body wrapper inserted by the edition-2024
1222    // `unsafe_op_in_unsafe_fn` migration. This block adds no new obligation:
1223    // the contract is stated on the `unsafe fn` signature above.
1224    unsafe {
1225        use core::arch::wasm32::*;
1226        const L1: f32 = core::f32::consts::LN_2;
1227        const L2: f32 = L1 * L1 / 2.0;
1228        const L3: f32 = L1 * L1 * L1 / 6.0;
1229        const L4: f32 = L1 * L1 * L1 * L1 / 24.0;
1230        const L5: f32 = L1 * L1 * L1 * L1 * L1 / 120.0;
1231        let sq2pi = f32x4_splat(0.797_884_56);
1232        let k = f32x4_splat(0.044_715);
1233        let one = f32x4_splat(1.0);
1234        let m2 = f32x4_splat(-2.0);
1235        let log2e = f32x4_splat(core::f32::consts::LOG2_E);
1236        let magic = f32x4_splat(MAGIC);
1237        let lo = f32x4_splat(-125.0);
1238        let hi = f32x4_splat(125.0);
1239        let (c1, c2, c3, c4, c5) = (
1240            f32x4_splat(L1),
1241            f32x4_splat(L2),
1242            f32x4_splat(L3),
1243            f32x4_splat(L4),
1244            f32x4_splat(L5),
1245        );
1246        let bias = i32x4_splat(127);
1247        let n = xs.len();
1248        let mut i = 0;
1249        while i + 4 <= n {
1250            let x = v128_load(xs.as_ptr().add(i).cast());
1251            let x2 = f32x4_mul(x, x);
1252            let z = f32x4_mul(f32x4_mul(sq2pi, x), f32x4_add(f32x4_mul(k, x2), one));
1253            let t = f32x4_mul(f32x4_mul(m2, z), log2e);
1254            let t = f32x4_pmin(hi, f32x4_pmax(lo, t));
1255            let r = f32x4_sub(f32x4_add(t, magic), magic);
1256            let f = f32x4_sub(t, r);
1257            let p = f32x4_add(f32x4_mul(f, c5), c4);
1258            let p = f32x4_add(f32x4_mul(f, p), c3);
1259            let p = f32x4_add(f32x4_mul(f, p), c2);
1260            let p = f32x4_add(f32x4_mul(f, p), c1);
1261            let p = f32x4_add(f32x4_mul(f, p), one);
1262            let e = i32x4_shl(i32x4_add(i32x4_trunc_sat_f32x4(r), bias), 23);
1263            let ex = f32x4_mul(p, e);
1264            v128_store(
1265                xs.as_mut_ptr().add(i).cast(),
1266                f32x4_div(x, f32x4_add(one, ex)),
1267            );
1268            i += 4;
1269        }
1270        while i < n {
1271            xs[i] = gelu_tanh(xs[i]);
1272            i += 1;
1273        }
1274    }
1275}