硬件循环计数器
地址适用于来自 libtpu-0.0.40-cp314 wheel 的 libtpu.so。其他版本会不同。
摘要
TPU 循环在两个位置之一计数,且该选择按 sequencer 决定,而不是按 program 决定。在 BarnaCore(以及 Jellyfish / Pufferfish AddressHandler)上,循环是真正的硬件循环:专用 silicon register 保存 trip count,硬件递减并测试它,bundle stream 包含显式的 loop-setup / loop-start / loop-end ops,但没有 software induction variable、没有 compare、没有 branch。AddressHandler 硬件循环只在 v3/v4 上有效——ViperfishTarget override 是一个 dead stub(如下),v5p/v6e 完全没有 override。在 TensorCore 和 SparseCore 上,循环是由 scalar-ALU op set 构成的 software back-edge——ADDri IV update、一个 CMPxx 和一个 BRcond——同时硬件 Loop Counter (LCC) register 镜像迭代计数,使 body 可以读取它(用于依赖迭代次数的寻址),而不需要保留自己的 counter。LCC 不驱动任何东西;它是一个可读快照。
loop-counter register 在整个 binary 中名为 LCC,是一个以两个 32-bit halves 读取的 64-bit value,并与 global time counter (GTC) 统一在同一个 enum(CycleCounterType { kLCC, kGTC })下。它不是可分配 register——register file 中没有与 S/V/M/P0..P31 并列的 LCC class;和 GTC 一样,它是一个读入 scalar destination 的特殊 control register。可寻址 LCC registers 的数量是本页固定的 per-generation count:Jellyfish 的 TensorCore 不暴露任何 LCC(循环只存在于 AddressHandler Loop slot 中);Pufferfish 通过 indexed read-register enum 暴露两个(LCC0、LCC1);Viperfish、Ghostlite 和 Trillium 通过一对专用 dest-only opcodes 暴露一个 implicit counter。
本页把 loop counter 作为重实现目标来记录:per-generation count 和 read mechanism、两种 loop mechanisms(hardware-counted 与 software-counted-with-readback)的 encoding、带有 even-offset / minimum-length 约束的 AddressHandler single-active-loop builder、证明 hardware-vs-software split 的两个 LLVM PipelinerLoopInfo subclasses,以及 sequencer 如何驱动 back-edge。op inventory 及其所在 lane 见 Sequencer Slot 页面;本页负责 counter 本身。
对重实现而言,约定如下:
- LCC 是一个 64-bit 特殊 control register,以 low+high 32-bit halves 读入 5-bit-selected scalar destination;最大可表示 trip count 为
2^64 − 1。它不在 allocatable register file 中。 - 每代 count:JF TC = 0(仅 AddressHandler
Loopslot),PF = 2(LCC0/LCC1,通过Tcs/Bcsread-register enum),V5+ = 1 implicit(通过ReadRegisterLcc{Low,High},一个无 index operand 的 dest-only opcode)。 - 两种机制:hardware-counted(BarnaCore / AddressHandler——
getIVUpdate/getCmp为 NULL,getTripCount为−1)与 software-counted(TC / SparseCore——真实的 IV/cmp/trip-count MIs),由analyzeLoopForTPUPipelining基于 subtarget feature bit 和 terminator opcode 选择。 - AddressHandler 硬件循环是 single-active(一个
loop_start_field,不是 stack),body length≥ 2instructions,并带有 mandatory non-loop preheader instruction。 - Nesting 受约束:每个 sequencer 一个 active hardware loop;PF 的两个 LCC registers 允许读取 depth-2 nest;outer loops 是 software back-edges。
| Counter register | LCC ("Loop Counter") — 64-bit,以 lo+hi 32-bit halves 读取 |
| Counter enum | LloInstruction::CycleCounterType { kLCC, kGTC }(与 GTC 共享 read datapath) |
| PF read mechanism | indexed TcsReadRegister / BcsReadRegister enum(LCC0, LCC1, …) |
| V5+ read mechanism | ReadRegisterLccLow / ReadRegisterLccHigh — dest-only opcodes,无 index |
| V5+ dest encoder | TensorCoreScalarAlu0Compact_ReadRegisterLccLow::set_dest @ 0x1f62ff60 → BitCopy(buf,467,&dest,0,5) |
| HW-vs-SW selector | TPUInstrInfo::analyzeLoopForTPUPipelining @ 0x13b804c0(feature bit [subtarget+0x158]&1 + terminator opcode) |
| HW loop-info class | (anon)::TPUBarnaCorePipelinerLoopInfo — getIVUpdate=NULL @ 0x13b86560, getTripCount=−1 @ 0x13b865a0 |
| SW loop-info class | (anon)::TPUSparseCorePipelinerLoopInfo — getTripCount=[this+0x28] @ 0x13b86260 |
| AddressHandler builder | AddressHandlerProgramBuilder::BeginLoop @ 0xfa90d40 / EndLoop @ 0xfa91300 |
| AddressHandler per-gen insert | JellyfishTarget::InsertAddressHandlerLoop @ 0x1d490e00(live)/ PufferfishTarget @ 0x1d495340(live)/ ViperfishTarget @ 0x1d49b980(__noreturn stub) |
| BarnaCore HW opcodes | bcLOOP_SETUP (0x194), bcLOOP_START (0xf8a), bcLOOP_END (0x193) |
| LLO loop kinds | LloLoopKindProto { LOOP_KIND_NONE, LOOP_KIND_WHILE, LOOP_KIND_DOWHILE } |
LCC Register 及其宽度
硬件 loop counter 在 binary 中处处命名为 LCC("Loop Counter"),是一个 64-bit value。它从不整值读取:program 将其作为两个 32-bit halves 读取(TensorCore 上的 rdreg.lcc.lo + rdreg.lcc.hi;V5+ 上的 ReadRegisterLccLow + ReadRegisterLccHigh),这与 global time counter (GTC) 完全镜像,后者也以相同的 lo+hi 方式读取。LLO IR 将二者统一在单个 enum 下——LloInstruction::CycleCounterType { kLCC, kGTC }——并共享 read-register datapath。LCC 是 per-loop iteration counter;GTC 是 global time counter。
关键的重实现事实:LCC 不是 allocatable register。 TPU register file 有 S / V / M / P0..P31 / XRF / DRF / ERF / SFRF / V2SF / CB / VAGG classes,但没有 LCC class。LCC 和 GTC 一样,是 sequencer 读入 scalar destination S0..S31 的特殊 control register。每个 half 落入一个 32-bit scalar register;destination 是 5-bit-selected scalar index,这一点从 V5+ encoder 中逐字节精确确认:
// gfc::isa::TensorCoreScalarAlu0Compact_ReadRegisterLccLow::set_dest(unsigned) @ 0x1f62ff60
int dest = a2; // the scalar destination index
return BitCopy(buf, /*dst_bit=*/467, &dest, /*src_bit=*/0, /*width=*/5); // 5-bit dest, abs bit 467
```text
`BitCopy(buf, 467, &dest, 0, 5)` 将 5-bit destination 写到 absolute bundle bit 467(`0x1d3`)——而且这是该 op 写入的**唯一** field。本页所有 bit positions 都是 **LSB-first**(bit 0 = byte 0 的 least-significant bit),与通用 `BitCopy(dst, dst_bit, src, src_bit, nbits)` packer(`0x1fa0a900`)以及 [Bundle Model §bit-numbering](bundle-model-overview.md) 中固定的约定一致。这也是 [Sequencer Slot](slot-sequencer.md#branch--jump--call-encoding) call/return-address encoder 写入的 bit 467 处同一个 5-bit dest。这里没有 loop-counter-index operand,这是 V5+ 暴露单个 implicit counter 的结构性证明(见 [Per-Generation Count](#per-generation-count-and-read-mechanism))。counter 可表示的最大 trip count 是 `2^64 − 1`;64-bit readback(lo+hi)是固定的,而 silicon down-counter 是否完整 64 bits 或更窄,无法从 binary 中分离判断。
> **注意 —** LLVM-generic `hardware-loop-counter-bitwidth` `cl::opt`("Set the loop counter bitwidth")属于 target-independent `HardwareLoops` pass,而不是 silicon LCC。按 lo/hi read structure,TPU LCC 固定为 64-bit;不要混淆二者。
---
## 每代 Count 和 Read Mechanism {#per-generation-count-and-read-mechanism}
“count”是 program 可读取的*可寻址* loop-counter registers 数量。binary 中存在两种不同的 register-naming conventions,它们直接揭示 count。
**Pufferfish (v4) — 两个 loop counters,显式 indexed。** Pufferfish read-register set 是一个 enum,即 `TcsReadRegister`(TensorCore Sequencer)和 `BcsReadRegister`(BarnaCore Sequencer),其值命名两个不同的 loop counters 和两个不同的 time counters:
```text
TcsReadRegister: BcsReadRegister:
TCS_READ_REGISTER_LCC0 BCS_READ_REGISTER_LCC0
TCS_READ_REGISTER_LCC1 BCS_READ_REGISTER_LCC1
TCS_READ_REGISTER_GTC0 BCS_READ_REGISTER_GTC0
TCS_READ_REGISTER_GTC1 BCS_READ_REGISTER_GTC1
TCS_READ_REGISTER_TAG_REGISTER BCS_READ_REGISTER_TAG_REGISTER
TCS_READ_REGISTER_TRACEMARK_REG BCS_READ_REGISTER_TRACEMARK_REG / FSR / HDRactive counter 由 scalar read-register op 的 reg enum field 选择,而不是由 dedicated opcode 选择——这就是 binary 中不存在 pxc::isa::*ReadRegisterLcc* function 的原因(nm set 中没有)。读取 LCC0 与 LCC1 是选择 enum value,而不是选择不同 instruction。
Viperfish (v5p) / Ghostlite (v6e) / 6acc60406 (TPU7x) — 一个 loop counter,implicit。 V5+ 将 indexed read 替换为每个 engine 两个 dedicated opcodes:ReadRegisterLccLow 读取 LCC[31:0],ReadRegisterLccHigh 读取 LCC[63:32],各自读入 scalar dest。vxc、gxc::glc 和 gxc::gfc 的 encoders 同时存在于 ScalarAlu0、ScalarAlu1 和 SparseCore 上。如上所示,该 op 的唯一 operand 是 5-bit destination——没有 index field,因此 program 每个 sequencer 正好能寻址一个(implicit)loop counter。
Jellyfish (v2) / Dragonfish (v3) — TensorCore 上没有 LCC read。 不存在 jellyfish::isa::*ReadRegisterLcc* symbol,也没有 Jellyfish read-register enum 命名 LCC;JF TensorCore 只有 cycle-counter reads。Jellyfish 只通过 AddressHandler Loop slot(如下)暴露 hardware loop,从不通过 TensorCore 上的 LCC read 暴露。
| Gen | TC LCC regs | SC/BCS LCC regs | Read mechanism |
|---|---|---|---|
| Jellyfish (v2) | 0 | n/a(BCAH Loop slot) | TC 上仅 cycle-counter |
| Dragonfish (v3) | 0(alias JF) | n/a | 继承 Jellyfish |
| Pufferfish (v4) | 2(LCC0/LCC1) | 2(BCS LCC0/LCC1) | Tcs/Bcs read-register enum |
| Viperfish (v5p) | 1(implicit) | 1(implicit) | ReadRegisterLcc{Low,High}(dest-only) |
| Ghostlite (v6e) | 1(implicit) | 1(implicit) | ReadRegisterLcc{Low,High}(dest-only) |
| 6acc60406 (TPU7x) | 1(implicit) | 1(implicit) | ReadRegisterLcc{Low,High}(dest-only) |
易错点 — Pufferfish 确实没有 V5+
ReadRegisterLcc{Low,High}opcode form,但它仍然有两个 LCC registers:它通过 indexedTcsReadRegister/BcsReadRegisterenum 读取它们。若重实现只根据 V5+ opcode 形态驱动,会完全漏掉 PF loop counters。
两种 Loop Mechanisms
循环使用哪种机制由 sequencer 决定,且该 split 由两个——也只有两个——LLVM PipelinerLoopInfo subclasses 证明。不存在 TensorCorePipelinerLoopInfo;TensorCore software loop 使用 SparseCore(software)info。
llvm::TPUPipelinerLoopInfo (base)
├─ (anon)::TPUBarnaCorePipelinerLoopInfo — hardware-counted (counter is in silicon)
└─ (anon)::TPUSparseCorePipelinerLoopInfo — software-counted (IV in the scalar register file)
```text
BarnaCore subclass 对每个 software-loop component 都返回 null,这是硬件执行计数的最清晰证据:
```c
// (anon)::TPUBarnaCorePipelinerLoopInfo — decoded byte-exactly
getIVUpdate() @ 0x13b86560 -> return 0; // NULL — no software induction variable
getCmp() @ 0x13b86580 -> return 0; // NULL — no software compare
getTripCount() @ 0x13b865a0 -> return -1; // -1 = "no software trip count"
adjustTripCount @ 0x13b86500 -> ret; // no-opSparseCore subclass 为每项返回真实 machine instructions:
// (anon)::TPUSparseCorePipelinerLoopInfo — decoded byte-exactly
getIVUpdate() @ 0x13b86220 -> return *((void**)this + 3); // [this+0x18] = ADDri MI
getCmp() @ 0x13b86240 -> return *((void**)this + 2); // [this+0x10] = CMP/BRcond MI
getTripCount() @ 0x13b86260 -> return *((void**)this + 5); // [this+0x28] = trip-count MI
```text
选择发生在 `analyzeLoopForTPUPipelining`(`0x13b804c0`)。它首先测试 subtarget feature bit(`[*subtarget + 0x158] & 1`——byte offset `0x158` = 344;BarnaCore hardware-loop mode);然后遍历 loop 的 terminators,寻找**三个** loop-terminator opcodes 之一(`325`、`403`、`328`)。匹配时,它读取 `[loop_header + 0x122]`(= 290)处的 per-loop HW-mode marker byte;当该 byte 为 `1` 时,它通过 `operator new(8u)` / `vtable = off_2192D4E8` arm 分配 BarnaCore info(函数中部,仅在 terminator match 后到达);否则它检测 IV 和 compare,并构建 48-byte SparseCore info(`operator new(0x30u)`):
```c
// llvm::TPUInstrInfo::analyzeLoopForTPUPipelining @ 0x13b804c0 (decoded byte-exactly)
subtarget = loop_header->parent_subtarget; // [a3+4]→[+32]
if ((subtarget->vtable_field[0x158] & 1) == 0) return 0; // not pipelineable on this target
for (term in loop terminators) { // walk the terminator chain
if (term.opcode == 325 || term.opcode == 403 || term.opcode == 328) {
if (loop_header.flags[+0x122] == 1) // BarnaCore HW-mode marker byte == 1
return new TPUBarnaCorePipelinerLoopInfo; // operator new(8); no IV/cmp/trip
// else: find predicate operand, detect IV update + compare (analyzeIVUpdateforPipelining)
...
return new TPUSparseCorePipelinerLoopInfo(iv, cmp, ...); // operator new(0x30)
}
}注意 — 三个 terminator opcodes(
325、403、328)和 predicate-operand opcode540是analyzeLoopForTPUPipelining匹配的 raw MC opcode integers;它们的 symbolic names 尚未解析(整数是从 decompile 中逐字节精确得到的——名称不是)。该函数在提取 predicate operand index 时还 special-cases opcode540。
因此 BarnaCore loop body 是:bcLOOP_SETUP(加载 count)→ bcLOOP_START(标记 body head;一个 value operand = bound)→ body → bcLOOP_END(硬件 decrement+test+branch-back terminator)。TC/SC loop body 是:preheader(MOV/ADDri 初始化 IV)→ body → ADDri(IV += stride)→ CMPxx → BRcond 回到 head,LCC register 镜像 count 供 in-body reads 使用。
特性 — BarnaCore loop index 本身是一个可读值,不同于 LCC snapshot。intrinsics
bc.extractvalue.loopindex(读取 current index)和bc.insertvalue.loopindex(seed 它)暴露bcLOOP_END递减的 live counter——这是不同于 software-counted engines 上ReadRegisterLccmirror 的 datapath。重实现不能假定“read the loop count”在两种机制上会映射到相同 op。
Sequencer 如何驱动 Loop
loop-control op 始终占用 scalar sequencer slot——scalar-ALU sub-bundle 的 lane 0,也是唯一能改变 program counter 的 lane(见 Sequencer Slot)。两种机制在那里放置不同的 ops。
Software loop(TC / SparseCore)。 back-edge 是 BRcond(conditional branch);IV update(ADDri)和 compare(CMPxx)是同一 lane family 中的 scalar-ALU ops。branch target 是 immediate slot 0 中的 signed 20-bit field,而不在 sequencer slot bytes 内;absolute-vs-relative 区分纯粹是该 field 之上的 opcode discriminator。CMPxx family 覆盖 register-immediate(CMPxxri)和 register-register(CMPxxrr)形式中的 signed/unsigned comparisons——immediate form 编码 compile-time-constant trip bound,register form 编码 dynamic / SPU-computed bound。因为 back-edge 是 branch,loop body 的最后一个 bundle 是 branch-terminator bundle,这会与 branch-delay-slot packing 交互。
Hardware loop(BarnaCore)。 bcLOOP_SETUP(opcode 0x194)在 preamble 中将 trip count 加载到 loop register;bcLOOP_START(0xf8a)标记 body head,并携带一个 value operand(bound——可能来自通过 SETUP 的 immediate 的 register-materialized value);bcLOOP_END(0x193)是硬件用于 decrement、test 并 branch back 的 back-edge terminator。BarnaCore channel-scalar slot 携带 loop bit 和 branch bit,同时设置二者是 encoding error——诊断 "Invalid Barnacore Channel Instruction with both Loop and Branch bits set" 强制一个 slot 要么是 loop control,要么是 branch,不能两者都是。
trip count 作为 canonical index space 进入 LLO loop:LloLoopProto.LoopIndexSpaceProto = { start, limit, step }。limit 可以是 constant 或 dynamic value;binary 跟踪 num_dynamic_loop_bounds_(每个 dynamic bound 占用两个 slots,offset < num_dynamic_loop_bounds_ * 2),且 num_dynamic_loop_bounds_ == 0 的 loop 完全 static。XLA-side WhileLoopBackendConfig { KnownTripCount, KnownInitStep, KnownInductionVariable } 将 trip count 向下传播到这个 index space。LLO pass (anon)::UpdateLoopCounter(LloRegion*) 遍历 region,找到 loop,并将 trip counter 物化到 loop 的 carried tuple 中,使 hardware LCC 或 software IV 可以被 seeded;当 CFG 过于纠结而无法识别单个 trip counter 时,它以 UnknownLoopCountComplexCFG 放弃。
zero-trip 在 loop-kind level 处理,而不是由 counter 处理:LloLoopKindProto = { LOOP_KIND_NONE, LOOP_KIND_WHILE, LOOP_KIND_DOWHILE }。WHILE 在 body 前测试,可以执行零次;DOWHILE 在之后测试,至少运行一次。MLIR property verify_non_zero_trip 断言 loop 运行 ≥ 1 iteration,因此它可以 lowered 为 DOWHILE / hardware-counted loop 而无需 guard。小的 constant-trip loops 完全绕过 counter——"Loops with a constant trip count smaller than this value will not use the count register"——并改为 unrolled 或 peeled。
AddressHandler 硬件循环(Jellyfish / Pufferfish)
Jellyfish 没有 TensorCore LCC,因此它的硬件循环完全存在于 BarnaCore AddressHandler sequencer 中,由 AddressHandlerProgramBuilder::BeginLoop(0xfa90d40)/ EndLoop(0xfa91300)构建。builder 跟踪一个单一 loop-region field,loop_start_(位于 this+0x18,第 7 个 int),带有 kNoLoopActive = −1 sentinel——不是 stack——这是 AddressHandler loops 不能 software-nest 的结构性证明。CHECKs 逐字节精确如下:
// AddressHandlerProgramBuilder::BeginLoop @ 0xfa90d40 (decoded)
CHECK(loop_start_ == kNoLoopActive); // -1; fails if a loop is already active (line 903)
loop_start_ = instructions_.size(); // record the body head
CHECK(loop_start_ >= 1); // "Code must start with one non-loop instruction" (line 905)
// AddressHandlerProgramBuilder::EndLoop @ 0xfa91300 (decoded)
CHECK(loop_start_ != kNoLoopActive); // (line 910)
CHECK(loop_start_ >= 1); // "Code must start with one non-loop instruction" (line 911)
CHECK(loop_start_ < instructions_.size()); // (line 912)
loop_length = instructions_.size() - loop_start_;
CHECK(loop_length >= 2); // "Jellyfish spec requires that loop must have at least two instructions" (line 915)
insn = instructions_[loop_start_ - 1]; // the preheader instruction
CHECK(!insn.scalar.loop_start); // (line 917)
insn.scalar.loop_start = 1;
insn.scalar.loop_count = loop_length - 1; // body length minus the preheader slot
loop_start_ = kNoLoopActive; // reset; loop is closed
```text
因此 AddressHandler loop 要求 mandatory non-loop preheader instruction(`loop_start_ >= 1`)和至少两个 instructions 的 body(`loop_length >= 2`)。`EndLoop` 将 loop-start flag 和 loop-count stamp 到 preheader instruction record 中,然后重置 `loop_start_`。相同的 Pufferfish check string——*"Pufferfish spec requires that loop must have at least two instructions"*——确认同样的 minimum-length rule 延续到 v4。
loop body **不是** offset field——per-generation `Target::InsertAddressHandlerLoop` overrides 将一个 **count** 写入专用的 `BarnaCoreAddressHandlerScalarSlot_Loop` proto sub-message。`JellyfishTarget::InsertAddressHandlerLoop`(`0x1d490e00`)重新检查 `program_in_loop.bundles_size() >= 2`(同一 *"Jellyfish spec requires that loop must have at least two instructions"* string,`target_jellyfish.h:90`),然后 default-construct `Loop` sub-message 并存储 `loop_count = bundles − 1`(`*(loop + 24) = v30 − 1`)——body-bundle count 减去 preheader,与 `EndLoop` stamp 的 `loop_count = loop_length − 1` 相同。`PufferfishTarget::InsertAddressHandlerLoop`(`0x1d495340`)形态相同,带有 *"Pufferfish spec requires that loop must have at least two instructions"* string,并写入相同的 `bundles − 1` count。
> **易错点 —** 诊断 *"loop end is out of range or not a positive multiple of 2"* / *"loop start is out of range or not a negative multiple of 2"* 属于 LLVM bundled **ARM** backend(`(anon)::ARMAsmParser::matchAndEmitInstruction` @ `0x15185a20`,Armv8.1-M low-overhead-loop `WLS`/`LE` validation),**不**属于任何 TPU `InsertAddressHandlerLoop` path。TPU AddressHandler loop 携带的是 iteration **count**(`bundles − 1`),不是 signed even byte-offset;TPU encode path 中没有 even-multiple constraint。
AddressHandler loop 只跨 **v3/v4** 持续存在(`JellyfishTarget` / `PufferfishTarget` overrides,二者都 live 且实际构建 proto)。`ViperfishTarget::InsertAddressHandlerLoop` override(`0x1d49b980`)存在,但它是一个 `__noreturn` stub,会 fatal *"Deepsea version not supported"*(`target_viperfish.h:320`)——因此 AddressHandler-style hardware loop 在 **Viperfish (v5)** 被丢弃;Ghostlite 或 6acc60406 完全没有 override。
| Element | BarnaCore / AddressHandler(HW loop) | TC / SparseCore(SW loop) |
|---|---|---|
| Loop begin | `bcLOOP_SETUP`(load count)+ `bcLOOP_START`(1 bound operand);BCAH `BeginLoop` 设置 `loop_start_` | preheader:初始化 scalar IV(`MOV` / `ADDri`) |
| Body length | `ScalarSlot_Loop` proto 中的 `loop_count = bundles − 1`;body `≥ 2` instr | implicit(basic-block span) |
| Loop end | `bcLOOP_END` — HW decrement+test+branch-back | `ADDri` + `CMPxx` + `BRcond` back-edge |
| Counter | dedicated HW loop register(1;PF 上为 LCC0/LCC1) | scalar-reg IV;HW LCC 镜像 count(可读) |
| Trip source | bound operand(reg / 通过 SETUP 的 immediate) | `CMPxxri`(imm)或 `CMPxxrr`(reg,dynamic) |
| Live index read | `bc.extractvalue.loopindex` | `ReadRegisterLcc{Low,High}` / indexed `Tcs`/`Bcs` enum |
| Nesting | single active loop(`loop_start_ != kNoLoopActive` CHECK) | software IVs 可自由 nest;只有 innermost 是 HW-counted |
---
## Nesting Model
hardware-loop nesting 被有意限制:
- **AddressHandler / BarnaCore**:一次只有一个 active hardware loop,由单个 `loop_start_` field 和上面的 `BeginLoop` CHECK 强制执行。此层级没有 loop-counter stack——没有 nested AddressHandler / BarnaCore hardware loops。
- LLVM-generic option strings *"force-nested-hardware-loop"* / *"nested hardware-loops not supported"* 确认默认 TPU `HardwareLoops` path 不支持 nested hardware loops;常见情形是一个 hardware-counted innermost loop,outer loops 作为 software back-edges 处理。
- **Pufferfish 的两个 LCC registers(LCC0, LCC1)**允许读取两个不同的 loop counters——支持最多 depth-2 nest,其中 inner 和 outer 各有不同 counter,由 read-register enum value 选择。LCC0/LCC1 对应的是(outer, inner)nest levels 还是(TC-issued, BC-issued)loops,此处未追踪。
- **V5+ 有单个 implicit LCC**,因此只有 innermost hardware-counted loop 的 count 可直接读取;outer loops 使用 software IVs。
- LLO loop region(`LloRegionMember::kLoop`)可以在 IR 中 nest——一个 `kLoop` member 的 sub-region 可以包含另一个 `kLoop`——但只有 innermost 获得 hardware counter;compiler 会 flatten、unroll 或 software-count 其余 loops。
---
## 尚未固定的内容
- **bcLOOP field bit positions。** `bcLOOP_SETUP` 的 trip-count immediate field,以及 BarnaCore bundle 内 `bcLOOP_START` / `bcLOOP_END` 的 body-offset fields 由 LLVM MC encoder(`TPUMCCodeEmitter`)路由;per-opcode `InstBits` records 此处尚未 byte-decoded。ops 及其角色已固定;精确 field widths 尚未固定。
- **silicon counter width。** 64-bit readback(lo+hi)是固定的;down-counter 是完整 64 bits 还是更窄,无法从 binary 中分离判断。
- **PF LCC0 vs LCC1 assignment policy。** 存在两个 counters;哪个 compiler pass 选择 LCC0 vs LCC1,以及该选择是否跟踪 nest level 或 issuing engine,此处未追踪。
- **三个 loop-terminator MC opcodes(`325`, `403`, `328`)和 predicate opcode `540`。** `analyzeLoopForTPUPipelining` 匹配的 integers 逐字节精确;它们的 symbolic LLVM-MC names(分别对应 `bcLOOP_END` / branch terminators 中的哪一个)此处尚未逐一解析。
---
## 交叉引用
- [Sequencer Slot](slot-sequencer.md) — loop-control ops(`BRcond`、`bcLOOP_*`、`ReadRegisterLcc`)占用的 lane-0 scalar slot,以及 JF software-bundle-index loop 与 V5+ LCC read。
- [Immediate Slot](slot-immediate.md) — immediate slot 0,signed-20-bit branch / back-edge target 和 loop bound 落入的位置。
- [Bundle Model](bundle-model-overview.md) — per-generation bundle widths,以及承载这些 slots 的、由 `(TpuVersion, TpuSequencerType)` keyed 的 codec。