每代 Encoder Latch 序列化
地址适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(BuildID md5
89edbbe81c5b328a958fe628a9f2207d,未 strip,包含完整 C++ 符号)。其他版本会有所不同。
摘要
Latch 分配决定每个 MXU weight-latch op 在其 MxuSequence 内携带哪个索引。本页是编码侧:每个 TPU generation 的 Encoder 如何把一个已完成的 latch op——已经携带其 GainLatchMode(BYTE[op+0x40])、unit-id/GMR(WORD[op+0x0b] bits 8–9)和 MSR(BYTE[op+0x44])——lower 到每代 TensorCore VLIW bundle 的 MXU / VectorExtended slot 的绝对 bit 位置。latch 从不以原始字段形式传递;它经过两阶段 lowering:每代的 *Emitter::EmitVectorLatch 把 LLO 字段写入一个 protobuf submessage,然后每代 encoder 再把该 submessage bit-pack 到 bundle bitstream 中。
编码工具包是 gloop 的 bitcoding.cc writer 家族:byte 级 Encoder 可增长缓冲区、带内联 PutBits/PutVarInt/PutGamma primitives 的 BitEncoder accumulator、与 reader 共享的 (1<<k)-1 mask_ 表,以及——对于 V5+ protobuf-backed slots——独立的 BitCopy(dst, dst_bit, src, src_bit, nbits) field-blitter,V5+ codecs 每个字段调用一次。相同工具包也服务 route-cache codec 和 profiler trace codec;本页记录序列化 MXU latch 的那一半。reader 半边(BitDecoder GetBits64/GetGamma/GetVarInt/SkipBits)位于 route-cache codec 页面。
跨 generation 有两种不同的 encoder 形态。Jellyfish (v2) 是单体式:EncoderJf::EncodeVectorExtendedInstruction 是一个函数,它读取 proto 的 VectorExtendedOpcode 字段并运行一个 35-case switch,把 opcode-field 常量直接 OR-shift 到 QWORD[bundle+12] 中,GLM 先通过 6-entry {7,10,9,12,8,11} 表映射。Pufferfish (v4)、Viperfish (v5p) 和 Ghostlite/6acc60406 (v6e/TPU7x) 是 oneof-typed:EmitVectorLatch 按 GLM 选择 typed PushGains*/Pushmatrix*/PushMatrix* submessage,并由每个 variant 一个生成的 Encode…<Variant> sub-encoder 用 BitCopy blit 其字段。只有 Viperfish 会拆分其 7-bit MXU opcode 字段,因此 latch 和 matmul 共享该字段,并且 matmul opcode 的 LSB 是 MSR-select bit(bundle bit 57)——这是 MSR-A/MSR-B overrun handshake 的 bundle-level 编码,也只有 Viperfish 拥有它。
最后,本页钉住 CreateVectorLatchLsf entry guard opcode_produced_register_type[gain_src.opcode] != 4。Register-type 4 是vector类;该 guard 要求 LSF latch 消费的 gain matrix 来自 vector register,否则走较慢的 LloModule::UpdateStatus diagnostic path。它是决定哪些 latch ops 会被构建的 gate,也是 latch 的 register_number 是 Vregno 的原因。
对于重新实现,契约是:
- latch 的
GainLatchMode变成 slot opcode(在 V5+ 上还变成 dtype/format)。 在 JF 上,它索引 6-entry GLM→VEopcode 表,然后进入 35-case VEopcode→opcode-field switch。在 PF 上,它选择一个PushGains*oneof,其 7-bit opcode@abs91遵循0x20 + variant + 8·transposed + 0x10·masked。在 VF 上,它选择一个Pushmatrix*oneof,写入 opcode-high14@abs59(5b) +MatmulDataFormat@abs51(4b);dtype 搭载在 format 字段上,而不是 opcode 上(masked variants 则把 opcode 提升到 15..23)。 - unit-id/GMR(MXU quadrant)变成 slot 字段。 JF 通过
AddMxuNumToVectorExtended把它写入 proto+0x70,编码为@abs27-28(2b)。 - MSR 是 Viperfish-only bit。 PF 只有单个 MSR,不发射 MSR bit。VF 把 MSR-A/MSR-B 编码为 7-bit matmul opcode
@abs57的 LSB;latch 从 proto+0x20写入 1-bit control field@abs57,并从 proto+0x24写入一个耦合 control bit@abs58。 BitCopy(dst, abs_bit, &value, 0, width)是 V5+ field writer。abs_bit是绝对 bundle bit,width是字段 bit 宽;bundle 是 flat byte buffer,每个 byte 内 LSB-first。- type-4(vector)gain-register guard 约束构建。
CreateVectorLatchLsf读取opcode_produced_register_type[gain_src.opcode];如果它不是 4,则在继续前通过UpdateStatus记录一个chunk->ProducesVreg()diagnostic。
| Bit-writer primitives | gloop coder.cc Encoder + bitcoding.cc BitEncoder(PutBits/PutVarInt/PutGamma,内联) |
| V5+ field blitter | BitCopy(dst, dst_bit, src, src_bit, nbits) sub_1FA0A900 |
| 共享 mask table | BitEncoder::mask_ @0xbe79440 — 65 qwords, mask_[k]=(1<<k)-1 |
| JF encoder(单体式) | EncoderJf::EncodeVectorExtendedInstruction sub_1E869F00 |
| JF GLM→VEopcode 表 | dword_AEF42AC = {7,10,9,12,8,11}(6 entries,按 GLM 0..5 索引) |
| PF emitter / encoder | PufferfishTensorCoreEmitter::EmitVectorLatch sub_1410E1A0 → PushGains* sub-encoders sub_1EDC1660… |
| VF latch encoder (bf16) | Encode…0PushmatrixBf16 sub_1EFAF820: op 14@abs59, fmt 3@abs51, MSR @abs57, ctl @abs58 |
| VF MSR-select bit | bundle bit 57 = matmul 7-bit opcode 的 LSB(…Msra=2, …Msrb=3) |
| Type-4 gain guard | CreateVectorLatchLsf sub_1D4D7AA0: opcode_produced_register_type[gain_src.opcode] != 4 → slow UpdateStatus |
| 置信度 | CONFIRMED(byte-anchored),除非某行或 callout 另有说明 |
Bit-Writer Primitives
目的
每个 bundle byte 都由同一个 gloop writer 工具包产生。该工具包有三层:byte 级可增长缓冲区(Encoder)、其上的 bit accumulator(BitEncoder),以及——用于 protobuf-backed V5+ slots——独立的 BitCopy,它按绝对 bit 位置直接把一个字段写入 bundle 的 flat byte array。JF 的单体式 encoder 绕过 BitCopy,把常量直接 shift 进一个 uint64 bundle word;V5+ encoders 则逐字段用 BitCopy blit。没有导出的 PutBits/WriteBits 符号——它们内联进每个 call site——因此 primitives 是从 BitEncoder::Initialize(gamma-table self-test,执行完整 encode→decode round-trip)和真实 V5+ sub-encoders 中 byte-exact 重构的。
Encoder byte buffer
Encoder(coder.cc)是一个四指针可增长缓冲区。其 destructor(sub_21073980)带有 buffer-overrun guard,这是其 layout 的最强证据:
struct Encoder { // 0x20 bytes
char* cursor; // +0x00 buf_ — next write position
char* limit; // +0x08 limit_ — one-past the writable region
char* begin; // +0x10 owned allocation base
char* alloc_end; // +0x18 alloc end | owns-heap flag (top bit); SSO marker 0x8000000000000028
};
function Encoder::~Encoder(this): // sub_21073980
if (this.cursor > this.limit): // CHECK buf_ <= limit_ (coder.cc:34)
LogFatal("buf_ <= limit_") // overrun guard — fires on any spill past limit_
if (this.alloc_end == this.begin): // a heap slot was taken
free(this.begin)GOTCHA — overrun guard 是 destructor CHECK,不是每次写入的 bounds check。
coder.cc:34(buf_ <= limit_)只在 buffer 被销毁时触发,因此 encoder 在 packing 期间越过limit_后会继续写过 allocation,直到 teardown 才 abort。重新实现必须预先把 buffer size 设为 bundle width;gloop writer 依赖 inline SSO buffer(0x8000000000000028marker)对所有已发布 bundle 都足够大,并且 grow/realloc path 不在任何 latch-encode trace 上。
PutBits / PutVarInt / PutGamma
BitEncoder 包装一个 Encoder、一个 uint64 accumulator 和一个 bit count。BitEncoder::Initialize(sub_21072D40)通过用 PutGamma 编码每个值 1..255 并用 GetGamma 重新解码、断言相等,构建 runtime gamma table——这 byte-exact 暴露了内联 primitives:
// PutBits(value, n): mask to n bits, OR into the accumulator at the current
// bit position, then spill whole bytes LSB-first to the Encoder.
function PutBits(value, n):
acc |= (value & mask_[n]) << bitpos // mask_ @0xbe79440 = (1<<k)-1
bitpos += n
while bitpos >= 8: // byte-spill loop (seen in Initialize)
*cursor = (uint8)acc // *(_BYTE*)v13 = v6
cursor += 1
acc >>= 8 // v12 >>= 8
bitpos -= 8 // v5 -= 8
// PutGamma(value>=1): Elias-gamma — b zero-bits, a 1, then the low b bits.
function PutGamma(value):
b = floor(log2(value)) // _BitScanReverse64(value | 1)
emit b zero-bits, then a 1, then (value & mask_[b])
// Initialize stores gamma_[i] = i | (codeword_len << 24);
// CHECK (value & 0xffffff) == value (bitcoding.cc:110)
// re-decodes via GetGamma; CHECK v == i (bitcoding.cc:128)
// PutVarInt(chunk, value): self-delimiting integer — exact inverse of GetVarInt.
function PutVarInt(chunk, value):
num_groups = max(1, ceil(bitlen(value) / chunk))
emit (num_groups-1) one-bits then a 0 // unary prefix
emit num_groups little-endian chunk-bit groups of valuemask_(@0xbe79440,.rodata,65 qwords)与 reader 共享;两者都用 value & mask_[n] 把值 clamp 到 n bits。gamma_(@0x22593d00,.bss,256 × uint32)在 runtime 构建,不是 baked。
NOTE — MXU latch path 不使用
PutVarInt/PutGamma。 这些 self-delimiting 形式只出现在 route-cache path(和 gamma self-test)上。JF 单体式 encoder 使用 raw shift/OR;V5+ encoders 使用 fixed-widthBitCopy。Latch 字段全是 fixed-width。这里记录PutVarInt/PutGamma只是因为它们共享Encoder/mask_substrate。
BitCopy — V5+ field blitter
V5+ TensorCore codecs 不累积;它们把每个字段直接写进 bundle byte array:
// BitCopy(dst, dst_bit, src, src_bit, nbits) // sub_1FA0A900
// dst : the bundle's flat byte buffer
// dst_bit : absolute bit position in the bundle (LSB-first within each byte)
// src : pointer to the field value (the sub-encoders pass &local_qword)
// src_bit : 0 at every latch call site
// nbits : field width in bits每个 V5+ latch 字段都以 local = value; BitCopy(bundle, abs_bit, &local, 0, width) 发射。该函数会 mask、shift 并 OR-merge 跨越目标 bytes 的 source bits(长拷贝使用 AVX2 batched body,否则使用 scalar tail),保留 [dst_bit, dst_bit+nbits) 之外的 bits。因此 sub-encoder 中的字段顺序与 wire 结果无关:每个 BitCopy 都是对固定 bit window 的独立 masked merge。
Jellyfish — 单体式 VectorExtended Encode
目的
Jellyfish(TPU v2)没有按 variant 生成的 encoder。单个 EmitVectorLatch 把 GLM 映射到 VectorExtendedOpcode,把它和 MXU number stamp 到 bundle 的 VectorExtendedInstruction proto 中,而 EncoderJf::EncodeVectorExtendedInstruction 随后把该 proto bit-pack 到 bundle 中。slot 位于 JF 41-byte bundle 的 VectorExtended 区域(@abs27-39);本节补充 latch-specific occupants。(Dragonfish,即 v3 gen,共享 JF emitter family——不过 AddMxuNumToVectorExtended 会以 TpuVersionToDeviceIdentifiers == kDragonfishIdentifiers gate 其 mxu_num proto write,因此在 plain Jellyfish 上该字段保留 default。)
入口点
JellyfishEmitter::EmitVectorLatch sub_140B8C20 ── GLM→VEopcode + emit
└─ EmitVectorExtendedInstruction sub_140B4F80 ── VEopcode → proto+0x60, Vs → proto+0x6c
└─ AddMxuNumToVectorExtended sub_140B8DA0 ── mxu_num → proto+0x70
… (bundle finalize) …
EncoderJf::EncodeVectorExtendedInstruction sub_1E869F00 ── proto → bundle bits算法
function JellyfishEmitter::EmitVectorLatch(glm, vs_reg, mxu_num): // sub_140B8C20
if (glm >= 6) LogFatal("Unexpected Software latch mode.") // jellyfish_emitter.cc:1647
veopcode = dword_AEF42AC[glm] // {7,10,9,12,8,11}
EmitVectorExtendedInstruction(veopcode, vs_reg, /*imm=*/0) // sub_140B4F80
AddMxuNumToVectorExtended(mxu_num) // sub_140B8DA0
function EmitVectorExtendedInstruction(veop, vs, imm): // sub_140B4F80
ve = bundle.mutable_vector_extended() // proto submessage @bundle+0x50
*(DWORD*)(ve + 0x60) = veop // VEopcode field
SetPredication(ve)
*(DWORD*)(ve + 0x6c) = vs // rotate-Vs / source reg
function AddMxuNumToVectorExtended(mxu): // sub_140B8DA0
CheckMxuNum(mxu)
*(DWORD*)(ve + 0x70) = mxu // mxu_num field
function EncoderJf::EncodeVectorExtendedInstruction(ve, bundle): // sub_1E869F00
word = QWORD[bundle + 12] // the VectorExtended bundle qword
word = (word & ~(0x1F << 35)) | ((pred & 0x1F) << 35) // predication @abs35-39
word = (word & ~(0x3 << 27)) | ((ve.mxu_num & 3) << 27) // mxu-id/GMR @abs27-28
switch (ve.VEopcode): // 35-case opcode→field switch
case 0: word = (word & 0xFFFFFFF81FFFFFFF) | 0x20000000 // field 1 @abs29-34 (no-op opcode)
case 7: word = (word & 0xFFFFFFF81FFFFFFF) | (0x9 << 29) // ← LATCH GLM0 (bf16 NO_XPOSE)
case 8: word = (word & 0xFFFFFFF81FFFFFFF) | (0xa << 29) // ← LATCH GLM4 (int8/S8)
case 9: word = (word & 0xFFFFFFF81FFFFFFF) | (0xb << 29) // ← LATCH GLM2 (packed-bf16)
case 10: word = (word & 0xFFFFFFF81FFFFFFF) | (0xd << 29) // ← LATCH GLM1 (bf16 alt)
case 11: word = (word & 0xFFFFFFF81FFFFFFF) | (0xe << 29) // ← LATCH GLM5 (fp8-conv)
case 12: word = (word & 0xFFFFFFF81FFFFFFF) | (0xf << 29) // ← LATCH GLM3 (fp8 / E5M2)
… (matmul / matres / EUP VEopcodes 1..6, 13..34) …
default: LogFatal("Unknown opcode: ") // encoder_jf.cc:2570
QWORD[bundle + 12] = word常量 << 29 是位于 @abs29-34 的 6-bit opcode-field;每个 populated case 都先清除该字段(& 0xFFFFFFF81FFFFFFF,bits 29–34),再 OR 进 case value。该字段不是统一为 odd——0xa(GLM4)和 0xe(GLM5)的 bit 29 为 clear——因此它是普通 6-bit opcode,而不是 opcode-plus-fixed-valid-bit。
latch GLM → bundle opcode 链
把两个表串起来运行——GLM 通过 dword_AEF42AC,然后 VEopcode 通过 encoder switch——得到六个 JF latch opcodes:
| GLM | 含义 | VEopcode | opcode-field @abs29 |
|---|---|---|---|
| 0 | bf16 NO_XPOSE | 7 | 0x9 |
| 1 | bf16 alt | 10 | 0xd |
| 2 | packed-bf16 | 9 | 0xb |
| 3 | fp8 / E5M2 | 12 | 0xf |
| 4 | int8 / S8 | 8 | 0xa |
| 5 | fp8-conv | 11 | 0xe |
NOTE — 六个 GLM opcode-fields 全部不同;该 map 是 bijection。 在
EncoderJf::EncodeVectorExtendedInstruction(0x1e869f00)中,LABEL_55cases(VEopcode 8, 11)在LABEL_53cases 使用的相同 base 上额外加0x20000000,因此 VEopcode 8 编码为 field0xa(不是0x9),VEopcode 11 编码为0xe(不是0xd)。所以六个 JF latch opcode-fields 是{0x9, 0xd, 0xb, 0xf, 0xa, 0xe}——全部不同,是六种 latch modes 上的 bijection。很容易误读为 GLM0/GLM4 和 GLM1/GLM5 发生碰撞(0x9/0xd);0x20000000项正是区分它们的部分。NOTE — JF VectorExtended slot 不携带单独的 format 字段。 虽然 opcode 字段区分所有六个 latch GLMs,但它是单个 6-bit 字段,没有像 VF 的
@abs51那样的配套MatmulDataFormat。dtype 隐含在 opcode value 中,因此 JF decoder 只从 opcode field 重构 data type(0x9→bf16 NO_XPOSE,0xa→int8, …),而不是从 side field 重构。
函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
JellyfishEmitter::EmitVectorLatch | sub_140B8C20 | GLM bound < 6、GLM→VEopcode lookup、emit chain |
JellyfishEmitter::EmitVectorExtendedInstruction | sub_140B4F80 | VEopcode → proto +0x60,Vs → proto +0x6c |
JellyfishEmitter::AddMxuNumToVectorExtended | sub_140B8DA0 | mxu_num → proto +0x70 |
EncoderJf::EncodeVectorExtendedInstruction | sub_1E869F00 | 35-case VEopcode → bundle bits;pred @abs35,mxu-id @abs27,opcode @abs29 |
dword_AEF42AC | .rodata | 6-entry GLM→VEopcode table {7,10,9,12,8,11} |
NOTE — encoder 从 proto
+0x64读取 MXU id;emitter 把它写到+0x70。 encoder 从 proto+0x64(*((DWORD*)ve + 25))读取 MXU id,而AddMxuNumToVectorExtended写入mxu_num到 proto+0x70。两者可解释为 emitter 通过一次 indirection 持有 submessage,而 encoder 直接接收它;unit-id →@abs27-28binding 无论哪种方式都是 byte-exact,但哪个 protobuf field number 占用+0x64vs+0x70未与 proto descriptor 交叉核对。精确 field-number layout 为 MEDIUM;bundle-bit binding 为 CONFIRMED。
Pufferfish — PushGains Oneof Encode
目的
Pufferfish(TPU v4)是第一代具备 generated、per-variant encoders 的 generation。EmitVectorLatch 把 GLM 分发到 TensorCoreVectorExtended0(MXU0)或 TensorCoreVectorExtended1(MXU1)submessage 内的 typed PushGains{Rounded,Low,Hi,Packed,Byte}{,Transposed}{,Masked} oneof;随后 generated Encode…<Variant> sub-encoder 用 BitCopy 写字段。PF 只有单个 MSR(没有 overrun handshake),因此不发射 MSR bit。
入口点
PufferfishTensorCoreEmitter::EmitVectorLatch sub_1410E1A0 ── switch(glm) → PushGains oneof
├─ DefaultConstruct<…_PushGainsRounded> (MXU0 slot, bundle flag 0x80)
│ or DefaultConstruct<…1_PushGainsRounded> (MXU1 slot, bundle flag 0x100)
└─ … 20 PushGains variants …
EncodeTensorCoreVectorExtended0PushGainsRounded sub_1EDC1660 ── BitCopy fields → bundle算法
EmitVectorLatch 是一个 switch(glm),按 GLM claim 一个 MXU slot 并构造匹配的 oneof。MXU0/MXU1 选择以 bundle 的 populated-slot flags 为 key(0x80 = MXU0 taken,0x100 = MXU1 taken;两者都 set ⇒ "All vector extended slots occupied."):
function PufferfishTensorCoreEmitter::EmitVectorLatch(glm, vs, msr, mxu): // sub_1410E1A0
bundle = CurrentBundle()
switch (glm):
case 0: variant = PushGainsRounded // oneof tag 104
case 1: variant = PushGainsRoundedTransposed // oneof tag 109
case 2: variant = PushGainsHi // oneof tag 106
case 3: variant = PushGainsHiTransposed // oneof tag 111
case 4: variant = PushGainsLow // oneof tag 105
… (GLM 5..13; 6..9 share an error branch) …
ve = (bundle.mxu0_free()) ? bundle.mutable_tcve0() // proto @bundle+0x50
: bundle.mutable_tcve1() // proto @bundle+0x58
SetPredicate(ve)
ve.set_oneof(variant); ve.set_vsreg(vs)
function EncodeTensorCoreVectorExtended0PushGainsRounded(bundle, ve): // sub_1EDC1660
op = 0x20; BitCopy(bundle, 91, &op, 0, 7) // opcode @abs91 (7b)
if ve.has_mode(): BitCopy(bundle, 89, &ve.mode, 0, 2) // mode @abs89 (2b)
if ve.has_subop(): BitCopy(bundle, 83, &ve.subop, 0, 3) // sub-op @abs83 (3b)
if ve.has_op0(): BitCopy(bundle, 225, &ve.op0, 0, 5) // register operands,
if ve.has_op1(): BitCopy(bundle, 182, &ve.op1, 0, 5) // 5b each, shared
if ve.has_op2(): BitCopy(bundle, 152, &ve.op2, 0, 5) // operand pool
if ve.has_op3(): BitCopy(bundle, 203, &ve.op3, 0, 5)
if ve.has_op4(): BitCopy(bundle, 172, &ve.op4, 0, 5)
// NOTE: predication is @abs98 (5b), written by the slot's shared pred pathPushGains opcode 表(@abs91, 7-bit)
20 个 variants 遵循规则结构:opcode = 0x20 + variant + 8·transposed + 0x10·masked,其中 variant ∈ {Rounded:0, Low:1, Hi:2, Packed:3, Byte:4}。
| Variant | base | +Transposed | +Masked | +Transp+Masked |
|---|---|---|---|---|
| Rounded | 0x20 | 0x28 | 0x30 | 0x38 |
| Low | 0x21 | 0x29 | 0x31 | 0x39 |
| Hi | 0x22 | 0x2a | 0x32 | 0x3a |
| Packed | 0x23 | 0x2b | 0x33 | 0x3b |
| Byte | 0x24 | 0x2c | 0x34 | 0x3c |
函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
PufferfishTensorCoreEmitter::EmitVectorLatch | sub_1410E1A0 | switch(glm) → PushGains* oneof;MXU0/MXU1 slot select |
Encode…0PushGainsRounded | sub_1EDC1660 | BitCopy opcode 0x20@abs91,mode @abs89,sub-op @abs83,operands |
19 个 sibling Encode…0PushGains* | sub_1EDC1920..sub_1EDC4E00 | per-variant opcode 0x21..0x3c @abs91 |
NOTE — Pufferfish 上没有 MSR bit。 PF 有单个 matrix-staging register,且
HasMsrOverrunChecks()为FALSE(见 latch assignment)。PushGainssub-encoders 不写 MSR-select 字段;PF codec 中没有任何带 MSR suffix 的 oneof types。MSR-A/MSR-B 区分是 Viperfish-only 功能。
Viperfish — Pushmatrix Oneof 和 MSR-Select Bit
目的
Viperfish(TPU v5p)把其 MXU0 opcode 打包进 @abs57-63 处的 7-bit 字段,latch 和 matmul 共享该字段。latch 只写 high 5 bits(@abs59)和 4-bit MatmulDataFormat(@abs51),留下 low two bits 作为 control;matmul 写入 @abs57 处的完整 7-bit opcode,并且其 LSB(@abs57)是 MSR-select bit。这是 MSR-A/MSR-B overrun handshake 的 bundle-level 编码——也是唯一拥有该 handshake 的 generation,与 HasMsrOverrunChecks 只在 Viperfish 上为 TRUE 一致。Viperfish 64-byte bundle 的 MXU slot map 位于 VF bundle 页面。
算法
latch sub-encoder(下面展示 bf16)通过一次 BitCopy 写入 opcode-high、format、两个 control bits、sub-fields 和 operands:
function EncodeTensorCoreVectorExtended0PushmatrixBf16(bundle, ve): // sub_1EFAF820
hi = 14; BitCopy(bundle, 59, &hi, 0, 5) // opcode-high @abs59 (5b) = 14 (bf16)
fmt = 3; BitCopy(bundle, 51, &fmt, 0, 4) // MatmulDataFormat @abs51 (4b) = 3 (Bf16)
if ve.has_sub0(): BitCopy(bundle, 48, &ve.sub0, 0, 3) // sub @abs48 (3b)
if ve.has_sub1(): BitCopy(bundle, 55, &ve.sub1, 0, 2) // sub @abs55 (2b)
if ve.has_msr(): BitCopy(bundle, 57, &ve.msr, 0, 1) // MSR-select @abs57 (1b), proto+0x20
if ve.has_ctl(): BitCopy(bundle, 58, &ve.ctl, 0, 1) // control @abs58 (1b), proto+0x24
// register operands @abs157/282/293/248/259/214/225/180 (6b each)matmul 侧证明了 MSR bit。U8 matmul 有两个 encoders,它们只在写到 @abs57 的 opcode value 上不同:
function Encode…0MatrixMultiplyU8LgmrMsra(bundle, ve): // sub_1EFA4A20
op = 2; BitCopy(bundle, 57, &op, 0, 7) // full 7-bit opcode = 2 ⇒ bit57 = 0 (MSR-A)
function Encode…0MatrixMultiplyU8LgmrMsrb(bundle, ve): // sub_1EFA4E00
op = 3; BitCopy(bundle, 57, &op, 0, 7) // full 7-bit opcode = 3 ⇒ bit57 = 1 (MSR-B)QUIRK — 在 Viperfish 上,latch dtype 搭载在 format 字段,而不是 opcode。 每个 non-masked
Pushmatrix*variant 都在@abs59写入 opcode-high14;data type 完全位于@abs51的 4-bitMatmulDataFormat中。只有 masked variants 会把 opcode-high 提升到 15..23。因此,一个读取@abs59来区分 bf16 和 int8 的 decoder 会看到两者都是14,必须查询@abs51。这与 JF slot 相反,后者在 opcode 字段中(部分)携带 dtype,且没有 format 字段。
每种 dtype 的 Pushmatrix opcode + format
| Oneof | op-high @abs59 | format @abs51 | Masked oneof | op-high @abs59 |
|---|---|---|---|---|
| PushmatrixRounded | 14 | 0 | RoundedMasked | 15 |
| PushmatrixPackedIf8Conv | 14 | 2 | PackedIf8ConvMasked | 17 |
| PushmatrixBf16 | 14 | 3 | Bf16Masked | 18 |
| PushmatrixBf8 | 14 | 4 | Bf8Masked | 19 |
| PushmatrixU8 | 14 | 5 | U8Masked | 20 |
| PushmatrixS8 | 14 | 6 | S8Masked | 21 |
| PushmatrixU4 | 14 | 7 | U4Masked | 22 |
| PushmatrixS4 | 14 | 8 | S4Masked | 23 |
MXU1(VectorExtended1)slot 是 MXU0 layout 下移 20 bits:opcode-high @abs39、format @abs31、MSR @abs37。
函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
Encode…0PushmatrixBf16 | sub_1EFAF820 | latch: op 14@abs59,fmt 3@abs51,MSR @abs57,ctl @abs58,operands |
15 个 sibling Encode…0Pushmatrix* | sub_1EFAE520..sub_1EFB2C40 | per-dtype op 14/masked 15..23,fmt 0..8 |
Encode…0MatrixMultiplyU8LgmrMsra | sub_1EFA4A20 | matmul opcode 2@abs57(bit57=0,MSR-A) |
Encode…0MatrixMultiplyU8LgmrMsrb | sub_1EFA4E00 | matmul opcode 3@abs57(bit57=1,MSR-B) |
NOTE — bit 57 把 encode 侧与 latch-assignment overrun gate 和 VF cost model 连接起来。 matmul 在
@abs57携带的 MSR-A/MSR-B 选择,正是 Viperfish cost model 以{Msr:2/6}计费的额外 reservation 所对应的 handshake(见 MatmulMode and Modifiers)。latch 的 MSR 字段(BYTE[op+0x44])决定 consuming matmul 读取哪个 bank,从而决定触发哪个…Msra/…Msrbmatmul encoder——也就是 bit 57 的值。SetLatchIndices只在 Viperfish wide formats 上分配的 first-latch index 是 scheduling-side decision;bit 57 是它的 bundle-level result。GOTCHA —
@abs58control bit 的角色未钉住。(LOW) VF latch 从 proto+0x24写入第二个 1-bit 字段@abs58,与@abs57的 MSR bit 相邻。它是独立的 per-latch control flag,但它是 arm overrun handshake、选择第二个 MSR,还是确认 transpose,尚未被隔离到命名字段。@abs57MSR-select 是 CONFIRMED(通过 matmul Msra/Msrb LSB);@abs58是 LOW。
Ghostlite / 6acc60406 — PushMatrix Oneof
目的
Ghostlite(v6e,glc)和 6acc60406(TPU7x,gfc)使用与 Viperfish 相同的 oneof-typed 形态,带有 PushMatrix*{,Masked} variants,并且也有 Msra/Msrb matmul family。两者的 dtype roster 不同:glc 将其 variants 命名为 {F32, Bf16, Bf8, If8, S4, S8, U4, U8},而 gfc 命名为 {F32, Bf16, E4m3, E5m2, …}——也就是说,gfc 上的 fp8 modes 按 encoding(E4m3/E5m2)而不是按 role(Bf8/If8)拼写。只有 bf16 latch opcode bit base 和 value 被逐 cell trace。
编码
| Gen | bf16 latch encoder | opcode bit / width | opcode value (bf16) |
|---|---|---|---|
Ghostlite (v6e, glc) | PushMatrixBf16 sub_1F33FE00 | @abs60 (6b) | 14 (0xe) |
6acc60406 (TPU7x, gfc) | PushMatrixBf16 sub_1F9A14E0 | @abs64 (6b) | 14 (0xe) |
dtype 是 oneof type;opcode value 是两者共享的 matpush constant 14。glc→gfc bit base 漂移 +4 bits。完整 latch-slot field roster(pred、format、MSR positions)在 opcode bit base 之外没有为任一 gen trace。opcode bit/value 之外为 MEDIUM。
Type-4 Gain-Register Guard
目的
在任何 latch op 被构建之前,CreateVectorLatchLsf 检查 gain source——feeding LSF latch 的 LloValue——是否产生 type 4(vector class)的 register。该检查是决定 latch 走 fast build path 还是记录 diagnostic 的 gate;它是 assignment-side guards 的 encode-pipeline analog,也是 latch 的 register_number(BYTE[op+0x0a])是 Vregno 的原因。
算法
function LloInstruction::CreateVectorLatchLsf(gain_src, glm, unit_id, region): // sub_1D4D7AA0
op_code = WORD[gain_src] // the gain SOURCE opcode
if (op_code >= 0x1CD) trap // bound 461 (ud1)
while (opcode_produced_register_type[op_code] != 4): // .data table @0x223a16c0
cf = new CheckFailer()
diag = make_unique<StatusWrapper>(cf, "chunk->ProducesVreg()",
{line 1073, llo_instruction.cc})
LloModule::UpdateStatus(region.module, diag) // slow diagnostic path
// loop re-checks after the status is recorded
if (glm > 0x33 || !bittest(0xF0000003C0C03, glm)):
LogFatal("LSF latch mode not expected.") // llo_instruction.cc:1089
op = LloInstruction::New(0x8d /*kVectorLatchLsf*/, {gain_src}, region)
set_latch_mode(op, glm) // BYTE[op+0x40] = glm
set_matrix_staging_register(op, 1) // BYTE[op+0x44] = 1 (LSF staging slot)
ValidateAndSetMxuAndSourceBus(unit_id, op) // WORD[op+0x0b] unit-id
return opopcode_produced_register_type(@0x223a16c0,.data;indices 0..460 的 461 个 one-byte entries——>= 0x1CD bound,位于 464-byte symbol span 内)按producer opcode 索引,并使用 taxonomy {0=none, 1=predicate, 2=scalar, 3=vector-mask, 4=vector}。latch opcodes 0x8d..0x96 自身映射到 type 0——它们没有 destination register;它们 push 到 systolic array 中。因此 guard 读取的是 gain source 的 entry,而不是 latch 的 entry。同样的 table-read 出现在 Matprep/IAR/Latch ISA 页面,该页把 matprep source guard 建模为 opcode_produced_register_type[source.opcode] == 4。
为什么是 register-type 4
Type 4 是 bulk vector register class。latch 加载到 MXU 中的 gain matrix 必须位于 vector register 中,因此 latch operand 的 producer 必须是 vector-producing op——load、scalar-to-vector、IAR read 等。如果 gain source 反而是 scalar(type 2)、mask(type 3)或 predicate(type 1)producer,guard 会通过 UpdateStatus(StatusWrapper slow path)记录 chunk->ProducesVreg() diagnostic,而不是直接 abort。
QUIRK — 该 guard 是 status-recording loop,不是 hard FATAL。 与其下方的 GLM-validity check(对 unexpected latch mode 调用
LogFatal)不同,type-4 mismatch path 会分配StatusWrapper、调用LloModule::UpdateStatus,然后在 loop 中重新检查——在 module 上记录 register-class diagnostic,同时仍继续构建 op。把 type-4 mismatch 变成 hard error 的重新实现,会偏离 binary 更柔和的 status-accumulating 行为。
函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
LloInstruction::CreateVectorLatchLsf | sub_1D4D7AA0 | gain-source bound、type-4 guard、slow path、field stamp |
opcode_produced_register_type | @0x223a16c0 (.data) | 461-entry(每项 1 byte)producer→reg-type table;4=vector |
set_latch_mode | sub_1D4D7C20 | BYTE[op+0x40] = glm |
set_matrix_staging_register | sub_1D4D7D40 | MSR opcode-mux: latch→+0x44,matmul→+0x46,load-LMR→+0x42,dwg→+0x41 |
set_latch_index_in_sequence | sub_1D4E7960 | WORD[op+0x42] = index,bound ≤ 0xFFFF |
NOTE — latch_index 字段不是 per-latch bundle bit。
set_latch_index_in_sequence(sub_1D4E7960)把分配的 ordinal 存到WORD[op+0x42](在llo_instruction.cc:3399重新检查LloOpcodeIsVectorLatch,并在:3400boundindex <= 65535),但没有 encoder 把该 ordinal blit 到 bundle 中。index 是 encode 前消费的scheduling artifact:在 Viperfish 上,first-latch index gate overrun handshake 是否触发,而它在 bundle 中只间接表现为 MSR-select bit@abs57。wire bundle 携带 opcode、format、unit-id 和 MSR——从不携带 sequence index。
LLO → Proto → Bundle 字段对应关系
对于一个 latch op,三种表示对齐如下:
| LLO field(assignment side) | Proto field | Bundle bit(per gen) |
|---|---|---|
GainLatchMode BYTE[op+0x40] | VEopcode / oneof type | JF opcode @abs29-34; PF op @abs91; VF op @abs59 + fmt @abs51; GXC op @abs60(glc)/@abs64(gfc) |
unit-id/GMR WORD[op+0x0b] bits 8–9 | mxu-num(JF proto +0x70) | JF @abs27-28 |
MSR BYTE[op+0x44] | Msra/Msrb oneof(VF) | VF @abs57(MSR-select);PF/JF: none |
register_number BYTE[op+0x0a] | gain-source Vregno | type-4-guarded vector reg selector |
latch_index_in_sequence WORD[op+0x42] | (assignment only) | 不是 per-latch bundle bit;间接 gate VF @abs57 |
相关组件
| 组件 | 关系 |
|---|---|
| Latch Assignment & Overrun | 填写 BYTE[op+0x40]/WORD[op+0x42] 的 assignment pass;本页是它的 encode-side consumer |
| MxuSequence / SequenceInfo | latches 在此编码的 sequence record |
| Matprep, IAR, and Latch Sub-Slots | latch-op LLO field layout,以及来自 ISA side 的 matprep type-4 source guard |
| MXU Slot | latched gains feed 的 systolic-array matmul slot |
| Viperfish 64-Byte Bundle | 本页拆分其 MXU0 7-bit opcode 字段 @abs57-63 的 VF bundle |
| Pufferfish 51-Byte Bundle | MXU0 slot 持有 PushGains opcode @abs91 的 PF bundle |
| Jellyfish 41-Byte Bundle | VectorExtended slot 持有 latch opcode @abs29-34 的 JF bundle |
交叉引用
- Latch Assignment & Overrun — 分配本页序列化的 latch indices 和 GLMs;
BYTE[op+0x40]、WORD[op+0x42]、BYTE[op+0x44]的来源。 - MxuSequence / SequenceInfo — 产生 latch ops 的
MxuSequencerecord 和LatchLhspartition。 - Matprep, IAR, and Latch Sub-Slots — latch-op
LloInstructionfield offsets,以及 matprepopcode_produced_register_type == 4source guard,即此处 gain guard 的 sibling。 - MXU Slot — matmul op family,其 Msra/Msrb encoders 设置 VF
@abs57MSR-select bit。 - Viperfish 64-Byte Bundle — 本页 latch fields 所占用的 VF MXU0/MXU1 slot map。
- Pufferfish 51-Byte Bundle — PF MXU0 slot map;
PushGainsopcode@abs91。 - Jellyfish 41-Byte Bundle — JF VectorExtended slot map;latch opcode-field
@abs29-34。 - Route-Cache Codec — 与本页 writer primitives 共享的
gloopBitDecoder/BitEncoderbit-codec 工具包的 reader 半边。 - MatmulMode and Modifiers — Viperfish
{Msr:2/6}overrun-check reservation;此处解码的 MSR-select bit 的 cost-side consumer。 - Binary:
extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d) - Index entry: Part VIII — Instruction Scheduling & Bundle Packing — 返回索引