Repository navigation
fix(cpu): restore map_ps's target_feature, which a merge moved onto map_bias_ps - #1227
Conversation
A merge left the 'Apply an 8-lane kernel across a slice' doc comment and both of its attributes attached to map_bias_ps, which was inserted between them and their function. map_ps was left with no #[inline] and, more importantly, no #[target_feature(enable = "avx2,fma")]. That matters because LLVM will not inline a callee whose feature set is a superset of its caller's: without avx2,fma on map_ps, the kernel closures cannot be inlined into its loop. It is harmless today only because every caller is itself avx2,fma, so map_ps inlines upwards into the caller and the closure folds in afterwards -- an inlining decision, not a guarantee. Grow map_ps past the threshold and every activation takes a real call per 8 elements. Codegen is byte-identical before and after (verified by diffing the disassembly of tanh_avx2), so this is a latent-hazard fix, not a perf change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…two docs Opus review found one real defect and three smaller ones. MUST-FIX -- `celu_scalar` destroyed NaN. Rust's `f32::max`/`min` are IEEE `maxNum`/`minNum`, so they return the *other* operand for NaN: transcribing ONNX's `max(0,x) + min(0, alpha*(exp(x/alpha)-1))` literally collapses both terms to zero and answers `0.0`. The AVX2 path was deliberately written to propagate NaN, and ORT propagates it too, so the result depended on whether the slice was long enough for the vector path and on whether the host had AVX2 -- which is the one thing `dispatch!` exists to prevent. Reachable by any tensor under `SIMD_MIN_LEN`, by every `Float64` tensor (which never touches SIMD), and by any non-AVX2 host. Guarded in `celu_scalar`, and `Activation::apply` now delegates to it instead of keeping a second copy of the formula; `apply_f64` carries the same guard. Two new tests pin it, both verified to fail without the fix: `celu_scalar_path_agrees_with_the_vector_path` runs the same specials through a slice too short for SIMD and a padded one, and `celu_and_mish_propagate_nan_on_every_path` covers f32 and f64 `apply`. The existing special-value tests could not catch this: `special_inputs()` pads to `SIMD_MIN_LEN`, so all of them take the vector path. SHOULD-FIX -- `mod log_c` landed between `mod exp_c`'s doc comment and `mod exp_c`, so `exp_c` lost its `#[cfg(target_arch = "x86_64")]` and its doc described the wrong module. Same shape as the `map_ps` defect in #1227, in the same file, found the same way. Each module now carries its own doc and its own gate. SHOULD-FIX -- added `Celu`/`Mish` to the `OWNED` list in `activation_and_norm_ops_clear_every_capability_filter`, which checks the dtype filter as well as the shape filter. Its comment said to add them the moment a kernel landed; it has. NIT -- the four new kernels had been inserted into the middle of `exp_full_ps`'s doc comment, splitting it mid-sentence. Moved below it. Also refreshed `CPU_ACTIVATION_GAPS.md`: "activations with no kernel at all" is now empty, and `Celu`/`Mish`/`Log` moved to the closed table. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔴 Benchmark Regression DetectedComparison of criterion micro-benchmarks: PR head vs merge-base, measured on the same runner in the same job (base first → PR second).
Visual flags: Host infoWhat this cannot catch
|
|
Verified locally and merged. The bug is real and I confirmed it on On the impact, I want to be precise rather than generous. The PR body is right that Verification: |
Rebased onto current main to preserve #1227 (map_ps's target_feature), which the original branch predated. Only the erf big-branch R polynomial and the exp polynomial are converted to Estrin's scheme; the map_ps / map_bias_ps attribute arrangement from #1227 is left intact. Co-authored-by: resch <resch@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… (1.66x -> 1.36x vs ORT) (#1218) ## What this changes `erf_ps`'s big-branch `R` polynomial and the `exp_ps` polynomial it feeds are switched from Horner's rule to **Estrin's scheme**. Horner is a six-deep dependent FMA chain; Estrin regroups it as `a^3·(c0 a^3 + c1 a^2 + c2 a + c3) + (c4 a^2 + c5 a + c6)`, three deep for two extra multiplies, whose halves the out-of-order engine runs in parallel. Only the two chains actually on `erf`'s critical path are converted; the small branch stays Horner (it runs in parallel and is off-path). Rebased onto current `main` so it **preserves #1227** (`map_ps`'s `#[target_feature(enable = "avx2,fma")]`). The original branch predated #1227 and its raw diff would have moved that attribute back onto `map_bias_ps`; that hunk is dropped. Only `simd_activations.rs`'s erf/exp polynomials and one accuracy test change here. ## Numerics — measured, not assumed (reviewer) Estrin reassociates f32 FMAs, so it is **not** bit-identical to Horner by construction. The squad reviewer measured the actual error with a two-build dump-and-diff (base Horner build vs this Estrin build, identical input grid of 404,045 points spanning `[-6,6]` densely, the `0.921875` split boundary, large `|x|`, denormals, and NaN/±inf), on **Intel Core i7-13800H, Windows 11**: - **Estrin vs Horner: max difference = exactly 1 ULP.** 4,401 of 404,043 finite points (1.09%) differ, every one of them by a single ULP; max absolute diff 5.96e-8. Nothing moves by more than one ULP anywhere in the domain. - **vs f64 `erf` truth:** max |Horner − erf| = 5.77e-8, max |Estrin − erf| = 6.55e-8 (worst at x≈0.933, just past the split). Both within ~1 ULP of the correctly rounded result; Estrin is at most ~0.8e-8 looser than Horner and never worse than 1 ULP from it. - **Special values are all correct and identical between the two:** large `|x|` (7,8,10,20,50,100,1000,1e30,f32::MAX) saturate to ±1.0; denormals produce bit-identical tiny outputs; NaN → NaN; +inf → +1.0; −inf → −1.0. This is a conscious, documented 1-ULP change, not a hand-wave. The accuracy gate `erf_reassociation_costs_no_accuracy` (worst scaled error pinned at 5.97e-8 = 2^-24 over `[-6,6]`) **passes** on this box, so a future regrouping that widens the envelope fails instead of being absorbed by the looser `ERF_BOUND`. ## Performance — measured on this box, ORT-independent The original body quoted "1.66× → 1.36× vs ORT" (≈ −17.7% kernel time). I could not reproduce that specific figure: no pinned ORT reference on this Windows box, and it is different hardware. Instead I timed `erf_avx2` directly, same machine, two builds, single-thread, min-of-200 × 9 rounds over a 1,048,576-element mixed-range buffer: | build | per-round min (ms) | best | | --- | --- | ---: | | Horner (base) | 0.6346–0.6348 (8/9 rounds) | 0.6346 | | **Estrin (this PR)** | **0.6042–0.6043 (8/9 rounds)** | **0.6042** | The ranges do not overlap, so the direction is solid: **Estrin is ~5.0% faster (1.05×)** on the erf kernel here. That is a real win but smaller than the −17.7% originally claimed — reported honestly as the measured kernel-internal delta rather than an unverified ORT ratio. The mechanism (a latency-bound chain freed by shorter dependency depth) is consistent with a positive but hardware-dependent magnitude. ## Validation (reviewer) - `cargo test -p onnx-runtime-ep-cpu --lib` — **1353 passed, 0 failed, 17 ignored** (base 1345 + #1183/#1149 + this test), including the accuracy gate. - `cargo clippy -p onnx-runtime-ep-cpu --all-targets -- -D warnings` — clean. - No MLAS feature involved; `map_ps`'s target_feature (#1227) verified intact in the merged result. Co-authored-by: justinchuby <223556219+Copilot@users.noreply.github.com> Co-authored-by: resch <resch@users.noreply.github.com>
…nt ORT activation deferrals) (#1235) ## What this changes Adds native CPU-EP kernels for **`Celu`** (opset 12) and **`Mish`** (opset 18), which had no kernel at all and were therefore silently handed to ORT's CPU EP, and vectorises **`Log`** (f32), which was still taking a scalar `libm` call per element. Celu/Mish are wired through all four capability filters (registry, `PHASE1_OPS`, dtype table, shape-inference table, plugin descriptors) and added to the two activation-coverage tests. ## Behavioural claim — verified independently (reviewer) The headline "removes the last silent ORT activation deferrals" is a behavioural claim, so I falsified it rather than trusting it, on **Intel Core i7-13800H, Windows 11**: - **The deferral was real and silent.** `GetCapability` runs three fail-closed filters; an op absent from the shape-inference table (Filter 2) or the dtype descriptor list (Filter 3) is handed to ORT with no diagnostic. Celu/Mish had no kernel, so they never reached `supports_op`. - **Falsification (RED/GREEN).** With the PR applied, `activation_and_norm_ops_clear_every_capability_filter` passes. Removing just the Celu/Mish entries from the shape-inference table (`compute.rs`, Filter 2) turns it **RED** with `::Celu: no shape rule (shape filter declines it) … would be handed to ORT's CPU EP` for both ops; restoring returns it to **GREEN**. The test genuinely guards the deferral. - **"The last" is defended structurally, not by spot-check.** The coverage test's `OWNED` list now enumerates the full activation/normalisation family and holds every member to both the shape and dtype filters; the companion `every_registered_op_has_a_shape_rule_or_is_a_known_gap` enumerates all registered ops, and its remaining shape-table gap (data-dependent, internal-fusion, and inferrable-but-unwritten ops) contains **no activations**. `Log` was never a deferral — it already had a scalar kernel — so this PR is a perf change for it, not a coverage change. ## Correctness The kernels' own tests (dense sweeps vs the scalar reference, subnormals, and the full IEEE special set) pass; the scalar reference, the f64 path, and the AVX2 path are held to agree, including NaN propagation (`f32::max`/`min` drop NaN, so each kernel guards it to match ORT). Full `cargo test -p onnx-runtime-ep-cpu --lib` = **1362 passed, 0 failed, 17 ignored** (base 1353 + 9 new). `cargo clippy --all-targets -D warnings` clean. `map_ps`'s `#[target_feature]` (#1227) verified intact. ## Performance — measured on this box, ORT-independent The gaps doc's ratios were taken on an **AMD EPYC 9V74** against ORT. I have no ORT reference on this host, so I measured the ORT-independent internal win instead: the AVX2 kernel vs the scalar-native fallback (which is what runs on non-AVX2 hosts and small tensors), on **Intel Core i7-13800H (14C/20T), Windows 11**, single thread, 1,048,576 elements, min-of-200 × 9 rounds. | kernel | scalar-native (ms) | AVX2 (ms) | speedup | | --- | ---: | ---: | ---: | | `Log` (positive domain) | 2.89 | 1.14 | **~2.54×** | | `Celu` (alpha=1, mixed) | 2.77 | 0.68 | **~4.07×** | | `Mish` (mixed) | 17.82 | 2.50 | **~7.13×** | These are hardware-dependent (see `docs/performance/CPU_ACTIVATION_GAPS.md` §32 lesson) and are **not** the same quantity as the doc's ORT-relative ratios — mine compare AVX2 against the native scalar path on this machine; the AMD EPYC figures compare native against ORT. Presented as peers, each with its hardware named. Both agree on direction: the native kernels are a clear win. --------- Co-authored-by: Resch <resch@squad.local> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…1240) # Zero-copy activation fast path, and an AVX2 Elu kernel Two changes in the same seam of `ActivationKernel::execute`: 1. **Generalise the contiguous-f32 fast path.** `silu_contiguous_f32` becomes `contiguous_f32(input, output, kernel)` — the kernel is now a parameter — so Celu, Mish and Elu write their result straight into the output buffer instead of taking the general path's two allocations and three passes (`to_dense_f32_widen` → `map` → `write_dense_f32_narrow`). 2. **An AVX2 Elu kernel** (`elu_ps` / `elu_f32_slice` / `elu_avx2`) built on the `exp_full_ps` that Celu and Mish already use, replacing a scalar `expf` loop. Base: `origin/main` with #1235 (Celu/Mish/Log) landed first. --- ## Reviewer measurements (Intel Core i7-13800H) All numbers below were taken by the reviewer on an **Intel Core i7-13800H (14C/20T, AVX2+FMA, no AVX-512), Windows 11**. No ORT reference build is available on this box, so the perf figure is an **ORT-independent, same-binary A/B**: the scalar Elu reference (`elu_scalar` loop) versus the AVX2 slice kernel (`elu_f32_slice`), 1M f32 elements, single-thread, min-of-200 iterations × 9 rounds, `black_box`ed on both arms of the same release binary. | Elu, 1M elems, 1 thread | scalar-native | AVX2 slice | speedup | |---|---|---|---| | min-of-200 × 9 rounds | 2.5842 ms | 0.5107 ms | **~5.06×** | This measures the vectorisation win itself (scalar vs AVX2) rather than a ratio against ORT, which is what the same-binary A/B can establish without an ORT build present. ### Peer measurement (original PR, AMD EPYC 9V74) The PR was originally measured on an **AMD EPYC 9V74, 16 physical cores, AVX2/FMA/F16C, no AVX-512**, as a ratio `ours / ORT` in a shared process. There, Elu went from **2.06× slower than ORT to ~4.2× faster** at 1M (4.96 ms → 0.57 ms against ORT's 2.39 ms), with Celu/Mish also improving from the fast path alone (their kernels are unchanged in this PR). These two results are **peers, not corrections**: different microarchitecture, different core count, and different reference (one is `ours/ORT`, the other is an internal scalar-vs-AVX2 A/B). Both show the AVX2 Elu kernel is a substantial win; neither refutes the other. --- ## The fast-path fallback is proven, not assumed `contiguous_f32` forms `&[f32]` and `&mut [f32]` over raw tensor pointers, which is only sound when both tensors are dense f32, agree on shape and strides, and do not overlap in memory. A fast path that silently produced wrong results on an unusual layout would be worse than a slow one, so the reviewer added a direct test — `contiguous_f32_fast_path_declines_every_unsafe_layout` — that pins the guard: - **dtype mismatch** (f64 input) → declines; - **non-dense strides** (a broadcast stride of 0) → declines; - **aliasing** (input and output over the same buffer) → declines; - **control**: two distinct dense f32 buffers of equal shape → the fast path runs *and* is the thing that produced the output. The closure passed on the decline arms panics if invoked, so the test fails if the fast path is ever wrongly taken. Verified by falsification: neutering the overlap check makes the aliasing arm run the kernel and the test goes **RED** (`fast path ran on a layout it must decline`); restoring it returns **GREEN**. The overlap check refuses even exact in-place aliasing, because forming `&[f32]` and `&mut [f32]` over the same bytes is UB in Rust regardless of what the elementwise machine code would compute. --- ## Numerics - `elu_special_values` — `-Inf → -alpha`, `-0 → -0` (bit-checked), `+0 → +0`, `+Inf`, `NaN`, `±MAX`, `±1e30`, and ordinary values against the scalar. - `elu_scalar_path_agrees_with_the_vector_path` — nine specials run once through a sub-`SIMD_MIN_LEN` slice (scalar) and once padded past it (vector), compared **bit for bit**, at alpha ∈ {0.25, 1, 2, 7.5}. - `elu_matches_scalar_across_alphas` — 20 003 points over `[-90, 90]` at four alphas with an alpha-scaled bound. `-0.0`: Elu is a select so the sign survives (`Elu(-0) = -0`), where Celu's trailing addition gives `Celu(-0) = +0`; both are pinned. The scalar reference moves from `alpha * x.exp_m1()` to `alpha * (x.exp() - 1.0)` so the scalar path, the vector path and ORT are the same function (there is no AVX2 `expm1`); the two spellings differ by at most ~6e-8 absolute, inside this module's 4e-7 bound. --- ## Verification (reviewer, Intel Core i7-13800H, Windows 11) - `cargo test -p onnx-runtime-ep-cpu --lib` — **1366 passed / 0 failed / 17 ignored** (+4 over #1235: three Elu tests plus the fallback falsifier). - `cargo clippy -p onnx-runtime-ep-cpu --all-targets -- -D warnings` — clean. - #1227's `#[target_feature(enable = "avx2,fma")]` confirmed intact on **both** `map_ps` and `map_bias_ps` after the merge. - MLAS remains non-default; `elu_f32_slice` uses `dispatch!` like its neighbours and touches no MLAS path. Co-authored-by: justinchuby <223556219+Copilot@users.noreply.github.com>
…u and Selu (#1243) # AVX2 kernels for LeakyRelu, HardSigmoid, ThresholdedRelu and Selu `LeakyRelu`, `HardSigmoid`, `ThresholdedRelu` and `Selu` had no slice kernel, so they took `ActivationKernel`'s generic path: widen into a `Vec`, `map()` a per-element `match`, collect into a second `Vec`, copy that into the output tensor. This PR gives each a slice kernel and routes them through the zero-copy `contiguous_f32` fast path (established in #1240), and adds a genuinely vectorised exponential for Selu. Stacked on #1240 (which is stacked on #1235). --- ## Reviewer measurements (Intel Core i7-13800H) — where the win actually comes from Measured by the reviewer on an **Intel Core i7-13800H (14C/20T, AVX2+FMA, no AVX-512), Windows 11**. No ORT build is available on this box, so these are ORT-independent, same-binary A/Bs, 1M f32 elements, single-thread, min-of-200 × 9 rounds. **Two different comparisons, because they answer different questions:** 1. **Fast-path routing vs the generic path** (what this PR changes at the call site). Emulating the old generic path (`to_vec` → `map` → `copy_from_slice`) against the new `*_f32_slice` fast path, for LeakyRelu: | LeakyRelu, 1M, 1 thread | generic path | fast path | speedup | |---|---|---|---| | min-of-200 × 9 | 1.8784 ms | 0.1712 ms | **~10.97×** | This — eliminating two allocations and two extra passes — is the dominant win for the three cheap ops. 2. **The AVX2 kernel vs its scalar reference** (isolating the hand-written SIMD arithmetic alone, both in the same release binary): | op, 1M, 1 thread | scalar | AVX2 | speedup | |---|---|---|---| | LeakyRelu | 0.1533 ms | 0.1524 ms | ~1.01× | | HardSigmoid | 0.1599 ms | 0.1583 ms | ~1.01× | | ThresholdedRelu | 0.1644 ms | 0.1619 ms | ~1.02× | | **Selu** | 1.4301 ms | 0.3391 ms | **~4.22×** | For the three cheap ops the hand-written AVX2 kernel is **no faster than the scalar loop**, because LLVM already auto-vectorises a compare-and-blend in release mode. Selu is the exception: its exponential does not auto-vectorise, so the vector kernel is a real ~4.2× on the arithmetic itself. **Honest attribution:** for LeakyRelu/HardSigmoid/ThresholdedRelu the speedup is the fast-path plumbing, not the SIMD; for Selu it is both. That is worth stating plainly so nobody credits the compare-and-blend intrinsics with a win the allocator elimination actually produced. ### Peer measurement (original PR, AMD EPYC 9V74) Originally measured on an **AMD EPYC 9V74, 16 physical cores, no AVX-512** as a ratio `ours / ORT` in a shared process, at 1M/1-thread: ThresholdedRelu 11.30 → 0.93, HardSigmoid 8.62 → 0.71, LeakyRelu 7.58 → 0.65, Selu 2.56 → 0.27. These are **peers, not corrections** to the reviewer's numbers: different microarchitecture, core count, and reference (one is `ours/ORT`, the other is an internal same-binary A/B). The AMD `ours/ORT` ratios and the i7 fast-path-vs- generic ratio are consistent — both attribute the cheap-op gains to removing the generic path's allocations and passes. The PR's own note that the three cheap ops remain **~1.2–1.5× ORT at 4k** (a per-call plugin-dispatch floor, not the kernel) stands and is a good separate follow-up. --- ## Correctness — falsifiers verified by the reviewer The PR ships two load-bearing invariants, both confirmed RED-on-break: - **HardSigmoid clamp operand order.** `minps`/`maxps` return their *second* operand for an unordered input, so the value must be second or a NaN silently becomes `1` then `0`. `hard_sigmoid_special_values` pins NaN→NaN. - **Selu signed-zero.** ONNX's `x > 0` sends `-0` down the exp branch; ORT returns `+0`, so both scalar and vector spell it `alpha*(exp(x)-1)` (there is no AVX2 `expm1`). `scalar_paths_agree_with_the_vector_paths` pins scalar and vector to the same answer, and `selu_zero_sign_does_not_depend_on_dtype` pins it across dtypes. Bit-identity where the op is exact (LeakyRelu, ThresholdedRelu asserted bit-identical to their scalars over 20 003 points); HardSigmoid allows one FMA rounding; Selu uses an alpha-scaled bound. --- ## Verification (reviewer, Intel Core i7-13800H, Windows 11) - `cargo test -p onnx-runtime-ep-cpu --lib` — **1415 passed / 0 failed / 17 ignored** for the activation family (all four ops' special-value, scalar-vs-vector bit-parity, dense-sweep and signed-zero tests green). - One unrelated flake observed under full-suite load: `task_runtime::pool::tests::slot_exhaustion_declines_instead_of_blocking` (from #1201's CPU task runtime, untouched by this PR) failed once under contention and passes deterministically in isolation. Not introduced here. - `cargo clippy -p onnx-runtime-ep-cpu --all-targets -- -D warnings` — clean. - #1227's `#[target_feature(enable = "avx2,fma")]` confirmed intact on `map_ps` and `map_bias_ps`. - MLAS remains non-default; all four kernels use `dispatch!` and touch no MLAS path. Co-authored-by: justinchuby <223556219+Copilot@users.noreply.github.com>
What this is
map_psis the loop driver behind every unary activation on AVX2. A merge(the
#1037/#1074layering) insertedmap_bias_psbetweenmap_ps'sdoc comment and
map_psitself, so the comment and both of its attributesended up on the wrong function:
map_bias_psneeds those attributes too, so nothing was broken outright —but
map_pswas left with no#[inline]and noavx2,fma.Why the missing
target_featureis a hazardLLVM will not inline a callee that requires a feature its caller lacks (a
strict superset; equal feature sets inline fine). Every kernel closure
map_pstakes (erf_ps,tanh_ps,sigmoid_ps, ...) isavx2,fma. Withmap_pscompiled at baseline features,those closures are not inlinable into its loop.
It is harmless today only by luck of ordering: every caller
(
tanh_avx2,erf_avx2, ...) is itselfavx2,fma, somap_psgets inlinedupwards into the caller first, and once there the closure folds in. That is
an inlining decision, not a guarantee. Grow
map_pspast the inline threshold— which is exactly what an unroll experiment does — and the closure becomes a
real call per 8 elements, and a kernel like
erf_pshas to re-materialise allof its constants on every one of those calls.
I hit this while unrolling
map_ps, which is how it surfaced.Evidence that this changes nothing today
Disassembled
tanh_avx2out of the built rlib before and after:Byte-identical, and
erf_avx2/sigmoid_avx2keep their instruction counts(216 / 117). No
callto any kernel remains in the lib. So this is alatent-hazard and documentation fix with zero codegen delta — not a perf
change, and it needs no A/B.
Also recorded here: a negative result on the divide
While looking at these loops I measured one algorithmic change and it lost, so
it is written down rather than left for someone to retry.
tanh_psandsigmoid_pseach end in_mm256_div_ps.vdivps ymmis theonly instruction in either loop that is not fully pipelined (Zen 4 retires one
about every 4.5 cycles), so replacing it with
vrcpps+ one Newton-Raphsonstep —
y1 = y0 + y0*(1 - q*y0), four pipelined uops for one blocking one —looked like the obvious win.
It is not:
bench_tanh_f32_4kInterleaved rebuild-and-alternate, 3 rounds, 1 thread,
taskset -c 8-15.The 4k case is the only trustworthy one here — it is L1-resident and ORT's own
p50 was identical to four decimal places across all six runs, whereas on the
1M cases ORT's p50 moved 4.6x mid-session and neither arm is quotable.
Consistent ~3% regression. The
rcp+ 2 FMA dependency chain is about aslong as the divide it replaces, so a latency-bound kernel gains nothing and
just pays two extra uops.
It also breaks four exactness proofs that #1121 landed:
tanh(±Inf)comes backas
0.99999994instead of exactly±1.0, because the input clamp makespand
qbit-equal at|v| = 9and only an exact divide turns that into exactly1.0. Buying that back would mean restoring the saturation blend #1121 provedredundant over 1.05 billion inputs — to fund a change that is already slower.
Not pursued. No fallback involved; the kernel keeps its own divide.
Validation
cargo test --release -p onnx-runtime-ep-cpu --lib— 1337 passed, 0 failedcargo clippy --release -p onnx-runtime-ep-cpu --all-targets— cleancargo fmt --all --check— cleantanh_avx2disassembly identical tomainReview
Opus review: no blockers, no should-fixes.
It independently confirmed the three things the PR rests on: every one of the
16
map_psand 2map_bias_pscall sites is already inside a#[target_feature(enable = "avx2,fma")]function (so nothing becomes unsoundor fails to compile),
map_bias_pskeeps exactly the attributes it had, andthere are no bare/function-pointer references to either.
It also did the scan I most wanted: all ~130
fndefinitions in the file werechecked for the same orphaned-doc-comment-after-attribute signature that caused
this bug. Zero other anomalies — every
*_pshelper and every*_avx2dispatcher carries the attributes it should. So this is the only instance.
features are "a superset" of the caller's. Read non-strictly that would
forbid inlining at equal feature sets, which is the opposite of what the
fix relies on. Reworded to "requires a feature its caller lacks — a strict
superset; equal feature sets inline fine".