VPU(Vector-ALU)Slot
本页所有地址都适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,未 strip)。.text与.rodataVMA 等于其文件偏移。其他 libtpu 构建会不同。
摘要
VPU 是 TensorCore 的逐 lane 向量 ALU:它在架构上的 8 sublanes × 128 lanes = 1024-element 向量寄存器上执行逐元素算术。在 VLIW bundle 中,它表现为一个或多个 VectorAlu* 子 bundle;重新实现者必须序列化该 slot,才能驱动向量 add/mul/min/max/shift/select/compare/convert/pack,并把 transcendental 推入 extended-unary pipeline。不同于 SSA 后端中 scheduler 在运行时追踪 hazard,TPU bundle 就是 issue packet:encoder 把每个存在的 VALU slot 放入 bundle byte buffer 中按代固定的 bit offset;缺失 slot 用 never-execute predicate 填充。不存在逐指令 header;opcode 立即数同时选择操作,并且在较老代际上还选择 vector-mask register 或 transcendental function。
VPU slot 不是单一 wire format,而是六种格式的家族。存在两条 encoder lineage。Jellyfish 及其字节相同的变体 Dragonfish(EncoderJf 路径)通过直接 and/shl/or 位操作,把 VALU word 打包为 64 位值并 OR-merge 到 bundle。Pufferfish 和所有 v5+ 代际(Viperfish、Ghostlite、6acc60406)则使用统一的表驱动 BitCopy(dst, dst_bit, src, src_bit, nbits) primitive(@0x1fa0a900),由按 opcode keyed 的 jump table 到达每个 opcode 的 Encode<gen>VectorAluN<Op> helper 来驱动。沿 lineage 发展,slot 数量从 2 → 4,opcode 字段从 6 → 7 → 8 bits 加宽,逐 slot predicate 字段从 5 → 4 → 2 bits 缩小,单个 XLU(transcendental)变成一对;每个变化都是对更宽计算结构的有意响应,而不是 padding。
本页把该 slot 作为重新实现目标进行记录:opcode enum 及其 op-family 分组;opcode / destination / 两个 source / Y-operand selector / predicate 的精确 bit 位置,全部锚定到已验证的 BitCopy 立即数;lane 几何;Y-operand(source-B)选择器模型;EUP/XLU push-pop 协议;predicate 和 vector-mask 寄存器文件;以及 JF→GF 演化。
重新实现的契约是:
- 两个 encoder 家族和通用
BitCopybit-packing primitive:何时直接打包,何时表分派。 - 每代字段布局:opcode、destination vreg、两个 source vreg、5 位 Y-operand selector 和 predicate 字段,在 bundle 内的精确 bit offset。
- 每代
VectorAluOpcode空间(6/7/8 位;63 / 131-op enum),按家族分组,加上VectorAluYEncoding(0..31)source-B 模型。 - lane 几何(通用 8×128)和 EUP/XLU push-pop,包括 v5+ 对 push 限制在 VALU slot 3。
| Slot proto | VectorAluInstruction (JF/DF/PF) / TensorCoreVectorAlu[0..3] (v5+) |
| 通用 bit-packer | BitCopy(void*, int dst_bit, const void*, int src_bit, int nbits) @ 0x1fa0a900 (_Z7BitCopyPviPKvii) |
| Opcode enum | VectorAluOpcode dense 0..62(VectorAluOpcode_descriptor @ 0x1fa1fca0);v5+ proto TensorCoreVectorAlu.<Op>H(131 ops) |
| Y-operand enum | VectorAluYEncoding dense 0..31(@0x1fa1fc40)— vreg / VS0-2 / IMM0-5 / hardwired constants |
| Lane 几何 | 8 sublanes × 128 lanes = 1024 elements / vreg(所有代际) |
| 寄存器文件 | v0..v1023 架构寄存器;6 位 slot window(PF/v5+)→ 每 slot 可直接寻址 64 个 |
| XLU push-pop | VALU push → EUP pipeline;VectorResult PopEupResult 在一个或多个 bundle 后 pop;single-issue("1 XLU Busy") |
两个 Encoder 家族
目的
重新实现者的第一个决定,是目标代际属于哪条 encoder lineage,因为两条路径会从同一条逻辑 VALU 指令产生互不兼容的 wire format。lineage 决定字段是由手写 shift 放置,还是由通用 copy primitive 放置,也决定每个 opcode 是否有自己的 emitter。
算法
Jellyfish 路径内联打包 slot。EncoderJf::EncodeVectorAluInstruction(@0x1e864f00)会按宽度 mask 每个字段并 shift 到 64 位 accumulator 中的位置,然后把 accumulator OR 到 bundle buffer:
function EncoderJf_EncodeVectorAluInstruction(inst, slot, bundle): // @0x1e864f00
if slot > 1: fatal("slot < kMaxVectorAluSlotsPerBundle") // only 2 VALU lanes
pred = inst.predicate & 0x1f // 5-bit predicate (and 0x1f)
op = inst.opcode & 0x3f // 6-bit opcode (and 0x3f)
if op >= 0x3e: error // opcode range 0..62 (cmp 0x3e=62)
if op == 0x18 or IsEupOpcode(op): reserve_xlu() // ProtoUtils::IsEupOpcode @0x1e875900
if slot == 0: // lane 0 → struct 0x1D window (abs 136..167)
bundle[0x1D] |= (Vx & 0x1f) << 0 // Vx @ abs 136
| (op & 0x3f) << 5 // op @ abs 141 (the "32 *" multiply)
| (pred & 0x1f) << 11 // pred@ abs 147
else: // lane 1 → struct 0x16 cross-word (abs 90..127)
word = (yenc & 0x1f) << 10 // Y-enc @ abs 90
| (Vx & 0x1f) << 25 // Vx @ abs 105 (binary-op path)
| (op & 0x3f) << 30 // op @ abs 110 (shl 0x1e)
| (pred & 0x1f) << 36 // pred @ abs 116
| (dst & 0x1f) << 41 // dst @ abs 121
bundle[0x16] |= word // OR-merge into 56-bit window
EncodeVectorAluYEncoding(inst, slot, bundle) // @0x1e864be0 — resolves Y-operand source
```text
Pufferfish 和 v5+ 路径改为经过逐 slot `Encode` dispatcher:它按 opcode 走 jump table 并 tail-call 逐 op helper;每个字段都由 `BitCopy` 写入:
```c
function VxcTensorCoreVectorAlu0Encoder_Encode(proto, out_span): // VF @0x1eef8a80
op = proto.opcode // read at proto +0x50
BitCopy(out, 306, &proto.predicate, 0, 4) // 4-bit predicate @ bit 306 (mov esi,0x132)
BitCopy(out, 299, &op, 0, 7) // 7-bit opcode @ bit 299 (mov esi,0x12b)
if op >= 0x80: error // opcode range 0..128 (cmp 0x80=128)
helper = jump_table[op] // table @ rodata 0xb84600c
helper(out, proto) // e.g. EncodeTensorCoreVectorAlu0VectorFloatAddBitCopy 本身是每个 v5+ 字段写入都会汇入的单一 primitive:rdi=destination buffer,esi=bundle 内 destination bit offset,rdx=source value 指针,ecx=source bit(几乎总是 0),r8d=bit count。它把 src[src_bit..] 中的 nbits 复制到 dst[dst_bit..],所以本页每个字段 offset 都表现为一次 call 0x1fa0a900 之前紧邻的 mov esi, <bit> / mov r8d, <width> 对。
函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
BitCopy(void*,int,const void*,int,int) | 0x1fa0a900 | 通用 v5+/PF bit-packer |
EncoderJf::EncodeVectorAluInstruction | 0x1e864f00 | JF/DF 直接打包 VALU encoder |
EncoderJf::EncodeVectorAluYEncoding | 0x1e864be0 | JF Y-operand selector encode |
EncoderJf::EncodePredication<VectorAluInstruction> | 0x1e864000 | JF 逐 slot predicate encode |
EncoderJf::EncodeBundleInternal | 0x1e86c7c0 | 对每个存在的 slot 调用 VALU encoder |
ProtoUtils::IsEupOpcode(VectorAluOpcode) | 0x1e875900 | JF EUP-push 分类器 |
pxc::isa::TensorCoreVectorAlu0Encoder::Encode | 0x1ed45060 | PF VALU0 dispatcher(struct …Alu0) |
pxc::isa::TensorCoreVectorAlu1Encoder::Encode | 0x1ed68d80 | PF VALU1 dispatcher(struct …Alu1) |
vxc::isa::TensorCoreVectorAlu{0..3}Encoder::Encode | 0x1eef8a80 / 0x1ef1c500 / 0x1ef3f120 / 0x1ef62880 | VF 4 个 VALU dispatcher(共享 struct) |
gxc::glc::isa::TensorCoreVectorAlu0Encoder::Encode | 0x1f250160 | Ghostlite VALU0 dispatcher |
gxc::gfc::isa::TensorCoreVectorAlu0Encoder::Encode | 0x1f8b53c0 | 6acc60406 (GF) VALU0 dispatcher |
gxc::{glc,gfc}::isa::SparseCoreTecVectorAlu{0..2}Encoder::Encode | 0x1eaa4880… / 0x1ec11100… | SparseCore TEC 3 个 VALU slot |
特例 — Pufferfish 给 VALU0 和 VALU1 使用不同 struct 类型(
TensorCoreVectorAlu0vsTensorCoreVectorAlu1),且 VALU1 接受更宽 opcode 范围:cmp rcx,0x43(67)对比 VALU0 的cmp rcx,0x3e(62)。VALU1 带有一些 VALU0 没有的 op。从 Viperfish 开始,四个 slot 共享一个TensorCoreVectorAlustruct 和一个 op 范围,所以重新实现可使用单一 encoder template;在 Pufferfish 上则不能。
Bit-Field 布局 — 按代际
目的
这是 byte-level wire format:每个字段位于 bundle byte buffer 的哪里。本页所有 bit position 都是 LSB-first:bit 0 是 byte 0 的最低有效位,匹配 Bundle Model 全文使用并由 BitCopy packer 强制执行的约定(它从 LSB 编号的 dst_bit 起向上写 nbits)。encode 路径中不存在任何 MSB-first 排序。下面所有 v5+ offset 都直接读取自代表性 VectorFloatAdd / VectorF32Add helper 中的 BitCopy mov esi/mov r8d 立即数;Jellyfish offset 来自 EncodeVectorAluInstruction 中的 and/shl 立即数。
编码
无论代际,encoder 都读取一组统一 struct 字段。VALU 指令的 opcode 位于 proto +0x50;operand descriptor(destination / sources / Y-encoding)挂在 +0x48;逐 slot predicate 是自己的字段。
// Field reads (proto offsets, consistent across the v5+ helpers):
proto +0x50 : VectorAluOpcode (the op immediate)
proto +0x48 : operand descriptor → { dst vreg, src0 vreg, Y-encoding, src1 vreg }
: per-slot predicate field source
```text
**Jellyfish / Dragonfish**(41-byte TC bundle,`EncoderJf::EncodeVectorAluInstruction` @ `0x1e864f00`)。直接 `and`/`shl`/`or`,*不是* `BitCopy`。两个 VALU lane 占用 328-bit bundle 中两个*独立* window,而不是一个重复 stride:lane 0(`slot == 0`)打包到 struct-`0x1D` window(绝对 bit 136..167),lane 1(`slot == 1`)打包到 struct-`0x16` cross-word window(绝对 bit 90..127)。每个 lane 内字段由字面 shift 常量放置;由于两个 window origin 不同,每个字段的绝对 bit 也随 lane 不同(raw shift 共享相对布局)。opcode 被 mask 为 `and 0x3f`(6-bit,范围 0..62,带 `cmp 0x3e` guard),predicate 为 `and 0x1f`(5-bit);register 和 Y-encoding 字段是 5-bit window。Dragonfish 共享 `EncoderJf` 和 `JellyfishCodecMetadata`,因此字节相同。交叉检查出的绝对位置(LSB-first,也列在 [Jellyfish 41-bit Bundle](bundle-jf-41b.md) 中):
| 字段 | 宽度 | Raw shift | Lane 0 bit(struct `0x1D`) | Lane 1 bit(window `0x16`) |
|---|---|---|---|---|
| Y-encoding(src1 vreg) | 5-bit | `<< 10` | —(slot-3 路径) | 90 |
| Vx(src0 vreg) | 5-bit | `0` / `<< 25` | 136 | 105 |
| opcode | 6-bit | `<< 5` / `<< 30`(`shl 0x1e`) | 141 | 110 |
| predicate | 5-bit | `<< 11` / `<< 36` | 147 | 116 |
| dst vreg | 5-bit | `<< 41` |(在 `0x1D` tail 中) | 121 |
> **陷阱 — JF VALU 是两个不同 window,不是 stride。** Lane 0 写 byte `0x1D`/qword2;lane 1 写 byte `0x16` 处的 56-bit cross-word(组装为 `dword[0x16] | word[0x1A]<<32 | byte[0x1C]<<48`)。若重新实现像 v5+ slot 那样通过给 lane 0 加固定 offset 来推导 lane 1,会把每个 lane-1 字段都放错。`EncodeVectorAluInstruction` 中的 shift 常量是相对于各 lane window origin(lane 0 为 136,lane 1 为 80)的,因此同一逻辑字段会落到不同绝对 bit,例如 opcode 在 lane 0 的 bit 141 和 lane 1 的 bit 110。
**Pufferfish**(51-byte TC bundle)。VALU0 `Encode` @ `0x1ed45060`,VALU1 @ `0x1ed68d80`。`BitCopy` 驱动,6-bit register 字段(64-window)。
| 字段 | 宽度 | VALU0 bit | VALU1 bit |
|---|---|---|---|
| predicate | 5-bit | 236 (`0xec`) | 193 (`0xc1`) |
| opcode range | 6-bit | `cmp 0x3e` (0..62) | `cmp 0x43` (0..67) |
| dst vreg | 6-bit | 230 (`0xe6`) | — |
| immediate(逐 imm-op) | 16-bit each | 272 / 288 / 304 / 320 / 338 | — |
| NOP fill | — | predicate ← `0x1f` (`kNeverExecute`) | same |
**Viperfish**(64-byte TC bundle)。四个 slot,共享 struct,**34-bit per-slot stride**。VALU0 `Encode` @ `0x1eef8a80`;代表性 `VectorFloatAdd` helper(op `0x0c`)@ `0x1eefa2c0`:
| 字段 | 宽度 | VALU0 bit | 来源 |
|---|---|---|---|
| opcode | 7-bit | 299 (`0x12b`) | `mov esi,0x12b; r8d,7` |
| dst vreg | 6-bit | 276 (`0x114`) | `mov esi,0x114; r8d,6` |
| src vreg | 6-bit | 282 (`0x11a`) | `mov esi,0x11a; r8d,6` |
| src vreg | 6-bit | 293 (`0x125`) | `mov esi,0x125; r8d,6` |
| Y-encoding | 5-bit | 288 (`0x120`) | `mov esi,0x120; r8d,5` |
| predicate | 4-bit | 306 (`0x132`) | `mov esi,0x132; r8d,4` |
| opcode range | — | `cmp 0x80` (0..128) | dispatcher |
四个 predicate 字段位于 bit 306 / 272 / 238 / 204(VALU0..3),统一 −34-bit 步进,所以 slot 占据 512-bit bundle 上部三分之一中的 `{opcode 7 + dst 6 + 2 src 6 + Y-enc 5 + pred 4} = 34` bit。
**Ghostlite**(64-byte TC bundle)。VALU0 `Encode` @ `0x1f250160`:predicate `BitCopy(…,309,…,4)`(`0x135`),opcode `BitCopy(…,302,…,7)`(`0x12e`),dispatch `cmp 0x83`(0..131)。6-bit register 字段,与 Viperfish 相同 template,但因加宽的 slot start 整体 +3 bit。
**6acc60406 (GF)**(64-byte TC bundle)。VALU0 `Encode` @ `0x1f8b53c0`;代表性 `VectorF32Add` helper @ `0x1f8b7860`:
| 字段 | 宽度 | VALU0 bit | 来源 |
|---|---|---|---|
| opcode | **8-bit** | 293 (`0x125`) | `mov esi,0x125; r8d,8` |
| dst vreg | 6-bit | 276 (`0x114`) | `mov esi,0x114; r8d,6` |
| src0 vreg | 6-bit | 270 (`0x10e`) | `mov esi,0x10e; r8d,6` |
| src1 vreg | 6-bit | 287 (`0x11f`) | `mov esi,0x11f; r8d,6` |
| Y-encoding | 5-bit | 282 (`0x11a`) | `mov esi,0x11a; r8d,5` |
| predicate | **2-bit** | 301 (`0x12d`) | `mov esi,0x12d; r8d,2` |
| opcode range | — | `cmp 0x83` (0..131) | dispatcher |
> **陷阱 —** 6acc60406 (GF) 的 predicate 字段只有 2 bit,*不足以*命名 16 个 predicate register 之一。它在 `{pred_0, pred_1, always, never}` 之间选择:bundle 的两个 active dual predicate 由专用 `TensorCorePredicates` slot 写入,VALU slot 只选择两者中哪一个适用。把 2-bit 字段当作 4-register 索引的重新实现,会把每个 GF VALU op 的 predicate 都弄错。见 [Predicate Slot](slot-predicate.md)。
### 每代 Slot 位置
| Gen | Bundle | #VALU | Lane-0 opcode bit | Lane-0 pred bit | Pred width | Per-slot stride |
|---|---|---|---|---|---|---|
| Jellyfish (v2) | 41 B | 2 | 141(lane 1 op @110) | 147(lane 1 @116) | 5-bit | two windows (136 / 80) |
| Dragonfish (v3 var) | 41 B | 2 | alias of Jellyfish | alias of Jellyfish | 5-bit | two windows (136 / 80) |
| Pufferfish (v4) | 51 B | 2 |(switch 分派) | 236; lane 1 @193 | 5-bit | distinct structs |
| Viperfish (v5p) | 64 B | 4 | 299 | 306 | 4-bit | 34 bits/slot |
| Ghostlite (v6e) | 64 B | 4 | 302 | 309 | 4-bit | ~34 bits/slot |
| 6acc60406 (TPU7x) | 64 B | 4 | 293 | 301 | 2-bit | ~34 bits/slot |
| SparseCore TEC | 64 B | 3 | `SparseCoreTecVectorAlu0..2`(同 template,SC bundle) | — | 4/2-bit | 未 leaf-decoded |
代际到 codename 的映射由 codec-metadata table 固定(见 [Bundle Model](bundle-model-overview.md)):`kJellyfish`=v2,`kDragonfish`=v3,`kPufferfish`=v4,`kViperfish`=v5p,`kGhostlite`=v6e,`k6acc60406`=TPU7x。二进制命名空间相应为 `jellyfish`(JF/DF,共享 proto)、`pxc`(PF)、`vxc`(VF)、`gxc::glc`(GL)、`gxc::gfc`(GF)。
---
## VectorAlu Opcode Enum — 按家族
### 目的
opcode 立即数选择操作。enum 有两个命名代际:dense Jellyfish `VectorAluOpcode`(`0..62`,63 个值)和 v5+ `TensorCoreVectorAlu.<Op>H` proto-message 集(Viperfish 上 131 个 op,Ghostlite/6acc60406 上 132 个)。二者覆盖同一套逻辑能力;只有 dtype 拆分和 Vmsk 处理方式不同。这里按家族分组,而不是平铺转储;SparseCore TEC 142-op 枚举(同一批家族更细粒度的 dtype 拆分)在源码中是字节精确的,并在本节末尾概述。
### 编码
proto enum descriptor 及其 dense range 可从 mangled `NameOfDenseEnum` template 实例恢复:
```text
NameOfDenseEnum<VectorAluOpcode, 0, 62> @ 0x22331a58 → 63 op values
NameOfDenseEnum<VectorAluYEncoding, 0, 31> @ 0x223dff40 → 32 Y-selector values
NameOfDenseEnum<VectorExtendedOpcode, 0, 34> @ 0x2239bce8 → 35 EUP/MXU staging ops
NameOfDenseEnum<VectorResultOpcode, 0, 2> @ 0x2239bd00 → 3 pop opsv5+ op 家族分组如下(代表性 proto 名;H 后缀 = v5+ proto-message 命名):
| 家族 | Op(代表) | 备注 |
|---|---|---|
| Float arithmetic | VectorFloatAdd, Subtract, Multiply, Max, Min | f32/bf16 lanes |
| Float compare | FloatEq/Neq/Gt/Gte/Lt/Lte, TotalLt/TotalLte, InfOrNan | 产生 vector mask |
| Integer arithmetic | IntegerAdd/Subtract/Multiply/Carry | s16/s32/u16/u32 |
| Integer compare | IntegerEq/Neq/Gt/Gte/Lt/Lte | 产生 vector mask |
| Bitwise | BitwiseAnd/Or/Xor | |
| Shift | LogicalShiftLeft/Right, ArithmeticShiftRight | |
| Move / misc | Move, Clamp, Classify, Relux, Ceiling, Floor | |
| Bit count | CountLeadingZeros, PopulationCount, ByteNez | |
| Convert | ConvertF32To{Bf16,Bf8,Hf16,If8,Int32}[Stochastic], ConvertInt32ToF32 | + FP8/FP4 narrow,stochastic round |
| Mask gen | CreateMask, LaneId | |
| Select | VectorSelectVmsk0..15, VectorSelectNotVmsk0..15(PF/VF)/ VectorSelect+VectorSelectNot(GL/GF) | 消费 Vmsk |
| Transcendental(EUP push) | Reciprocal, ReciprocalSqrt, Tanh, ShiftedSigmoid, LogTwo, PowTwo, EupPush | issue 到 XLU |
特例 — Pufferfish 和 Viperfish 上的 32 个
VectorSelect[Not]Vmsk0..15条目不是 32 个不同操作;它们是同一个 select op,只是 mask-register index(Vmsk0..15)被烘焙进 opcode。Ghostlite 和 6acc60406 把它们合并成单个VectorSelect/VectorSelectNotopcode,并把Vmskindex 移到独立字段(宽度未测量;很可能是 16 masks 的 4-bit — LOW)。重新实现必须知道目标代际使用哪种方案,否则要么会膨胀 opcode 空间,要么找不到 mask index。
lower 到这些 opcode 的 LLO IR mnemonic(几何后缀 .8x128 = 原生 vreg)在 .rodata 中确认了能力范围:vadd.8x128.{f32,bf16,s32,s16}、vmul.8x128.{f32,bf16,u32,u16}(加上宽 vmul.u32.u64 slot-pair)、vand/vor/vxor/vandn.8x128.u32、vshll/vshra/vshrl、vcmp.{f32,f64}、vsel.8x128、.xlane tree-reduce 家族、vcvt.* convert 集(包括 .sr stochastic-round 和 FP8/FP4 narrow 类型)、vpack/vunpack sub-byte 家族,以及 transcendental vrsqrt/vrcp/vtanh/verf/vsinq/vcosq/vpow 和匹配的 .pop 形式。
注意 — SparseCore TEC vector ALU 以更细的 dtype 粒度运行同一套家族分类:Ghostlite TEC consumer 枚举 142 个 op(92 个 integer/float core,18 个 transcendental = 9 个家族 ×
{f32,bf16},32 个 quant pack/unpack),而 Viperfish TEC 为 95 个(dtype 合并 op,没有cosq/erf/sinq)。这种拆分 —tanh→tanh_f32+tanh_bf16,unpack_*_sublanes_*→unpack_compressed_*_lanes_*_to_*— 是 SC TEC table 比 TensorCore proto enum 更大的原因;它在 SparseCore router analysis 中按 opcode 记录,而不在此处复述。
Lane 几何与 Operand Register File
目的
每个 VALU op 都作用于完整向量寄存器;几何是重新实现者布局 vreg 和推理 sub-byte packing 时必须复现的内容。
编码
原生 vreg 是 8 sublanes × 128 lanes = 1024 elements,所有代际通用(.8x128 mnemonic 后缀;HAL 访问按 (sublane, lane),例如 ReadVectorRegister(core, sublane, lane) @ 0x0e755c20)。小于 32-bit 的类型在 lane 内打包:
32-bit element : 8 × 128 = 1024 elements (.8x128)
16-bit element : 8 × 128 × 2 (bf16/s16/hf16, two per 32-bit lane; .8x128x2)
8-bit element : 8 × 128 × 4 (s8/u8/e4m3/e5m2, four per lane; .8x128x4)
4-bit / fp4 : 8 × 128 × 4 with sub-element packing
("32x16x128 only supports fp4 element types")
```text
V5+ 还为 layout-inference pass 暴露更宽的物理布局(`16x128`、`8x256`、`4x8x128`、`8x8x128`、twisted/untwisted);这些是多 vreg *tile aggregation*,不是逐指令 1024-lane count 的变化。
架构寄存器文件是 `v0..v1023`(`TPURegStrings` 中 1024 个名称)。一个 VALU slot 编码一个 destination 和最多三个 source vreg;在 Pufferfish 和 v5+ 上每个都是 **6-bit 字段**(Jellyfish 上是 5-bit register-class window)。六位可直接寻址 64 个寄存器,所以每个 slot 看到的是 1024-name 文件的一个 *window*;每个 subtarget 的 `getVyEncodings(unsigned)` map 会把架构 vreg 翻译为 slot encoding(当某个 vreg 不可在该 slot window 中编码时返回 `0xffffffff`):
| Subtarget | `getVyEncodings` |
|---|---|
| `TPUBcSubtarget`(PF BarnaCore) | `0x13c58de0` |
| `TPUVfcSubtarget`(Viperfish) | `0x13c5ec20` |
| `TPUGlcSubtarget`(Ghostlite) | `0x13c60a20` |
| `TPUGfcSubtarget`(6acc60406) | `0x13c625a0` |
> **注意 —** window-map 内容(哪个架构 vreg 映射到哪个 6-bit slot code)尚未转储;lookup 形状和 `-1` sentinel 已确认,逐项 table 尚未确认。重新实现者必须在编码真实程序前恢复每代 window assignment。
---
## Y-Operand(Source-B)选择器
### 目的
二元 VALU op 的第二个 source 不是普通 vreg 字段;它由一个 5-bit `VectorAluYEncoding` 值选择,该值可命名 vreg、共享 vector-source read port、immediate slot 或 hardwired constant。这一机制让常见 scale/bias 算术无需消耗 immediate slot。
### 编码
该字段为 5-bit(`VectorAluYEncoding` dense `0..31`,`@0x1fa1fc40`),由 operand-descriptor `+0x20` 经 `BitCopy` 写入。32 个值(从 `.rodata` `VECTOR_ALU_Y_*` 字符串逐字确认):
| 组 | 值 | 含义 |
|---|---|---|
| Vreg | `VREG` | 显式 vector register(使用 src1 vreg 字段) |
| VS ports | `VS0`, `VS1`, `VS2` | bundle 共享 vector-source read port 0/1/2(v5+ 上为 4 个 port) |
| Float constants | `FLOAT_ONE`, `FLOAT_TWO`, `FLOAT_NEGATIVE_ONE`, `FLOAT_ZERO_POINT_FIVE` | hardwired `1.0`/`2.0`/`-1.0`/`0.5` |
| Integer constants | `INTEGER_ONE`, `INTEGER_NEGATIVE_ONE`, `ZERO` | hardwired |
| Immediate slots | `IMM0_ZERO`..`IMM5_ZERO`, `ZERO_IMM0`..`ZERO_IMM5`, `ONES_IMM0`..`ONES_IMM5` | 引用 bundle imm slots 0..5,带 zero/ones extension |
| Paired-slot wide | `IMM1_IMM0`, `IMM3_IMM2`, `IMM5_IMM4` | 两个 imm slot 融合成宽 immediate |
VS ports 是稀缺 bundle 资源:同一 bundle 中的多个 VALU slot 共享少量 read port,packer 的 `SlotTracker` 会把它们计为 bundling constraint。hardwired float constant 是 scaling 和 bias op 不消耗 immediate slot 的原因。
---
## XLU(Transcendental)Push-Pop
### 目的
Transcendental — `rsqrt`、`rcp`、`tanh`、`sigmoid`、`log2`、`pow2`,以及 mnemonic-pool 的 `erf`/`sin`/`cos`/`pow` — 不在 VALU slot 内完成。它们从 VALU slot 被 *pushed* 进 Extended-Unary Pipeline(XLU),并在一个或多个 bundle 后从 `VectorResult` slot 被 *popped*。重新实现者必须把它建模为带有结构性 single-issue hazard 的两指令协议,而不是单周期 op。
### 算法
```c
// STAGE 1 — PUSH (issued from a VALU slot)
// VALU op = the XLU push. Proto names: ReciprocalH / ReciprocalSqrtH /
// TanhH / ShiftedSigmoidH / LogTwoH / PowTwoH / EupPushH (generic push).
// IsEupOpcode(op) @0x1e875900 classifies which opcodes are pushes so the
// packer reserves the XLU resource for this bundle.
//
// STAGE 2 — POP (issued one+ bundle later from a VectorResult slot)
// VectorResult op PopEupResultH reads the XLU result into a dest vreg.
// The .pop mnemonic suffix (vrsqrt.f32.pop, vrcp.bf16.pop, …) is this pop.在 Viperfish 上,push 限制为 VALU slot 3:唯一 EUP-push helper 是 EncodeTensorCoreVectorAlu3EupPush(@0x1ef6e400,vxc anonymous namespace),它把 7-bit opcode 放到 bit 197(0xc5)、5-bit function selector 放到 bit 186(0xba)、6-bit source 放到 bit 191(0xbf)。因为 XLU 是 single-issue,只有 Alu3(而不是 Alu0/1/2)发起 push;transcendental helper 只存在于 Alu3 集。
XLU 是 single-issue 硬件;诊断字符串 "1 XLU Busy" 存在于 .rodata,bundle cost model 把 XLU 分配到 overlap-blended resource,因此 pipeline drain 按约 50% residual overlap 计费。Jellyfish 有 1 个 XLU;v5+ 代际有 2 个(packer 的 AddXluRequirements 会相应 reserve)。
注意 — 单独的
VectorExtendedOpcodeenum(dense0..34,35 个 op,@0x1fa1fd00)是 MXU / matmul staging 路径:MatrixMultiply<fmt>、LoadMatrixRegister{Gmr,Lmr}、LaneBroadcast、LaneRotate、LoadStagingUpperBlock。它不同于 VALU transcendental push,但共享同一个 EUP pipeline 和同一个PopEupResultpop。见 MXU Slot。
函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
vxc::isa::EncodeTensorCoreVectorAlu3EupPush | 0x1ef6e400 | VF EUP push(仅 Alu3) |
jellyfish::isa::ProtoUtils::IsEupOpcode | 0x1e875900 | 对 EUP-push opcode 分类 |
proto::Arena::DefaultConstruct<gxc::glc::isa::TensorCoreVectorAlu_EupPush> | 0x1fb49c00 | Ghostlite EUP push proto |
proto::Arena::DefaultConstruct<…TensorCoreVectorResult_PopEupResult> | 0x1fb55b40 (glc) / 0x1fb9e660 (gfc) | result pop proto |
proto::Arena::DefaultConstruct<pxc::isa::TensorCoreVectorResult{0,1}_PopEupResult> | 0x1fa86240 / 0x1fa86ac0 | PF dual result pop |
Predicate 与 Vector-Mask Register Files
目的
两个独立寄存器文件会接触 VALU slot,而且很容易混淆。predicate 字段门控 slot 的 op 是否执行;vector mask(Vmsk)是 conditional select 的数据操作数。它们是不同文件。
编码
逐 slot predicate 字段宽度和 predicate register 数量:
| Gen | Pred width | VALU0 pred bit | 语义 | Pred regs |
|---|---|---|---|---|
| Jellyfish | 5-bit | (packed word) | 0..14 reg,15 = always,31 = never | 15 |
| Pufferfish | 5-bit | 236 (V0) / 193 (V1) | same | 15(BarnaCore 上 16) |
| Viperfish | 4-bit | 306 | 16 个 pred regs 之一 | 16 |
| Ghostlite | 4-bit | 309 | 16 个 pred regs 之一 | 16 |
| 6acc60406 | 2-bit | 301 | {pred_0, pred_1, always, never} | 16 |
v5+ subtarget 的 16-register 数量是已确认的 inline constant:getNumPredicateRegisters 对 TPUVfcSubtarget(@0x13c5f6e0)、TPUGlcSubtarget(@0x13c615c0)、TPUGfcSubtarget(@0x13c630e0)和 TPUBcSubtarget(@0x13c59780)都返回 mov eax,0x10; ret。Jellyfish/Pufferfish-TC 使用 base TPUSubtarget 的 15 个计数。
vector-mask 文件是 16 个寄存器(Vmsk0..15),不同于 predicate 文件。Compare op 产生 Vmsk;VectorSelect 消费一个。每代 select opcode/field 拆分见上面的 opcode-enum 小节。
注意 — 6acc60406 (GF) 的窄 2-bit predicate 之所以可行,是因为完整的逐 bundle predicate-register 写入已从 VALU slot 移到专用
TensorCorePredicatesslot。VALU slot 选择两个预写 dual predicate 中哪一个适用。精确的 2-bit value-to-meaning 映射(0=pred_0?3=never?)尚未解码 — LOW。
JF → GF 演化
该 lineage 是一个计算结构逐步加宽的连贯故事,而不是任意的逐代变动:
| 轴 | JF (v2) | PF (v4) | VF (v5p) | GL (v6e) | GF (TPU7x) |
|---|---|---|---|---|---|
| VALU slots | 2 | 2(不同 structs) | 4 | 4 | 4 |
| Encoder | direct and/shl/or | BitCopy | BitCopy | BitCopy | BitCopy |
| Slot struct | shared proto,two windows (136 / 80) | Alu0 ≠ Alu1 | shared | shared | shared |
| Opcode bits | 6 (0..62) | 6 (V0 0..62 / V1 0..67) | 7 (0..128) | 7 (0..131) | 8 (0..131) |
| Register field | 5-bit window | 6-bit | 6-bit | 6-bit | 6-bit |
| Y-encoding | 5-bit | 5-bit | 5-bit | 5-bit | 5-bit |
| Predicate field | 5-bit | 5-bit | 4-bit | 4-bit | 2-bit (dual) |
| Predicate regs | 15 | 15(16 BC) | 16 | 16 | 16 |
| Vmsk select | per-Vmsk opcode | per-Vmsk opcode | per-Vmsk opcode | single op + field | single op + field |
| XLUs | 1 | 1 | 2 | 2 | 2 |
叙事如下:Pufferfish 保留两个 slot,但切换到表驱动 encoder 和 64-window register,把两个 slot 拆成不同 struct(VALU1 更宽),并加入 vmul.u32.u64 slot-pair wide multiply。Viperfish 翻倍到四个 slot,把 opcode 加宽到 7 bit,以容纳 FP8/FP4 convert + stochastic-round + sublane pack/unpack 集,predicate 缩窄到 4 bit,并加入第二个 XLU。Ghostlite 把 32 个 per-Vmsk select opcode 折叠成一个 op 加 mask 字段,并按 dtype 拆分 reciprocal。6acc60406 (GF) 把 opcode 加宽到 8 bit(留出空间;当前最大 131),并把逐 slot predicate 缩小到 2-bit dual-predicate selector,因为 predicate-register 写已移到专用 slot。
陷阱 — v5+ 上 EUP push 是 slot-3-specific,不是 slot-agnostic。Viperfish encoder 只把 push helper 暴露为
EncodeTensorCoreVectorAlu3EupPush(slot 3);single-issue XLU 只由Alu3发起。
未解码内容
- 每个 PF/VF/GL/GF VALU op 的 leaf per-opcode 子字段 offset。binary-ALU / select / convert / pack-unpack / EUP-push template 已解码到精确 offset;其余 op 复用同一 template,仅 opcode 立即数变化,但特殊形式(sublane-masked pack/unpack、
vmul.u32.u64pair、CreateMask、LaneId)携带额外子字段,未逐 op 枚举。 - index-ordered
VectorAluOpcodevalue→name array(descriptor 和 dense0..62范围已定位;未遍历逐 index 名称)。 - v5+ 131-op enum value→name 映射(op 名称是已确认集合;只有少数 opcode 立即数,例如
VectorFloatAdd = 0x0c,已确认 index)。 - SparseCore TEC
VectorAlu0..2leaf bit layout(encoder 存在;SC TEC bundle 内的逐 slot bit offset 未逐一解码)。 - 每代
getVyEncodingswindow-map 内容。 - Ghostlite/6acc60406 上的 Vmsk-index 字段宽度(推断约 4-bit)。
- 6acc60406 (GF) 的 2-bit dual-predicate value 语义。
交叉引用
- Bundle Model — VALU slot 打包进入的 VLIW bundle;每代 byte width 和 codec metadata
- Viperfish 64-bit Bundle — 上下文中的绝对 VALU bit 位置,以及 Alu3 EUP push
- Jellyfish 41-bit Bundle — 完整 JF bundle 中的 direct-pack VALU encoder
- Pufferfish 51-bit Bundle — 上下文中的不同
Alu0/Alu1slot struct - Ghostlite Bundle — v6e VALU slot 和合并后的 select op
- EUP / Transcendental Slot — XLU push-pop pipeline 和 result pop
- MXU Slot — 共享 EUP 和
PopEupResult的VectorExtendedmatmul-staging 路径 - Predicate Slot — predicate register file 和 6acc60406 (GF) 的 dual-predicate slot
- VCreate / Mask / Mregister — select 消费的 16 个
Vmskvector-mask register - Pack/Unpack Precision — sub-byte convert 和 pack/unpack 家族语义
- LLO Opcode Enum — lower 到 VALU slot 的 LLO IR opcode
- MC Emitter — lower 到此编码的 LLVM MC
VADD*/VMUL*/VSEL*masked mnemonic - Performance / Cost Model — VALU op throughput 和 single-issue XLU overlap blend