Skip to content

分段加法扫描

本页中的每个 opcode 值、Matches() 比较立即数、字段 shift/width、per-gen capability predicate 和 intrinsic 名称,均从 libtpu-0.0.40-cp314 wheel(build-id 89edbbe81c5b328a958fe628a9f2207d;build libtpu_lts_20260413_b_RC00)中的 libtpu.so 逐字节读取而来;来源包括每个 SparseCoreTecVectorExtendedSegmentedAddScan<dtype>Opcode::Matches() 立即数和字段 accessor、mlir::sparse_core::SegmentedScanOp::buildaddOperands 顺序,以及五个 xla::jellyfish::<Gen>Target::Supports{VectorPack,VectorUnpack,DynamicUnpack}Ops / SupportsBf16AluInstructions 函数体。地址适用于此 build;其他版本会有所不同。

摘要

SegmentedAddScan 是 SparseCore 的 embedding-row reducer:它是按 segment 执行的 inclusive prefix sum,并且运行中的 accumulator 会在每个 segment boundary 处重置。它是 DLRM embedding lookup 的 reduce 阶段;一个 TILE_SPMEM tile 持有若干 sample 的 gathered row,每个输出 row 必须是某个 sample 的连续 run 的和。segment boundary 是 XlaSparseDenseMatmulWithCsrInput 携带的 compressed-sparse-row(CSR)offset vector;scan 会在每个新 sample 处重置 partial sum。forward sum-lookup 是一个 SegmentedAddScan;结果经由 XRF → VectorResult drain,并通过 VectorStore …Add{dt} mux scatter-add 回去。这是 gather → load → reduce → drain → scatter pipeline 的第 3 阶段 reduce。

关键架构事实是:SegmentedAddScan 是一个已退役 TensorCore primitive 的 SparseCore 继任者,并且它以不同方式携带 segment boundary。旧 TensorCore 路径(仅 Jellyfish/Pufferfish)使用侧向控制寄存器:SetSegmentPatternVsetspr,LLO 0x8c)op 会编程一个 per-lane boundary register,kVector{Max,Min,Add}SegmentReduceF32 opcode(0xfa/0xfb/0xfc)会遵守它。SparseCore 没有 SetSegmentPattern analog。 相反,segment boundary 被折叠进一个普通向量 operand,即 SegmentedScanOp 的 operand 1,并像任何数据输入一样 register-allocate 到 V read port。没有 prologue pattern 指令,也没有 per-XLU pattern cache;segment id 作为向量沿数据路径传递,而不是控制寄存器。较新的世代(Viperfish、Ghostlite)完全移除了 TensorCore reduce(SupportsSegmentedReduce false),并把 embedding-segment-reduce 移到专用 SparseCore VectorExtended scan unit 上。

本页负责三件事:SegmentedAddScan op 族(六种 dtype 变体、证明 segment id 必须是现有 V operand 的 op-invariant operand frame,以及 SparseCore SetSegmentPattern 不存在这一否定发现);per-gen VpackFormat capability matrix(五个 TensorCore target 各自支持哪些 pack/unpack/dynamic-unpack format,即 bf16↔f32 widen 和 embedding-quant dequant 所依赖的供给侧);以及把 XlaSparseDenseMatmulWithCsrInput 连接到 SparseCoreTecVectorExtended_SegmentedAddScan{dtype} opcode 的 HLO → dialect → intrinsic → ISA emit chain。op-invariant VEX frame 和 opcode 清单位于 VectorExtended;scan datapath 和 mask 消费位于 Scan Datapathreduction_op × element-type lowering switch 位于 Segmented Scan。这些内容在此链接而不重复。

对于重新实现,契约如下:

  • SegmentedAddScan 是同一 VEX opcode 形式的六种 dtype 变体,存在于两个 SparseCore 引擎(gxc::gfc general-fetch-core 和 gxc::glc general-load-core)中:F32S32S16PartialSum{S16,S32}Bf16PartialSum{Bf16,F32}。opcode 是位于 word0x28 bit 16..21(gfc)的 flat 6-bit selector;SegmentedAddScanS32 = 10(Matches cmp 0xa0000),SegmentedAddScanF32 = 15(cmp 0xf0000)。
  • operand frame 与普通 AddScan twin 逐字节一致。 每个字段 accessor(SourceOneVmaskVstSourceV0/V1/V2 Y_VREG+X)都与普通 AddScanF32 具有相同的 word/shift/mask;只有 6 位 opcode 不同(普通 AddScanF32 = 5,segmented = 15)。Segmented 变体不增加字段,因此 segment boundary 必须是现有 V operand 之一。
  • segment boundary 是 SegmentedScanOp 的 operand 1,而不是控制寄存器。 build 按顺序添加两个 Value operand:(data, segment-id),再添加 reduction_op StringAttr"sum"/"min"/"max" 之一;add 路径的 attribute 字符串字面量是 "sum",不是 "add")。segment id 被分配到一个 V read port(按 build 顺序推断为 V1)。不存在 SparseCore SetSegmentPattern
  • 只有 add 有 PartialSum-widening 变体tpu_add_half_seg_scan2xN);min/max 不需要更宽 accumulator。PartialSum 变体(Bf16→F32S16→S32)是 embedding-row sum primitive:许多窄 row 累加成一个宽 partial,避免精度损失或溢出。
  • VpackFormat capability 按 JF ⊂ PF ⊂ VF ⊂ GL 单调增长。 重新实现者需要针对 target 的 Supports*Ops predicate gate 每个 pack/unpack;每代支持的 format 集合是 §The VpackFormat Capability Matrix 中逐字节确定的 bitmask。
ISA opSparseCoreTecVectorExtended_SegmentedAddScan{F32,S32,S16PartialSum{S16,S32},Bf16PartialSum{Bf16,F32}}
Slot64 字节 TEC bundleVectorExtended(VEX)slot
Opcode 字段6-bit @ word0x28 bits 16..21(gfc);S32=10(0xa0000),F32=15(0xf0000
引擎gxc::gfc(fetch-core)和 gxc::glc(load-core)二者均支持,已由符号确认
Dialect opmlir::sparse_core::SegmentedScanOp::build0x145fd4a0)— 2 个 operand (data, segment-id) + reduction_op
LoweringSegmentedScanOpLowering::matchAndRewrite0x13589d40)— 参见 Segmented Scan
HLO sourceXlaSparseDenseMatmul[Grad]WithCsrInput0xe650800 / 0xe65e920)— CSR offset = segment boundary
Proto oneofsegmented_add_scan_f32(inst case 0x23/35;mutable_segmented_add_scan_f32 0x13aaf600
TC 前身kVector{Max,Min,Add}SegmentReduceF32 0xfa/0xfb/0xfc + SetSegmentPattern 0x8c — 仅 JF/PF,已在 VF/GL 上退役
置信度CONFIRMED(decompile / Matches-immediate & accessor-shift anchored),除非某行或 callout 另有说明

NOTE — 本页负责 SegmentedAddScan op 族、per-gen VpackFormat capability,以及 pack/unpack supersession 叙事。 op-invariant VEX operand frame 和完整 opcode 清单位于 VectorExtended;masked-scan datapath 位于 Scan Datapathreduction_op × element-type lowering switch 位于 Segmented Scan。这些内容在此链接而不重复。


SegmentedAddScan Op 族

用途

SegmentedAddScan 计算 per-segment inclusive prefix scan:在每个 segment 内,运行中的 accumulator 对各 lane 求和;在 segment boundary 处,accumulator 重置为 reduction identity(add 的 identity 为 0)。当一个 VREG 持有多个 sample 的 row 时,这正是 embedding-bag aggregation:每个 sample 是一个 segment,每个 sample 的和就是该 segment 最终的 scan value。它是 tf.math.segment_sum 的 SparseCore analog,在一个 VEX bundle slot 中执行,而不是通过软件循环执行。

六种 dtype 变体

该 op 是一个由六种 accumulation dtype 参数化的 VEX 形式,全部存在于 gxc::gfcgxc::glc 中。PartialSum 后缀表示输出(accumulator)宽度相对于输入 element 宽度:窄输入被累加进更宽的 partial,使长 embedding run 不会溢出或损失精度。

变体输入 → accumulator角色
SegmentedAddScanF32f32 → f32float embedding sum(op 15)
SegmentedAddScanS32s32 → s32integer embedding sum(op 10)
SegmentedAddScanS16PartialSumS16s16 → s16narrow-int sum,不 widen
SegmentedAddScanS16PartialSumS32s16 → s32int8/int16 row sum widen 到 s32
SegmentedAddScanBf16PartialSumBf16bf16 → bf16bf16 sum 保持在 bf16
SegmentedAddScanBf16PartialSumF32bf16 → f32bf16 embedding-row sum(多行 → f32 partial)

每个变体都是一个 distinct C++ 类型,携带完整 accessor 集:…<dtype>Opcode::Matches…<dtype>SourceOneField…V0/V1/V2{X,YVreg}Field…VmaskField…VstSourceField;反编译中已确认 Bf16PartialSumBf16Bf16PartialSumF32S16PartialSumS16S16PartialSumS32F32S32 全部存在。这六个是 12-op Segmented* 族中的 SegmentedAddScan 行(普通 scan 的 segmented twin:AddMinMaxMinIndexMaxIndex × {32-bit ops 10..19, 16-bit/bf16 ops 40..51});完整清单位于 VectorExtended

op-invariant operand frame

关键结构发现:SegmentedAddScanF32 的字段 bit 布局与普通 AddScanF32 逐字节一致。只有 6 位 opcode 不同。op 就是函数;operand 是按位置固定的。

text
SegmentedAddScanF32 operand frame (gfc)  —  byte-identical to plain AddScanF32
 field        word       shift  width  accessor (Seg gfc)   role
 ───────────  ─────────  ─────  ─────  ───────────────────  ─────────────────────────────────
 Opcode       0x28       16     6      Matches & 0x3f0000   plain AddScanF32=5, Segmented=15
 SourceOne    0x28       13     3      0x1eca9aa0           scan seed-port / carry-in selector
 Vmask        0x28       5      5      0x1eca9ba0           lane predicate mask
 VstSource    0x30       27     6      0x1eca9ac0           fused store-source (== VectorStore Source)
 V0YVreg      0x40/0x38  shld4  6      0x1eca9ae0           operand-0 VREG (data)
 V0X          0x40       8      6      0x1eca9b00           operand-0 selector
 V1YVreg      0x38       23     6      0x1eca9b20           operand-1 VREG (segment-id, INFERRED)
 V1X          0x38       35     6      0x1eca9b40           operand-1 selector
 V2YVreg      0x30       50     6      0x1eca9b60           operand-2 VREG
 V2X          0x38/0x30  shld2  6      0x1eca9b80           operand-2 selector
```text

`Matches()` predicate 隔离 opcode 字段并与该 op 的 signature 比较。以下内容逐字节来自反编译函数体:

```c
// SparseCoreTecVectorExtendedSegmentedAddScanF32Opcode::Matches  (gfc)
return (*((uint32_t *)this + 10) & 0x3F0000) == 0xF0000;   // word0x28 bits16..21 == 15
// SegmentedAddScanS32Opcode::Matches
return (*((uint32_t *)this + 10) & 0x3F0000) == 0xA0000;   // == 10
// plain AddScanF32Opcode::Matches (for contrast)
return (*((uint32_t *)this + 10) & 0x3F0000) == 0x50000;   // == 5

QUIRK — Segmented 变体不增加字段,因此 segment boundary 不可能是新 operand。 期望存在 segment-mask accessor(类似“boundary register”的 obvious analog)的重新实现者找不到它。frame 与普通 scan 相同;因此 segment id 必须是现有三个 V operand pair 之一。dialect build(下一节)把它固定为 operand 1。opcode-keyed extraction 规则,即同一 bit 区域由 op 选择解释方式,与 VectorExtended 用于 SourceTwo/VexDest 的规则相同。

NOTE — glc 编码把 opcode 放在不同 bit 位置。 某些 Matches 函数体测试 & 0x1F8000 而不是 & 0x3F0000;glc(load-core)的 VEX 字段位于 word0x28 bit 15(不是 16),且 mask 少一位。opcode 以相同方式恢复(glc 使用 cmp >> 15);per-gen field delta 由 VectorExtended 负责。


Segment Boundary:没有 SetSegmentPattern

用途

这是它与已退役 TensorCore reduce 最重要的差异。在 TensorCore 上,segment boundary 位于侧向 XLU 控制寄存器中;在 SparseCore 上,它是普通向量 operand。移植 TensorCore datapath 的重新实现者会寻找 boundary-register prologue op,但不应发出它。

否定发现

EmitVectorSetSegmentPattern 存在于二进制中,但只存在于七个 TensorCore / BarnaCore emitter 中:JellyfishEmitter0x140b58e0)、PufferfishTensorCoreEmitter0x1411bfa0)、PufferfishBarnaCoreChannel0x140cf020)、PufferfishBarnaCoreSequencer0x140e4680)、BarnaCoreAddressHandler0x141654a0)、ViperfishTensorCoreEmitter0x141dd280)和 GhostliteTensorCoreEmitter0x1429ff20,会 LogFatal "not supported")。没有任何 gxc/gfc/glc 符号。LLO factory LloInstruction::CreateVectorSetSegmentPattern0x1d4d64a0)是纯 TensorCore op(LLO 0x8c,一个 operand,无 mode 字段)。SparseCore 从不构建它。

boundary 作为 build operand

c
// mlir::sparse_core::SegmentedScanOp::build(OpBuilder&, OperationState&,    (0x145fd4a0)
//                                           Type result, Value data, Value segment, StringAttr reduction_op)
function SegmentedScanOp_build(state, result_ty, data, segment, reduction_op):
    addTypes(state, result_ty)
    addOperands(state, &data,    1)      // operand 0 = DATA              (0x145fd4ca)
    addOperands(state, &segment, 1)      // operand 1 = SEGMENT-ID        (0x145fd4db)
    setProperty(state, reduction_op)     // "add" / "min" / "max" StringAttr

// plain twin: mlir::sparse_core::ScanOp::build  (0x145f9480) — (data, 2nd operand) instead
```text

`reduction_op` `StringAttr` 由 `sc_ops` attribute constraint(`0x145f8f80`)验证,即对 `"add"`/`"min"`/`"max"` 进行长度为 3 的比较。在 ISA emit 阶段,read-port allocator(`FindAndEmitToUnusedPort`,gfc `0x13ab2aa0` / glc `0x13a4b680`)连接最多六个 read port,写入 `V0`@`struct+0x1c`、`V1`@`+0x24`、`V2`@`+0x28`、`Vmask`@`+0x2c`。`EmitVectorResultUnop<…SegmentedAddScanF32>`(gfc `0x13aaf560`)读取 `operand[0x10]` → `Vmask` 和 `operand[0x20]` → primary data Vregno;其余 V port 携带 segment id。

> **GOTCHA — segment id 占用哪个 V port 是 INFERRED,而非已固定。** `build` 固定 *SSA operand 顺序*(operand 0 = data,operand 1 = segment),read-port allocator 按结构连接 `V0`/`V1`/`V2`/`Vmask`;但精确的 operand-index → V-port-field 绑定是从 allocator 结构读出的,而不是来自单一 operand-index 常量。segment id 落在 **V1** 是“第二个 operand”的自然解读,但重新实现者必须确认 allocator 的 port-assignment policy,而不是硬编码 V1。6-read-port 预算(`cmp 6`)已逐字节确认。

---

## HLO → Dialect → Intrinsic → ISA Emit 链

### 用途

forward embedding sum-lookup 及其 gradient 都经由这条链 lower。DLRM op 携带的 CSR(compressed-sparse-row)row offset 会变成 scan 重置所依据的 per-lane segment-id vector。

### 链路

```text
1. HLO       XlaSparseDenseMatmulWithCsrInputOp::Compile        @0xe650800   (forward lookup)
             XlaSparseDenseMatmulGradWithCsrInputBase::Compile  @0xe65e920   (gradient)
             CSR input supplies per-sample row offsets = SEGMENT BOUNDARIES
             GetMaxIdsAndUniques @0xe651fa0  bounds the gather/dedup window

2. SC HLO    EmbeddingDataFormattingDecomposer  @0x1095b6a0
   pass      builds the dialect SegmentedScanOp (reduction_op ∈ {add,min,max},
             operand-0 = data, operand-1 = segment-id)

3. dialect→  SegmentedScanOpLowering::matchAndRewrite  @0x13589d40           (see Segmented Scan)
   intrinsic  switch reduction_op × elementType {F32, I32, I16, BF16}
             → emit the matching tpu_*_seg_scan* intrinsic

4. ISA       SparseCoreTecVectorExtended_SegmentedAddScan{dtype}
             proto `inst` oneof case  (segmented_add_scan_f32 = 0x23)

lowering 读取 getReductionOp()(3 字符 scan kind)和结果 VectorType 的 element type(通过 Builder::getF32Type / getI32Type / getI16Type / getBF16Type),构建一个 i1 VectorType(per-lane segment-boundary mask)和一个 LLVMStructType 字面量({value, segment-id} reduce pair),然后创建匹配的 intrinsic,并用 LLVM::ExtractValueOp 把 value 取回。完整 dispatch table 由 Segmented Scan 负责;它发出的六个 segmented intrinsic 是:

reductionelem-typesegmented intrinsicISA dtype
addf32tpu_add_seg_scan1xNf (0x146d5a80)SegmentedAddScanF32
addi32tpu_add_seg_scan1xNi (0x146d5c40)SegmentedAddScanS32
addbf16tpu_add_half_seg_scan2xN (0x146d45c0)SegmentedAddScanBf16PartialSum*
maxf32tpu_max_seg_scan1xNf (0x14730e00)SegmentedMaxScanF32
maxi32tpu_max_seg_scan1xNi (0x14730fc0)SegmentedMaxScanU32
minf32tpu_min_seg_scan1xNf (0x147316c0)SegmentedMinScanF32
mini32tpu_min_seg_scan1xNi (0x14731880)SegmentedMinScanU32

1xNf/1xNi/2xN 后缀表示 lane packing:1xNf = 每个 lane 一个 f32,1xNi = 一个 int32,2xN = 每个 lane 两个 bf16(packed pair)。half 是 PartialSum widen(bf16/s16 累加到 f32/s32)。只有 add 具有 half widen;min/max 不需要更宽 accumulator。

GOTCHA — tpu_max_seg_scan2xN / tpu_min_seg_scan2xN op 类型存在,但 lowering 从不发出它们。 反编译显示 tpu_max_seg_scan2xNtpu_min_seg_scan2xN 的完整 op-definition 符号集(ISA 也携带 SegmentedMin/MaxScanBf16 op 48/49)。但 SegmentedScanOpLoweringmin/max segmented scan 只创建 1xN 形式;packed-bf16-pair segmented min/max 在 dialect 中为完整性而定义,但从该 lowering 不可达。根据 op-definition 列表驱动的重新实现者会分配 lowering 永远不会调用的 handler。只有 add 有可达的 segmented 2xN 路径(tpu_add_half_seg_scan2xN)。


VpackFormat 能力矩阵 {#the-vpackformat-capability-matrix}

用途

SegmentedAddScan PartialSum 变体和 EUP 的 bf16↔f32 widen 都依赖 XLU/EUP 能够 pack 与 unpack 相关 lane format。该能力是 per-generation 的,也是本页记录的供给侧:26 个 VpackFormat enum 值中,五个 TensorCore target 各自能 pack(lanes → narrow slot)、unpack(narrow slot → lanes)、dynamic unpack,以及原生计算哪些格式。bf16 pack/unpack 算术本身(kVectorPack 0x126 / kVectorUnpack 0x109<<16 / &0xffff0000 widen)归 LLO XLU 页面负责;本节记录 capability gate。

四个 capability 方法

每代 Target 暴露四个 predicate。前三个存在于 +0x10 subobject vtable 中(slot +0x4c8/+0x4d8/+0x4e0);第四个是 primary vtable 中的 EUP lane sub-element-width selector。

  • SupportsVectorPackOps(VpackFormat) — XLU/EUP 可pack的 format。
  • SupportsVectorUnpackOps(VpackFormat, TpuCoreType) — 可 unpack的 format。TpuCoreType arg 在 VF/GL 上被忽略(core-type-invariant;反编译的 VF/GL signature 只接收 format)。
  • SupportsDynamicUnpackOps(VpackFormat)0x10f kVectorDynamicUnpack gate。
  • SupportsBf16AluInstructions() — EUP AluEp lane 宽度:false → 32-bit/F32 lane(packed bf16 必须 unpack 到 F32,计算,再 re-pack);true → 16-bit/BF16 lane(原生 per-lane bf16 op,无需 unpack)。

分代 predicate(逐字节)

c
// JellyfishTarget                                     (Dragonfish ≡ Jellyfish: vtable slots identical, no override)
SupportsVectorPackOps(fmt)        = (fmt == 7);                       // 0x1d4907c0  → {7}
SupportsVectorUnpackOps(fmt,_)    = false;                            // 0x1d490840  → {}
SupportsDynamicUnpackOps(fmt)     = false;                            // 0x1d490860  → {}
SupportsBf16AluInstructions()     = false;                           // 0x1d4916e0  (32-bit F32 lane)

// PufferfishTarget
SupportsVectorPackOps(fmt)        = (fmt == 7 || fmt == 1);           // 0x1d494e00  → {1,7}
SupportsVectorUnpackOps(fmt,_)    = (fmt == 1);                       // 0x1d494e20  → {1}
SupportsDynamicUnpackOps(fmt)     = false;                            // 0x1d494e40  → {}
SupportsBf16AluInstructions()     = false;                           // 0x1d495c20  (32-bit F32 lane)

// ViperfishTarget
SupportsVectorPackOps(fmt)        = ((uint16_t)(fmt - 1) < 0xA);      // 0x1d49b1a0  → {1..10}
SupportsVectorUnpackOps(fmt,_)    = (fmt < 0xE) & (0x39FE >> fmt);    // 0x1d49b1c0  → {1..8,11,12,13}
SupportsDynamicUnpackOps(fmt)     = false;                            // 0x1d49b1e0  → {}
SupportsBf16AluInstructions()     = false;                           // 0x1d49c0e0  (32-bit F32 lane)

// GhostliteTarget
SupportsVectorPackOps(fmt)        = (fmt < 0x17) & (0x7807FE >> fmt); // 0x1d498020  → {1..10,19..22}
SupportsVectorUnpackOps(fmt,_)    = (fmt < 0x17) & (0x7839FE >> fmt); // 0x1d498040  → {1..8,11..13,19..22}
SupportsDynamicUnpackOps(fmt)     = (fmt < 8)    & (0x8E   >> fmt);   // 0x1d498060  → {1,2,3,7}
SupportsBf16AluInstructions()     = true;                            // 0x1d498ce0  (16-bit BF16 lane, native)
```text

`Base` `Target` 的 `Pack`/`Unpack`/`Dyn` slot(`0x1d61e200`/`40`/`80`)是 pure-virtual `LogFatal`;每个 concrete generation 都必须 override。

> **NOTE — Dragonfish ≡ Jellyfish,用于 pack/unpack。** `DragonfishTarget` 没有自己的 `Supports{VectorPack,VectorUnpack,DynamicUnpack}Ops`。它的 `+0x10` subobject vtable slot `+0x4c8`/`+0x4d8`/`+0x4e0` 解析(通过 `R_X86_64_RELATIVE`)到与 `JellyfishTarget` *完全相同*的地址(`0x1d4907c0`/`0x1d490840`/`0x1d490860`)。`SupportsBf16AluInstructions` 也是 `false`(无 override)。对所有四项 capability 都把 DF 当作 JF 处理。

### format-support 交叉表

`VpackFormat` enum26 个条目(`0` 为 invalid sentinel)。支持集合单调增长:JF 只能 *pack* interleaved bf16;PF 增加最小的 bf16↔f32 round-trip;VF 打开完整 sub-byte int pack 和 f16/f8 unpack;GL 增加 U8/S8/U4/S4→bf16 embedding-quant dequant,是唯一支持 dynamic unpack 的一代,也是唯一具有原生 bf16 ALU lane 的一代。

| fmt | name | JF/DF | PF | VF | GL |
|---:|---|---|---|---|---|
| 1 | CompressedBf16 || P U | P U | P U **D** |
| 2 | CompressedB16 ||| P U | P U **D** |
| 3 | CompressedB8 ||| P U | P U **D** |
| 4 | CompressedB4 ||| P U | P U |
| 5 | CompressedB2 ||| P U | P U |
| 6 | CompressedB1 ||| P U | P U |
| 7 | InterleavedBf16 | **P** | P | P U | P U **D** |
| 8 | InterleavedB16 ||| P U | P U |
| 9 | InterleavedB8 ||| P | P |
| 10 | InterleavedB4 ||| P | P |
| 11 | CompressedHf16 (f16) ||| U | U |
| 12 | CompressedF8E4M3B11 ||| U | U |
| 13 | CompressedF8E5M2 (bf8) ||| U | U |
| 1418 | f8e4m3fn / EXMY→bf16 |||||
| 19 | CompressedU8ToBf16 |||| P U |
| 20 | CompressedS8ToBf16 |||| P U |
| 21 | CompressedU4ToBf16 |||| P U |
| 22 | CompressedS4ToBf16 |||| P U |
| 2325 | Join / Interleaved-f8 |||||

(P = 支持 pack,U = 支持 unpack,**D** = dynamic-unpack。f8e4m3fn / EXMY→bf16(14..18)和 Join/Interleaved-f8(23..25)format 在这五个 target 上均未启用。)

> **QUIRK — `SupportsBf16AluInstructions` 是 Ghostlite 以一半 op 数处理 packed bf16 的原因。** 在 JF/DF/PF/VF(`false`)上,进入 EUP AluEp 的 packed bf16 vector 必须先 *unpack 到 32-bit F32 lane*,以 F32 计算,再 re-pack;每个 element 需要多个 LLO `kVectorUnpack`/`kVectorPack` 步骤。在 GL(`true`)上,BF16 ALU 原生运行 per-lane bf16 op(1:1 EUP-macro path,无 unpack)。因此 GL 上的 `SegmentedAddScanBf16PartialSumF32` 不需要 widen prologue;VF 上相同 op 先 unpack 到 F32。

### VpackFormat fan-in

lane fan-in(多少个 sub-lane pack 到一个 slot 中)是 `VpackFormatSublanesIndices` 表(`0xb53c790`,26 个 `int32`);`fmt0 = -1` 是 invalid sentinel。4-fan-in format 是 sub-byte / f8 / u4/s4 dequant pack(`{3,9,12,13,14,15,21,22}`);其余为 2

```text
 fmt:   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25
 fan:  -1   2   2   4   2   2   2   2   2   4   2   2   4   4   4   4   2   2   2   2   2   4   4   2   2   2

TensorCore → SparseCore 取代关系

用途

SegmentedAddScan 并非凭空出现;它替代了一个 TensorCore XLU primitive。理解这一差异既能修正架构认知,也能告诉重新实现者某一代实际使用哪条路径执行 embedding-segment-reduce。

已退役的 TensorCore 路径

在 Jellyfish 和 Pufferfish 上,per-segment cross-lane reduce 是三个仅 F32 的 XLU opcode:kVectorMaxSegmentReduceF320xfa)、kVectorMinSegmentReduceF320xfb)、kVectorAddSegmentReduceF320xfc),由 LloOpcodeIsSegmentedReduction0x1d60c340(op - 0xfa) < 3)gate。boundary 来自侧向寄存器:SetSegmentPatternVsetspr,LLO 0x8c)编程 per-lane segment-id register,reduce 会遵守它;一对 fused reduce 共享一个 Vsetspr setup(按 per-XLU 缓存)。不存在 bf16 segment reduce;TensorCore 路径仅 F32。

per-gen support gate:

TargetSupportsSegmentedReduceSupportsSegmentedReducePartialResultsTC emitter 行为
Jellyfishtrue (0x1d4909c0)true (0x1d490a00)emits VEX 0xe/0x1e/0x1f/0x20
Pufferfishtrue (0x1d494f80)false (0x1d494fc0)emits(无 partial-result drain)
Viperfishfalse (0x1d49b380)false (0x1d49b3c0)改用 SparseCore VectorExtended
Ghostlitefalse (0x1d4981c0)false (0x1d498200)LogFatal "Operation not supported on Ghostlite"

架构差异

text
TENSORCORE (JF/PF)                         SPARSECORE (VF/GL/gfc/glc — this page)
 ─────────────────                          ─────────────────────────────────────
 SetSegmentPattern (0x8c, Vsetspr)          NO SetSegmentPattern op exists
   → side control register (per-XLU)        segment id = operand-1 of SegmentedScanOp
 kVectorAddSegmentReduceF32 (0xfc)          SparseCoreTecVectorExtended_SegmentedAddScan{dtype}
   F32-only, 3 opcodes                        6 dtypes incl. bf16/s16 PartialSum widen
 two reduces share one Vsetspr (cached)     boundary rides the data path as a vector
 retired on VF/GL (SupportsSegmentedReduce  the embedding-segment-reduce engine on VF/GL
   false)
```text

较新的世代把 embedding segment-reduce 从带有侧向 pattern register 和 shared-pattern fusion 的 TensorCore XLU,迁移到 SparseCore 专用 VectorExtended scan unit,并把 segment boundary 折叠进普通向量 operand。TensorCore reduce 现在是 JF/PF legacy primitive。

> **NOTE — TensorCore reduce 从 TC LLO/emit 侧记录,不在本页展开。** 本页记录的是 *SparseCore 继任者*。TensorCore `0xfa`/`0xfb`/`0xfc` reduce、`Vsetspr` `SetSegmentPattern`、Reemit shared-pattern fusion,以及 per-gen VEX-opcode map 属于 LLO XLU 页面;此处只负责取代边界,即哪一代使用哪条路径。

---

## 示例:bf16 multi-sample embedding lookup-sum

一个 minibatch 持有多个 sample;每个 sample lookup 一段连续 bf16 embedding row,这些 row 被 gather 到一个 `TILE_SPMEM` tile 中。forward output row 是该 sample 这段 run 的 per-sample sum。

```text
SparseCore (VF/GL, this page) — NO SetSegmentPattern:
  data       = bf16 embedding rows (gathered, one tile)
  segment_id = CSR-offset vector (per-sample boundaries from XlaSparseDenseMatmulWithCsrInput)
  mlir::sparse_core::SegmentedScanOp(data, segment_id, reduction_op="add")
    → tpu_add_half_seg_scan2xN
    → SparseCoreTecVectorExtended_SegmentedAddScanBf16PartialSumF32  (op 47)
  one ISA op: per-segment prefix sum of the bf16 rows into an f32 partial (the PartialSum widen),
  the segment_id riding the V1 operand as a normal vector (INFERRED).
  GL: SupportsBf16AluInstructions=true  → native bf16 lane, no unpack.
  VF: SupportsBf16AluInstructions=false → EUP AluEp unpacks to F32 first.
  result drains via XRF → VectorResult, scatter-adds via VectorStore TileSpmemStoreAddBf16.

TensorCore equivalent (JF/PF, the retired path):
  SetSegmentPattern (0x8c, Vsetspr) programs the per-lane boundary register;
  kVectorAddSegmentReduceF32 (0xfc) sums each segment (accumulator resets at boundaries),
  F32-only — the bf16 rows widened by VunpackF32 (<<16 / &0xffff0000) first.

SparseCore 把 boundary 折叠进数据路径,并在一个 op 中保持 bf16 partial sum;TensorCore 需要一个独立 boundary-register prologue,并先 pre-widen 到 F32。


尚未固定的内容

  • segment id 的精确 V-port index。 build 固定 operand 顺序(0 = data,1 = segment-id),read-port allocator 连接 V0@+0x1c/V1@+0x24/V2@+0x28/Vmask@+0x2c(6 port);operand-index → V-port 绑定是结构读取结果,而不是固定到常量。INFERRED segment-id = V1。(INFERRED)
  • SourceOne 3 位 scan-seed enum(reduction identity / carry-in:add 为 0,min/max 为 ±inf,或 multi-tile chaining 的 prev-accumulator)。与普通 scan 相同字段;其 8 个值未枚举。(LOW)
  • DLRM front-end 来源XlaSparseDenseMatmulWithCsrInput 的 CSR row-offset 如何 materialise 成 segment-id vector operand。HLO → SegmentedScanOp 链路已确认;把 offset 转成 per-lane segment id 的 decomposer 未端到端追踪。(Chain HIGH;materialiser 未追踪。)
  • SupportsBf16AluInstructions 的精确 primary-vtable offset。 per-gen 返回值(JF/DF/PF/VF false,GL true)已从方法体逐字节确认,但相邻 vtable slot 返回不同值,因此精确 slot index 仍未定。(Return values CONFIRMED;slot index LOW。)
  • f8e4m3fn / EXMY→bf16(14..18)和 Join/Interleaved-f8(23..25)format 是否在此处没有 distinct vtable 的后续 TC target 上启用。 VF/GL 从不启用它们。(VF/GL 已 CONFIRMED;后续 target 未追踪。)

交叉引用

  • VectorExtended (VEX) — op-invariant VEX operand frame,以及 SegmentedAddScan 族所在的完整 0..52 opcode 清单
  • Scan Datapath — masked-scan datapath、mask 消费,以及该 segmented 变体所对应的 ScanOp lowering
  • Segmented Scan — 选择 intrinsic 的 SegmentedScanOpLowering reduction_op × element-type dispatch switch
  • VectorStore — scan 结果 drain 到的 VstSource fused-store mux(…Add{dt} scatter-add)
  • Dedup / Multiplicity — 在 segment-reduce 之前界定 per-sample run 的 gather-side dedup/uniquify
  • SparseCore Overview — 此 op 作为 reduce 阶段所属的 gather → load → reduce → drain → scatter embedding pipeline