Skip to content

Sequencer 槽

本页中的每个偏移、地址、位位置和常量,都从 libtpu-0.0.40-cp314 wheel 中的 libtpu.so 逐字节读取而来(BuildID md5 89edbbe81c5b328a958fe628a9f2207d,未 strip,1,233,709 个符号)。其他版本会有所不同。

摘要

每个 TPU VLIW bundle 都恰好携带一个 sequencer 槽:这是唯一拥有 program-counter mutation 权限的标量 ALU lane。它是编码 branch / jump、call、halt、pipeline-balancing delay op、hardware-loop-counter read,以及(在 SparseCore engine 上)sync-flag 和 barrier op 的 lane。矩阵单元、向量 lane 和内存端口由 bundle 发射,但只有 sequencer 槽能改变下一个周期执行什么。从功能上看,这是片上控制 CPU 被缩减为 issue word 中的一个槽。

该槽的身份在整条硅片产品线上都有字节锚定。在 Jellyfish(TPU v2)和 Pufferfish(TPU v4)上,它名为 SLOT_SCALAR_0;从 Viperfish(v5e)开始,它名为 SLOT_SCALAR_ALU_0。在两套命名方案中,PC-mutating opcode 都只在 lane 0 中合法——lane 1(SLOT_SCALAR_1 / SLOT_SCALAR_ALU_1)承载 halt / fence / delay 以及标量 ALU 的镜像,但绝不承载 branch 或 call。在 Jellyfish 上,这条规则是 ProtoUtils::ScalarOpAllowedInSlot0x1e875a20)中的字面 bitmask;在 V5+ 上,它体现为 proto-message 结构 bundle.scalar_alu().scalar_alu_0().branch_relative()。本页把该槽作为重实现目标来记录:每代中它是哪条 lane,把 control-flow intent 转换成 bundle bytes 的三层编码路径,branch / call / halt / delay / loop-read 字段布局,以及 predication 字段如何兼作条件分支条件。逐(generation × sequencer-type)的 op 清单位于配套页面;hardware-loop-counter 细节位于 Hardware Loop-Counter

对重实现而言,契约如下:

  • 每个 bundle 一个 sequencer 槽,始终是 scalar-ALU sub-bundle 的 lane 0;PC mutation 仅限 lane 0,编译器必须强制执行。
  • branch / call target 是 signed 20-bit 字段−0x80000 .. +0x7FFFF),落在 bundle 的 immediate slot 0 中,而不是 sequencer 槽字节内部;absolute-vs-relative 纯粹是同一字段上的 opcode discriminator。
  • call 会把返回地址写入一个 dest 标量寄存器(SparseCore 路径上的 link register sreg #5);没有专用 return opcode——return 是读取 link register 的 BranchSreg
  • 每个 branch/call 携带 0..5 delay-slot count(3-bit);在 V5+ 上,delay slot 是 bundle-packer padding,而不是编码后的槽 bit。
  • predication 字段就是 branch conditionPREDICATION_ALWAYS = 无条件,谓词寄存器索引 = 条件,PREDICATION_NEVER = 被门控关闭(空槽编码)。
  • 两种循环模型:Jellyfish 使用软件 bundle-index backward branch(BeginLoop/EndLoop);V5+ 通过 ReadRegisterLccLow/High 读取硬件 loop counter。
槽身份(JF/PF)SLOT_SCALAR_0 / SLOT_SCALAR_1
槽身份(V5+)SLOT_SCALAR_ALU_0 / SLOT_SCALAR_ALU_1
PC-mutating lane仅 lane 0(branch/call);lane 1 = halt/fence/delay 镜像
JF 槽合法性规则ProtoUtils::ScalarOpAllowedInSlot @ 0x1e875a20(slot-0 mask 0x18000000f00
JF branch 分类器ProtoUtils::IsBranch @ 0x1e876120op & ~3 == 8 → 8..11)
JF call 分类器ProtoUtils::IsCall @ 0x1e876140op & ~3 == 12 → 12..15)
V5+ SCS branch 编码器isa_emitter::EmitBranchOp<…BranchRelative> @ 0x13a5d3e0
V5+ SCS call 编码器isa_emitter::EmitCallOp<…CallAbsolute> @ 0x13a5d4c0
gfc TC seq dispatchgfc::isa::TensorCoreScalarAlu0Encoder::Encode @ 0x1f87b420
gfc TC opcode-HIGH / familybit 483,宽度 6(branch/call-immediate 为 0
gfc TC opcode-LOW / discriminatorbit 478,宽度 5(BranchAbsolute=4/Rel=5/CallAbs=6/Rel=7
gfc TC seq predicate selectorbit 489,宽度 2(*(scalar_alu+28)
Branch/call targetsigned 20-bit,immediate slot 0(EmitImmediate<SparseCoreImmediates>
Call link register写入 destsreg #5(SCS 路径)
Delay-slot 字段0..5(3-bit);verifier delay_slots_op.getImm() >= 0 && <= 5
Predication = conditionPREDICATION_ALWAYS/NEVER/OR_NEVER/OR_INVERTED_NEVER
V5+ predication 编码TPUMCCodeEmitter::encodePredicateOperand @ 0x13c77c40(7-bit)
空槽标记kNeverExecute = 310xb834cfc
置信度CONFIRMED(字节锚定),除非某行另有说明

Sequencer 槽是什么

bundle 是一个 VLIW issue word;bundle model 覆盖 no-scoreboard 契约。在这个 word 内,sequencer 槽是唯一一个其 op 能改变 program counter 的 lane。发射 Halt,core 就停止;发射 BranchRelative,下一个被 fetch 的 bundle 是分支目标,而不是 fall-through;发射 CallAbsolute,fall-through 地址会被捕获进一个标量寄存器,让 callee 能返回到它。bundle 中的其他所有东西——matrix push、vector ALU、load、store——都“就地”执行,并 fall through 到下一个顺序 bundle。

硬件通过让 scalar-ALU sub-bundle 恰好有两条 lane,并且只允许其中一条进行 PC mutation,来强制每个 bundle 只有一个 sequencer。该约束在二进制中很具体。ProtoUtils::ScalarOpAllowedInSlot0x1e875a20)接受一个 ScalarOpcode 和一个槽索引,对于槽索引 ≤ 1,它用两个 64-bit bitmask 测试 opcode:

c
// ProtoUtils::ScalarOpAllowedInSlot(ScalarOpcode op, int slot)  @ 0x1e875a20
// (decoded byte-exactly from objdump)
if (slot <= 1) {
    // either lane (lane-0 OR lane-1):
    if (bt(0x6000060070, op)) return true;     // movabs $0x6000060070; bt %rdx
    // lane-0 ONLY (PC-mutating + a couple of others):
    if (bt(0x18000000f00, op)) return (slot == 0);  // movabs $0x18000000f00; bt %rdx
}
```text

slot-0-only mask `0x18000000f00` 在 opcode `{8, 9, 10, 11}`(四个 branch opcode)以及 `{39, 40}` 上置位——这些是 lane-0-exclusive op。either-lane mask `0x6000060070`(bit `{4, 5, 6, 17, 18, 37, 38}`)覆盖两条 lane 都合法的标量 op。允许 packer 把 branch 放进 lane 1 的重实现会产生硬件拒绝的 bundle。

> **陷阱 — sequencer 槽是两 lane sub-bundle 的一条 lane,而不是整个 bundle 字段。** scalar-ALU sub-bundle 有 `scalar_alu_0` 和 `scalar_alu_1`;sequencer op 是 `scalar_alu_0` 的 `oneof` 成员。Lane 1 确实存在,承载 ALU compute、halt、fence 和 delay,但 lane 1 中的 branch/call 是非法的。把“sequencer”建模为单个逐 bundle 字段,而不是两 lane 标量 sub-bundle 的 lane 0,会丢失 lane-1 ALU 容量,并错误检查槽合法性。

---

## 各代槽身份与位置

该槽在每一代都位于 scalar-ALU sub-bundle 的 lane 0,但周边 sub-core 分类会变化:每个 TPU 代际包含若干 **sequencer type**(`TpuSequencerType`),每种 sequencer type 都有自己的 bundle 和自己的 scalar-ALU sub-bundle。配套页面枚举了 [TpuSequencerType enum](sequencer-ops-per-gen.md#the-tpusequencertype-enum);下表固定逐(generation × sequencer-type)的槽位置。

| Gen | Sequencer type | Bundle B | Sequencer lane | Lane 1(无 PC mutation) |
|---|---|---:|---|---|
| Jellyfish (v2) | TensorCore | 41 | `SLOT_SCALAR_0` | `SLOT_SCALAR_1`(halt/fence/delay) |
| Jellyfish (v2) | BarnaCoreAddressHandler | 16 | 专用 BCAH `Branch` ScalarSlot | n/a |
| Dragonfish (v3) | TC / BCAH | 41 / 16 | Jellyfish codec 的别名 | (同 Jellyfish) |
| Pufferfish (v4) | TensorCore | 51 | `Scalar0`(`TensorCoreScalar0_*`) | `Scalar1`(halt/fence/delay) |
| Pufferfish (v4) | BarnaCoreSequencer | 32 | `BarnaCoreSequencerScalar0` | `Scalar1`(halt/fence/delay + sync) |
| Viperfish (v5e) | TensorCore | 64 | `ScalarAlu0`(`vxc::isa`) | `ScalarAlu1` |
| Viperfish (v5e) | SCS / TAC / TEC | 32 / 64 / 64 | `ScalarAlu0`(`vxc::vfc::isa`) | `ScalarAlu1` |
| Ghostlite (v6e) | TC / SCS / TAC / TEC | 64 / 32 / 64 / 64 | `ScalarAlu0`(`gxc::glc::isa`) | `ScalarAlu1` |
| 6acc60406 (TPU7x) | TC / SCS / TEC | 64 / 32 / 64 | `ScalarAlu0`(`gxc::gfc::isa`) | `ScalarAlu1` |

> **说明 — 6acc60406 (TPU7x) 移除了 TileAccess sequencer。** 二进制中缺少 `gxc::gfc::isa::SparseCoreTac*` 符号,而 `SparseCoreTec*` 和 `SparseCoreScs*` 存在(`nm -C`)。Viperfish 和 Ghostlite 携带全部三个 SparseCore sequencer engine(SCS + TAC + TEC);`6acc60406` 只携带 SCS + TEC。TensorCore sequencer 存在于每一代。

在 V5+ 上,通过固定 proto 路径抵达该 lane,这在二进制嵌入的 assertion string 中逐字可见:`bundle.scalar_alu().scalar_alu_0().branch_relative()`、`…scalar_alu_0().call_absolute()`、`…scalar_alu_0().halt()`。`scalar_alu_0` message 是一个 `oneof`,其成员是 sequencer op(`branch_absolute`、`branch_relative`、`branch_sreg`、`call_absolute`、`call_relative`、`call_sreg`、`halt`、`delay`、`scalar_fence`、`read_register_lcc_low`、`read_register_lcc_high`,以及 ALU compute op)。从符号表枚举出的 Viperfish TC scalar-ALU `oneof`(`vxc::isa::TensorCoreScalarAlu_*`)在约 60 个 ALU compute op 旁边保存完整 control-flow 集合——sequencer 槽和 scalar-ALU lane 是同一条物理 lane,仅通过设置了哪个 `oneof` 成员来区分。

---

## 三层编码模型

一个 control-flow intent 会通过三层结构转换为 bundle bytes:

1. **逐代 proto message 类型**——结构化形式。每个 control-flow op 都是一个不同的 protobuf message:`vxc::isa::TensorCoreScalarAlu_BranchRelative`、`gxc::glc::isa::SparseCoreScalarAlu_CallAbsolute`、`pxc::isa::TensorCoreScalar0_ScalarBranchAbsolute`,等等。在 Jellyfish 上,形式是一个扁平 enum:`platforms_deepsea::jellyfish::isa::ScalarOpcode`(62 个值),其 `ScalarOpcode_descriptor()` 位于 `0x1fa1fc00`。

2. **逐代 compact ref wrapper**——读取侧访问器形式。每个 op 都有一个 `*Compact_<Op>{,ConstRef,Ptr}` 家族,其访问器是基于逐代 `BitfieldsRefImpl`(实际 bit holder)的虚拟 thunk。这些访问器确认字段集合:`…Compact_BranchSregConstRef::x()` 是分支目标寄存器,`…Compact_CallSregConstRef::dest()` 是返回地址寄存器,`…Compact_BranchAbsoluteConstRef::has_delay_slots()` / `delay_slots()` 暴露逐分支 delay count。这些 bit 位于 vtable 背后,而不是 inline——例如 `gfc BranchSregConstRef::has_delay_slots()` 是 vtable `+0x18` thunk,`delay_slots()` 是 `+0x20`,`x()` 是 `+0x40`。

3. **字节发射路径**——把 proto/MCInst 转为原始字节。SparseCore(SCS/TAC/TEC)lane 使用模板化的 `xla::tpu::sparse_core::isa_emitter::EmitBranchOp<Bundle, Op>` / `EmitCallOp<Bundle, Op>` 填充器;TensorCore(Jellyfish)lane 使用 `JellyfishEmitter::EmitScalar*`;BarnaCore-AH lane 使用 `BarnaCoreAddressHandlerEmitter::EmitScalar*`。最终发射是逐代的:JF/PF 使用 `EncoderJf` / `EncoderPf::EncodeBundleInternal`(动态字节打包),V5+ 使用逐槽 `<Slot>Encoder::Encode` 调用(例如 `gfc::isa::TensorCoreScalarAlu0Encoder::Encode` @ `0x1f87b420`),通过通用 bit-packer `BitCopy`(`0x1fa0a900`)写入 64-byte buffer。

> **陷阱 — V5+ LLVM-MC emitter 不贡献任何 branch bit。** MC opcode `BRabs`(505)、`BRind`(507)、`BRrel`(508)、`BRrelrot`(509)、`CALLabs`(514)、`CALLrel`(515)、`HALT`(571)到达 `TPUMCCodeEmitter::getBinaryCodeForInstr`(`0x13c74da0`)后,索引其 jump table,并路由到 **zero-base default**——它们的 `InstBits` record 全为零。真正的 offset、dest/x register 和 predication 由 proto-bundle 的 `EmitBranchOp` / `EmitCallOp` / `EmitImmediate` / `EmitPredicationToSlot` 路径写入。见 [MC-Emitter](mc-emitter.md) 和 [Instruction Name Data](instr-name-data.md)。

---

## Branch / Jump / Call 编码

branch 和 call target 是一个 **signed 20-bit 字段**,落在 bundle 的 **immediate slot 0** 中,这是从 Ghostlite SCS branch 和 call emitter 逐字节解码得到的。`EmitBranchOp<…BranchRelative>`(`0x13a5d3e0`)和 `EmitBranchOp<…BranchAbsolute>`(`0x13a5d220`)字段完全相同——abs/rel 区别只是 opcode discriminator:

```c
// isa_emitter::EmitBranchOp<SparseCoreScalarAlu_BranchRelative> @ 0x13a5d3e0
// (decoded byte-exactly from objdump)
rsi = MCInst.getOperand(0).Imm;        // the PC offset / target
if ((uint64_t)(rsi + 0x80000) >= 0x100000)   // lea 0x80000(%rsi); cmp 0x100000; jae fail
    return RetCheckFail();             // reject if NOT in [-0x80000, +0x7FFFF]
bundle.byte[0x10] |= 0x04;             // orb $0x4, 0x10 — set scalar-alu PRESENT bit (bit 2)
rsi &= 0xFFFFF;                        // and $0xfffff — mask to 20 bits
EmitImmediate<SparseCoreImmediates>(slot=0, rsi);  // 20-bit value → immediate slot 0

范围检查 (value + 0x80000) < 0x100000 正是 signed-20-bit 测试:−524288 .. +524287BranchAbsoluteBranchRelative 都写入同一字段;opcode 决定这个 20-bit 值被解释为 PC-relative delta 还是 bundle-index absolute target。

call 增加返回地址机制。EmitCallOp<…CallAbsolute>0x13a5d4c0):

c
// isa_emitter::EmitCallOp<SparseCoreScalarAlu_CallAbsolute> @ 0x13a5d4c0
// (decoded byte-exactly from objdump)
assert(operand.kind == 5);             // cmpb $0x5 — MCImmExpr
offset = GetValueFromSubExpr(GetTPUMCImmExpr(...));
if ((uint64_t)(offset + 0x80000) >= 0x100000) return RetCheckFail();  // SAME 20-bit check
bundle.byte[0x10] |= 0x04;             // scalar-alu present bit
EmitImmediate<SparseCoreImmediates>(slot=0, offset);   // 20-bit target → imm slot 0
link = GetSregno(MCOperand{kind=1, reg=5});  // movq $0x5; GetSregno — LINK REG = sreg #5
bundle.dword[0x18] = link;             // mov %eax, 0x18 — write dest (return-addr) sreg
bundle.byte[0x10] |= 0x01;             // orb $0x1 — set dest-present bit (bit 0)
```text

所以 call 是 `{20-bit target in imm slot 0} + {dest = return-address scalar register}`。在 SCS 路径上,link register 硬编码为 `sreg #5`。callee 通过 branch 到 `dest` 中的值来返回——即读取 link register 的 `BranchSreg`。**任何代际都没有专用 return opcode。**

间接形式从寄存器读取计算出的 target:`BranchSreg` 有 `x()` 字段(target sreg);`CallSreg` 同时有 `x()`(target)和 `dest()`(返回地址)。在 Jellyfish 上,间接 branch 改为读取由标量 `SET_BRANCH_TARGET_REGISTER` op 或 TTU `set_btr` op 设置的 Branch-Target-Register——二进制嵌入了冲突 assertion *"Cannot have a scalar SET_BRANCH_TARGET_REGISTER instruction and a TTU set_btr instruction in the same bundle."*

| 字段 | 位置 | 宽度 | 来源 |
|---|---|---:|---|
| branch/call target | immediate slot 0(GF 上 bit 423| 20signed| `EmitBranchOp` @ `0x13a5d3e0`;`EmitImmediate` |
| abs vs rel vs indirect | opcode discriminator || 不同 proto message / `ScalarOpcode` |
| call dest(return-addr sreg) | `dest` 字段,bundle `+0x18`(SCS);GF bit 467 | 5 | `EmitCallOp` @ `0x13a5d4c0` |
| call link register(SCS) | `sreg #5` || `movq $0x5; GetSregno` @ `0x13a5d560` |
| indirect target(`x()`) | `BranchSreg`/`CallSreg` reg 字段;GF bit 472 | 6(GF) | compact ref `x()` 访问器 |

> **陷阱 — branch target 不在 sequencer 槽字节中。** abs 和 rel 都把 20-bit 值写入 bundle 的一个*共享 immediate slot*(slot 0),只有 sequencer 槽中的一个 opcode bit 表示如何解释它。搜索 sequencer-slot byte window 来寻找 20-bit offset 的解码器不会找到任何东西;offset 在 bundle 的 immediate region 中。这也是 sync op 复用来保存 sflag id/threshold 的同一个 immediate slot。

在 V5+ TensorCore lane 上,abs/rel/call discriminator 写在 sequencer 槽**内部**——上面的 SCS *emitter* 路径是 SparseCore engine;TensorCore engine 有自己的逐 op `BitCopy` 填充器。`gfc` TensorCore 槽从逐 op encoder 和 dispatching `TensorCoreScalarAlu0Encoder::Encode`(`0x1f87b420`)逐字节解码而来。下面每个绝对 bit 位置都是 **LSB-first**——bit 0 是 byte 0 的 least-significant bit,与 [bundle model](bundle-model-overview.md) 记录的通用 `BitCopy(dst, dst_bit, src, src_bit, nbits)` packer(`0x1fa0a900`)一致。每个 TC sequencer op 都先在 **bit 483 写一个 6-bit opcode-HIGH “family”字段**,并在 **bit 478 写一个 5-bit opcode-LOW “discriminator”字段**;branch/call-immediate family 将 opcode-HIGH 固定为 `0`,并通过 LOW 字段选择 op:

```c
// EncodeTensorCoreScalarAlu0BranchAbsolute @ 0x1f87f5c0 (decoded byte-exactly)
BitCopy(slot, 483, 0, 6);              // opcode-HIGH "family" = 0 (branch/call-immediate)
BitCopy(slot, 478, 4, 5);              // opcode-LOW discriminator = 4 → BranchAbsolute
// …if x() present:
BitCopy(slot, 472, x_reg, 6);          // 6-bit operand / 2nd-source field

四种 immediate branch/call discriminator 除 LOW 值外字段完全相同;register-indirect 形式(BranchSreg/CallSreg)则把 opcode 放在 HIGH 字段。call 的返回地址 sreg 落在 bit 467 的 5-bit 字段中,间接 target / link-source sreg 落在 bit 472 的 6-bit 字段中:

Oponeof caseopcode-HIGH @483(w6)opcode-LOW @478(w5)Encoder
BranchAbsolute62040x1f87f5c0
BranchRelative63050x1f87f660
CallAbsolute65060x1f87f7e0
CallRelative66070x1f87f8e0
BranchSreg644(x at 472,dest n/a)0x1f87f700
CallSreg675(x at 472,dest at 467)0x1f87f9e0

CallAbsolute/CallRelative 还会把返回地址 sreg 写入 bit 467 的 5-bit 字段BitCopy(slot, 467, dest, 5)),并把 call-target / link-source sreg 写入 bit 472 的 6-bit 字段——这确认 SCS 路径的返回地址机制也逐字节存在于 TensorCore lane 上。opcode-HIGH 0 family 也被非控制 sequencer op 共享(ScalarFence LOW=0、Delay LOW=3、SetTag LOW=8、ReadRegisterLccLow LOW=10),因此 LOW discriminator 只有在 HIGH=0 family 内才单独识别 branch/call。该映射与 Bundle GF §Sequencer Slot 一致。

说明 — SCS 和 TC lane 共享编码契约,但不共享代码路径 SparseCore(SCS/TAC/TEC)sequencer 通过 LLVM-MC 驱动的 isa_emitter::EmitBranchOp / EmitCallOp 模板抵达其字节;TensorCore sequencer 通过由 TensorCoreScalarAlu0Encoder::Encode dispatch 的 proto-bundle gfc::isa::Encode* 填充器抵达其字节。二者都把 20-bit signed target 落在 immediate slot 0,并把 abs/rel/call discriminator 落在 sequencer 槽中,但重实现不能假设一个函数同时发射两者——它们是按 TpuSequencerType keyed 的独立填充器家族。

Jellyfish branch/call discriminator 是逐字节解码出的连续 ScalarOpcode 范围:

c
ProtoUtils::IsBranch(op): (op & ~3) == 8    // and $0xfffffffc; cmp $0x8  → {8,9,10,11}
ProtoUtils::IsCall(op):   (op & ~3) == 0xc  // and $0xfffffffc; cmp $0xc  → {12,13,14,15}
```text

四个 branch opcode(`ScalarBranchRelative` / `ScalarBranchAbsolute` / `ScalarBranchIndirect` + 一个)和四个 call opcode(`ScalarCallRelative` / `ScalarCallAbsolute` / `ScalarCallIndirect` + 一个)。每个范围的前三个名称从 `.rodata` 字符串确认;`8..11` / `12..15` 内部精确的 integer↔name 绑定从连续性推断(MEDIUM)。

---

## Delay-Slot 字段

每个 branch 和 call 都携带一个 delay-slot count——即 branch 后、PC 改变生效前仍会 issue 的 bundle 数量。访问器存在于每个 branch/call op 上(`scalar_alu_0.branch_absolute().has_delay_slots()`、`…call_sreg().has_delay_slots()` 等),LLVM-MC verifier 对该值设边界:

```text
delay_slots_op.getImm() >= 0 && delay_slots_op.getImm() <= 5

因此 delay-slot count 是一个 0..5 字段(3 bits)。packer 会在 branch bundle 后追加这么多个空 bundle;逐代 base count 位于 TpuSubtarget +0x914(其精确逐代值未提取——MEDIUM)。独立 Delay op(…Compact_Delay::delay_count())是不同的 pipeline-balancing NOP-with-count,而不是逐分支 delay。在 V5+ 上没有 in-bundle delay-slot bit——count 纯粹是 packer pad-count(GF sequencer 槽没有 delay-slot 字段)。JF / JF-BCAH / PF-TC / PF-BCS / PF-BCChan / VF-TC / GL-TC 存在 MarkBranchDelaySlot(bool) emitter 方法;6acc60406(gfc)不存在此类符号,与 V5+ 无编码 delay 模型一致。


条件编码——Predication 就是 Condition

没有单独的 branch-condition 字段。sequencer 槽的 predication 字段兼作条件分支条件。二进制嵌入了 assertion !scalar_alu_0.has_predication() || scalar_alu_0.predication() == PREDICATION_ALWAYS——无条件分支必须具有 ALWAYS predication 或没有 predication——而 query IsConditional(scalar_alu_0)ProtoUtils::IsConditional)会测试槽的 predication,以决定 branch 是否为条件分支。

来自 .rodata 字符串的 predication enum 值:

含义
PREDICATION_ALWAYS无条件(predicate true)
PREDICATION_NEVER槽被门控关闭(NOP / never-execute)
PREDICATION_OR_NEVERpredicate-OR 变体
PREDICATION_OR_INVERTED_NEVER带 inversion 的 predicate-OR
PREDICATION_SLOT_NEVER槽级 never(V5+ 双谓词)

条件 branch 在 predication 字段中编码谓词寄存器索引;当且仅当该谓词为 true 时 branch 被 taken(或者,对于 OR/inverted 变体,组合谓词为 true)。在 Jellyfish 上,predication 字段是每槽 5 bits,常量逐字节精确:

text
HardwareBundleBits::kPredicateRegisterCount = 15   (0xb834cf4: 0x0f)
HardwareBundleBits::kAlwaysExecute          = 15   (0xb834cf8: 0x0f)
HardwareBundleBits::kNeverExecute           = 31   (0xb834cfc: 0x1f)
```text

因此值 `0..14` 是谓词寄存器 P0..P14,`15` 是 always-execute,`31` 是 never-execute。空(未填充)槽保持为 `kNeverExecute = 31`,这是规范空槽编码——见 [Bundle Model §Empty-Slot Convention](bundle-model-overview.md#empty-slot-and-nop-convention)。

在 V5+ 上,predication 是一个 **7-bit 字段**,由 `TPUMCCodeEmitter::encodePredicateOperand`(`0x13c77c40`)逐字节精确编码:

```c
// encodePredicateOperand @ 0x13c77c40 (decoded byte-exactly)
reg = reg_encoding_table[op.reg];      // movzwl (%rdi,%rcx,2)
APInt::insertBits(out, reg, /*pos=*/0, /*width=*/4);   // bits [0:3] = predicate-reg index
if (flag_byte & 1)                     // test $0x1, %r14b
    out.flags |= 0x10;                 //   bit [4] = predicate sense / present
mode = (flag_byte >> 5) & 3;           // shr $0x5; and $0x3
APInt::insertBits(out, mode, /*pos=*/5, /*width=*/2);  // bits [5:6] = predication MODE

也就是 {4-bit reg, 1-bit sense, 2-bit mode} = 7-bit 字段,是 Jellyfish 5-bit 字段的超集。四种 mode 对应 ALWAYS / NEVER / OR_NEVER / OR_INVERTED_NEVER。当 branch target 是尚未解析的 label 而不是 inline immediate 时,操作数走 LLVM-MC fixup 路径(getMachineOpValue @ 0x13c777e0,label relocation 使用 24-bit operand class)。

说明 — 6acc60406 (GF) 添加了双谓词。 6acc60406(gfc)把逐槽 4-bit 寄存器索引替换为专用双谓词槽(TensorCorePredicates,bits 496..505 上的两项 (reg, invert) 池)加逐槽 2-bit selector(sequencer 槽 @ bit 489)。GF 条件 branch 可由两个逐 bundle 谓词中的任意一个 guard;GF SparseCore 添加了读取旋转谓词环的旋转谓词 branch(BranchRelativeRotatingPreg)。见 Bundle GF §Dual-Predicate Slot


Hardware Loop 和 Sync 的位置

两种循环模型按代际清晰拆分。Jellyfish / Dragonfish 没有 loop-counter register;hardware loop 是由 AddressHandlerProgramBuilder::BeginLoop0xfa90d40)/ EndLoop0xfa91300)构建的软件 bundle-index backward branchBeginLoop 记录当前 bundle index(并 assert 没有嵌套循环处于活动状态),EndLoop 发射一个回到该 index 的 backward branch。Viperfish / Ghostlite / 6acc60406 有 64-bit hardware loop counter(LCC),由 sequencer op ReadRegisterLccLow / ReadRegisterLccHigh(低 + 高 32-bit half)读入标量寄存器;循环体使用一个由 LCC 值计算出的谓词 guard 的条件 BranchRelative。LCC read op 存在于 VF/GL/GF 的 TC 和 SCS 上,在 JF/PF 上缺失(nm -C)。完整循环细节见 Hardware Loop-Counter

在大多数代际上,sync op 位于 sequencer scalar slot 中。在 Jellyfish 上,sync-flag work 从向量路径发射(JellyfishEmitter::EmitVectorSyncFlagSet/Add/...);在 Pufferfish BCS 上,专用 sync op 位于 Scalar0Scalar1 中;在 V5+ 上,独立的 ScalarMisc lane(与 ScalarAlu0 sequencer lane 分离)拥有 sync family。barrier op 编码器 EmitBarrierSync<…ScalarMisc>0x13a5f100)设置 ScalarMisc present bit,并通过 ScalarY 操作数写入 sflag id/threshold,其 immediate 形式复用 branch offset 使用的同一个 20-bit immediate slot 0。逐代 sync-slot 位置和 op 名册在配套页面中制表。


交叉引用

  • Bundle Model — VLIW bundle、按 (TpuVersion, TpuSequencerType) keyed 的 codec,以及空槽 kNeverExecute 约定。
  • Sequencer Ops Per Gen — 逐(generation × sequencer-type)的 control-flow op 清单以及 TpuSequencerType enum。
  • Hardware Loop-Counter — JF 软件 bundle-index loop 对 V5+ LCC counter read。
  • SPU / Scalar Slot — 与 sequencer 共享 lane 的 scalar-ALU compute op。
  • MC-Emitter — 为什么 V5+ branch/call/halt opcode 路由到 zero-base default。
  • Bundle GF — 字节精确的 GF sequencer 槽(selector @ 489,opcode-HIGH @ 483)和双谓词槽。