MatmulMode 和 Modifiers
本页上的所有地址都适用于来自
libtpu-0.0.40-cp314wheel 的libtpu.so(buildlibtpu_lts_20260413_b_RC00,BuildID md589edbbe81c5b328a958fe628a9f2207d)。该二进制文件未被 stripped —— 每个符号都是反混淆后的 C++ 名称。Section map:.text/.rodataVMA == 文件偏移;.data.rel.roVMA − 0x200000 == 文件偏移。
摘要
TPU MXU 上的一次矩阵乘不是一个操作 —— 它是一串 feed pass,每个 pass 对应操作数 significand 的一个切片。全精度 bf16 matmul 是三次 int8-emulated pass;int8 matmul 最多是八次 byte-plane pass;int4 matmul 是四次 nibble-plane pass。xla::jellyfish::MatmulMode 是包含 16 个 ordinal 的枚举,用于命名每个 pass-role —— lowering 为一个操作数选择的逐切片 feed,成本模型也会分别为其定价。本页记录该枚举、operand dtype 映射到候选 mode list 的方式,以及将一个 mode(及其 data format)绑定到 MxuLatencyTable 中某个 reservation row 的 MatmulModifier / MatpushModifier key。
参考框架是软件仿真的宽乘法:要在只支持更窄 mantissa 的硬件上乘两个 bf16 值,就把每个操作数拆成 significand slices,逐 slice 相乘,再带 shift 重组 —— 正是 “bf16x3” / “int8x8” 技巧。MatmulMode 枚举这些 slice:{Round, High, Low, Soft Middle Eight, Soft Low Eight} 是 bf16/fp32 精度 pass(ordinals 0–4);{Soft Byte k, Soft Signed Byte k} 是 int8 byte planes(ordinals 5–11);{Nibble k, Signed Nibble k} 是 int4 nibble planes(12–15)。每个 ordinal 在 16 项表中携带一个权重;lowering 形成 LHS 与 RHS 候选 list 的笛卡尔积,并按权重和对 pair 进行 stable_sort,因此最便宜的精度 pair 会先被消费。
本页后半部分是与成本模型的绑定。MatmulDataFormat 是 matmul 或 latch 携带的数据路径宽度 code(bf16-packed、int8/x8、int4、fp8 variants);GetMatmulDataFormat 从 operand dtype 和 convolution lowering strategy 推导它。MatmulModifier(matmul family)和 MatpushModifier(latch / matprep family)是 MxuLatencyTable lookup 由该 format 构建的 key,并选择为 op 定价的 reservation group —— {2,1,1} bf16、{4,3,2} transposed、{8,7,6} x8。两个次级表 —— matmul-format key list 和 latch/vxpose format ordinals —— 定义 format byte 如何放入 key。
对于重新实现,契约是:
- 16 个
MatmulModeordinal、其显示字符串,以及为 mode pair 排序的 16 项 weight table。 GetMatmulModes(operand):按PrimitiveType划分的候选 mode list,以及由精度驱动的默认值。MatmulDataFormat:format code,以及产生它们的GetMatmulDataFormatdtype/strategy dispatch。MatmulModifier/MatpushModifierkey bytes,以及 format → reservation-group 绑定。
| MatmulMode 枚举 | xla::jellyfish::MatmulMode —— 16 个 ordinal;由 operator<< @0x1d6294e0 打印 |
| Weight table | @0xae0f480 = [5,4,3,2,1,40,40,30,30,20,10,10,40,40,40,40] |
| Pair comparator | ConvMatmulModes::operator< @0x130e12a0 —— W[lhs]+W[rhs] |
| 按 dtype 的 mode list | SpatialMajorConvolution::GetMatmulModes(operand) @0x130dfbe0(dtype jt @0xae0f26c) |
| 笛卡尔积 + sort | GetMatmulModes() @0x130df600 —— 跳过 {Low,Low},按权重和 stable_sort |
| Data-format 推导 | convolution_util::GetMatmulDataFormat @0x1307be40(dtype jt @0xae0d6f4) |
| Modifier key 类型 | MatmulModifier(8-byte key),MatpushModifier(4-byte key) |
16 个 MatmulMode Ordinals
目的
MatmulMode 命名一个 pass 中一个操作数的 feed-role。lowering 从 dtype 为每个操作数选择 mode list;成本模型则按所选 mode pair 和由此产生的 MatmulDataFormat 为 matmul 定价。
枚举
xla::jellyfish::operator<<(ostream&, MatmulMode) @0x1d6294e0 是一个针对 16 项 jump table(@0xb53c6e4)的 jmp *jt[ord] dispatcher;每个 case 都内联构造其显示字符串。逐字节精确解码,并与 16 项 weight table @0xae0f480 进行 1:1 绑定:
| Ord | MatmulMode | Weight | 组 / feed role |
|---|---|---|---|
| 0 | Round | 5 | bf16/fp32 —— round-to-nearest,单 pass |
| 1 | High | 4 | bf16/fp32 —— high-significand pass |
| 2 | Low | 3 | bf16/fp32 —— low-significand pass |
| 3 | Soft Middle Eight | 2 | bf16 3-pass split —— 中间 8 bits |
| 4 | Soft Low Eight | 1 | bf16 3-pass split —— 低 8 bits |
| 5 | Soft Byte 0 | 40 | int8 ×8 —— byte plane 0 |
| 6 | Soft Signed Byte 0 | 40 | int8 ×8 signed —— byte plane 0 |
| 7 | Soft Byte 1 | 30 | int8 ×8 —— byte plane 1 |
| 8 | Soft Signed Byte 1 | 30 | int8 ×8 signed —— byte plane 1 |
| 9 | Soft Byte 2 | 20 | int8 ×8 —— byte plane 2 |
| 10 | Soft Byte 3 | 10 | int8 ×8 —— byte plane 3(最高) |
| 11 | Soft Signed Byte 3 | 10 | int8 ×8 signed —— byte plane 3 |
| 12 | Nibble 0 | 40 | int4 ×4 —— nibble plane 0 |
| 13 | Signed Nibble 0 | 40 | int4 ×4 signed —— nibble plane 0 |
| 14 | Nibble 1 | 40 | int4 ×4 —— nibble plane 1 |
| 15 | Signed Nibble 1 | 40 | int4 ×4 signed —— nibble plane 1 |
注意 — ordinal 4 的
operator<<显示字符串是字面值"Soft Low Eight"(长度 14,三个以空格分隔的词)。这是 jellyfish C++ 枚举的打印字符串,区别于由MatmulModeAttr::print发出的 MLIRllo::MatmulModeattribute 拼写soft_low_eight(下划线)—— 这两个枚举打印方式不同;见本页末尾的 second-enum NOTE。
三个语义组
- {0–4} bf16/fp32 精度 pass(权重 5,4,3,2,1)。
Round是单 pass;High/Low是 2-pass significand split;Soft Middle Eight/Soft Low Eight完成 3-pass int8-emulated bf16(高精度 bf16×3)。 - {5,6,7,8,9,10,11} int8 ×8 byte planes ——
Soft Byte kunsigned(ords 5/7/9/10 = byte planes 0/1/2/3),Soft Signed Byte ksigned(ords 6/8/11 = signed planes 0/1/3);×8 = 4 个 byte planes 0–3 分别 latch。 - {12,13,14,15} int4 ×4 nibble planes ——
Nibble k/Signed Nibble k。
怪癖 — 权重不跟踪 ordinal 顺序。bf16 组很便宜(1–5),byte planes 中等到昂贵(10–40),int4 nibbles 加上 signed-byte-0 固定为 40。这是有意的:comparator 会把两个权重相加并升序排序,因此 bf16 pair 总是在 int8 pair 之前排序,lowering 会先消费最便宜的精度组合。
Pair comparator
ConvMatmulModes::operator< @0x130e12a0(重新逐字节精确验证)按操作数权重和比较两个 mode pair:
function ConvMatmulModes::operator<(a, b): // @0x130e12a0, W[] @0xae0f480
return W[a.lhs] + W[a.rhs] < W[b.lhs] + W[b.rhs]; // setb on int sums
```text
次级 key 是 `std::stable_sort` 插入顺序(来自 `GetMatmulModes()` 的 LHS-outer × RHS-inner),因此平局会按笛卡尔积产生它们的顺序解决 —— 这由针对 `ConvMatmulModes*` 的 `__stable_sort` / `__inplace_merge` 实例(`@0x130dfe40`、`@0x130e0760`)确认。
---
## 按 Dtype 的 Mode Lists — GetMatmulModes
### 目的
`GetMatmulModes(operand)` 从元素类型为一个操作数生成候选 `MatmulMode` list。无参 `GetMatmulModes()` 随后将 LHS 与 RHS list 交叉组合成 `ConvMatmulModes` pairs。
### 算法
`SpatialMajorConvolution::GetMatmulModes(long operand)` `@0x130dfbe0`(在反编译中经字节确认)首先对 depthwise 情况短路,然后基于 `element_type` dispatch:
```c
function GetMatmulModes(operand): // @0x130dfbe0
if strategy.is_depthwise[+504] or is_batch_group_depthwise[+505]:
return {Round} // mode 0
switch operand.shape.element_type(): // dtype jump table @0xae0f26c
case 1,6,22,27,31: return {Soft Byte 0} // mode 5 (byte=0x05)
case 2,21,26,30: return {Soft Signed Byte 0} // mode 6 (byte=0x06)
case 3 (S16): return {Soft Byte 0, Soft Signed Byte 1} // {5,8} (word=0x0805)
case 4 (S32): return {Soft Byte 0, Soft Byte 1, Soft Byte 2, Soft Signed Byte 3} // {5,7,9,11}
case 7 (U16): return {Soft Byte 0, Soft Byte 1} // {5,7} (word=0x0705)
case 8 (U32): return {Soft Byte 0, Soft Byte 1, Soft Byte 2, Soft Byte 3} // {5,7,9,10}
default: // bf16 / fp16 / fp32 / fp8 — drive off the convolution precision
switch GetConvPrecision(operand): // @0x131916e0 → 0 / 1 / 2
case 2: return {Soft Low Eight, Soft Middle Eight, High} // {4,3,1} bf16×3 hi-acc
case 1: return {Low, High} // {2,1} bf16×2
else: return {Round} // {0} bf16×1每个 case 的 packed bytes 都直接从反编译中读取:case 3 写入 16-bit 立即数 2053 = 0x0805 → modes {5,8};case 4 写入 0x0B090705 → {5,7,9,11};case 8 写入 0x0A090705 → {5,7,9,10};precision-2 默认分支写入 0x0304 加上第三个 byte 1 → {4,3,1}。dtype jump table @0xae0f26c 有 31 项(element_type − 1 index);默认 arm 覆盖所有 floating type。
无参 GetMatmulModes() @0x130df600 生成 LHS × RHS 的笛卡尔积,跳过退化的 {Low,Low} = {2,2} pair,并按上面的 operator< 进行 stable_sort。结果是 emitter 遍历的 (MatmulMode lhs, MatmulMode rhs) pair 的有序 list。
易错点 —
{Low,Low}跳过不是可选项。2-pass bf16 操作数贡献{Low, High};否则笛卡尔积会发出{Low,Low},而它不会产生可用的 significand product。保留它的重新实现会发出冗余 pass,并错误估算 matmul 成本。
MatmulDataFormat 和 Modifier Keys
目的
MatmulDataFormat 是 matmul 或 latch 携带的数据路径宽度 code。它不同于 MatmulMode:mode 是逐 significand-slice 的 feed role;format 是 operand lane 的 packing。MxuLatencyTable modifier key 是由format构建的,而不是由 mode 构建 —— 因此 format byte 才是选择 reservation group 的内容。
Format codes
GetMatmulDataFormat(convolution_util::GetMatmulDataFormat @0x1307be40)从 operand PrimitiveType 和 convolution lowering strategy 推导 format(两阶段 dispatch:strategy packing bits 会短路到 format 1/2;否则使用 22 项 dtype jump table @0xae0d6f4)。format codes(经字节确认的目标):
| Format | 含义 | Reservation group |
|---|---|---|
| 1 | bf16 packed | {2,1,1}(单 bf16) |
| 2 | bf16 packed,alternate path | {2,1,1} |
| 3 / 9 | F8E4M3Fn(native / converted) | fp8 |
| 4 | F32 | wide |
| 5 / 7 | F8E5M2(native / variant) | fp8 |
| 6 | int8 / x8 | {8,7,6}(x8 quad) |
| 8 | int4 unsigned | x4 |
| 10 | fp8 fnuz variant | fp8 |
stage-2 dtype targets:PrimitiveType 2 → 6,6 → 5,19 → 3 / 9(native vs converted,由一个 Target vtable slot 设门槛),20 → 10,21 → 6 / 8(int4 signed vs unsigned,取决于 strategy x4/x8 bits 上的 ±2),22 → 5 / 7,23 → 4;3..18 中的所有其他 type 都进入 FATAL arm。
Modifier keys
MxuLatencyTable::GetResourceUsage 根据 op family 构建两种 key struct 之一(lookup 本身记录在 mxu-latency-overview 中):
| Key | Family | Width | Byte layout | Source helpers |
|---|---|---|---|---|
MatmulModifier | matmul | 8 B | byte[0] = format(VF matmul cases 212 / 218 / 230 中为 1 / 2 / 6);byte[1..] = 0 | inline,format-key list @0x84a2644 |
MatpushModifier | latch / matprep | 4 B | byte[0] = GainLatchModeToMatmulDataFormat(latch_mode);byte[1..2] = LatchModeIsTranspose(latch_mode);byte[3] = LatchOpcodeToMsr(opcode)(opcode < 0x8F MSR-select bit) | 下方 helpers |
matpush key 是 format → reservation 绑定的实际体现。GainLatchModeToMatmulDataFormat @0x1d629260 将 LLO GainLatchMode attribute(slot-matprep-iar-latch 的逐 pass latch role)映射到 MatmulDataFormat code,并成为 key 的 byte[0];LatchModeIsTranspose @0x1d628ea0 设置 transpose bytes;LatchOpcodeToMsr @0x1c8a1300 返回 matrix-staging-register select bit(除非 opcode 在 [0x8D,0x96] 中,否则 FATAL,随后为 opcode < 0x8F)。选择 matpush variant 的 opcode 还会预先变换 latch mode —— xpose pass(VF opcode 271)使用 ^0xB,wide pass(VF opcode 277)使用 |0x14 —— 将同一个物理 latch 路由到 transposed {4,3,2} 或 wide {8,7,6} reservation group。
Format → reservation-group 绑定
reservation triplet 随 format 的数据路径宽度缩放 —— 即单个 matpush 必须 feed 的 byte planes:
format 1/2 (bf16 single) → {res0:2, MSR_a:1, MSR_b:1} = {2,1,1}
format (bf16 transposed/dbl) → {res0:4, MSR_a:3, MSR_b:2} = {4,3,2}
format 6 (int8 x8 / wide) → {res0:8, MSR_a:7, MSR_b:6} = {8,7,6}
```text
因此 bf16 matmul(`GetMatmulDataFormat → 1`)使用 `{2,1,1}` group,int8/x8 matmul(`→ 6`)使用 `{8,7,6}` —— 4× hold 反映 4-byte-plane x8 latch sequence。实际的每个 `(format × MSR)` 整数位于各代页面。
---
## 次级表
### Matmul-format key list
matmul-family modifier key byte[0] 从一个小 format-key list `@0x84a2644` 取得(entries `01, 02, 03, 04, …`,loop bound `cmp $7`)。VF matmul cases 固定其中三个:opcode 212 → format 1,218 → format 2,230 → format 6 —— 也就是 `MxuLatencyTable this+0x20` 处 matmul map 所 keyed on 的相同三个 byte 值。matmul-modifier map policy(一个 `pair<MatmulDataFormat,int>` array)位于 `@0x21c20650`。
### Latch / vxpose format ordinals
latch(matpush)和 vector-transpose op 通过 `GainLatchMode` feed format byte,而不是通过 matmul format-key list。`GainLatchModeToMatmulDataFormat` `@0x1d629260` 是执行该转换的 jump table(`GainLatchMode` ordinal → `MatmulDataFormat`);transpose flag 是独立的 `LatchModeIsTranspose` `@0x1d628ea0`。每个 latch ordinal 的精确 `GainLatchMode` → format 映射位于 [`slot-matprep-iar-latch`](../isa/slot-matprep-iar-latch.md)。
> **注意 —** 二进制中有*第二个、独立的* `MatmulMode`:5 值的 `mlir::llo::MatmulMode` rounding-mode attribute(`round`、`high`、`low`、`soft_middle_eight`、`soft_low_eight`;`MatmulModeAttr::print` `@0x13e53d20`)。它只是 bf16 precision group(jellyfish ordinals 0–4)的 MLIR-attribute 对应物 —— int8/int4 feed modes(5–15)**不会**出现在 MLIR attribute 中,而是通过 `MatmulDataFormat` + `GainLatchMode` 承载。不要混淆这两个枚举。
### 函数映射
| 函数 | 地址 | 作用 |
|---|---|---|
| `operator<<(ostream&, MatmulMode)` | `0x1d6294e0` | 16-case display dispatcher(jt `@0xb53c6e4`) |
| `ConvMatmulModes::operator<` | `0x130e12a0` | pair comparator —— `W[lhs]+W[rhs]` |
| `SpatialMajorConvolution::GetMatmulModes(long)` | `0x130dfbe0` | 按 dtype 的候选 mode list |
| `SpatialMajorConvolution::GetMatmulModes()` | `0x130df600` | 笛卡尔积、跳过 `{Low,Low}`、`stable_sort` |
| `convolution_util::GetConvPrecision` | `0x131916e0` | bf16 precision 0 / 1 / 2 selector |
| `convolution_util::GetMatmulDataFormat` | `0x1307be40` | dtype + strategy → format code(jt `@0xae0d6f4`) |
| `GainLatchModeToMatmulDataFormat` | `0x1d629260` | latch mode → format byte(matpush key byte[0]) |
| `LatchModeIsTranspose` | `0x1d628ea0` | matpush key transpose bytes |
| `LatchOpcodeToMsr` | `0x1c8a1300` | matpush key staging-register byte |
| weight table | `0xae0f480` | `[5,4,3,2,1,40,40,30,30,20,10,10,40,40,40,40]` |
| matmul-format key list | `0x84a2644` | `01,02,03,04,…` —— matmul key byte[0] source |
---
## 示例 — bf16 matmul mode selection
`Dot(lhs[B=512,K=256], rhs[K=256,N=128])`,bf16,运行于 Ghostlite:
```text
1. GetMatmulModes(lhs): bf16, GetConvPrecision==1 → {Low(2), High(1)}.
GetMatmulModes(rhs): bf16 → {Low(2), High(1)}.
2. Cross-product, skip {Low,Low}={2,2}; summed weights:
{High,High}=4+4=8 ; {High,Low}=4+3=7 ; {Low,High}=3+4=7.
stable_sort ascending → {Low,High}(7), {High,Low}(7), {High,High}(8).
First-consumed pair = {Low,High} (lhs=Low ord 2, rhs=High ord 1).
3. GetMatmulDataFormat(bf16, strategy): no x8/x4 pack bits → format 1 (bf16 packed).
4. MatmulModifier{format=1} → reservation group {2,1,1}: res0 held 2 cy, the two
matrix-staging registers held 1 cy each. Back-to-back bf16 latches pipeline at
~1-cy issue while the multi-hundred-cycle systolic latency hides across array depth.
5. For int8 (S32 operand): GetMatmulModes → {Soft Byte 0, Soft Byte 1, Soft Byte 2,
Soft Signed Byte 3} = modes {5,7,9,11}; GetMatmulDataFormat → format 6; the matpush
reservation jumps to {8,7,6} — 4× the bf16 hold, the 4-byte-plane x8 latch sequence.相关组件
| 名称 | 关系 |
|---|---|
mxu-latency-overview | 消费这些 modifier keys 来索引 reservation rows 的模型 |
mxu-latency-vf / -gl / -gf / -pf | format byte 所选择的按代整数矩阵 |
slot-mxu | matmul ops 携带 MatmulMode pair 的 MXU instruction slot |
slot-matprep-iar-latch | latch / matprep ops 以及 GainLatchMode → format 映射 |
交叉引用
- MXU Latency Overview ——
MatmulModifier/MatpushModifierkeys 所索引的MxuLatencyTable模型 - MXU Latency: VF —— Viperfish
array<int,19>reservation integers for each format group - MXU Latency: GL (Ghostlite) —— Ghostlite
array<int,11>reservation integers - MXU Latency: GF (6acc60406) —— TPU7x reservation integers
- MXU Latency: PF —— Pufferfish reservation integers
- MXU Slot —— LLO MXU slot;携带
MatmulModepair 的 matmul ops - Matprep / IAR / Latch —— latch ops 和完整的
GainLatchMode→MatmulDataFormatjump table - Resource Enum (23-slot) —— 更高层的
Resourcevector(不同于 MXU-internalMxuResource) - MxuOpHoldIssues Stall Recurrence —— reservation group 如何变成 issue stall