EUP 修正系数
本页中的每个偏移、
u32和浮点数都从libtpu-0.0.40-cp314wheel 中的libtpu.so按字节精确读取(BuildID md589edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,未 stripped)。.text和.rodataVMA 等于其文件偏移;.data.rel.roVMA 减去0x200000等于其文件偏移。下面每个系数u32都已在所引用的.rodata偏移处通过对文件执行struct.unpack确认。其他 libtpu 构建会有所不同。
摘要
EUP / 超越函数槽 页面确立了核心结构事实:在支持 EUP 的代际上,九个 V*Decomposed builder 会发出裸硬件 push + 裸 pop,除此之外没有任何内容,也就是说原始硬件超越函数就是答案,没有内联修正多项式。本页是该编码视角的数值补充。它逐字节编目了 software 超越函数代码加载的 fp32 .rodata 魔数,这些系数实际存在于两个位置:
- 共享的 Newton / rational-minimax 精化 helper,少数对精度敏感的函数会把它们包在原始 push 周围,或把它们用作软件路径:
EmitRecpNrIteration、EmitRsqrtNrIteration、EmitTanhPolyApproximation、EmitAtan2Approximation。这些是recip/rsqrtNewton 步以及tanh/atan2有理式。 *NoEupF32软件 fallback,当某个代际或数据类型缺少硬件 EUP 时,它们完全用 VALU 实现超越函数:VexpNoEupF32、Vexpm1NoEupF32、VlnNoEupF32、Vln1pNoEupF32、Vlog2NoEup、VtanhNoEupF32。这些存在的原因是没有硬件 EUPexp或ln,只有pow2和log2push,因此exp/expm1/ln/ln1p要么通过缩放硬件pow2/log2push 构造(*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 延迟边。
每个常量池条目都会被积极共享:0x84a2444(1.0)和 0x84a2dc8(log2e)各自出现在五个函数中。因此下面的偏移是每个常量的权威身份;同一个物理 word 会被不同函数中的不同 VALU op 消耗。
对于重新实现,契约是:
recipNewton 步y·(2 − x·y)(一个常量)和rsqrt步y·(1.5 − 0.5·x·y²)(两个常量)。tanh有理式x·P(x²)/Q(x²):clamp 窗口、小|x|线性阈值、7 系数分子P、4 系数分母Q、±1饱和,全部按.rodata加载顺序排列。atan28 系数奇 Horner,加上四个象限常量(π/2、π、π/4、3π/4)以及 NaN/inf 哨兵。exp/expm1/ln/ln1p/log2no-EUP 多项式及其完整系数列表和偏移,以及*Eup变体的缩放/修正常量。- 按代际区分的
kErfEUP 结果 FIFO 深度,以及修正窗口隐藏的 push→pop 延迟边。
| 精化 emitter | jellyfish::Emit{Recp,Rsqrt}NrIteration、EmitTanhPolyApproximation、EmitAtan2Approximation |
| No-EUP fallback | LloRegionBuilder::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/x 或 1/sqrt(x) 的现有估计 y 精化到完整 fp32 精度,并由 VrecpNr(除法 / 高精度倒数)和 rsqrt 精度模式调用。二者都接收参数 x 和当前估计 y,并返回一个改进后的估计。
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.175494e−38 | `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 / 0x84a2444VdivF32 操作数 trace 确定了奇偶性:与 clamp 后的 x 相乘的 accumulator(形成奇函数 x·P)是分子;裸偶 Horner 是分母。这使 tanh 在构造上就是奇函数。
分子 P(x²) — 7 个系数,在 x² 中按 low→high Horner,整个 P 随后 ×x:
| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| c0 | 0xa59f25c0 | -2.7607683663038313e-16 | 0x84a2654 |
| c1 | 0x2a61337e | 2.0001879384549948e-13 | 0x84a2428 |
| c2 | 0xaebd37ff | -8.604671836165423e-11 | 0x84a2ef4 |
| c3 | 0x335c0041 | 5.122297253024044e-08 | 0x84a27d8 |
| c4 | 0x3779434a | 1.4857223504805006e-05 | 0x84a24f0 |
| c5 | 0x3a270ded | 0.0006372619536705315 | 0x84a242c |
| c6 | 0x3ba059dc | 0.004893524572253227 | 0x84a24a0 |
分母 Q(x²) — 4 个系数,在 x² 中按 low→high Horner:
| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| d0 | 0x35a0d3d8 | 1.1982583600911312e-06 | 0x84a2e8c |
| d1 | 0x38f895d6 | 0.00011853470641653985 | 0x84a2658 |
| d2 | 0x3b14aa05 | 0.0022684347350150347 | 0x84a2564 |
| d3 | 0x3ba059dd | 0.0048935250379145145 | 0x84a27dc |
括号范围和饱和:
| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| clamp lo | 0xc1100000 | -9.0 | 0x84a283c |
| clamp hi | 0x41100000 | +9.0 | 0x84a2a84 |
| small thresh | 0x39d1b717 | 0.00039999998989515007 | 0x84a2758 |
| saturate −1 | 0xbf800000 | -1.0 | 0x84a26cc |
| saturate +1 | 0x3f800000 | 1.0 | 0x84a2444 |
注意 — 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 的 r² 中 8 系数 Horner,再做象限/符号修正。Horner 从交替的 VimmF32 / VmulF32 + VaddF32 链读取(这里 emitter 使用显式 mul+add,而不是 FMA)。
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:
| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| p0 | 0x3e013d7b | 0.12621109187602997 | 0x84a2c14 |
| p1 | 0x3e5c9fc9 | 0.21545328199863434 | 0x84a2598 |
| p2 | 0xbe540971 | -0.20706726610660553 | 0x84a28fc |
| p3 | 0xbe74b2ad | -0.23896284401416779 | 0x84a29d4 |
| p4 | 0x3e936e69 | 0.28795173764228821 | 0x84a26e0 |
| p5 | 0xbeb8ae28 | -0.36070370674133301 | 0x84a2874 |
| p6 | 0x3ef639b7 | 0.4809090793132782 | 0x84a2684 |
| p7 | 0xbf38aa38 | -0.72134733200073242 | 0x84a2a3c |
| p8 | 0x3fb8aa3b | 1.4426950216293335 | 0x84a2dc8 |
| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| sqrt2 split | 0x3fb504f3 | 1.4142135381698608 | 0x84a2524 |
−inf (x==0) | 0xff800000 | -inf | 0x84a2578 |
| NaN (x<0) | 0x7fc00000 | nan | 0x84a2ff8 |
整数 mask 0x7fffff(mantissa)和 0x3f800000(set-exponent-1)是 LloModule::VectorU32Constant immediate(由 esi 携带),不是 .rodata fp32 加载;p8(0x84a2dc8)与 exp 的 log2e 是同一个物理 word。
ln — VlnNoEupF32(@0x1d534740)
ln(x) = Vlog2NoEup(x) · ln2。该函数是一个两行 wrapper:调用 Vlog2NoEup,然后乘以 ln2。
| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
ln2 | 0x3f317218 | 0.6931471824645996 | 0x84a25f4 |
log1p — Vln1pNoEupF32(@0x1d534a20)
log1p(x) = ln(1 + x),使用专门面向近 1 参数的 8 系数 Horner(外加在 x² 重组合中消耗的第 9 个系数 −0.5),并通过 VpairFloatAddF32 双 word 重构应用 Cody-Waite ln2 拆分。尾数/指数路径镜像 Vlog2NoEup;区别在于输入是 1 + x,且多项式作用于重新居中的有效数。
8 系数 Horner(low→high):
| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
| q0 | 0xbd43a4d3 | -0.0477646104991436 | 0x84a2560 |
| q1 | 0x3dda59bb | 0.10661645978689194 | 0x84a2940 |
| q2 | 0xbe066c58 | -0.13127267360687256 | 0x84a2e00 |
| q3 | 0x3e13d018 | 0.14434850215911865 | 0x84a27d4 |
| q4 | 0xbe2a7741 | -0.16647054255008698 | 0x84a2498 |
| q5 | 0x3e4cbc51 | 0.1999371200799942 | 0x84a2fe4 |
| q6 | 0xbe800036 | -0.25000160932540894 | 0x84a2838 |
| q7 | 0x3eaaaabf | 0.33333393931388855 | 0x84a2b50 |
| 作用 | u32 | f32 | .rodata off |
|---|---|---|---|
recombine −0.5 | 0xbf000000 | -0.5 | 0x84a28f4 |
ln2-hi | 0x3f317200 | 0.693145751953125 | 0x84a2f44 |
ln2-lo | 0x35bfbe8e | 1.428606765330187e-06 | 0x84a282c |
−inf (1+x==0) | 0xff800000 | -inf | 0x84a2578 |
| NaN (1+x<0) | 0x7fc00000 | nan | 0x84a2ff8 |
函数映射
| Function | Address | Method |
|---|---|---|
EmitRecpNrIteration | 0x1d5a9ec0 | recip Newton y·(2−xy) |
EmitRsqrtNrIteration | 0x1d5a9e20 | rsqrt Newton y·(1.5−0.5xy²) |
EmitTanhPolyApproximation | 0x1d5a9f40 | tanh 有理式 x·P(x²)/Q(x²) |
EmitAtan2Approximation | 0x1d5aa1c0 | atan2 8-coeff 奇 Horner + 象限 |
VexpNoEupF32 | 0x1d533820 | 2^(x·log2e) Cody-Waite + 5-term Taylor |
Vexpm1NoEupF32 | 0x1d533ec0 | expm1 自有 4-coeff Taylor + selectors |
Vlog2NoEup | 0x1d556a60 | 尾数提取 + 9-coeff Horner |
VlnNoEupF32 | 0x1d534740 | log2 · ln2 |
Vln1pNoEupF32 | 0x1d534a20 | log1p 8-coeff Horner + Cody-Waite |
VtanhNoEupF32 | 0x1d535b20 | thunk → EmitTanhPolyApproximation |
VrecpNr | 0x1d5580c0 | recip Newton + 饱和 wrapper |
使用 EUP 的变体 — 在硬件 Push 上缩放 / 修正
exp、expm1 和 ln1p 也有使用 EUP 的变体,它们依赖硬件 pow2/log2 push,并且只在其周围应用小的 fp32 缩放或修正。这些用于支持 EUP 的代际,在那里硬件超越函数可用,但所需函数(exp/ln1p)不是直接硬件 op。
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 @0xb53e270TpuVersion | generation | kErf depth |
|---|---|---|
| 0 | Jellyfish | 4 |
| 1 | Dragonfish | 4 |
| 2 | Pufferfish | 32 |
| 3 | Viperfish | 16 |
| 4 | Ghostlite (V5e/V6e-class) | 16 |
| 5 | 6acc60406 (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 数量,不得混淆。运行时
EupResultFifoEntryproto 是在途结果的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 按代际延迟整数 拥有,这里只为修正窗口大小进行摘要:
| gen | push→pop latency edge | kErf FIFO depth | correction-window note |
|---|---|---|---|
| Jellyfish | 4 | 4 | 一个延迟窗口的在途结果 |
| Dragonfish | 4 | 4 | 继承 Jellyfish(+0x30 unchanged) |
| Pufferfish | 7 | 32 | 深 FIFO;半速 EUP issue(reservation 2) |
| Viperfish | 6 | 16 | |
| Ghostlite | 13 (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 之前,可以同时在途的独立超越函数数量。
函数映射
| Function | Address | Role |
|---|---|---|
ResultFifoEntryCount | 0x1d631520 | 按 TpuVersion 区分的 FIFO 深度;kErf arm → 0xb53e270 |
LatencyTableJellyfish::LatencyBetweenInternal | 0x1c8a0d60 | JF/DF push→pop clamp 到 Performance[+0x30]=4 |
kErf depth table | .rodata 0xb53e270 | int[6] {4,4,32,16,16,16} |
交叉引用
- EUP / 超越函数槽 — 编码视角:裸 push + pop、
V*Decomposedbuilder、函数 selector 映射,以及这些系数所补充的二元性 - EUP 延迟概览 — 修正窗口供给的 push→pop 软件流水成本模型
- EUP 按代际延迟整数 — 按字节固定的 PF/VF/GL push→pop 延迟整数(7 / 6 / 13–14)和
Get<Gen>Instruction → GetLatency机制 - EUP Payne-Hanek 范围规约 — 与
sin/cosEUP push 配对的整数1/(2π)规约表(这里未编目的三角函数参数规约半边) - EUP Lane-Width / Unpack —
VexpEup的 packed0x3fb93fb9log2e immediate 背后的 bf16 子 lane 打包 - ResultFifo / ArchRegister 枚举 —
kErf(ResultFifo0x12)、EupResultFifoEntryruntime proto,以及ResultFifoEntryCount的完整 25-arm 深度模型