Skip to content

MatmulMode 和 Modifiers

本页上的所有地址都适用于来自 libtpu-0.0.40-cp314 wheel 的 libtpu.so(build libtpu_lts_20260413_b_RC00,BuildID md5 89edbbe81c5b328a958fe628a9f2207d)。该二进制文件被 stripped —— 每个符号都是反混淆后的 C++ 名称。Section map:.text/.rodata VMA == 文件偏移;.data.rel.ro VMA − 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 个 MatmulMode ordinal、其显示字符串,以及为 mode pair 排序的 16 项 weight table。
  • GetMatmulModes(operand):按 PrimitiveType 划分的候选 mode list,以及由精度驱动的默认值。
  • MatmulDataFormat:format code,以及产生它们的 GetMatmulDataFormat dtype/strategy dispatch。
  • MatmulModifier/MatpushModifier key 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 comparatorConvMatmulModes::operator< @0x130e12a0 —— W[lhs]+W[rhs]
按 dtype 的 mode listSpatialMajorConvolution::GetMatmulModes(operand) @0x130dfbe0(dtype jt @0xae0f26c
笛卡尔积 + sortGetMatmulModes() @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 绑定:

OrdMatmulModeWeight组 / feed role
0Round5bf16/fp32 —— round-to-nearest,单 pass
1High4bf16/fp32 —— high-significand pass
2Low3bf16/fp32 —— low-significand pass
3Soft Middle Eight2bf16 3-pass split —— 中间 8 bits
4Soft Low Eight1bf16 3-pass split —— 低 8 bits
5Soft Byte 040int8 ×8 —— byte plane 0
6Soft Signed Byte 040int8 ×8 signed —— byte plane 0
7Soft Byte 130int8 ×8 —— byte plane 1
8Soft Signed Byte 130int8 ×8 signed —— byte plane 1
9Soft Byte 220int8 ×8 —— byte plane 2
10Soft Byte 310int8 ×8 —— byte plane 3(最高)
11Soft Signed Byte 310int8 ×8 signed —— byte plane 3
12Nibble 040int4 ×4 —— nibble plane 0
13Signed Nibble 040int4 ×4 signed —— nibble plane 0
14Nibble 140int4 ×4 —— nibble plane 1
15Signed Nibble 140int4 ×4 signed —— nibble plane 1

注意 — ordinal 4 的 operator<< 显示字符串是字面值 "Soft Low Eight"(长度 14,三个以空格分隔的词)。这是 jellyfish C++ 枚举的打印字符串,区别于由 MatmulModeAttr::print 发出的 MLIR llo::MatmulMode attribute 拼写 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 k unsigned(ords 5/7/9/10 = byte planes 0/1/2/3),Soft Signed Byte k signed(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:

c
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

GetMatmulDataFormatconvolution_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
1bf16 packed{2,1,1}(单 bf16)
2bf16 packed,alternate path{2,1,1}
3 / 9F8E4M3Fn(native / converted)fp8
4F32wide
5 / 7F8E5M2(native / variant)fp8
6int8 / x8{8,7,6}(x8 quad)
8int4 unsignedx4
10fp8 fnuz variantfp8

stage-2 dtype targets:PrimitiveType 2 → 66 → 519 → 3 / 9(native vs converted,由一个 Target vtable slot 设门槛),20 → 1021 → 6 / 8(int4 signed vs unsigned,取决于 strategy x4/x8 bits 上的 ±2),22 → 5 / 723 → 4;3..18 中的所有其他 type 都进入 FATAL arm。

Modifier keys

MxuLatencyTable::GetResourceUsage 根据 op family 构建两种 key struct 之一(lookup 本身记录在 mxu-latency-overview 中):

KeyFamilyWidthByte layoutSource helpers
MatmulModifiermatmul8 Bbyte[0] = format(VF matmul cases 212 / 218 / 230 中为 1 / 2 / 6);byte[1..] = 0inline,format-key list @0x84a2644
MatpushModifierlatch / matprep4 Bbyte[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:

text
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 / -pfformat byte 所选择的按代整数矩阵
slot-mxumatmul ops 携带 MatmulMode pair 的 MXU instruction slot
slot-matprep-iar-latchlatch / matprep ops 以及 GainLatchMode → format 映射

交叉引用