分段加法扫描
本页中的每个 opcode 值、
Matches()比较立即数、字段 shift/width、per-gen capability predicate 和 intrinsic 名称,均从libtpu-0.0.40-cp314wheel(build-id89edbbe81c5b328a958fe628a9f2207d;buildlibtpu_lts_20260413_b_RC00)中的libtpu.so逐字节读取而来;来源包括每个SparseCoreTecVectorExtendedSegmentedAddScan<dtype>Opcode::Matches()立即数和字段 accessor、mlir::sparse_core::SegmentedScanOp::build的addOperands顺序,以及五个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)使用侧向控制寄存器:SetSegmentPattern(Vsetspr,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 Datapath;reduction_op × element-type lowering switch 位于 Segmented Scan。这些内容在此链接而不重复。
对于重新实现,契约如下:
SegmentedAddScan是同一 VEX opcode 形式的六种 dtype 变体,存在于两个 SparseCore 引擎(gxc::gfcgeneral-fetch-core 和gxc::glcgeneral-load-core)中:F32、S32、S16PartialSum{S16,S32}、Bf16PartialSum{Bf16,F32}。opcode 是位于word0x28bit 16..21(gfc)的 flat 6-bit selector;SegmentedAddScanS32= 10(Matchescmp0xa0000),SegmentedAddScanF32= 15(cmp0xf0000)。- operand frame 与普通
AddScantwin 逐字节一致。 每个字段 accessor(SourceOne、Vmask、VstSource、V0/V1/V2Y_VREG+X)都与普通AddScanF32具有相同的 word/shift/mask;只有 6 位 opcode 不同(普通AddScanF32= 5,segmented = 15)。Segmented 变体不增加字段,因此 segment boundary 必须是现有 V operand 之一。 - segment boundary 是
SegmentedScanOp的 operand 1,而不是控制寄存器。build按顺序添加两个Valueoperand:(data, segment-id),再添加reduction_opStringAttr("sum"/"min"/"max"之一;add 路径的 attribute 字符串字面量是"sum",不是"add")。segment id 被分配到一个 V read port(按 build 顺序推断为 V1)。不存在 SparseCoreSetSegmentPattern。 - 只有
add有 PartialSum-widening 变体(tpu_add_half_seg_scan2xN);min/max不需要更宽 accumulator。PartialSum 变体(Bf16→F32、S16→S32)是 embedding-row sum primitive:许多窄 row 累加成一个宽 partial,避免精度损失或溢出。 VpackFormatcapability 按 JF ⊂ PF ⊂ VF ⊂ GL 单调增长。 重新实现者需要针对 target 的Supports*Opspredicate gate 每个 pack/unpack;每代支持的 format 集合是 §The VpackFormat Capability Matrix 中逐字节确定的 bitmask。
| ISA op | SparseCoreTecVectorExtended_SegmentedAddScan{F32,S32,S16PartialSum{S16,S32},Bf16PartialSum{Bf16,F32}} |
| Slot | 64 字节 TEC bundle 的 VectorExtended(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 op | mlir::sparse_core::SegmentedScanOp::build(0x145fd4a0)— 2 个 operand (data, segment-id) + reduction_op |
| Lowering | SegmentedScanOpLowering::matchAndRewrite(0x13589d40)— 参见 Segmented Scan |
| HLO source | XlaSparseDenseMatmul[Grad]WithCsrInput(0xe650800 / 0xe65e920)— CSR offset = segment boundary |
| Proto oneof | segmented_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 — 本页负责
SegmentedAddScanop 族、per-genVpackFormatcapability,以及 pack/unpack supersession 叙事。 op-invariant VEX operand frame 和完整 opcode 清单位于 VectorExtended;masked-scan datapath 位于 Scan Datapath;reduction_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::gfc 和 gxc::glc 中。PartialSum 后缀表示输出(accumulator)宽度相对于输入 element 宽度:窄输入被累加进更宽的 partial,使长 embedding run 不会溢出或损失精度。
| 变体 | 输入 → accumulator | 角色 |
|---|---|---|
SegmentedAddScanF32 | f32 → f32 | float embedding sum(op 15) |
SegmentedAddScanS32 | s32 → s32 | integer embedding sum(op 10) |
SegmentedAddScanS16PartialSumS16 | s16 → s16 | narrow-int sum,不 widen |
SegmentedAddScanS16PartialSumS32 | s16 → s32 | int8/int16 row sum widen 到 s32 |
SegmentedAddScanBf16PartialSumBf16 | bf16 → bf16 | bf16 sum 保持在 bf16 |
SegmentedAddScanBf16PartialSumF32 | bf16 → f32 | bf16 embedding-row sum(多行 → f32 partial) |
每个变体都是一个 distinct C++ 类型,携带完整 accessor 集:…<dtype>Opcode::Matches、…<dtype>SourceOneField、…V0/V1/V2{X,YVreg}Field、…VmaskField、…VstSourceField;反编译中已确认 Bf16PartialSumBf16、Bf16PartialSumF32、S16PartialSumS16、S16PartialSumS32、F32 和 S32 全部存在。这六个是 12-op Segmented* 族中的 SegmentedAddScan 行(普通 scan 的 segmented twin:Add、Min、Max、MinIndex、MaxIndex × {32-bit ops 10..19, 16-bit/bf16 ops 40..51});完整清单位于 VectorExtended。
op-invariant operand frame
关键结构发现:SegmentedAddScanF32 的字段 bit 布局与普通 AddScanF32 逐字节一致。只有 6 位 opcode 不同。op 就是函数;operand 是按位置固定的。
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; // == 5QUIRK — 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 字段位于word0x28bit 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 中:JellyfishEmitter(0x140b58e0)、PufferfishTensorCoreEmitter(0x1411bfa0)、PufferfishBarnaCoreChannel(0x140cf020)、PufferfishBarnaCoreSequencer(0x140e4680)、BarnaCoreAddressHandler(0x141654a0)、ViperfishTensorCoreEmitter(0x141dd280)和 GhostliteTensorCoreEmitter(0x1429ff20,会 LogFatal "not supported")。没有任何 gxc/gfc/glc 符号。LLO factory LloInstruction::CreateVectorSetSegmentPattern(0x1d4d64a0)是纯 TensorCore op(LLO 0x8c,一个 operand,无 mode 字段)。SparseCore 从不构建它。
boundary 作为 build operand
// 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 是:
| reduction | elem-type | segmented intrinsic | ISA dtype |
|---|---|---|---|
| add | f32 | tpu_add_seg_scan1xNf (0x146d5a80) | SegmentedAddScanF32 |
| add | i32 | tpu_add_seg_scan1xNi (0x146d5c40) | SegmentedAddScanS32 |
| add | bf16 | tpu_add_half_seg_scan2xN (0x146d45c0) | SegmentedAddScanBf16PartialSum* |
| max | f32 | tpu_max_seg_scan1xNf (0x14730e00) | SegmentedMaxScanF32 |
| max | i32 | tpu_max_seg_scan1xNi (0x14730fc0) | SegmentedMaxScanU32 |
| min | f32 | tpu_min_seg_scan1xNf (0x147316c0) | SegmentedMinScanF32 |
| min | i32 | tpu_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_scan2xNop 类型存在,但 lowering 从不发出它们。 反编译显示tpu_max_seg_scan2xN和tpu_min_seg_scan2xN的完整 op-definition 符号集(ISA 也携带SegmentedMin/MaxScanBf16op 48/49)。但SegmentedScanOpLowering对min/maxsegmented scan 只创建1xN形式;packed-bf16-pair segmented min/max 在 dialect 中为完整性而定义,但从该 lowering 不可达。根据 op-definition 列表驱动的重新实现者会分配 lowering 永远不会调用的 handler。只有add有可达的 segmented2xN路径(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。TpuCoreTypearg 在 VF/GL 上被忽略(core-type-invariant;反编译的 VF/GL signature 只接收 format)。SupportsDynamicUnpackOps(VpackFormat)—0x10fkVectorDynamicUnpackgate。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(逐字节)
// 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` enum 有 26 个条目(`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 |
| 14–18 | f8e4m3fn / EXMY→bf16 | — | — | — | — |
| 19 | CompressedU8ToBf16 | — | — | — | P U |
| 20 | CompressedS8ToBf16 | — | — | — | P U |
| 21 | CompressedU4ToBf16 | — | — | — | P U |
| 22 | CompressedS4ToBf16 | — | — | — | P U |
| 23–25 | 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 2TensorCore → SparseCore 取代关系
用途
SegmentedAddScan 并非凭空出现;它替代了一个 TensorCore XLU primitive。理解这一差异既能修正架构认知,也能告诉重新实现者某一代实际使用哪条路径执行 embedding-segment-reduce。
已退役的 TensorCore 路径
在 Jellyfish 和 Pufferfish 上,per-segment cross-lane reduce 是三个仅 F32 的 XLU opcode:kVectorMaxSegmentReduceF32(0xfa)、kVectorMinSegmentReduceF32(0xfb)、kVectorAddSegmentReduceF32(0xfc),由 LloOpcodeIsSegmentedReduction(0x1d60c340,(op - 0xfa) < 3)gate。boundary 来自侧向寄存器:SetSegmentPattern(Vsetspr,LLO 0x8c)编程 per-lane segment-id register,reduce 会遵守它;一对 fused reduce 共享一个 Vsetspr setup(按 per-XLU 缓存)。不存在 bf16 segment reduce;TensorCore 路径仅 F32。
per-gen support gate:
| Target | SupportsSegmentedReduce | SupportsSegmentedReducePartialResults | TC emitter 行为 |
|---|---|---|---|
| Jellyfish | true (0x1d4909c0) | true (0x1d490a00) | emits VEX 0xe/0x1e/0x1f/0x20 |
| Pufferfish | true (0x1d494f80) | false (0x1d494fc0) | emits(无 partial-result drain) |
| Viperfish | false (0x1d49b380) | false (0x1d49b3c0) | 改用 SparseCore VectorExtended |
| Ghostlite | false (0x1d4981c0) | false (0x1d498200) | LogFatal "Operation not supported on Ghostlite" |
架构差异
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) SourceOne3 位 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 变体所对应的
ScanOplowering - Segmented Scan — 选择 intrinsic 的
SegmentedScanOpLoweringreduction_op× element-type dispatch switch - VectorStore — scan 结果 drain 到的
VstSourcefused-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