[XLA:CPU] Lower log and log1p to YNNPACK SIMD bitcode kernels.
### Status quo
In the XLA:CPU MLIR fusion pipeline, `f32` `log` uses a handwritten LLVM IR polynomial (`GenerateVF32Log`, 1.19 ULP max error), `f64` `log` falls back to scalar `libm` `log` calls (0.5 ULP, unvectorized), and `log1p` lowers to an algebraic composition around `log` (up to ~3.0 ULP in `f32`).
### Why
Compiling YNNPACK's `log` and `log1p` SIMD kernels (`f32` and `f64`, up to 512-bit vectors) to LLVM bitcode and inlining them during codegen vectorizes `f64` `log` and `log1p`, speeds up `f32` `log` and `log1p` across x86 and ARM64, improves `f32` accuracy (`log` 1.19 -> 1.02 ULP, `log1p` 3.0 -> 1.65 ULP), and deletes XLA:CPU's handwritten `GenerateVF32Log` C++ IR builder.
End-to-end `elementwise_benchmark_test` (`N = 128, 1024, 8192` geomean cycles/op vs baseline):
- `f32` (`BM_LogF32`, `BM_Log1pF32`): `-19.1%` on x86 AVX2+FMA3, `-28.8%` on x86 AVX-512, `-36.6%` on ARM64 NEON.
- `f64` (`BM_LogF64`, `BM_Log1pF64`): `1.7x-4.4x` faster across AVX2, AVX-512, and ARM64 NEON.
### Design choices
- Expose YNNPACK `log` and `log1p` wrappers for `f32` and `f64` vector widths up to 512 bits in `xla/codegen/intrinsic/cpp/ynnpack_unary.{h,cc}`, compile a single `ynnpack_unary_ll` bitcode library, inline from it during codegen, and stop splitting `v16f32` `log` on AVX-512.
- Keep upcasting `f16` and `bf16` to `f32`, since YNNPACK has no native full-precision 16-bit `log`/`log1p` kernels on x86.
- Preserve `log1p(-0.0) = -0.0` via `copysign`, and keep the existing `libm` and algebraic `Log1p` lowerings on GPU and when CppGen bitcode is unavailable.
### Compromises
Maximum error bounds in ULPs (from JAX's numerics test suite and `docs/accuracy.md`):
| Op | CPU (before) | CPU (this CL) | NVIDIA GPU | TPU (v6e / 7x) |
|:---|:---:|:---:|:---:|:---:|
| `log` (`f32`) | 1.5 | 1.5 | 1.0 | 2.5 |
| `log` (`f64`) | 0.5 | 1.5 | 1.5 | N/A |
| `log1p` (`f32`) | 3.0 | 2.0 | 1.0 | 3.0 |
| `log1p` (`f64`) | 2.0 | 2.0 | 1.5 | N/A |
| `log10` (`f32`) | 3.0 | 2.5 | 2.5 | 3.0 |
| `logit` (`f32`) | 3.0 | 2.0 | 3.0 | 4.0 / 4.5 |
| `acosh` (`f32`) | 4.5 | 3.5 | 2.5 | 4.5 / 5.0 |
| `asinh` (`f32`) | 3.5 | 2.0 | 2.0 | 3.5 |
| `atanh` (`f32`) | 3.0 | 2.0 | 3.5 | 3.5 |
`f64` `log` trades scalar `libm`'s 0.5 ULP correct rounding for SIMD vectorization, matching GPU's 1.5 ULP bound (measured 1.23 ULP).
The `c128` `Log` exhaustive test gets a 1 ULP relative bound on CPU for inputs with tiny imaginary parts. CPU already flushes those imaginary parts to zero, which fails the distance check, so the real part `log|z|` was effectively held to exact `libm` results.
### Alternatives
- Tested a pure degree-8 Estrin polynomial (`Q = 1`) for `f32` `log` in YNNPACK, which avoided `fdiv` latency in isolation on wide AVX-512 vectors, but ran slower in XLA's unrolled elementwise loops across AVX2, AVX-512, and ARM64 NEON than the `[3/2]` rational kernel.
- Reaching `<= 1.0` ULP for both `log` and `log1p` requires split `ln(2)` plus double-word addition for `1 + x`, which added 18-68% per-vector overhead in microbenchmarks for accuracy tighter than GPU (`f64`) and TPU (`f32`).
### Updating tests
CPU `log` and `log1p` results move by about 1 ULP on many inputs, and by more in code that amplifies small differences (iterative solves, log-dets, KL divergences, derivatives). Tests that check exact equality, use tolerances below 1 ULP, or compare against goldens captured from the old output may fail. Please update them to allow a few ULP, or set goldens to exact reference values, rather than pinning bit-exact results.
PiperOrigin-RevId: 1020506814