ResultFifo 和 ArchRegister 枚举
本页中的每个偏移、值和地址都按字节精确读取自
libtpu-0.0.40-cp314wheel 中的libtpu.so(BuildID md589edbbe81c5b328a958fe628a9f2207d)。其他版本会有所不同。
摘要
TensorCore LLO 层命名了两个扁平枚举,供每操作码元数据表(opcode_info_big)和跨 lane 单元(XLU)调度器持续引用:ResultFifo,即 bundle 槽可以推入或弹出的 25 个硬件结果 FIFO;以及 ArchRegister,即每操作码读/写寄存器列表所表达的 50 成员物理架构寄存器编号。二者都不同于虚拟 LLO 寄存器空间。ResultFifo 是硬件资源枚举(matmul 暂存 bank、transpose bank、EUP 和跨 lane drain);ArchRegister 是实例解析后的物理槽索引,位于 ArchRegno 编号 中记录的按世代架构寄存器编号之下的最底层。
如果你熟悉 LLVM,ArchRegister 最接近的类比是 MCRegister,即目标物理寄存器编号;而 ResultFifo 则像一组架构可见队列的固定池,例如 x87 栈或 SIMD 累加器文件,只不过这里有若干 FIFO 是 banked 的(多个物理实例由运行时实例索引选择)。枚举本身不携带每目标尺寸;把 ArchRegister 序号转换成可打印 v3 / s12 / vm5 / p2 的编号是在 Target 构造时建立的(参见 ArchRegno 编号)。本页按名称固定这两个枚举,解码 banked 与 single 的划分,并追踪同时读取它们的一个消费者:LloXluGraphOptimizer::ComputeXluOperations,它遍历依赖图,并为每个跨 lane 操作码发出一个 XLU 操作,同时附带其读取和写入的 ArchRegister 集合。
对重实现而言,契约是:
- 25 个
ResultFifo序号及每个 FIFO 的含义,加上把(ResultFifo, instance)对映射到扁平物理 FIFO id 的FifoInstancebank 算术,以及每个 FIFO 在各版本的深度(ResultFifoEntryCount)。 - 5 成员
RegisterType枚举和 50 成员ArchRegister物理编号:哪 12 个序号是多实例 bank,哪 38 个是单寄存器,以及每个解析到的(RegisterType, regno)对。 ComputeXluOperations:拓扑遍历、21 个操作码的 XLU 选择、opcode_info_big +0x08读取列表 /+0x14写入列表构造,以及它为每个操作发出的variant<TransposeTile, RpuOperation, XluControlOperation>。
ResultFifo 字符串化 | ResultFifoToString @ 0x14441340 — 25 个内联立即数分支 |
ResultFifo 实例解析器 | internal::FifoInstance @ 0x14446040 — 5 个 banked 分支 + 直通 |
ResultFifo 深度 | ResultFifoEntryCount @ 0x1d631520 — 25 个分支,TpuVersion < 6 门控 |
RegisterType 字符串化器 | RegisterTypeToString @ 0x1d640560,RegisterTypeToMnemonic @ 0x1d640600 |
ArchRegister 实例解析器 | internal::ArchRegisterInstance @ 0x126b3240 — 12 个 banked 分支 + 默认直通 |
| 每操作码元数据 | opcode_info_big @ 0x227b5570 — LloOpcodeBigInfo[461](符号大小 0x326c = 12908 B = 461 × 28),28 字节步幅 |
| XLU 消费者 | LloXluGraphOptimizer::ComputeXluOperations @ 0x126d9780 |
RegisterType
RegisterType 是 5 成员寄存器文件类别枚举。两个字符串化器逐字节精确地拼写它,二者都是普通 switch 表:
RegisterTypeToString@0x1d640560把长名称写入小字符串缓冲区(长度存储在buffer[23])。RegisterTypeToMnemonic@0x1d640600写入构建架构寄存器名称所用的一或两个字符前缀。
| 序号 | ToString | ToMnemonic | 长度 | 含义 | 操作码计数 |
|---|---|---|---|---|---|
| 0 | "none" | "" | 4 / 0 | 无 GPR 结果 | 216 |
| 1 | "pregs" | "p" | 5 / 1 | 谓词寄存器 | 11 |
| 2 | "sregs" | "s" | 5 / 1 | 标量寄存器 | 58 |
| 3 | "vmregs" | "vm" | 6 / 2 | 向量掩码寄存器 | 19 |
| 4 | "vregs" | "v" | 5 / 1 | 向量寄存器 | 157 |
字符串立即数已按字节确认:case 1 存储 DWORD "preg" 再存 's',case 2 存储 "sreg"+'s',case 4 存储 "vreg"+'s';case 0 和 3 使用 strcpy 写入 "none" / "vmregs";助记符分支使用两字节立即数 'p'、's'、'v' 以及 strcpy("vm")。“操作码计数”列是每个 LLO 操作码产生哪个类别的按 RegisterType 直方图。
注意 — 寄存器分配器把这五个类别分成两组,可由二进制中的两条断言字符串见证:
"type == RegisterType::kPreg || type == RegisterType::kVmreg"(不可 spill 的谓词/掩码组)和"type == RegisterType::kSreg || type == RegisterType::kVreg"(可 spill 的标量/向量数据组)。kNone从不被分配。
ArchRegister 及其物理编号
ArchRegister 是一个 50 成员枚举(序号 1..0x32),它不是一组名称。它是物理编号:internal::ArchRegisterInstance(ArchRegister, optional<int> instance) @ 0x126b3240 把 (enum, instance-index) 对映射到扁平物理架构寄存器槽。该函数是一个 switch,在值空间 1..0x32 上有十二个显式分支,每个分支对应一个 banked 序号;其他所有序号命中默认分支并原样返回。
Banked 与 single 寄存器
十二个序号是多实例 bank,即存在多个物理副本并需要运行时实例选择器的寄存器。它们的 switch 分支会验证实例索引(*unit_id < count),然后返回 ordinal + instance,因此每个 bank 占据一段连续物理范围 [ordinal .. ordinal + count - 1]。其他 38 个序号落入默认分支并直接返回该序号。
| Banked 序号 | 实例数 | 物理基址 | 槽范围 |
|---|---|---|---|
| 0x01 | 3 | 1 | 1..3 |
| 0x05 | 3 | 5 | 5..7 |
| 0x0b | 4 | 11 | 11..14 |
| 0x0f | 4 | 15 | 15..18 |
| 0x13 | 4 | 19 | 19..22 |
| 0x17 | 4 | 23 | 23..26 |
| 0x1b | 4 | 27 | 27..30 |
| 0x1f | 4 | 31 | 31..34 |
| 0x26 | 4 | 38 | 38..41 |
| 0x2a | 4 | 42 | 42..45 |
| 0x2e | 4 | 46 | 46..49 |
| 0x32 | 4 | 50 | 50..53 |
两个 count-3 bank(0x01、0x05)断言 *unit_id < 3;十个 count-4 bank 断言 *unit_id < 4。基址始终等于序号(return ordinal + instance)。
不存在静态的 ArchRegister 到名称表:没有静态 ArchRegister->ToString,因此单靠这个枚举无法解析每序号的符号名(循环计数器、同步标志 bank 等)。
陷阱 — 不存在静态的
ArchRegister到名称表。任何架构寄存器的可打印名称都由上一层的RegisterNumbering::ToArchRegString@0x1275e2a0产生,它会在通过Target::InitRegisterNumbering构建的每Target编号表解析槽之后,打印"<RegisterTypeToMnemonic(type)><regno>"(例如v3、s12、vm5、p2)。所以 “ArchRegister 23” 没有固定名称;只有绑定目标编号后,它才会打印为vN/sN/ 等。参见 ArchRegno 编号。
MRB 伪寄存器命名空间
在约 50 个真实架构寄存器之上,是用于 matmul-result-buffer(MRB)条目的伪寄存器命名空间。PseudoArchRegisterFromMrbEntry @ 0x1443c860 返回 (mrb_id << 9) + mrb_entry + 0x36,其中 mrb_entry < 0x200 且 mrb_id < 4,即四个 512 条目的 bank,以物理槽 0x36 为基址,紧接在真实架构寄存器之上。这些是调度器为结果缓冲区活跃性所跟踪的 FIFO 支持伪寄存器槽。
ResultFifo
ResultFifo 枚举 25 个硬件结果 FIFO(序号 0..0x18)。权威名称来源是 ResultFifoToString @ 0x14441340,一个纯 switch,每个分支把名称写作内联 ASCII 立即数,并把长度存储在 buffer[23]。计数由 ResultFifoEntryCount @ 0x1d631520 独立交叉确认,后者拥有相同的 25 个有效分支。
| 序号 | 名称 | FIFO 类别 |
|---|---|---|
| 0x00–0x03 | kMsrA0..kMsrA3 | Matmul 暂存结果,bank A,实例 0..3 |
| 0x04–0x07 | kMsrB0..kMsrB3 | Matmul 暂存结果,bank B,实例 0..3 |
| 0x08–0x0b | kMrf0..kMrf3 | Matmul 结果 FIFO,实例 0..3 |
| 0x0c–0x0e | kTsf0..kTsf2 | Transpose 暂存 FIFO,实例 0..2 |
| 0x0f–0x11 | kTrf0..kTrf2 | Transpose 结果 FIFO,实例 0..2 |
| 0x12 | kErf | EUP 结果 FIFO(超越/激活 drain) |
| 0x13 | kV2sf | 向量到标量 FIFO(vector→scalar bridge) |
| 0x14 | kSfrf | 同步标志结果 FIFO(同步标志读回) |
| 0x15 | kCrf | 跨 lane 结果 FIFO(XLU permute/reduce 结果) |
| 0x16 | kDrf | DivRem 结果 FIFO(标量除法/余数) |
| 0x17 | kSccf | 跨 core / channel 结果 FIFO |
| 0x18 | kCcrf | Cmem / 跨 core 结果 FIFO |
名称逐字节精确:分支 0..7 是 strcpy("kMsrA0") … strcpy("kMsrB3");分支 8..17 存储 DWORD 前缀("kMrf"、"kTsf"、"kTrf")加单个数字字节;分支 18..24 存储 "kErf"、"kV2sf"、"kSfrf"、"kCrf"、"kDrf"、"kSccf"、"kCcrf"。前十个暂存/结果 FIFO 的类别说明同时由名称前缀和 FifoInstance bank 算术支撑;kSccf / kCcrf 类别遵循名称前缀。
怪异点 — 同级字符串化器
MsrToString@0x1d629720确认两个 matmul 暂存结果 bank 是"msra"/"msrb",匹配kMsrA*/kMsrB*。另一个XmrToString@0x1d629740把矩阵寄存器文件命名为"gmra"(gain-matrix register)和"lmr"(latch-matrix register);这些是不同于ResultFifo的另一种寄存器文件,见 Slot: MXU。
FifoInstance — bank 算术
internal::FifoInstance(ResultFifo, optional<int> instance) @ 0x14446040 把 (base-FIFO, instance) 对折叠成扁平物理 FIFO id。只有多实例暂存/结果 FIFO 拥有专门的 switch 分支;其他所有值都原样直通。
| 基础序号 | FIFO | 实例数 | 返回值 |
|---|---|---|---|
| 0x00 | kMsrA0 | 4(*unit_id < 4) | instance |
| 0x04 | kMsrB0 | 4 | instance + 4 |
| 0x08 | kMrf0 | 4 | instance + 8 |
| 0x0c | kTsf0 | 3(*unit_id < 3) | instance + 12 |
| 0x0f | kTrf0 | 3 | instance + 15 |
| (other) | — | — | 序号(直通) |
因此,带实例的物理域是 matmul + transpose 的暂存/结果 bank;更高序号(作为具名枚举值的 kTrf1/2、kErf、kV2sf、kSfrf、kCrf、kDrf、kSccf、kCcrf)是单实例 FIFO,不通过 bank 算术。
陷阱 —
FifoInstance的cmp edi,0xf不是枚举大小:那个0..0xf边界是物理实例子域(接受实例索引的 bank)。ResultFifoToString和ResultFifoEntryCount都显示 25 个成员(0..0x18):枚举宽度为 25,只有前 16 个序号参与FifoInstance算术。
ResultFifoEntryCount — 每 FIFO、每版本深度
ResultFifoEntryCount(ResultFifo, TpuVersion) @ 0x1d631520 返回 FIFO 的缓冲深度。它是在 FIFO 序号上的 25 分支 switch;每个分支都受 TpuVersion < 6 门控(版本 6 及以上会命中 "invalid platform type" fatal)。深度解析分为两种模式:
- 常量深度,与版本无关:
kTsf0/kTsf1/kTsf2(case 12–14)都返回 16;kSfrf(case 19)返回 128。 - 按版本索引的表:其他所有 FIFO 都用
TpuVersion索引每 FIFO 的int[]表。若干分支共享一张表(例如 case 0–2 读取一张表,4–6 读取另一张),反映深度随硅片世代一起变化的 FIFO 组。
function ResultFifoEntryCount(fifo, version): // sub_1d631520
switch (fifo):
case kTsf0..kTsf2: // cases 12-14
if (version >= 6) Fatal("invalid platform type")
return 16 // depth fixed across gens
case kSfrf: // case 19
if (version >= 6) Fatal("invalid platform type")
return 128
case <grouped FIFOs>:
if (version >= 6) Fatal("invalid platform type")
return depth_table_for_group[version] // version-indexed int[]
```text
> **注意 —** `TpuVersion < 6` 门控意味着此构建的深度表覆盖 `kJellyfish`(0) 到版本 5 代号;版本 6+ 是尚未支持的平台。这里没有逐格枚举完整的 25 × `TpuVersion` 深度矩阵。
---
## opcode_info_big — 枚举的消费位置
两个枚举都从每操作码元数据表 `opcode_info_big` @ `0x227b5570` 引用(`LloOpcodeBigInfo[461]`,符号大小 `0x326c` = 12908 B = 461 × 28,28 字节步幅,按 `LloOpcode` 索引;`ComputeXluOperations` 用 `opcode < 0x1CE` 约束索引并在其他情况下陷阱,因此该边界允许越过 461 项表一个索引)。每条记录携带三个以哨兵结尾的 `int8` 列表:
```c
struct LloOpcodeBigInfo { // sizeof 28 (0x1c)
int8_t result_fifos[8]; // +0x00 : ResultFifo 0..0x18, neg-terminated (forward reader)
int8_t arch_registers_read[12]; // +0x08 : ArchRegister 1..0x32, neg-terminated (-12 counter reader)
int8_t arch_registers_written[8]; // +0x14 : ArchRegister 1..0x32, neg-terminated (forward reader)
};+0x08 的读取列表由一个循环读取,该循环从计数器 -12 开始并索引 record[counter + 0x14],因此扫描偏移 +0x08..+0x13(12 项),并在第一个负字节处停止。+0x14 的写入列表和 +0x00 的结果 FIFO 列表则向前读取。每个 ArchRegister 代码都通过 ArchRegisterInstance 解析为物理槽,实例索引取自 LLO 值的元数据字(见下文)。完整描述符参见 opcode_info_big。
注意 —
+0x08..+0x13字段是arch_registers_read[12]列表。它的三个读取者,即ComputeXluOperations和两个GetPseudoArchRegistersRead<…>实例化,都从-12(0xfffffffffffffff4)开始循环计数器,并把它加到+0x14位移上,从而落在+0x08,不是+0x14。名为GetPseudoArchRegistersRead的消费者确认该字段是寄存器读取列表。
LloXluGraphOptimizer::ComputeXluOperations
目的
ComputeXluOperations @ 0x126d9780 构建跨 lane 单元(XLU)操作列表,并为每个操作附加读取和写入的 ArchRegister 集合。该每操作寄存器数据流随后被 XLU 调度器转换成跨 XLU 依赖图(参见 ArchRegno 编号)。这是读取两个 ArchRegister 列表并发出 XLU 操作 variant 的唯一消费者。
算法
function ComputeXluOperations(this): // sub_126d9780
nodes = LloDependencyGraph::NodesInTopologicalOrder(false) // sub_1442b8c0
for node in nodes:
op = WORD[node.value] // LloOpcode
// dispatch: lea ecx,[op-0x8b]; cmp 0xca; ja skip → JT[op-0x8b]
if not (0x8b <= op <= 0x155) or JT[op-0x8b] == skip_arm:
continue // 182 of 203 band opcodes are non-XLU
// ---- this is one of the 21 XLU opcodes ----
instance = nullopt
meta = WORD[node.value + 0xb]
if (meta & 0x400): // explicit-instance flag
instance = ((meta >> 8) & 3) | has_value // optional<int>
// READ set: opcode_info_big +0x08 list, -12 loop, neg-terminated
record = &opcode_info_big[28 * op]
for c = -12; c < 0; ++c:
code = record[c + 0x14] // offsets +0x08..+0x13
if code < 0: break
read_set.emplace(ArchRegisterInstance(code, instance)) // InlinedVector<ArchRegister,2>
// WRITTEN set: forward +0x14 list
written_set = LloOpcodeArchRegistersWritten(op, instance) // sub_126b2ea0
// emit one variant per op (stride 0x48, discriminant at +0x40)
emit variant<TransposeTile, RpuOperation, XluControlOperation>(op, read_set, written_set)
return StatusOr<vector<variant<...>>>
```text
分发是位于 `0xadf5504` 的跳转表,按 `op - 0x8b` 索引(203 项),只有两个分支:XLU 分支(21 个操作码)和 skip 分支(182 个操作码)。范围外操作码(`< 0x8b` 或 `> 0x155`)也跳过。
### 21 个 XLU 操作码
| 操作码 | 名称 | Variant |
|---|---|---|
| 0x08b | `kVectorSetPermutePattern` | `XluControlOperation` |
| 0x08c | `kVectorSetSegmentPattern` | `XluControlOperation` |
| 0x0a6 | `kVectorTranspose` | `TransposeTile` |
| 0x0a7 | `kVectorTransposeBinary` | `TransposeTile` |
| 0x0f5 | `kVectorMinReduceF32` | `RpuOperation` |
| 0x0f6 | `kVectorMaxReduceF32` | `RpuOperation` |
| 0x0f7 | `kVectorAddReduceF32` | `RpuOperation` |
| 0x0f8 | `kVectorMaxIndexReduceF32` | `RpuOperation` |
| 0x0f9 | `kVectorMinIndexReduceF32` | `RpuOperation` |
| 0x0fa | `kVectorMaxSegmentReduceF32` | `RpuOperation` |
| 0x0fb | `kVectorMinSegmentReduceF32` | `RpuOperation` |
| 0x0fc | `kVectorAddSegmentReduceF32` | `RpuOperation` |
| 0x0fd | `kVectorMinReduceBf16` | `RpuOperation` |
| 0x0fe | `kVectorMaxReduceBf16` | `RpuOperation` |
| 0x0ff | `kVectorAddReduceBf16` | `RpuOperation` |
| 0x100 | `kVectorMaxIndexReduceBf16` | `RpuOperation` |
| 0x101 | `kVectorMinIndexReduceBf16` | `RpuOperation` |
| 0x14f | `kVectorXlaneResult` | `XluControlOperation`(弹出 `kCrf`) |
| 0x150 | `kVectorPermuteResult` | `XluControlOperation` |
| 0x154 | `kVectorTransposeResult` | `TransposeTile`(弹出 `kTrf*`) |
| 0x155 | `kVectorTransposeClear` | `TransposeTile` |
Variant 分配在发射时由四个逐字节精确的分类器决定(`LloOpcodeUsesTranspose`、`LloOpcodeUsesRpu`、`LloOpcodeIsRpuControl`、`LloOpcodeIsRpuResult`);每操作码到 variant 的单元格已在 [ArchRegno 编号](archregno-numbering.md#per-opcode-variant-classification) 中确认。编码侧的 opcode→factory 映射参见 [XLU 操作清单](xlu-op-roster.md)。
> **注意 —** reduce 家族(0xf5..0x101)是连续的:F32 min/max/add reduce、F32 max/min index-reduce、F32 max/min/add segment-reduce,然后是 bf16 镜像(min/max/add reduce + max/min index-reduce)。这种连续性正是 `LloOpcodeUsesRpu` 用 `(op - 0xf5) < 0xd` 测试的范围。
### 一个工作示例 — kVectorAddReduceF32 (0xf7)
1. 拓扑遍历到达节点;操作码为 `0xf7`。分发索引为 `0xf7 - 0x8b = 0x6c`;跳转表选择 XLU 分支。
2. 读取集合:`opcode_info_big[0xf7] + 0x08` 列表,其中每个代码通过 `ArchRegisterInstance(code, instance)` 解析;如果 bit `0x400` 已置位,则 `instance = (WORD[value+0xb] >> 8) & 3`,否则为 `nullopt`。该操作读取保存 reduce 源的 vreg。
3. 写入集合:`LloOpcodeArchRegistersWritten(0xf7)` 读取 `+0x14` 向前列表,也就是结果落入的跨 lane 结果 FIFO(`kCrf`)支持寄存器。
4. 向 XLU-op 向量发出 `RpuOperation`。读/写集合供给跨 XLU 依赖跟踪器,因此后续*读取* `kCrf` 的 `kVectorXlaneResult`(0x14f)会被排在这个*写入*它的 reduce 之后。
---
## 相关组件
| 组件 | 关系 |
|---|---|
| `opcode_info_big`([记录格式](record-format.md)) | 保存由这些枚举作为键的 `result_fifos` / `arch_registers_read` / `arch_registers_written` 列表 |
| `RegisterNumbering`([archregno 编号](archregno-numbering.md)) | 按目标把 `ArchRegister` 物理槽转换成可打印的 `(RegisterType, regno)` |
| XLU 调度器([archregno 编号](archregno-numbering.md#crossxlu-data-dependency-tracker)) | 消费 `ComputeXluOperations` 构建的每操作读/写 `ArchRegister` 集合 |
## 交叉引用
- [ArchRegno 编号](archregno-numbering.md) — `ArchRegister` 序号如何成为按世代的 `(RegisterType, regno)` 架构寄存器编号,以及消费读/写集合的跨 XLU 依赖跟踪器
- [XLU 操作清单](xlu-op-roster.md) — 此处命名的跨 lane 单元操作的 opcode→factory 表
- [Slot: VPU](slot-vpu.md) — 其操作产生这些 FIFO 所 drain 的 vreg 的向量处理槽
- [Slot: MXU](slot-mxu.md) — 填充 `kMsr*` / `kMrf*` 暂存和结果 FIFO 的 matmul 槽
- [MC 发射器](mc-emitter.md) — 把这些操作布置进 bundle 槽的机器码发射器