Module Ir.Online_softmax

The online-softmax attention rewrite (gh-ocannl-483): the first member of the algebraic-rewrite tier over lowered code (Rewrites), a pattern-directed substitution that CHANGES the computation where the schedule transforms only rearrange it.

The composed attention softmax (q * k^T) * v lowers to a max-reduction and a sum-reduction over the key axis with two elementwise nests between them, then a reduction over the key axis against v. Two of its intermediates are seq^2-shaped and get materialized: the scores are read by both reductions, and the probabilities are read once per value-width iteration of the final reduction. This pass rewrites two shapes, both gated by the config key online_softmax (default off, on in the approximate profile):

What stays as it was: the score reduction q * k^T keeps its own placement decision -- the recompute cap virtualize_max_inline_reduction decides whether it is replayed at its two read sites (the scan and the hoisted read) or stored once. Recomputing is flash attention's memory trade (no seq, seq buffer at all); storing was faster at every cell measured on cc and Metal (benchmarks/report-gh483-online-softmax.md, seq 128 to 1024), and a training step's backward reads the scores again through cross-routine splicing.

The pass runs at lowering, ahead of the analyses (Rewrites.rewrite.apply, from Assignments.lower, over the raw lowered code), so the traced store and the placements see the rewritten routine and no analysis digest needs the knob: the code itself carries the decision (Code_borne). It is idempotent, as the tier's fixpoint requires: its output has no max-reduction nest left to match, and a hoisted reduction is no longer a single nest.

val enabled : unit -> bool

Whether the rewrite applies: the programmatic override if one is set, else the config key online_softmax.

val set_enabled : bool option -> unit

Programmatic override of the config key, for experiments and tests; None restores the key. Takes effect at the next lowering.

val reset : unit -> unit

Drops the memoized scope-local nodes (the tier's session-reset hook; also run ahead of an accessibility snapshot). Sibling lowerings of one program mint the same nodes, which is what keeps them on one analysis-cache key; a reset only makes the next lowering mint afresh.

val rewrite : Low_level.t -> Low_level.t

The pass over raw lowered code (no gate: the tier consults enabled). Every normalizer pattern in the routine is rewritten; a routine without one is returned as is. The minted scope-local nodes carry memory-mode provenance "483:online-softmax-state".