SC 后端流水线
地址、符号和 pass 顺序适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d);该二进制随完整 C++ 符号发布(未 strip)。其他版本会不同。
摘要
当 XLA 降低 SparseCore offload 计算时,稠密的 tpu 方言 IR 会先经过一条固定的后端 pass 流水线,然后才变成可编码的 SC bundle。该流水线由 CustomKernelEmitter::RunPasses(0x13202780)构建并驱动。本页把这条流水线记录为一份契约:十二个 pass 的精确有序列表、每个 pass 的 MLIR factory 入口点和命名空间、每个 pass 的挂接方式(顶层 addPass 还是 func 嵌套 nest)、倒数第二个运行的 device-id remap pass,以及 MEGACORE barrier —— 当部件是双核 SparseCore megachip 时,流水线最后的 lowering pass 会发出的偶/奇核心对跨核同步。
最重要的结构事实是,RunPasses 不是构建一个持有十二个 pass 的 PassManager。它会按顺序构建并运行十二个独立的单 pass PassManager,每个都只携带一个 pass,每个都有自己的 IR 打印 gating closure,并且每个都通过 jellyfish::RunPass 独立驱动。这对重新实现很重要:十二个阶段之间没有共享的 pass-manager analysis cache,IR dump 按阶段 gate,顺序硬编码在调用序列中,而不是从 registry 组装出来。LLVM 读者可以宽松类比:这相当于在同一个 ModuleOp 上连续运行十二次 mlir::PassManager::run 的手写版本,而不是一条填充好的单一流水线。
第二个结构事实是,MEGACORE barrier 不是一种 barrier kind。EmitScsBarrier(从最后一个 pass 到达的 SC 侧 barrier dispatcher)只分派 GLOBAL 和 CUSTOM;MEGACORE enum 值在那里是非法的(会 RetCheck)。megacore 偶/奇 partner sync 是一条内部代码路径,只要 Target::LogicalDevicesPerChip(SparseCore) == 2,GLOBAL 和 CUSTOM 两种 lowering 都会运行它。"Megacore" 命名的是硬件拓扑(双核 megachip),不是 producer 选择的一种 barrier。
对重新实现而言,流水线契约是:
- 十二个单 pass manager,固定顺序。
ConvertIntegerMemrefs → InferMemRefLayout → Canonicalizer → CanonicalizeOperations → CanonicalizeMemorySpace → TilingPropagation → mosaic_sc::InferVectorLayout → mosaic_sc::InsertRelayout → mosaic_sc::ApplyVectorLayout → CSE → LogicalToPhysicalDeviceId → LowerToMlo。顺序是先 vector-layout 再 lower;重排 layout 三件套(7/8/9)或把LogicalToPhysicalDeviceId移到LowerToMlo之后都会破坏构建。 - Pass 7/8/9 是
mlir::mosaic_sc,不是mlir::tpu。 SC vector-layout 三件套(InferVectorLayout、InsertRelayout、ApplyVectorLayout)位于mosaic_sc命名空间。Pass 3 是通用的mlir::createCanonicalizerPass;pass 4/5 是tpu专用 canonicalizer。 - device-id remap 必须紧挨着在
LowerToMlo之前运行。LogicalToPhysicalDeviceIdPass(pass 11)把tpu::EnqueueDMAOp/tpu::SemaphoreSignalOp的device_id操作数从逻辑 id 重写为物理核心 id(对 SC 为i32(CoreIndex * stride + TileId))。它必须在 barrier-lowering pass 之前运行,这样LowerToMlo发出的 sync op 才会指向正确的物理核心。 - MEGACORE 是拓扑 gate,不是 barrier 类型。 偶/奇 partner sync(
partner = local core id XOR 1)在GLOBAL和CUSTOM两个 barrier 分支内部都由LogicalDevicesPerChip(SC) == 2gate。单核部件(LDPC(SC) == 1)会抑制 partner 路径(它会RetCheck),只保留本地 sync。
| 流水线构建器 | xla::tpu::sparse_core::CustomKernelEmitter::RunPasses @ 0x13202780 |
| 逐 pass 驱动器 | jellyfish::RunPass @ 0x14514d60; mlir::PassManager ctor @ 0x1cb700a0; 通过 OpPassManager::addPass @ 0x1cb6c000 / ::nest @ 0x1cb6d3e0 挂接 |
| Pass 数量 / 形状 | 12 个 pass,每个都在自己的单 pass PassManager 中(不是一条 12-pass 流水线) |
| 最后一个 pass | createLowerToMloPass @ 0x1322adc0 — SC → MLO lowering;collective op → EmitScsBarrier |
| Device-id remap | pass 11 createLogicalToPhysicalDeviceIdPass @ 0x132d5720; body runOnOperation @ 0x132d5ec0; GetPhysicalCoreID @ 0x132d8de0 |
| SC barrier 分派 | CollectiveEmitterBase::EmitScsBarrier @ 0x13352500 — 仅 GLOBAL(1)/CUSTOM(3) |
| Megacore gate | Target::LogicalDevicesPerChip(SparseCore) == 2; partner = OffloadFactory::ToPartnerGlobalCoreId @ 0x133e66e0(core XOR 1) |
十二 Pass 流水线
RunPasses(ModuleOp module, bool, bool, BarrierConfig const* bc) 保存 BarrierConfig*(它会被传入最后一个 pass),然后按顺序运行十二个 pass。每个 pass 都遵循相同的四步惯用法:
- 用名称和
Nesting构造一个新的mlir::PassManager(ctor0x1cb700a0); - 安装 IR 打印的 "should-print" closure(逐阶段 VLOG/dump gate);
- 通过
OpPassManager::addPass(顶层,作用于ModuleOp)或OpPassManager::nest<func::FuncOp>(...).addPass(func 嵌套,逐函数作用)挂接唯一的 pass; - 通过
jellyfish::RunPass(pm, module, ...)运行它。
在 pass 之间,RunPasses 会读取 HLO BackendConfig、SparseCoreConfig 默认值以及逐元素 layout flag(ShouldEnableLarge2ndMinorLayoutForX16/X8/X4)来填充 pass 选项 —— 最明显的是传给 InferMemRefLayout 的 tiling span,以及 InferVectorLayoutPassOptions / ApplyVectorLayoutPassOptions。
tpu-dialect SparseCore module (post region→sequencer outlining)
│
▼ RunPasses @0x13202780 — 十二个单 pass PassManager,按顺序:
│
1 createConvertIntegerMemrefsPass addPass int-memref 合法化
2 createInferMemRefLayoutPass nest tile/layout 推断(X16/X8/X4 flag)(逐 func)
3 createCanonicalizerPass addPass 通用贪婪 canonicalization
4 createCanonicalizeOperationsPass nest tpu op canonicalization (逐 func)
5 createCanonicalizeMemorySpacePass nest memory-space canonicalization (逐 func)
6 createTilingPropagationPass nest 传播 {N,2} tiling (逐 func)
7 mosaic_sc::createInferVectorLayoutPass nest SC vector layout 推断 (逐 func)
8 mosaic_sc::createInsertRelayoutPass nest 插入 layout-conversion op (逐 func)
9 mosaic_sc::createApplyVectorLayoutPass nest 应用选定的 vector layout (逐 func)
10 createCSEPass addPass common-subexpression elim.
11 createLogicalToPhysicalDeviceIdPass nest device_id → 物理核心 id (逐 func) ── §Device-Id Remap
12 createLowerToMloPass addPass SC → MLO lowering(最后) ── reaches EmitScsBarrier
│
▼
MLO IR ─→ SC bundle emission (per-engine SCS/TAC/TEC codecs)
```text
### 逐 pass 表
这十二个 `create*Pass` 调用严格按此顺序出现;pass 7/8/9 带有 `mosaic_sc` 限定;`addPass`/`nest` 的划分与调用点一一对应。
| # | Factory (VMA) | 命名空间 | 挂接 | `mlir::Pass` 子类 / 作用 |
|---|---|---|---|---|
| 1 | `createConvertIntegerMemrefsPass` @ `0x132bca60` | `mlir::tpu` | `addPass` | `ConvertIntegerMemrefsPass` — integer-memref 合法化 |
| 2 | `createInferMemRefLayoutPass` @ `0x132c0f00` | `mlir::tpu` | `nest` | `InferMemRefLayoutPass` — tile/layout 推断;opts = `(int, Span<long const> tiling, TpuTilingFlags)`,由 X16/X8/X4 layout flag 构建 |
| 3 | `createCanonicalizerPass` @ `0x1c941920` | `mlir`(通用) | `addPass` | `Canonicalizer` — 通用贪婪 canonicalization |
| 4 | `createCanonicalizeOperationsPass` @ `0x132bb260` | `mlir::tpu` | `nest` | `CanonicalizeOperationsPass` — `tpu` op canonicalization |
| 5 | `createCanonicalizeMemorySpacePass` @ `0x132a2280` | `mlir::tpu` | `nest` | `CanonicalizeMemorySpacePass` — memory-space canonicalization |
| 6 | `createTilingPropagationPass` @ `0x132e0900` | `mlir::tpu` | `nest` | `TilingPropagationPass` — 传播 `{N,2}` tiling;opts = `(array<long,2>, bool)` |
| 7 | `createInferVectorLayoutPass` @ `0x132ecf60` | `mlir::mosaic_sc` | `nest` | `InferVectorLayoutPass` — SC vector-layout 推断;opts = `InferVectorLayoutPassOptions` |
| 8 | `createInsertRelayoutPass` @ `0x132eff80` | `mlir::mosaic_sc` | `nest` | `InsertRelayoutPass` — 插入 layout-conversion op |
| 9 | `createApplyVectorLayoutPass` @ `0x132e57e0` | `mlir::mosaic_sc` | `nest` | `ApplyVectorLayoutPass` — 应用选定 layout;opts = `ApplyVectorLayoutPassOptions` |
| 10 | `createCSEPass` @ `0x1c93e180` | `mlir`(通用) | `addPass` | `CSE` — common-subexpression elimination |
| 11 | `createLogicalToPhysicalDeviceIdPass` @ `0x132d5720` | `mlir::tpu` | `nest` | `LogicalToPhysicalDeviceIdPass` — remap DMA/semaphore `device_id` → 物理核心 id(见 [§Device-Id Remap](#device-id-remap-pass-11)) |
| 12 | `createLowerToMloPass` @ `0x1322adc0`(**最后**) | `mlir::tpu` | `addPass` | `LowerToMloPass` — SC → MLO lowering;collective op lower 到 `EmitScsBarrier` |
### 逐 pass 构造惯用法
十二个阶段中的每一个都用同一套固定调用序列构建 —— `RunPasses` 的主体实际上是此模板的十二份拷贝,只在 factory 和挂接模式上不同:
```text
// for each pass p in the twelve:
pm = mlir::PassManager(ctx, name, Nesting) // 0x1cb700a0 — a fresh single-pass manager
pm.enableIRPrinting( $_0 should-print closure ) // 0x132048e0 — the per-stage IR-dump gate
op = pm.<addPass | nest<func::FuncOp>().addPass>( create<P>Pass(opts) ) // 0x1cb6c000 / 0x1cb6d3e0
jellyfish::RunPass(pm, module, dump_prefix, ...) // 0x14514d60 — run this one pass over the module
// pm destructed before the next stage十二个 pass 中有三个携带非平凡的 options struct,RunPasses 会在 pass 之间读取周边配置来物化这些选项:
- Pass 2(
InferMemRefLayout) —Span<long const> tiling和TpuTilingFlags由Target::ShouldEnableLarge2ndMinorLayoutForX16/X8/X4(0x1d6b6920/0x1d6b6860/0x1d6b6840)构建;layout flag 随代际和元素宽度而不同,因此这个 option block 是流水线中按代际变化的接缝。 - Pass 7 和 9 —
InferVectorLayoutPassOptions/ApplyVectorLayoutPassOptions由SparseCoreConfig默认值(SparseCoreConfig_globals_@0x223a99c8)和 HLOBackendConfig(0xf58e6c0)填充。 - Pass 11(
LogicalToPhysicalDeviceId) — 它的optional<DeviceAssignment>参数来自HloModuleConfig::static_device_assignment()(0x10fb7f60);ChipTopology和尾部的bool来自 emitter 的 target。见 §Device-Id Remap。
NOTE — 十二个 manager,而不是一个。
RunPasses为每个 pass 构造一个新的mlir::PassManager,并在下一个阶段前销毁它。没有携带的 analysis state,也没有 fused nesting:每个通过nest挂接的 pass 都包装在自己的 manager 中,其顶层是ModuleOp,唯一的嵌套 pass 会在每个func::FuncOp上运行。构建一个PassManager并addPass所有十二个 pass 的重新实现,在正确性上行为等价,但不会复现二进制发出的逐阶段 IR-dump gate 或逐阶段RunPass计时,并且会折叠二进制刻意丢弃的 analysis cache。GOTCHA — layout 三件套顺序对加载顺序敏感。
InferVectorLayout(7)标注选定的 vector layout,InsertRelayout(8)在相邻 op layout 不一致处物化 conversion op,ApplyVectorLayout(9)把 op 重写为选定 layout。在InsertRelayout之前运行ApplyVectorLayout会留下没有 conversion op 桥接的 layout mismatch;在InferVectorLayout之前运行任一 pass 都会针对未设置的 layout 重写。三者是严格的三阶段序列,全都是mosaic_sc,全都是 func 嵌套。
Device-Id Remap(Pass 11) {#device-id-remap-pass-11}
Pass 11,LogicalToPhysicalDeviceIdPass,是流水线能够发出命中正确硬件的跨核 barrier 的原因。它紧挨着在最后的 LowerToMlo lowering 之前运行,把 DMA/semaphore op 上的 device_id 操作数从逻辑设备索引重写为物理 SparseCore 核心 id。Pass 名称字符串从 .rodata 读取:kPassName = "LogicalToPhysicalDeviceIdPass",kArgumentName = "logical-to-physical-device-id"。
构造
Factory(0x132d5720,签名 (optional<DeviceAssignment>, ChipTopology, bool))在堆上分配 pass,memcpy optional<DeviceAssignment>(设置其 present-bit)和 ChipTopology 到 pass 对象中,并将逐 CoreType topology map 清零初始化。在 RunPasses 中,DeviceAssignment 来源是 HloModuleConfig::static_device_assignment()(0x10fb7f60),它供给 pass-11 构造。
runOnOperation(0x132d5ec0)
主体读取 Target::CoresPerChip(TpuCoreType)、Target::LogicalDevicesPerChip(TpuCoreType) 和 TpuChipConfig::Megachip(),从复制的 ChipTopology + DeviceAssignment 构建 FlatHashMap<CoreType, CoreTopology>,然后遍历每个 func::FuncOp(mlir::detail::walk,主 callback 0x132d71c0)。Callback 匹配携带 (target_core_type, device_id) 操作数对的 op,并重写 device_id:
for each matched op in func:
phys = GetPhysicalCoreID(builder, loc, op.getTargetCoreType()) // §below
op.getDeviceIdMutable().assign(phys) // MutableOperandRange::assign
```text
反编译 callback(`0x132d71c0`)确认它处理 `tpu::EnqueueDMAOp` 和 `tpu::SemaphoreSignalOp`(二者都通过 `getTargetCoreType` / `getDeviceIdMutable`),在四处调用 `GetPhysicalCoreID`,并通过 `MutableOperandRange::assign` 提交。该 pass 也可能 `insertArguments` 来传递物理核心索引函数参数。
### `GetPhysicalCoreID`(`0x132d8de0`)
这是逻辑→物理的物化器。对 `CoreType == SparseCore`,它发出如下 MLIR op 链(反编译第 177–183 行,按顺序):
```text
ci = sparse_core::CoreIndexOp::create(b, loc) // current SC core index
k = arith::ConstantIndexOp::create(b, loc, stride) // per-tile physical stride
mul = arith::MulIOp::create(b, loc, ci, k)
tid = sparse_core::TileIdOp::create(b, loc) // tile id within the core
add = arith::AddIOp::create(b, loc, mul, tid)
i32 = Builder::getI32Type()
phys = arith::IndexCastOp::create(b, loc, i32, add) // → physical_core_id : i32
// = i32(CoreIndex * stride + TileId)对非 SC(TensorCore)路径,它直接返回 llo::CoreIndexOp::create(b, loc)。stride 通过遍历 ChipTopology core-entry 数组(逐核心 role tag)选择,其字面值取决于 chip-config proto。
NOTE — 为什么它必须先于
LowerToMlo。 Pass 12 中的 barrier lowering 会把 collective op 转成硬件 sync(SemaphoreSignal/SyncAdd),其目标是device_id操作数。如果这些操作数仍保存逻辑 id,发出的 barrier 就会 signal 错误的物理核心。把 remap 作为 pass 11 运行,保证LowerToMlo消费时每个 DMA/semaphore op 都携带物理核心 id。
MEGACORE Barrier
最后一个 pass,LowerToMloPass,会 lower SC collective op;对于 barrier,它会到达 CollectiveEmitterBase::EmitScsBarrier(0x13352500)。megacore 偶/奇核心对 sync 位于该 emitter 分派的两个 barrier 分支内部 —— 它不是第四个分派 case。
分派:EmitScsBarrier(0x13352500)
Emitter 读取 HLO BackendConfig → BarrierConfig(缺失时默认用 BarrierConfig_globals_),然后在 offset +0x20 处 switch barrier_type(反编译为 *((_DWORD*)cfg + 8)):
barrier_type | 分派 | SFLAG-number 来源 |
|---|---|---|
1 GLOBAL | → EmitGlobalBarrier @ 0x13352820 | GetSyncFlagForBarrierId(reserved id)(见 Barrier → SFLAG Binding) |
3 CUSTOM | → EmitCustomBarrierFromConfig @ 0x13352cc0 → EmitCustomBarrierStart @ 0x13352fc0 | GetSyncFlagForBarrierId(colored id) |
0 / 2 / 4(含 MEGACORE=4) | → RetCheckFailSlowPath(source line 0x5f) | n/a — 在 SC kernel emission 中非法 |
反编译主体精确确认了这一点:v11 == 1 调用 EmitGlobalBarrier,v11 == 3 调用 EmitCustomBarrierFromConfig,而 else 分支用消息 "backend_config.barrier_config().barrier_type() == jellyfish::BarrierType::CUSTOM" 执行 RetCheckFail。
GOTCHA — MEGACORE 不是 barrier kind。
BarrierType::MEGACORE(4)到达EmitScsBarrier是硬错误(RetCheck)。该 enum 值是producer 侧 annotation,不是 SC kernel emitter 会消费的东西。实际 megacore 行为是一个由拓扑 gate 的 partner 跨核 sync(ToPartnerGlobalCoreId+ partnerSyncAddOp),只要LogicalDevicesPerChip(SC) == 2,GLOBAL和CUSTOM两个分支都会共享它;显式偶/奇Predicated/Notleader/partner 谓词化在EmitGlobalBarrier(GLOBAL分支)中物化,而CUSTOMstart emitter 携带 partner-add 但没有Predicated包装。重新实现者不得把 "megacore barrier" 建模为一个独立 lowering 分支。
Megacore 拆分:EmitGlobalBarrier(0x13352820)
当部件是双核 megachip 时,EmitGlobalBarrier 构建偶/奇 leader/partner predicate,并把 barrier 发成两个互斥的 predicated region。反编译主体按顺序显示了算术:
ChipCount(); ldpc = LogicalDevicesPerChip(SC); // total SC cores = ChipCount * ldpc
// even/odd LSB selector:
Shli(x, 1); And(...); Compare(eq) ── is_even predicate (LSB == 0 ⇒ leader)
lowering_util::Assert(...)
core_index = GetCoreIndex()
chip = DivU(core_index, CoresPerChip(SC))
gcid = ToGlobalCoreId(chip, ...)
pred = Compare(gcid, ...) ── leader/partner predicate
Predicated(pred, $_0) ── EVEN / LEADER arm @0x13355b60
Predicated(Not(pred), $_1) ── ODD / PARTNER arm @0x13355f80
```text
### 两个分支
每个分支都是一个 `__policy_func` thunk,接收 `OpBuilder&` 并返回 `Status`。反编译 op-create 计数(逐分支验证):
| 分支 | `SyncWaitOp` | local `SyncAddOp` | partner `SyncAddOp` | `scf::ForOp` |
|---|:---:|:---:|:---:|:---:|
| `$_0` even / leader(`0x13355b60`) | 1 | 1 | 1(在 `ForOp` 内) | 1 |
| `$_1` odd / partner(`0x13355f80`) | 1 | 1 | 1 | 0 |
每个分支:
- 发出 `sparse_core::SyncWaitOp::create`(`0x14618fa0`),并通过 `setAttr` 用值为 **`259`(= `0x103` = `IciDim::kCoresOnChip`)** 的 `getStringAttr` twine 标记 —— 也就是说,wait 被明确标记为跨核(cores-on-chip)wait。字面量 `259` 直接来自反编译 thunk(`v46 = 259`);
- 发出简单的本地 `sparse_core::SyncAddOp::create`(`0x14611f40`)—— 本地 sync increment;
- 发出 **megacore-partner** `sparse_core::SyncAddOp::create` 变体(`0x146120c0`,较长的 `(…CoreTypeAttr, bool)` 签名),以 partner core 为目标,跨核 sflag offset 通过 `SubsliceToFullSlice`(`0x133e79a0`)计算。在 leader 分支中,此 partner add 被包装在 `scf::ForOp`(`0x17866d60`)里,范围为 `LogicalDevicesPerChip(SC)`。
`0x103`/`kCoresOnChip` tag 把这个 barrier 绑定到 SC tensor-split 使用的同一个 megacore knob —— 见 [Megacore Even/Odd Split](../twist/megacore-even-odd.md)。
### Partner:`ToPartnerGlobalCoreId`(`0x133e66e0`)
跨核 `SyncAdd` 指向的*partner*核心由 `OffloadFactory::ToPartnerGlobalCoreId` 计算。反编译主体毫不含糊:
```text
RetCheck( LogicalDevicesPerChip(SC) == 2 ) // megacore-only; else RetCheck line 0x50
local = GetCoreIndex()
chip = DivUIOp(local, CoresPerChip(SC))
gcid = ToGlobalCoreId(chip, ...)
return XOrIOp(gcid, ConstantIndexOp(1)) // flip the low bit ⇒ even↔odd partnerPartner 是当前核心 id 翻转低位(XOR 1)后的值:megachip 对内 core 0 ↔ core 1。主体先 flatten(ToGlobalCoreId(chip)),再对得到的 global id 执行 XOR 1。如果 LDPC(SC) == 2 RetCheck 失败,慢路径会记录 "GetPartnerGlobalCoreId() is only available for 2 logical devices per chip configurations." —— 除非部件是双核 megachip,否则不存在 partner。
Worked rendezvous — 两个分支完成的事
具体来说,取一个带 SC core {0, 1} 的 megachip(LDPC(SC) == 2)。Core 0 是偶数(leader,运行 $_0);core 1 是奇数(partner,运行 $_1)。每个核心发出的程序,用 MLIR 术语表示为:
core 0 ($_0 leader): core 1 ($_1 partner):
SyncWait(local sflag, tag=kCoresOnChip) SyncWait(local sflag, tag=kCoresOnChip)
SyncAdd(local sflag) SyncAdd(local sflag)
for d in 0..LDPC(SC): SyncAdd( partner = ToPartnerGlobalCoreId()
SyncAdd( partner = ToPartnerGlobalCoreId() = (1) XOR 1 = core 0 )
= (0) XOR 1 = core 1 )
```text
Core `0` 的 partner 是 `0 XOR 1 = 1`;core `1` 的 partner 是 `1 XOR 1 = 0`。每个核心都会递增*另一个*核心的 sync flag(通过 `SubsliceToFullSlice` 寻址到 partner 的 sflag window 的跨核 `SyncAdd`),并等待自己的 sync flag(带 `kCoresOnChip` tag 的 `SyncWait`),因此这对核心会在继续前 rendezvous。这个结构在单核部件上完全不存在 —— 那里会抑制 partner add 和偶/奇谓词化,barrier 退化为纯本地 `SyncAdd`/`SyncWait`。
### CUSTOM 分支共享 partner 跨核 sync
`EmitCustomBarrierStart`(`0x13352fc0`,通过 color-id closure thunk `0x13355420` 从 `CUSTOM(3)` 分派到达)调用 `ToPartnerGlobalCoreId`、`GetSyncFlagForBarrierId`(colored barrier id → SFLAG number)和 megacore-partner `SyncAddOp` 变体(`0x146120c0`,两次 —— 一次用于本地侧,一次通过 `SubsliceToFullSlice` `0x133e79a0` 跨核)—— 也就是 `GLOBAL` 分支使用的同一个 partner 跨核 sync。跨核 SFLAG 寻址来源是 `GLOBAL` 和 `CUSTOM` 之间唯一的一阶差异(reserved id vs colored id)。注意 `EmitCustomBarrierStart` 自身**不**调用 `Predicated`/`Not`:不同于 `EmitGlobalBarrier`,偶/奇谓词化不会在 CUSTOM start emitter 内部物化(它携带 partner-add 机制,但没有显式 leader/partner predicate split)。两个分支共享的是 `ToPartnerGlobalCoreId` partner core 计算和 partner `SyncAddOp`,不是 `Predicated` 包装。
> **GOTCHA — 单核部件没有 partner 路径。** 在 `LogicalDevicesPerChip(SC) == 1` 的部件上,`ToPartnerGlobalCoreId` 会 `RetCheck`,所以 partner-add 和偶/奇谓词化都不会触发 —— 只剩本地 `SyncAdd` / `SyncWait`。重新实现者必须用 `LDPC(SC) == 2` gate 整套 partner 机制,而不是无条件发出。
---
## SC 流水线与 TensorCore 栈的对比
[TensorCore scheduling stack](../sched/overview.md) 是跨两个 IR 的三阶段流程(HLO latency-hiding scheduler → MXU/MRB assignment → LLO bundle packer),由 bundle cost model 定价。SC 后端流水线是另一类东西:一条作用于 `tpu`/`mosaic_sc` 方言的扁平十二 pass MLIR 流水线,以 MLO lowering 结束,**没有独立的 resource-assignment 阶段**,也**没有由 cost-model 定价的重排序**。SC 流水线唯一“接近调度”的决策是 vector layout(pass 7–9)以及在此流水线*之前*发生的逐 engine outlining(见 [Region → Sequencer Outliner](region-to-sequencer-outliner.md))。两条流水线只在 chip 边界相遇:此流水线发出的 SC barrier 通过共享 sync-flag 池与 TensorCore 同步,而 device-id remap(pass 11)正是让这些跨 engine signal 落到正确物理核心上的机制。
---
## 交叉引用
- [SparseCore 概览](overview.md) — 三类 engine、逐代际存在性,以及此流水线 lower 的 embedding data path。
- [SparseCore 架构](architecture.md) — engine 角色和 embedding datapath 详解。
- [Region → Sequencer Outliner](region-to-sequencer-outliner.md) — 生成此流水线消费的 `tpu` 方言 module 的逐 engine outlining。
- [SC EmitX Dispatcher](sc-emitx-dispatcher.md) — 下游逐 engine codec 使用的 seq3/seq4/seq5 → EmitX jump table。
- [getSequencerType](getsequencertype.md) — SCS/TAC/TEC engine-selection 函数。
- [SCS(Scalar)Engine](scs-engine.md) — 运行发出的 sync op 的 scalar sequencer。
- [TAC Engine](tac-engine.md) — tile-access / DMA-issuer engine。
- [TEC(Vector)Engine](tec-engine.md) — layout pass(7–9)面向的宽 vector compute engine。
- [SC Core Selection](sc-core-selection.md) — 物理 SparseCore 核心选择,是此流水线 device-id remap 的对应部分。
- [GetSparseCoreConfig](getsparsecoreconfig.md) — 供给流水线的 offload op-type 配置来源。
- [Barrier → SFLAG Number Binding](../barrier/barrier-to-sflag-binding.md) — barrier 分支调用的 colored/reserved barrier id → hardware SFLAG number 映射。
- [BarrierColoring](../barrier/barrier-coloring.md) — 分配 CUSTOM barrier color 的 graph-coloring engine。
- [Global-Barrier SFLAG Window](../barrier/global-barrier-window.md) — GLOBAL 分支取用的 reserved SFLAG window。
- [Megacore Even/Odd Split](../twist/megacore-even-odd.md) — 此 barrier 的 partner sync 共享的 `LDPC(SC)==2` / `kCoresOnChip` 拓扑 knob。
- [Physical-Core Placement](../collectives/physical-core-placement.md) — SC-offload collective 的逐 color 物理核心映射。
- [TPU Scheduling Pipeline](../sched/overview.md) — 用于对比的 TensorCore 侧三阶段 scheduling stack。
- **Binary:** `extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so`(build-id `89edbbe81c5b328a958fe628a9f2207d`)
- **索引条目:** Part IX — SparseCore & BarnaCore / SparseCore back-end — [返回索引](../index.md)