Skip to content

EUP 修正系数

本页中的每个偏移、u32 和浮点数都从 libtpu-0.0.40-cp314 wheel 中的 libtpu.so 按字节精确读取(BuildID md5 89edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,未 stripped)。.text.rodata VMA 等于其文件偏移;.data.rel.ro VMA 减去 0x200000 等于其文件偏移。下面每个系数 u32 都已在所引用的 .rodata 偏移处通过对文件执行 struct.unpack 确认。其他 libtpu 构建会有所不同。

摘要

EUP / 超越函数槽 页面确立了核心结构事实:在支持 EUP 的代际上,九个 V*Decomposed builder 会发出裸硬件 push + 裸 pop,除此之外没有任何内容,也就是说原始硬件超越函数就是答案,没有内联修正多项式。本页是该编码视角的数值补充。它逐字节编目了 software 超越函数代码加载的 fp32 .rodata 魔数,这些系数实际存在于两个位置:

  1. 共享的 Newton / rational-minimax 精化 helper,少数对精度敏感的函数会把它们包在原始 push 周围,或把它们用作软件路径:EmitRecpNrIterationEmitRsqrtNrIterationEmitTanhPolyApproximationEmitAtan2Approximation。这些是 recip/rsqrt Newton 步以及 tanh/atan2 有理式。
  2. *NoEupF32 软件 fallback,当某个代际或数据类型缺少硬件 EUP 时,它们完全用 VALU 实现超越函数:VexpNoEupF32Vexpm1NoEupF32VlnNoEupF32Vln1pNoEupF32Vlog2NoEupVtanhNoEupF32。这些存在的原因是没有硬件 EUP expln,只有 pow2log2 push,因此 exp/expm1/ln/ln1p 要么通过缩放硬件 pow2/log2 push 构造(*Eup 变体),要么在没有 EUP 的地方通过完整的 Cody-Waite / 尾数提取多项式构造。

这是 TPU 超越函数精度的重新实现规范:tanh 的精确有理式 P(x²)/Q(x²)exp 的 Cody-Waite ln2 拆分和 5 项 Taylor、log2 的 9 系数尾数提取 Horner,以及 Newton 迭代常量。它还固定了一个确实是 libtpu 字面量的 EUP 结果 FIFO 数量,即来自 ResultFifoEntryCount 的按 TpuVersion 区分的 kErf FIFO 深度,以及这些系数通过软件流水隐藏的 push→pop 延迟边。

每个常量池条目都会被积极共享:0x84a24441.0)和 0x84a2dc8log2e)各自出现在五个函数中。因此下面的偏移是每个常量的权威身份;同一个物理 word 会被不同函数中的不同 VALU op 消耗。

对于重新实现,契约是:

  • recip Newton 步 y·(2 − x·y)(一个常量)和 rsqrty·(1.5 − 0.5·x·y²)(两个常量)。
  • tanh 有理式 x·P(x²)/Q(x²):clamp 窗口、小 |x| 线性阈值、7 系数分子 P、4 系数分母 Q±1 饱和,全部按 .rodata 加载顺序排列。
  • atan2 8 系数奇 Horner,加上四个象限常量(π/2ππ/43π/4)以及 NaN/inf 哨兵。
  • exp/expm1/ln/ln1p/log2 no-EUP 多项式及其完整系数列表和偏移,以及 *Eup 变体的缩放/修正常量。
  • 按代际区分的 kErf EUP 结果 FIFO 深度,以及修正窗口隐藏的 push→pop 延迟边。
精化 emitterjellyfish::Emit{Recp,Rsqrt}NrIterationEmitTanhPolyApproximationEmitAtan2Approximation
No-EUP fallbackLloRegionBuilder::V{exp,expm1,ln,ln1p,tanh}NoEupF32(anon)::Vlog2NoEup
使用 EUP 的变体LloRegionBuilder::V{exp,expm1,ln1p}Eup — 在 HW pow2/log2 push 上缩放/修正
常量池.rodata 0x84a2400..0x84a3040;VMA == file offset;fp32(vmovss RIP-relative)
tanh 有理式x·P(x²)/Q(x²),deg P = 6(7 coeffs),deg Q = 3(4 coeffs)
exp 方法2^(x·log2e),Cody-Waite ln2 拆分,5 项 Taylor,VextractExponent/VcomposeF32
log2 方法尾数 AND 0x7fffff / OR 0x3f800000[1,2),sqrt2 拆分,9-coeff Horner
EUP 结果 FIFO 深度ResultFifoEntryCount(kErf, ver) = {4, 4, 32, 16, 16, 16},对应 TpuVersion 0..5
Push→pop 延迟边JF/DF 4,PF 7,VF 6,GL 13/14 — 修正软件流水填充的深度

Newton-Raphson 精化

两个共享 emitter 生成粗略 recip/rsqrt 种子的 Newton 精化。它们本身不是超越函数实现,而是把 1/x1/sqrt(x) 的现有估计 y 精化到完整 fp32 精度,并由 VrecpNr(除法 / 高精度倒数)和 rsqrt 精度模式调用。二者都接收参数 x 和当前估计 y,并返回一个改进后的估计。

c
function EmitRecpNrIteration(x, y):       // jellyfish::EmitRecpNrIteration @0x1d5a9ec0
    c = VimmF32(1.0)                       //  0x84a2444
    t = VmulF32(x, y)                      //  x·y
    t = VsubF32(c, t)                      //  1 − x·y
    t = VmulF32(y, t)                      //  y·(1 − x·y)
    return VaddF32(y, t)                   //  y + y·(1 − x·y) = y·(2 − x·y)

function EmitRsqrtNrIteration(x, y):      // jellyfish::EmitRsqrtNrIteration @0x1d5a9e20
    h = VimmF32(0.5)                        //  0x84a27e8
    t = VmulF32(x, y)                       //  x·y
    t = VmulF32(t, y)                       //  x·y²
    t = VmulF32(h, t)                       //  0.5·x·y²
    one_half = VimmF32(1.5)                 //  0x84a2680
    t = VsubF32(one_half, t)                //  1.5 − 0.5·x·y²
    return VmulF32(y, t)                    //  y·(1.5 − 0.5·x·y²)
```text

| 作用 | function | u32 | f32 | .rodata off |
|---|---|---|---|---|
| `1.0` | `EmitRecpNrIteration` | `0x3f800000` | 1.0 | `0x84a2444` |
| `0.5` | `EmitRsqrtNrIteration` | `0x3f000000` | 0.5 | `0x84a27e8` |
| `1.5` | `EmitRsqrtNrIteration` | `0x3fc00000` | 1.5 | `0x84a2680` |

`VrecpNr`(`@0x1d5580c0`)用特殊值饱和包装 `EmitRecpNrIteration`:它使用整数常量 `0x7fffffff`(一个 `VectorU32Constant`,不是 `.rodata` fp32)执行 abs-mask,并将上溢/下溢 clamp 到 `2^126`(`0x7e800000` @ `0x84a3004`)和 `2^126`(`0x00800000` @ `0x84a2f64`),通过 `SimplifyVweird` 在 NaN/inf 上进行选择。

| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| `2^126` 饱和 hi | `0x7e800000` | 8.507059e+37 | `0x84a3004` |
| `2^126` 饱和 lo | `0x00800000` | 1.175494e38 | `0x84a2f64` |

---

## Tanh 有理式 — `tanh(x) ≈ x·P(x²)/Q(x²)`

`EmitTanhPolyApproximation`(`@0x1d5a9f40`)是 no-EUP 软件 `tanh`;`VtanhNoEupF32`(`@0x1d535b20`)是进入它的薄 thunk(已按字节确认:它不加载自己的常量,并尾调用该 emitter)。从反编译的 `VimmF32`/`VmulAddF32`/`VdivF32` 链直接读取到的结构如下:

```c
function EmitTanhPolyApproximation(x):    // @0x1d5a9f40
    t   = VclampFloat(x, -9.0, +9.0)       //  0x84a283c / 0x84a2a84
    small = VltF32(VabsF32(t), 4e-4)       //  0x84a2758  (linear-region predicate)
    x2  = VmulF32(t, t)

    // NUMERATOR: 7-coefficient Horner P(x²), low→high, seed c0 then 6 FMAs, then ×x
    acc = VimmF32(c0)
    for ck in [c1, c2, c3, c4, c5, c6]:
        acc = VmulAddF32(x2, acc, ck)       //  acc = acc·x² + ck
    num = VmulF32(t, acc)                    //  x·P(x²)   — the odd factor

    // DENOMINATOR: 4-coefficient Horner Q(x²), low→high, seed d0 then 3 FMAs
    den = VimmF32(d0)
    for dk in [d1, d2, d3]:
        den = VmulAddF32(x2, den, dk)        //  den = den·x² + dk

    q = VdivF32(num, den)                    //  x·P(x²) / Q(x²)
    r = Vselect(small, t, q)                 //  return x in the linear region
    return VclampFloat(r, -1.0, +1.0)        //  0x84a26cc / 0x84a2444

VdivF32 操作数 trace 确定了奇偶性:与 clamp 后的 x 相乘的 accumulator(形成函数 x·P)是分子;裸 Horner 是分母。这使 tanh 在构造上就是奇函数。

分子 P(x²) — 7 个系数,在 中按 low→high Horner,整个 P 随后 ×x

作用u32f32.rodata off
c00xa59f25c0-2.7607683663038313e-160x84a2654
c10x2a61337e2.0001879384549948e-130x84a2428
c20xaebd37ff-8.604671836165423e-110x84a2ef4
c30x335c00415.122297253024044e-080x84a27d8
c40x3779434a1.4857223504805006e-050x84a24f0
c50x3a270ded0.00063726195367053150x84a242c
c60x3ba059dc0.0048935245722532270x84a24a0

分母 Q(x²) — 4 个系数,在 中按 low→high Horner:

作用u32f32.rodata off
d00x35a0d3d81.1982583600911312e-060x84a2e8c
d10x38f895d60.000118534706416539850x84a2658
d20x3b14aa050.00226843473501503470x84a2564
d30x3ba059dd0.00489352503791451450x84a27dc

括号范围和饱和:

作用u32f32.rodata off
clamp lo0xc1100000-9.00x84a283c
clamp hi0x41100000+9.00x84a2a84
small thresh0x39d1b7170.000399999989895150070x84a2758
saturate −10xbf800000-1.00x84a26cc
saturate +10x3f8000001.00x84a2444

注意 — c6(0x3ba059dc)和 d3(0x3ba059dd)只在 LSB 上不同。首要分子和分母系数有意接近相等,因此当 |x| → 9(饱和限制)时 x·P/Q → ±1;这是该有理式的高阶渐近线,也是外层 [−1, +1] clamp 在边界精确的原因。它们是按字节确认的不同常量,不是转录产物。


Atan2 — 8 系数奇 Horner + 象限常量

EmitAtan2Approximation@0x1d5aa1c0)根据比值 r = min(|x|,|y|) / max(|x|,|y|) 计算 atan2(y, x),先执行一个乘以 r 中 8 系数 Horner,再做象限/符号修正。Horner 从交替的 VimmF32 / VmulF32 + VaddF32 链读取(这里 emitter 使用显式 mul+add,而不是 FMA)。

c
function EmitAtan2Approximation(y, x):    // @0x1d5aa1c0
    r2  = VmulF32(r, r)                     //  r = min/max
    acc = VimmF32(p0)
    for pk in [p1, p2, p3, p4, p5, p6, p7]:
        acc = VaddF32(VmulF32(acc, r2), pk) //  Horner in r²  (8 coeffs total)
    core = VaddF32(VmulF32(VmulF32(acc, r2), r), r)   //  r·(1 + r²·Horner)
    // quadrant fix-ups: subtract from π/2 if |x|<|y|, reflect through π / π/4 / 3π/4,
    // NaN/inf sentinels, final Vcopysign(core, y)
```text

多项式 `P(r²)` — 8 个系数,Horner low→high:

| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| p0 | `0x3b369013` | 0.0027856871020048857 | `0x84a2594` |
| p1 | `0xbc81f96a` | -0.015866000205278397 | `0x84a2e38` |
| p2 | `0x3d2df75a` | 0.042472220957279205 | `0x84a2520` |
| p3 | `0xbd998ca7` | -0.07497530430555344 | `0x84a278c` |
| p4 | `0x3dda01d4` | 0.10644879937171936 | `0x84a2d24` |
| p5 | `0xbe117ae1` | -0.14207030832767487 | `0x84a300c` |
| p6 | `0x3e4cbba4` | 0.19993454217910767 | `0x84a2790` |
| p7 | `0xbeaaaa6c` | -0.33333146572113037 | `0x84a26dc` |

象限常量和哨兵:

| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
|/2` | `0x3fc90fdb` | 1.5707964 | `0x84a29d0` |
| `π` | `0x40490fdb` | 3.1415927 | `0x84a2e3c` |
|/4` | `0x3f490fdb` | 0.78539819 | `0x84a25f8` |
| `3π/4` | `0x4016cbe4` | 2.3561945 | `0x84a28f8` |
| NaN | `0x7fc00000` | nan | `0x84a2ff8` |
| inf | `0x7f800000` | inf | `0x84a2a34` |

> **注意 —**/4` 从 `0x84a25f8` 加载,它位于 `0x84a25ec` 处 16 字节对齐常量 pair 的偏移 `+0xC`。该 pair 的 `+0x8` word(`0x84a25f4`,`0x3f317218` = `ln2` = 0.6931472)是 `VlnNoEupF32` 和 `Vln1pEup`(见下文)使用的 `ln2` 乘数。该 pair 将 `π/4` 和 `ln2` 相邻打包,这是一个常量池布局细节,只有当重新实现者镜像精确的 RIP-relative 地址时才值得复现。

---

## No-EUP 软件 Fallback

当某个代际或数据类型没有硬件 EUP 超越函数时,`*NoEupF32` 家族会用纯 VALU 实现该函数。完全没有硬件 EUP `exp` 或 `ln` push(只有 `pow2`/`log2`),因此这些是唯一的 `exp`/`expm1`/`ln`/`ln1p` 路径;`tanh`/`log2` 既有硬件 push,也有 no-EUP fallback。

### `exp` — `VexpNoEupF32`(`@0x1d533820`)

`exp(x) = 2^(x · log2e)`,进行范围 clamp,使用 Cody-Waite `ln2` 拆分来保持小数规约准确,在规约后的小数 `f ∈ [−0.5, 0.5]` 上使用 5 项 Taylor 多项式,并通过 `VextractExponent` / `VcvtF32ToS32` / `VcomposeF32` 重构指数。上溢会饱和到产生 `inf` 的 clamp-hi 路径;下溢由 clamp-lo 比较选择掉。

| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| `1.0` | `0x3f800000` | 1.0 | `0x84a2444` |
| `0.5` | `0x3f000000` | 0.5 | `0x84a27e8` |
| clamp hi | `0x42b1722d` | 88.7229995727539 | `0x84a2734` |
| clamp lo | `0xc2aeac4f` | -87.33654022216797 | `0x84a2878` |
| log2e | `0x3fb8aa3b` | 1.4426950216293335 | `0x84a2dc8` |
| `ln2`-hi (Cody-Waite) | `0xbf318000` | -0.693359375 | `0x84a2d28` |
| `ln2`-lo (Cody-Waite) | `0x395e8083` | 0.00021219444170128554 | `0x84a2d7c` |
| Taylor t0 | `0x3efffffc` | 0.49999988079071045 | `0x84a2ebc` |
| Taylor t1 | `0x3e2aaa47` | 0.166665181517601 | `0x84a2794` |
| Taylor t2 | `0x3d2aadcc` | 0.04166965186595917 | `0x84a2b18` |
| Taylor t3 | `0x3c091de6` | 0.008368944749236107 | `0x84a26e4` |
| Taylor t4 | `0x3ab42872` | 0.001374496379867196 | `0x84a2ca4` |

### `expm1` — `Vexpm1NoEupF32`(`@0x1d533ec0`)

一个单独的 `expm1(x) = exp(x) − 1` 实现,带有自己的系数集(通过常量池复用共享 `log2e`、Cody-Waite `ln2` word 和 `88.723` clamp-hi),另有更紧的小 `x` 界限 `−17.32868`,以及用于 `expm1` 专用结果修正的 `{−1, 0.5, −0.5, 2}` selector 常量。

| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| clamp lo | `0xc18aa123` | -17.328680038452148 | `0x84a3038` |
| clamp hi | `0x42b17218` | 88.72283935546875 | `0x84a2ce8` |
| log2e | `0x3fb8aa3b` | 1.4426950216293335 | `0x84a2dc8` |
| `ln2`-hi | `0x3f317200` | 0.693145751953125 | `0x84a2f44` |
| `ln2`-lo | `0x35bfbe8e` | 1.428606765330187e-06 | `0x84a282c` |
| Taylor (t·1) | `0x3e2aaa6f` | 0.16666577756404877 | `0x84a26ac` |
| Taylor (t·2) | `0x3d2aaab6` | 0.041666708886623383 | `0x84a2754` |
| Taylor (t·3) | `0x3c09055f` | 0.008363096974790096 | `0x84a2fe0` |
| Taylor (t·4) | `0x3ab654c9` | 0.0013910765992477536 | `0x84a2c54` |
| selector −1 | `0xbf800000` | -1.0 | `0x84a26cc` |
| selector +0.5 | `0x3f000000` | 0.5 | `0x84a27e8` |
| selector −0.5 | `0xbf000000` | -0.5 | `0x84a28f4` |
| selector 2.0 | `0x40000000` | 2.0 | `0x84a2850` |
| inf | `0x7f800000` | inf | `0x84a2a34` |

### `log2` — `Vlog2NoEup`(`@0x1d556a60`)

2 为底的对数。尾数通过整数 masking 提取(`AND 0x7fffff` 清除指数,`OR 0x3f800000` 强制为 `[1, 2)`),sqrt2 拆分(`1.4142135` @ `0x84a2524`)把有效数重新居中到 `[1/2, √2)` 并按 ±1 调整整数指数,然后 `(m − 1)` 中的 9 系数 Horner 产生小数 `log2`,最后加回整数指数(cvt `S32→F32`)。异常:`x == 0 → −inf`,`x < 0 → NaN`,通过 `SimplifyVweird` + `VcmpHelper` select 处理。

```c
function Vlog2NoEup(x):                    // @0x1d556a60
    m   = (x AND 0x7fffff) OR 0x3f800000   //  significand in [1, 2)
    e   = SplitF32_exponent(x)             //  integer exponent
    // sqrt2 recentering: if m > 1.4142135 (0x84a2524) then m·=0.5, e+=1
    f   = m - 1.0                          //  0x84a2444
    acc = VimmF32(p0)
    for pk in [p1..p8]:
        acc = VmulAddF32(f, acc, pk)        //  9-coeff Horner in (m−1)
    return cvtS32ToF32(e) + f·acc           //  log2(x)

(m − 1) 中的 9 系数 Horner,low→high:

作用u32f32.rodata off
p00x3e013d7b0.126211091876029970x84a2c14
p10x3e5c9fc90.215453281998634340x84a2598
p20xbe540971-0.207067266106605530x84a28fc
p30xbe74b2ad-0.238962844014167790x84a29d4
p40x3e936e690.287951737642288210x84a26e0
p50xbeb8ae28-0.360703706741333010x84a2874
p60x3ef639b70.48090907931327820x84a2684
p70xbf38aa38-0.721347332000732420x84a2a3c
p80x3fb8aa3b1.44269502162933350x84a2dc8
作用u32f32.rodata off
sqrt2 split0x3fb504f31.41421353816986080x84a2524
−inf (x==0)0xff800000-inf0x84a2578
NaN (x<0)0x7fc00000nan0x84a2ff8

整数 mask 0x7fffff(mantissa)和 0x3f800000(set-exponent-1)是 LloModule::VectorU32Constant immediate(由 esi 携带),不是 .rodata fp32 加载;p80x84a2dc8)与 explog2e 是同一个物理 word。

lnVlnNoEupF32@0x1d534740

ln(x) = Vlog2NoEup(x) · ln2。该函数是一个两行 wrapper:调用 Vlog2NoEup,然后乘以 ln2

作用u32f32.rodata off
ln20x3f3172180.69314718246459960x84a25f4

log1pVln1pNoEupF32@0x1d534a20

log1p(x) = ln(1 + x),使用专门面向近 1 参数的 8 系数 Horner(外加在 重组合中消耗的第 9 个系数 −0.5),并通过 VpairFloatAddF32 双 word 重构应用 Cody-Waite ln2 拆分。尾数/指数路径镜像 Vlog2NoEup;区别在于输入是 1 + x,且多项式作用于重新居中的有效数。

8 系数 Horner(low→high):

作用u32f32.rodata off
q00xbd43a4d3-0.04776461049914360x84a2560
q10x3dda59bb0.106616459786891940x84a2940
q20xbe066c58-0.131272673606872560x84a2e00
q30x3e13d0180.144348502159118650x84a27d4
q40xbe2a7741-0.166470542550086980x84a2498
q50x3e4cbc510.19993712007999420x84a2fe4
q60xbe800036-0.250001609325408940x84a2838
q70x3eaaaabf0.333333939313888550x84a2b50
作用u32f32.rodata off
recombine −0.50xbf000000-0.50x84a28f4
ln2-hi0x3f3172000.6931457519531250x84a2f44
ln2-lo0x35bfbe8e1.428606765330187e-060x84a282c
−inf (1+x==0)0xff800000-inf0x84a2578
NaN (1+x<0)0x7fc00000nan0x84a2ff8

函数映射

FunctionAddressMethod
EmitRecpNrIteration0x1d5a9ec0recip Newton y·(2−xy)
EmitRsqrtNrIteration0x1d5a9e20rsqrt Newton y·(1.5−0.5xy²)
EmitTanhPolyApproximation0x1d5a9f40tanh 有理式 x·P(x²)/Q(x²)
EmitAtan2Approximation0x1d5aa1c0atan2 8-coeff 奇 Horner + 象限
VexpNoEupF320x1d5338202^(x·log2e) Cody-Waite + 5-term Taylor
Vexpm1NoEupF320x1d533ec0expm1 自有 4-coeff Taylor + selectors
Vlog2NoEup0x1d556a60尾数提取 + 9-coeff Horner
VlnNoEupF320x1d534740log2 · ln2
Vln1pNoEupF320x1d534a20log1p 8-coeff Horner + Cody-Waite
VtanhNoEupF320x1d535b20thunk → EmitTanhPolyApproximation
VrecpNr0x1d5580c0recip Newton + 饱和 wrapper

使用 EUP 的变体 — 在硬件 Push 上缩放 / 修正

expexpm1ln1p 也有使用 EUP 的变体,它们依赖硬件 pow2/log2 push,并且只在其周围应用小的 fp32 缩放或修正。这些用于支持 EUP 的代际,在那里硬件超越函数可用,但所需函数(exp/ln1p)不是直接硬件 op。

c
function VexpEup(x):                        // @0x1d556080
    if ShouldUseBf16FloatOps(x):
        s = VectorU32Constant(0x3fb93fb9)    //  packed bf16 log2e pair (two bf16 values)
        t = VmulBf16(x, s)                   //  op 0x15b kVectorMultiplyBf16
        return CreateVectorUnop(0x146, t)    //  kVectorPow2Bf16AndPop (fused HW pow2)
    else:
        t = VmulF32(x, log2e)                //  0x84a2dc8 = 1.4426950
        return Vpow2(t)                      //  HW pow2 push + pop
```text

| function | address | HW push consumed | scale / correction constant(s) |
|---|---|---|---|
| `VexpEup` | `0x1d556080` | `0x146` `kVectorPow2Bf16AndPop` (bf16) / `Vpow2` (f32) | f32 log2e `0x3fb8aa3b` @ `0x84a2dc8`;bf16-packed log2e `0x3fb93fb9`(`VectorU32Constant`) |
| `Vexpm1Eup` | `0x1d5561a0` | `Vexp`(使用 HW pow2) | `1.0` @ `0x84a2444`,`0.5` @ `0x84a27e8`,small-x thresh `0x3c4c7ad2` = 0.012480455 @ `0x84a2fbc` |
| `Vln1pEup` | `0x1d557340` | `Vlog2`(HW log2 push) | `1.0` @ `0x84a2444`,`−0.5` @ `0x84a28f4`,`ln2` `0x3f317218` @ `0x84a25f4`,correction `0x39e81ecb` = 0.00044273 @ `0x84a3008` |

`Vexpm1Eup` 通过 EUP `exp` 计算 `exp(x) − 1`,然后进行小参数修正;对于很小的 `|x|`(低于 `0.012480455` 阈值),它会回退到 `tanh` 风格的级数,而不是从接近 1 的值中减去 1(灾难性抵消 guard)。`Vln1pEup` 用 EUP `log2` push 执行 `ln1p(x) = log2(1+x)·ln2 + correction·x`。

> **注意 —** `VexpEup` 的 bf16 路径通过 `VectorU32Constant` 加载 `0x3fb93fb9`,而不是 `.rodata` fp32;它是两个 bf16 `log2e` 值(`0x3fb9`)打包到一个 32 位 lane 中,即 f32 `log2e` `0x3fb8aa3b` 的 lane-width-2 形式。面向 bf16 的重新实现者必须打包 bf16 常量,而不是加载两次 f32 常量。参见 [EUP Lane-Width / Unpack](eup-lane-width-unpack.md) 了解子 lane 打包模型。

---

## EUP 结果 FIFO 深度和延迟边

上述系数会针对 EUP push→pop 延迟进行软件流水:晚期 decomposer 从其 pop 中分离出来的 `V*Decomposed` push,会留下一个 bundle 窗口,scheduler 必须用 Newton/poly 修正 VALU 工作(或无关的独立工作)填充该窗口。两个 libtpu 字面量限定这个窗口:一个**按代际区分的 FIFO 深度**和一个**按代际区分的延迟边**

### `kErf` FIFO 深度 — `ResultFifoEntryCount(kErf, ver)`

与运行时 `EupResultFifoEntry` proto 列表(一个 HW 状态*snapshot*,不是大小常量;参见 [ResultFifo / ArchRegister 枚举](../isa/resultfifo-archregister.md))不同,物理 EUP 结果 FIFO 深度**确实**是一个 libtpu 字面量。`ResultFifoEntryCount`(`@0x1d631520`,对 `ResultFifo` 的 25-arm switch)通过 `.rodata` `0xb53e270` 处按 `TpuVersion` 区分的 `int[]` 表解析 `kErf` ordinal(`0x12`),并由 `TpuVersion < 6` gate:

```c
case kErf:                                  // ResultFifo ordinal 0x12
    if (version >= 6) Fatal("invalid platform type")
    return (uint32_t[6]){4, 4, 32, 16, 16, 16}[version]   // .rodata @0xb53e270
TpuVersiongenerationkErf depth
0Jellyfish4
1Dragonfish4
2Pufferfish32
3Viperfish16
4Ghostlite (V5e/V6e-class)16
56acc60406 (TPU7x-class)16

六个深度值 {4, 4, 32, 16, 16, 16} 是从 0xb53e270 按字节精确读取的。TpuVersion → generation 顺序在此函数中没有标注;它是成本模型其余部分索引的同一个 TpuVersion 枚举(ordinal 0 = Jellyfish,见 ResultFifo / ArchRegister 枚举)。一个强一致性信号支持该绑定:TpuVersion 0/1 的深度 4 精确等于 Jellyfish/Dragonfish 的 push→pop 延迟 clamp 4(见下文),在旧代际上 FIFO 刚好容纳一个延迟窗口中的在途 EUP 结果。

注意 — 存在两个不同的 EUP-FIFO 数量,不得混淆。运行时 EupResultFifoEntry proto 是在途结果的 repeated-message snapshot(硬件状态 dump,没有固定大小)。ResultFifoEntryCount(kErf, ver) 是 scheduler 强制的独立编译期深度,而且它确实是 libtpu 字面量:0xb53e270 处的 int[6] {4, 4, 32, 16, 16, 16}

Push→Pop 延迟边

延迟边是修正软件流水填充的深度。旧 Jellyfish/Dragonfish 路径在 LatencyTableJellyfish::LatencyBetweenInternal@0x1c8a0d60)中将该边 clamp 到 Performance[+0x30] = 4;较新的代际通过按指令的 heap array 路由。这些整数由 EUP 按代际延迟整数 拥有,这里只为修正窗口大小进行摘要:

genpush→pop latency edgekErf FIFO depthcorrection-window note
Jellyfish44一个延迟窗口的在途结果
Dragonfish44继承 Jellyfish(+0x30 unchanged)
Pufferfish732深 FIFO;半速 EUP issue(reservation 2)
Viperfish616
Ghostlite13 (F32) / 14 (BF16)16最深延迟;最大修正窗口

软件 fallback 发出的修正 VALU op 数量(例如 7 系数 tanh Horner = 约 11 个 FMA/mul op)按隐藏此延迟来确定大小:在 Ghostlite 上,13-cycle EUP 延迟为完整有理式留下空间;在 Jellyfish 上,4-cycle 边由较短的 Newton 步填满。该深度限定了 scheduler 必须让 push 停顿等待 pop drain 之前,可以同时在途的独立超越函数数量。

函数映射

FunctionAddressRole
ResultFifoEntryCount0x1d631520TpuVersion 区分的 FIFO 深度;kErf arm → 0xb53e270
LatencyTableJellyfish::LatencyBetweenInternal0x1c8a0d60JF/DF push→pop clamp 到 Performance[+0x30]=4
kErf depth table.rodata 0xb53e270int[6] {4,4,32,16,16,16}

交叉引用

  • EUP / 超越函数槽 — 编码视角:裸 push + pop、V*Decomposed builder、函数 selector 映射,以及这些系数所补充的二元性
  • EUP 延迟概览 — 修正窗口供给的 push→pop 软件流水成本模型
  • EUP 按代际延迟整数 — 按字节固定的 PF/VF/GL push→pop 延迟整数(7 / 6 / 13–14)和 Get<Gen>Instruction → GetLatency 机制
  • EUP Payne-Hanek 范围规约 — 与 sin/cos EUP push 配对的整数 1/(2π) 规约表(这里未编目的三角函数参数规约半边)
  • EUP Lane-Width / UnpackVexpEup 的 packed 0x3fb93fb9 log2e immediate 背后的 bf16 子 lane 打包
  • ResultFifo / ArchRegister 枚举kErf(ResultFifo 0x12)、EupResultFifoEntry runtime proto,以及 ResultFifoEntryCount 的完整 25-arm 深度模型