Skip to content

屏障 → SFLAG 号码绑定

此页面上的所有地址适用于 libtpu-0.0.40-cp314 轮中的 libtpu.so(内部版本号 89edbbe81c5b328a958fe628a9f2207d,内部版本 libtpu_lts_20260413_b_RC00)。该二进制文件带有完整的 C++ 符号; .text VMA == 文件偏移量。其他版本会有所不同。

摘要

编译器发出的每个 TensorCore 屏障都以单个整数结束:一个芯片 SFLAG 编号,参与核心发出信号并自旋等待的片上同步标志层中一个原子计数器的索引 (SFLAG 同步标志层)。此页面拥有将 BarrierType 转换为该数字的算术,用于三个命名的跨核心插槽(全局、全归约阶段和超级核心)以及它们全部测量的基本/计数窗口**。它是屏障数据路径的底部:着色引擎InferBarrierConfig 标准化器 决定 BarrierTypeid 集体承载;该页面是这些选择成为硬件地址的地方。

这些数字未存储在表中。 Target::Init 从芯片配置原型中读取每核心类型 compiler_reserved 整数范围,检查它是否是连续的升序块,并将两个 int32 标量存储在 Target 对象上: base = CR_TC[0] 位于 Target+0x8c0count = |CR_TC| − 5Target+0x8c4−5 就是整个技巧——它从可用的每个 id 窗口 [base, base+count) 中提取该范围的前 5 个 SFLAG 编号,并将它们保留给指定的跨核心屏障。然后,三个微小的 const 访问器将每个命名槽计算为 base+count 上方的固定偏移量,因此命名屏障位于 [base+count, base+count+5) 中,而每键/副本屏障位于它们下方。每个命名槽没有结构体字段;偏移量绑定。

重新实现者必须按此顺序重现四件事: 窗口读取(GetSpecialPurposeSyncFlags(kTensorCore) → 连续性检查 → base/count = size − 5);三个访问器公式(base+count / base+count+phase+1 / base+count+4);绑定它们的两个 CHECK 门(all-reduce 上为 0 < phase < 3,megacore 上为 Megacore());事实上,SparseCore 端使用不同的计数器(GetSparseCoreBarrierSyncFlagCount 读取 SparseCoreTarget+0x1d4没有 −5),因此这两个范围永远不会别名。每个颜色的 CUSTOM 和每个环的 SparseCore 屏障,索引[基数、基数+计数) 属于其他地方 — 请参阅屏障着色按代号编译器保留;此页记录了窗口上方的五个保留插槽以及窗口自行读取的内容。

窗口对象xla::jellyfish::Targetbase @Target+0x8c0 (this[560])、count @Target+0x8c4 (this[561])
窗口源TpuChipConfigProto.special_purpose_sync_flags(kTensorCore).compiler_reserved(重复int32)
窗雕Target::Init @0x1d60fc20(第 2067–2068 行:base = CR_TC[0]count = |CR_TC| − 5)
可用的每个 id 窗口[base, base+count) — 复制品/定制 ID,0 ≤ id < count
全局插槽GetGlobalBarrierSyncFlagNumber @0x1d60f420base + count + 4
全减槽GetAllReduceSyncFlagNumber(phase) @0x1d60f440base + count + phase + 1 (0 < phase < 3)
Megacore 插槽GetMegacoreBarrierSyncFlagNumber @0x1d60f4e0base + count(Megacore() 门控)
SC 对应GetSparseCoreBarrierSyncFlagCount @0x10972fa0SparseCoreTarget+0x1d4(无 −5)

1. 窗口:basecount

用途

指定的屏障编号都是一个锚点 base + count 之上的相对偏移量,因此整个绑定简化为了解这两个 int32。它们在目标构造时从芯片配置原型的每核心类型保留的 SFLAG 范围中读取一次,并且永远不会重新计算。正确读取窗口的重新实现者可以免费获得每个访问器。

字段

这两个标量在 Target 对象上相邻。反编译器将它们寻址为 Target 基指针的 _DWORD 索引;字节偏移量是索引乘以四:

名称反编译形式字节偏移量含义
base*((_DWORD *)this + 560)Target+0x8c0TC保留范围的第一个SFLAG编号 = CR_TC[0]
count*((_DWORD *)this + 561)Target+0x8c4每个 id 可用插槽计数 = |CR_TC| − 5

注意 — count 不是保留范围的大小。它的大小是减去五个指定的顶部插槽。保留范围有|CR_TC|整数;其中 count = |CR_TC| − 5 是 per-id 窗口,其余五个是该页面计算的命名跨核心屏障。将 count|CR_TC| 混淆会使可用 id 数量增加恰好 5 个,并使每个键的屏障与全局槽发生冲突。

读取 — Target::Init

Target::Init @0x1d60fc20 在施工接近尾声时执行雕刻(第 1969-2069 行)。该序列是一个空检查的原始访问器、一个连续性断言和两个标量存储:

c
function Target_Init_carve_sflag_window(target, chip_config):   // 0x1d60fc20, lines 1969-2069
    spsf = chip_config.GetSpecialPurposeSyncFlags(kTensorCore)   // 0x20afcf40
    if (spsf == NULL):                                           // line 1971
        DieBecauseNull("chip_config.GetSpecialPurposeSyncFlags("
                       "::tpu::TpuCoreType::kTensorCore)")        //   TC entry is mandatory

    size = spsf->compiler_reserved.size()   // *(spsf + 16)       // v284, the repeated-int32 length
    data = spsf->compiler_reserved.data()   // *(spsf + 8)        // v285 / v286 (copied)

    // CHECK the range is a contiguous ascending int block: arr[i] == arr[i-1] + 1
    for i in 1 .. size-1:                                        // unrolled x8, lines 1989-2065
        CHECK(data[i] == data[i-1] + 1,                          // RetCheck line 1153
              "compiler_reserved_tensor_core_sync_flags[i] =="
              " compiler_reserved_tensor_core_sync_flags[i - 1] + 1")

    target[0x8c0] = data[0]      // base  = CR_TC[0]   *((_DWORD*)target + 560), line 2067
    target[0x8c4] = size - 5     // count = |CR_TC|-5  *((_DWORD*)target + 561), line 2068
    free(data)
```text

重新实现者必须从中得出两个事实:

- **范围必须是连续的并且按 1 递增。**1153 行的 CHECK 表示保留的 SFLAG 编号不是任意集合 - 它们是一个区间 `[CR_TC[0], CR_TC[0] + |CR_TC|)`。因为范围是连续的,所以 `base` 加上整数索引 ** SFLAG 编号;不需要每个 ID 的查找表。这就是为什么所有访问器都是纯算术的。
- **TensorCore 条目是强制性的。** `GetSpecialPurposeSyncFlags(kTensorCore)` 返回 null 是致命的(`DieBecauseNull`,第 1971 行),因此在任何受支持的 TC 目标上,`base`/`count` 始终在降低任何屏障之前填充。访问器永远不必防御未设置的窗口。

> **明白了 —** `−5` 是 `Target::Init` 中的编译时文字(字节级别的 `add $0xfffffffb`,`−5` 的二进制补码),**不是**每代值。每一代都保留了五个指定的顶级位置。从芯片配置中读取保留插槽计数的重新实现将是错误的;这五个是硬编码的并且与生成无关。相比之下,文字 `CR_TC[0]` 和 `|CR_TC|` 整数**每个 `(codename, deployment)` 并且不可静态提取 - 请参阅 [按代号编译器保留](per-codename-compiler-reserved.md)。

---

## 2. `GetMegacoreBarrierSyncFlagNumber` — `base + count`

### 用途

超级核心屏障是五个保留插槽中最低的,正好位于可用的每个 id 窗口的顶部:`base + count`。它在超级核心部署 (`CoresPerChip(TensorCore) == 2`) 中交会共享一个芯片的两个 TensorCore。访问器在实际处于兆核模式的芯片上进行门控,因此在单 TC 芯片上,插槽被保留但未使用,并且访问器会陷入陷阱而不是返回无意义的数字。

### 算法

```c
function GetMegacoreBarrierSyncFlagNumber(target):              // 0x1d60f4e0
    // target[119] is topology_; +24 -> chip_config
    if (!topology_->chip_config().Megacore()):                  // line 154
        LogFatal("topology_->chip_config().Megacore()")         //   trap: not a megacore chip
    return (uint32)(target[560] + target[561])                  // base + count

门是通过 *(*((_QWORD*)this + 119) + 24) 到达的 tpu::TpuChipConfig::Megacore()topology_ 指针位于 Target+0x3b8(索引 119),然后其芯片配置位于 +0x18。在非兆核芯片上,该调用是 LogMessageFataltarget.cc:154;它永远不会返回错误的号码。

QUIRK — 这是一个命名槽,其与每个 id 窗口的边界 (base + count) 一致。它不与可用 id 重叠 — id < count 表示最后一个可用 id 是 base + count − 1,位于兆核插槽下方的一个 — 但它是五个中最紧的一个。其上方的四槽间隙(+1+4)是全归约/全局块(§3、§4)。


3. GetAllReduceSyncFlagNumber(phase)base + count + phase + 1

用途

all-reduce 屏障是每阶段:钳形 all-reduce 分两个阶段运行,每个阶段都需要自己的集合槽,因此访问器采用 phase 参数并为每个阶段返回一个不同的 SFLAG 编号。有效相为12phase 0phase ≥ 3是非法的和陷阱的。两个合法阶段映射到插槽 base+count+2base+count+3,将 base+count+1 留下作为永久间隙(没有 phase 产生它)。

算法

c
function GetAllReduceSyncFlagNumber(target, phase):             // 0x1d60f440
    if (phase <= 0):                                            // line 143
        LogFatal("phase > 0")                                   //   phase 0 is illegal
    if ((uint64)phase >= 3):                                    // line 144
        LogFatal("phase < 3")                                   //   phase 3+ is illegal
    return (uint32)(target[560] + phase + target[561] + 1)      // base + phase + count + 1
```text

绑定是由两个 `LogMessageFatal` CHECK(`target.cc:143` `"phase > 0"`、`target.cc:144` `"phase < 3"`)强制执行的硬 `0 < phase < 3`。所以该函数只返回两个值:

| `phase` | 公式 | SFLAG 号 | 状态 |
|---|---|---|---|
| `0` ||| 非法(检查 `phase > 0`,第 143 行) |
| `1` | `base + count + 1 + 1` | `base + count + 2` | 有效(钳形0相屏障) |
| `2` | `base + count + 2 + 1` | `base + count + 3` | 有效(钳形一期屏障) |
| `≥3` ||| 非法(检查 `phase < 3`,第 144 行) |

> **GOTCHA —** 插槽 `base + count + 1` **从未** 由该访问器生成 — 没有生成它的 `phase` 值,因为 `phase 0` 被拒绝,并且 `phase 1` 已映射到 `base+count+2`。它是保留块中的结构间隙,而不是可用的屏障。重新实现从 `base+count` 开始分配五个连续的命名槽,并将第二个槽分配给 all-reduce 阶段 1,这将与**构建中的任何内容发生冲突,但它不会与原始布局匹配:原始布局将 `+1` 留空并在 `+2` 处开始 all-reduce。

---

## 4. `GetGlobalBarrierSyncFlagNumber` — `base + count + 4`

### 用途

全局屏障是顶部保留插槽 `base + count + 4` — 设备范围的全核集合点。它是规范器引用的 `BarrierType::GLOBAL(1)` 插槽,其通道集体降级引脚 `id =1`(哨兵,而不是索引)正是因为全局屏障不索引每个 id 窗口;它在保留范围的最顶部有自己的固定插槽。

### 算法

```c
function GetGlobalBarrierSyncFlagNumber(target):               // 0x1d60f420
    return (uint32)(target[561] + target[560] + 4)             // count + base + 4  ==  base + count + 4

这是三个中最简单的一个——没有门,没有参数。反编译器按 count + base + 4 顺序 (this[561] + this[560] + 4) 发出加数,这与 base + count + 4 的交换性相同。没有 CHECK,因为全局屏障在任何 TC 目标上始终是合法的。

注意 — 该子系统中有两种不同的“全局”机制,并且只有其中一种使用此访问器。 GetGlobalBarrierSyncFlagNumber 是运行时/ICI 式全核屏障的保留固定插槽MaybeInsertGlobalBarrier 插入的 Mosaic 功能级 TC 树屏障 不会调用此访问器 — 它完全从不同的 SparseCoreTarget 字段 (全局屏障窗口) 构建每个核心的 SFLAG 窗口。不要假设每个 GLOBAL(1) 障碍都解析为 base+count+4;只有保留时隙路径才可以。


5.预留五槽位图

将四个访问器放在一起,TC compiler_reserved 系列的顶部被布置为可用的每个 id 窗口上方的固定五插槽块。窗口[基数、基数+计数) holds the REPLICA(2) / CUSTOM(3) ids (推断屏障配置);其上方的五个插槽是指定的跨核心屏障:

text
SFLAG number space (TC compiler_reserved range = [base, base+|CR_TC|) ):

  base ─────────────────────────────────────────────────────────────────► higher
  │                                                                    │
  │   usable per-id window  [base, base+count)                         │  reserved 5 slots
  │   REPLICA / CUSTOM ids  (id < count)                               │  (the −5)
  ├────────────────────────────────────────────────────┬──────────────┴──────────────────────┐
  │ base+0   base+1   ...   base+count-1                │ +count  +count+1  +count+2  +count+3  +count+4
  │                                                     │ MEGA    (gap)     AR(1)     AR(2)     GLOBAL
  └─────────────────────────────────────────────────────────────────────────────────────────┘
                                                          ▲         ▲        ▲         ▲        ▲
                                       GetMegacoreBarrier ─┘         │  GetAllReduce(1) GetAllReduce(2)  GetGlobalBarrier
                                                            permanent gap (no phase maps here)
```text

| 插槽 | 配件 | 公式 | 门/界 |
|---|---|---|---|
| `base + count + 0` | `GetMegacoreBarrierSyncFlagNumber` @`0x1d60f4e0` | `base + count` | `chip_config().Megacore()`(第154行) |
| `base + count + 1` | —(永久间隙) | `base + count + 1` | 无法由任何访问器生成 |
| `base + count + 2` | `GetAllReduceSyncFlagNumber(1)` @`0x1d60f440` | `base + count + phase + 1`,`phase=1` | `0 < phase < 3`(第 143/144 行) |
| `base + count + 3` | `GetAllReduceSyncFlagNumber(2)` @`0x1d60f440` | `base + count + phase + 1`,`phase=2` | `0 < phase < 3`(第 143/144 行) |
| `base + count + 4` | `GetGlobalBarrierSyncFlagNumber` @`0x1d60f420` | `base + count + 4` | (无) |

`Target::Init` 中的 `−5` 和这五个插槽是一个事实的两个视图:雕刻保留了超出范围顶部的五个整数,并且访问器索引正好返回到这五个整数。 `+1` 处的间隙意味着五个保留槽中只有四个被访问器实现——第五个整数是故意的间隔符。

> **QUIRK —** 命名障碍通过*偏移算术*寻址,而不是通过结构体字段。没有`Target::megacore_barrier_sflag_`会员;每次调用时,巨核编号都会重新计算为 `base+count`。在字段中缓存五个数字的重新实现在功能上是等效的,但与二进制文件不同,二进制文件仅存储 `base` 和 `count` 并派生其余部分。保持两个标量的权威性。

---

## 6. SparseCore 对应项 — 不相交的范围,无 `−5`

TensorCore 窗口不是唯一保留的 SFLAG 块。 `SparseCoreTarget::Init` 将 `compiler_reserved(kSparseCore)` 执行类似的雕刻为 `SparseCoreTarget+0x1d0`(基础)/ `+0x1d4`(计数) - 但**不减去 5**。 SC 系列完全可用作每环屏障 ID;它的全局屏障被保留在该范围内,而不是高于它。公开 SC 计数的访问器确认偏移量:

```c
function GetSparseCoreBarrierSyncFlagCount(target):            // 0x10972fa0
    if (!target->SupportsSparseCore()):                         // vtable +0x260 (608/8)
        LogFatal("SupportsSparseCore()")                        //   target.h:3027
    return *(uint32)(target[297] + 0x1d4)                       // SparseCoreTarget+0x1d4 = SC count

target[297]SparseCoreTarget指针指向Target+0x948+0x1d4 是 SC 计数,镜像 TC +0x8c4。两个后果:

  • TC 和 SC 范围从不别名。 它们来自不同的 SpecialPurposeSyncFlags 原始消息(每个 TpuCoreType 一个),因此 [TC_base, TC_base+|CR_TC|)[SC_base, SC_base+|CR_SC|) 在结构上是不相交的。具有相同数值的 SparseCore 屏障 id 和 TensorCore 屏障 id 是不同的 SFLAG 计数器。
  • SC侧无五槽位预留 count_SC = |CR_SC|(全系列); SC 全局屏障是窗口内的保留 id,通过 GetSyncFlagForBarrierId(SparseCore 屏障页拥有的 per-id 算术)到达,而不是通过任何 base+count+k 访问器。窗口上方的五个命名槽是仅限 TensorCore 的构造。

陷阱 — SparseCoreTarget+0x90 不是 SFLAG 窗口 base。该字段是 TpuCoreParts::SequencerCount(kSparseCore sequencer),即每核 sequencer count;tree-barrier 窗口长度来自 +0x1fcGetUserRegion,jellyfish MemorySpace::kSparseCoreSequencerSmem = 14),这是与 compiler_reserved 块不相交的第三个 region。两者都不是本页记录的 [base, base+count) 窗口的一部分。TC tree barrier(Global-Barrier Window)是唯一使用 GetGlobalBarrierSyncFlagNumber 的 "global" 路径。


7. 验证说明

libtpu.so v0.0.40 中的字节精确:

  • GetGlobalBarrierSyncFlagNumber @0x1d60f420return (uint32)(this[561] + this[560] + 4) = base + count + 4 — 精确,无门。
  • GetAllReduceSyncFlagNumber @0x1d60f440:检查 phase > 0 (target.cc:143) 和 phase < 3 (target.cc:144); return (uint32)(this[560] + phase + this[561] + 1) = base + phase + count + 1;合法阶段 {1,2} → 插槽 {base+count+2, base+count+3} — 准确。
  • GetMegacoreBarrierSyncFlagNumber @0x1d60f4e0:门tpu::TpuChipConfig::Megacore(*(*(this+119)+24))(target.cc:154"topology_->chip_config().Megacore()"); return (uint32)(this[560] + this[561]) = base + count — 准确。
  • Target::Init @0x1d60fc20 窗雕(第 1969–2069 行):GetSpecialPurposeSyncFlags(kTensorCore) + DieBecauseNull(第 1971 行);连续性检查 "compiler_reserved_tensor_core_sync_flags[i] == compiler_reserved_tensor_core_sync_flags[i - 1] + 1" (target.cc:1153); *((_DWORD*)target + 560) = data[0](底座,第 2067 行); *((_DWORD*)target + 561) = size - 5(计数,第 2068 行)— 准确。
  • GetSparseCoreBarrierSyncFlagCount @0x10972fa0SupportsSparseCore() 门(虚函数表 +0x260target.h:3027); return *(uint32)(*(this+297) + 0x1d4) = SparseCoreTarget+0x1d4 — 准确;确认 SC 系列 −5

[低] CR_TC[0] (base) 和 |CR_TC|(因此 count)的每代字面值是 不可静态提取的 — 它们存在于嵌入式芯片配置 binarypb memfile blob 中,在运行时解析(codename, deployment-name)。窗口几何(雕刻、−5、五槽地图)已确认与世代无关;整数是 memfile 依赖项。请参阅 按代号编译器保留


交叉引用