pub fn softmax_bwd(y: &[f32], dy: &[f32], rows: usize, cols: usize) -> Vec<f32>
Softmax backward: dx = y * (dy - sum(y * dy)) per row. The causal-masked forward writes exact zeros at masked entries, so no mask is needed here.