VEX 操作数端口绑定
本页中的每个 enum 值、switch case、proto 偏移、present-mask bit 和 bundle 位位置,都逐字节读取自
libtpu-0.0.40-cp314wheel 中的libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d,buildlibtpu_lts_20260413_b_RC00)—— 来源包括GetVexSourcePortEncodingswitch(glc/Ghostlite0x1c5ee280,vfc0x1c5d2e80)、FindAndEmitToUnusedPort7 端口分配器函数体(glc0x13a4b840)、逐操作SparseCoreTecVectorExtendedEncoderBitCopy立即数,以及EmitXrfResultOpPopXrf 结果提交(glc0x13a14180,gfc0x13ab8c60)。地址适用于此 build;其他版本会不同。全文中:glc= Ghostlite,gfc= 6acc60406/TPU7x,vfc= Viperfish。
摘要
本页负责 SparseCore VectorExtended (VEX) scan/sort 引擎的操作数到端口层:VEX 操作的向量操作数如何绑定到读端口,以及这些绑定落在 64 字节 TEC bundle 的何处。这里有两个不同的端口空间,把它们混为一谈是最常见的建模错误:
- 前端使用的逻辑
VregReadPort(0..9)—— 由GetVexSourcePortEncoding解析为 VEX scan 的SourceOne/seed 选择器所携带的 8 值VexSourcePortEncoding。这就是页面标题中的源端口编码:合法源端口为0..7共 8 个,端口8(V3_X)和端口9(MISC_AUX)对 VEX 明确拒绝。 - 编码器使用的物理
SparsecoreVregReadPort(V0..V6)—— 由FindAndEmitToUnusedPort按贪心最低空闲策略分配,并 BitCopy 到 VEX bundle word 中 7 个分散的 6 位选择器字段。
决定性的结构事实是:源端口编码是一个返回 StatusOr 的解析器 switch(不是表),因此重新实现者必须复制精确合法集合 {0..7} 和两个拒绝情形;物理分配器与操作无关且与代际无关(逐操作类型只改变错误字符串);结果提交操作(PopXrf)通过逐 opcode 常量索引选择其写组,并通过操作数存在模式选择其写入 lane 子集,绝不是反过来。
对重新实现而言,契约如下:
VexSourcePortEncoding是 8 个值0..7,是合法VregReadPort的 1:1 映像。GetVexSourcePortEncoding(port)对port ∈ [0,7]返回编码== port且状态为OK,对port == 8(V3_X)和port == 9(MISC_AUX)返回InvalidArgument。enum 名称把 V0..V3 的Y_VREG/X子端口与 0 处的VST_SOURCE总线交错排列(见 §8 个源端口编码)。- 物理操作数分配器是 7 端口贪心最低空闲 btree set。
FindAndEmitToUnusedPort弹出最低的空闲SparsecoreVregReadPort ∈ [0,6],从port_is_free中删除它,把操作数的 vregno 写入 proto 槽+0x1c+4*p,并在 proto+0x10中设置 present-mask bit1<<(p+1)。其函数体在每个 VEX 操作和各代之间逐字节相同。 - 7 个物理端口选择器占据非连续的 6 位 bundle 字段。
SparseCoreTecVectorExtendedEncoder将每个 present proto 槽 BitCopy 到其绝对 bundle bit:V0→0x15a,V1→0x1bb,V2→0x1c7,V3→0x196,V4→0x1a2,V5→0x171,V6→0x17d。此布局与操作无关(逐操作子操作码和 mask/dest-port bit 位于 VEX 掩码 / 目标端口 / 子操作码)。 PopXrf(pop vector-register-file)提交 VEX 结果:index = 写组,presence = lane 子集。EmitXrfResultOp门控index < 3(vres_unit ∈ {0,1,2}),从逐 opcode 常量取得写组索引,并纯粹根据三个结果操作数的isVoidOppresence 选择WriteAll/Partial0..4—— 每个 present lane 被提交为 vreg(GetVregno)或 vector-mask-dest(GetVMDestregno)。
| 层 | 64 字节 TEC bundle 中 VEX 槽的 operand→port 绑定 |
| 逻辑端口空间 | VregReadPort 0..9 → VexSourcePortEncoding 0..7(解析器,8 个合法值) |
| 解析器 | GetVexSourcePortEncoding — glc 0x1c5ee280,vfc 0x1c5d2e80(StatusOr,switch) |
| 物理端口空间 | SparsecoreVregReadPort V0..V6(7 个端口) |
| 分配器 | FindAndEmitToUnusedPort — 贪心最低空闲 btree set;glc 0x13a4b840,gfc 0x13ab2aa0 |
| Proto 槽映射 | port p → vregno [proto+0x1c+4*p],present bit 1<<(p+1) in [proto+0x10] |
| Bundle 选择器 bits | V0=0x15a V1=0x1bb V2=0x1c7 V3=0x196 V4=0x1a2 V5=0x171 V6=0x17d(各 6 位) |
| 结果提交 | PopXrf via EmitXrfResultOp(glc 0x13a14180,gfc 0x13ab8c60,vfc 0x139a8240);index∈{0,1,2},oneof tags 7..0xc |
| 置信度 | CONFIRMED(由反编译锚定),除非某行或说明另有标注 |
注意 —— 本页负责 operand→port 绑定(两个端口空间、分配器、源端口编码、PopXrf)。 VEX opcode→op dispatch 清单在 VectorExtended (VEX);逐操作子操作码常量、目标读端口字段和向量掩码字段在 VEX 掩码 / 目标端口 / 子操作码;64 字节 bundle 几何在 TEC Engine。它们相互链接,不在这里重复。
两个端口空间,一个数据通路
如果重新实现者只构建一个“端口”抽象,就会误解 VEX。二进制保留了两个:
logical (front-end) physical (encoder)
──────────────────── ──────────────────
VregReadPort 0..9 SparsecoreVregReadPort V0..V6 (0..6)
│ │
│ GetVexSourcePortEncoding (resolver) │ FindAndEmitToUnusedPort (allocator)
▼ ▼
VexSourcePortEncoding 0..7 proto slot +0x1c+4*p (present 1<<(p+1) @ +0x10)
(the scan's SourceOne / seed selector) │
│ SparseCoreTecVectorExtendedEncoder (BitCopy)
▼
bundle 6-bit selector @ {0x15a,0x1bb,0x1c7,0x196,0x1a2,0x171,0x17d}
```text
- **逻辑**空间是 `SourceOne` 风格选择器引用的对象:“把 scan 的 seed/carry-in 从*这个*源总线送入。”它的合法映像是 8 个 `VexSourcePortEncoding` 值。两个 `VregReadPort` 值 —— `V3_X`(8)和 `MISC_AUX`(9)—— 不能作为 VEX 源编码到达,并且由解析器硬拒绝。
- **物理**空间是逐 bundle 读端口分配:编码器在 VEX bundle word 中有 7 个物理操作数选择器字段(V0..V6),`FindAndEmitToUnusedPort` 为每个发出的操作数分配最低的仍空闲端口。落入选择器的是操作数的 vregno(0..0x3f),不是源总线代码。
二者的连接是:逻辑 `VexSourcePortEncoding` 命名 seed 从中读取的*总线*;物理 V0..V6 选择器携带 bundle 每周期读取的 *vregnos*。bundle 中携带的 [SourceOne seed](vectorload-slot.md#the-sourceone-seed-enum) 是逻辑编码;它分配的操作数读取是物理选择器。
---
## 源端口编码(`VexSourcePortEncoding`)
### 解析器
`GetVexSourcePortEncoding(VregReadPort)` 是逐目标自由函数,返回 `StatusOr<VexSourcePortEncoding>`(这里是 `absl::internal_statusor::Helper*`,int payload 在 `+0x8`,status word 在 `+0x0`)。glc(Ghostlite)实例位于 `0x1c5ee280`,vfc(Viperfish)实例位于 `0x1c5d2e80`,二者的 switch 形状逐字节相同;只有错误字符串后缀和源文件路径不同:
```c
// xla::ghostlite::GhostliteProtoUtils::GetVexSourcePortEncoding(VregReadPort port) (glc/Ghostlite 0x1c5ee280)
// this = StatusOr<VexSourcePortEncoding> out; *(int*)(this+8) = encoding; *(qword)this = 1 (=OK)
switch (port) {
case 0: *(int*)(this+8) = 0; *(qword)this = 1; return this; // VST_SOURCE → OK
case 1: *(int*)(this+8) = 1; goto ok; // V0_Y_VREG
case 2: *(int*)(this+8) = 2; goto ok; // V0_X
case 3: *(int*)(this+8) = 3; goto ok; // V1_Y_VREG
case 4: *(int*)(this+8) = 4; goto ok; // V1_X
case 5: *(int*)(this+8) = 5; goto ok; // V2_Y_VREG
case 6: *(int*)(this+8) = 6; goto ok; // V2_X
case 7: *(int*)(this+8) = 7; ok: *(qword)this = 1; return this; // V3_Y_VREG → OK
case 8: /* InvalidArgument: "The V3_X slot (port number 8) cannot be used by a VEX instruction." (len 66) */
case 9: /* InvalidArgument: "MISC_AUX not supported on GLC" (len 29) */
}合法范围精确为 [0,7]:解析器对这八个值返回 identity encoding(encoding == port)和 OK,并对 8 和 9 构造 InvalidArgument Status。编码就是值本身,不是表索引 —— 重新实现者必须硬编码 {0..7}→{0..7} identity 和两个拒绝消息,而不是推导它们。
陷阱 —— 解析器在
[0,7]上是 identity,但不是 no-op。 它存在是为了验证请求的逻辑读端口是否为合法 VEX 源:V3_X(端口 8)和MISC_AUX(端口 9)在其他地方是有效VregReadPort值,但无法从 VEX seed 选择器到达。跳过验证会让非法SourceOne到达编码器,而编码器会静默误打包。检查才是门;应把它重新实现为返回Result/StatusOr的 switch,而不是encoding = port。注意 —— Ghostlite 的消息写作 “GLC”,Viperfish 的消息写作 “VFC”。
MISC_AUX not supported on …消息按代命名:0x1c5ee280处的 Ghostlite 副本写作 “MISC_AUX not supported on GLC”(29 字节),0x1c5d2e80处的 Viperfish 副本写作 “MISC_AUX not supported on VFC”(29 字节),各自指向自己的…_proto_utils.cc。V3_X消息(66 字节)在二者中完全相同。合法{0..7}集合和两个拒绝端口跨代稳定;消息字符串是唯一差异。二代均 CONFIRMED。
8 个源端口编码 {#the-8-source-port-encodings}
该编码把向量操作数 V0..V3 的 Y_VREG 和 X 子端口交错排列,并把 VST(vector-store)源总线放在 0。与 VectorLoad SourceOne seed 交叉确认,后者在 bundle 中携带同一个 enum:
| value | enum name | source bus |
|---|---|---|
| 0 | VEX_SOURCE_PORT_ENCODING_VST_SOURCE | VST(vector-store)源总线 |
| 1 | VEX_SOURCE_PORT_ENCODING_V0_Y_VREG | V0 操作数,Y_VREG 子端口 |
| 2 | VEX_SOURCE_PORT_ENCODING_V0_X | V0 操作数,X 子端口 |
| 3 | VEX_SOURCE_PORT_ENCODING_V1_Y_VREG | V1 操作数,Y_VREG 子端口 |
| 4 | VEX_SOURCE_PORT_ENCODING_V1_X | V1 操作数,X 子端口 |
| 5 | VEX_SOURCE_PORT_ENCODING_V2_Y_VREG | V2 操作数,Y_VREG 子端口 |
| 6 | VEX_SOURCE_PORT_ENCODING_V2_X | V2 操作数,X 子端口 |
| 7 | VEX_SOURCE_PORT_ENCODING_V3_Y_VREG | V3 操作数,Y_VREG 子端口 |
| — | (V3_X, logical port 8) | 拒绝:“cannot be used by a VEX instruction” |
| — | (MISC_AUX, logical port 9) | 拒绝:“not supported on GLC/VFC” |
拒绝 V3_X(本应为编码 8)正是合法集合止于 7 的原因:V3 只把它的 Y_VREG 子端口贡献给 VEX 源,绝不会贡献 X。因此四个 V 对提供 {V0_Y, V0_X, V1_Y, V1_X, V2_Y, V2_X, V3_Y} —— 七个 V 源加上 VST_SOURCE,总计 8 个编码。
怪癖 ——
V3_X作为VregReadPort存在,但在结构上不能作为 VEX 源。 如果重新实现者把 V 子端口枚举为规则的{V0..V3} × {X,Y_VREG}(8 格)乘积,就会产生一个非法格。硬件/ISA 明确把它剔除:第七个 V 源是V3_Y_VREG,而V3_X会抛出InvalidArgument。为什么 V3 相比微架构原因呈现不对称(只有Y_VREG)为HIGH—— 结构由解析器确认,硅层理由为推断。
物理端口分配器(FindAndEmitToUnusedPort)
在 btree set 上贪心最低空闲
物理读端口分配是一个模板 FindAndEmitToUnusedPort<SparsecoreVregReadPort, SparseCoreTecVectorExtended_<Op>>,每个 VEX 操作实例化一次。每个实例都有逐字节相同的函数体 —— 只有重定位后的分支目标和逐操作错误字符串模板参数不同(已验证 MinScanU32 0x13a4b840 与 SegmentedAddScanF32/MaxScanU32 兄弟实例)。反编译的 MinScanU32 实例:
// FindAndEmitToUnusedPort<…SparsecoreVregReadPort, …VectorExtended_MinScanU32> (glc 0x13a4b840)
// a1 = StatusOr<port>* out ; a2 = &port_is_free (btree_set) ; a3 = vregno ; a4 = proto submessage (DWORD*)
if ( *(qword*)(a2 + 16) ) { // RetCheck !port_is_free.empty()
port = *(int*)(**(qword**)a2 + 12); // btree FIRST node, [node+0xc] = LOWEST free port
btree_container::erase(a2, &port); // greedy: take it, remove from free set
switch (port) { // port ∈ [0,6] (else "Unsupported Port Value")
case 0: a4[7] = vregno; mask = 0x02; break; // a4[7]=+0x1c bit1
case 1: a4[8] = vregno; mask = 0x04; break; // a4[8]=+0x20 bit2
case 2: a4[9] = vregno; mask = 0x08; break; // a4[9]=+0x24 bit3
case 3: a4[10] = vregno; mask = 0x10; break; // a4[10]=+0x28 bit4
case 4: a4[11] = vregno; mask = 0x20; break; // a4[11]=+0x2c bit5
case 5: a4[12] = vregno; mask = 0x40; break; // a4[12]=+0x30 bit6
case 6: a4[13] = vregno; mask = 0x80; break; // a4[13]=+0x34 bit7
default: /* MakeError "Unsupported Port Value: $0" (isa_emitter_base.h:2664) */
}
a4[4] |= mask; // a4[4]=+0x10 present-mask: set bit 1<<(port+1)
*(int*)(a1 + 8) = port; // StatusOr payload = allocated port index
*(qword*)a1 = 1; // status = OK
} else {
// RetCheckFailSlowPath(…:2637, "!port_is_free.empty()")
}
```text
机制逐步如下:
1. **空检查。** `port_is_free` 是 `absl::btree_set<SparsecoreVregReadPort>`;`[a2+0x10]` 是其 size。空 → `isa_emitter_base.h:2637` 处的 `RetCheck` 失败(`"!port_is_free.empty()"`)。
2. **最低空闲端口。** `*(**a2 + 0xc)` 读取 btree 第一个(最左)节点中的值 —— **最低**空闲端口索引。这就是贪心最低空闲策略:端口按升序 V0→V6 发放。
3. **删除。** `btree_container::erase` 移除所选端口,使下一个操作数不能在同一个 bundle 内复用它。
4. **边界 + 槽写入。** 7 臂 switch 分派 `[0,6]`;越界 → `MakeError "Unsupported Port Value: $0"`(`isa_emitter_base.h:2664`)。对有效端口 `p`,操作数的 vregno 存到 `[proto+0x1c+4*p]`,present bit `1<<(p+1)` OR 到 `[proto+0x10]`。
5. **返回。** 已分配端口索引以 `OK` 状态返回到 `StatusOr` payload(`[out+0x8]`)。
### Proto 端口槽映射
| physical port | proto slot (vregno) | present-mask bit (`[proto+0x10]`) |
|---|---|---|
| V0 | `+0x1c` | `0x02` (bit 1) |
| V1 | `+0x20` | `0x04` (bit 2) |
| V2 | `+0x24` | `0x08` (bit 3) |
| V3 | `+0x28` | `0x10` (bit 4) |
| V4 | `+0x2c` | `0x20` (bit 5) |
| V5 | `+0x30` | `0x40` (bit 6) |
| V6 | `+0x34` | `0x80` (bit 7) |
| (dest read-port) | `+0x18` | `0x01` (bit 0) |
Present-mask bit 0(`0x01`)是 `+0x18` 处的**目标读端口**槽(哪个 V-port 持有操作结果),由逐操作 emit body 写入,而不是由此分配器写入。七个源端口占据 bits 1..7。这是 proto-message 表示;编码器把每个 present 槽映射到 bundle(下一节)。
> **注意 —— 分配器与代际无关。** gfc 副本 `utils::FindAndEmitToUnusedPort`(`0x13ab2aa0`)结构相同:同样的空检查、同样的 `[node+0xc]` 最低端口读取、同样的 `erase`、同样的 `[0,6]` 边界、同样的 `+0x1c`/`0x2` 端口 0 分支。7 端口计数、proto 偏移和 present-mask bit 分配均跨代稳定。*btree free-set 生命周期* —— 谁在 bundle 起始插入 V0..V6,以及目标读端口如何相对源端口被选择 —— 为 `LOW`:此分配器只*消费* free set。
### 调用者的操作数读取
分配前,逐操作 emit body 从 `MCInst` 中提取操作数寄存器。操作数位于 `[MCInst+0x10]`,步长 `0x10`;helper 验证寄存器区间并重基 id:
| helper | reg-id band | rebase | yields |
|---|---|---|---|
| `GetVregno` (`0x13a659c0`) | `[0xd0, 0x10f]` | `id − 0xd0` | vregno 0..0x3f(64 个 vregs) |
| `GetVectorMask` (`0x13a33320`) | `[0x5f, 0x7e]` | `id − 0x5f` | mask 0..0x1f(32 个 vmasks) |
| `GetVMDestregno` (`0x13a65b20`) | `[0x5f, 0x6e]` | `id − 0x5f` | VM-dest 0..0xf(16) |
如果 `MCOperand` 不是寄存器(kind byte `!= 1`),`GetVregno` 会 LogFatal;reg-id 位于操作数 `+0x8`。向量掩码(存在时)存到单独的 proto 字段并 OR 到 mask-present bit;`FindAndEmitToUnusedPort` 为其分配端口的是源 vregno。
---
## Bundle 选择器位布局
Proto 端口槽由逐操作 `SparseCoreTecVectorExtendedEncoder<Op>`(`Encode` at `0x1eb30ee0` → per-op `EncodeSparseCoreTecVectorExtended<Op>` at `0x1eb32000+`)向下一层映射到绝对 TEC-VEX-bundle bit 位置。每个 present proto 字段都由 `BitCopy(dst=bundle, dst_bit, src=&proto_field, src_bit=0, nbits)`(`0x1fa0a900`)复制,并由 `[proto+0x10]` present-mask 和 `cmp [proto+0x50],<oneof-tag>` union guard 门控。7 个源端口选择器位置**与操作无关**(已验证 `AddScanF32` `0x1eb32380` sub-op `0x05` 和 `MaxScanF32` `0x1eb32a80` sub-op `0x07` 相同):
```text
TEC-VEX bundle — the 7 physical port selectors (6-bit each), NON-contiguous
proto slot present bit bundle dst_bit nbits field
────────── ─────────── ────────────── ───── ─────────────────────
+0x1c 0x02 0x15a 6 V0 read-port vregno
+0x20 0x04 0x1bb 6 V1 read-port vregno
+0x24 0x08 0x1c7 6 V2 read-port vregno
+0x28 0x10 0x196 6 V3 read-port vregno
+0x2c 0x20 0x1a2 6 V4 read-port vregno
+0x30 0x40 0x171 6 V5 read-port vregno
+0x34 0x80 0x17d 6 V6 read-port vregno这些字段是分散的(0x15a, 0x1bb, 0x1c7, 0x196, 0x1a2, 0x171, 0x17d —— 非升序)—— 这是 VEX 槽的物理操作数选择器布局,不是打包数组。重新实现者必须使用每个端口的绝对 bit,绝不能使用 base + 6*p。
注意 —— 目标读端口、向量掩码和逐操作子操作码共享此编码器,但记录在别处。 同一个
SparseCoreTecVectorExtendedEncoder还会 BitCopy 目标读端口(+0x18→ bundle0x10c,3 位)、向量掩码(+0x38→ bundle0x104,5 位)以及逐操作 6 位子操作码常量(bundle0x10f)。这三个字段和完整子操作码清单由 VEX 掩码 / 目标端口 / 子操作码 负责;本页只负责 7 个源端口选择器。它们共享编码器,而不是共享页面。怪癖 —— 同一个物理子操作码
0x1b在 VEX 外被复用。 子操作码0x1b(十进制 27)在 VEX 槽中是UniquifyFloat(已验证:EncodeSparseCoreTecVectorExtendedUniquifyFloatglc0x1eb3c580把27写入 bundle bit0x10f处的 6 位子操作码字段)。combine_four_lanescross-lane fold 是 gxc-familyVectorAlu操作(存在于 glc/gfc,在 vfc/Viperfish 上缺失);其 opcode 值和位布局在 VectorAlu opcode pages 上,不在这里。其源操作数是否也通过同一个FindAndEmitToUnusedPort分配器路由尚未追踪 ——LOW。
PopXrf —— VEX 结果提交
PopXrf 是什么
PopXrf(pop vector-register-file)是 VectorResult 槽中用于 VEX scan/sort 结果的提交操作:它从 extended-result file(XRF)拉取结果,并写回 VRF 中最多三个结果 lane。它由 EmitXrfResultOp 发出,一个模板覆盖全部六种提交变体 {WriteAll, Partial0, Partial1, Partial2, Partial3, Partial4}。glc(Ghostlite)实例位于 0x13a14180;gfc(6acc60406)实例是 0x13ab8c60 处的结构孪生,vfc(Viperfish)实例位于 0x139a8240:
// EmitXrfResultOp<…PopXrfWriteAll, …Partial0..4, …VectorResult> (glc/Ghostlite 0x13a14180)
// a1 = MCInst* ; a2 = index (vres_unit) ; a3 = VectorResult& slot proto
if ( (unsigned)a2 >= 3 ) // RetCheck: vres_unit ∈ {0,1,2}
return RetCheckFail(…:3171, "vres_unit == 0 || vres_unit == 1 || vres_unit == 2");
// the 3 result operands op0/op1/op2 (MCInst operand stride 0x10), each tested for void:
v0 = isVoidOp(&op0, a2); v1 = isVoidOp(&op1, a2); v2 = isVoidOp(&op2, a2);
// the present/void pattern of (op0,op1,op2) selects the oneof variant (tags 7..0xc);
// each PRESENT lane is committed: GetVregno → +0x1c/+0x20, GetVMDestregno → +0x24/+0x20;
// index (a2) → proto +0x18 with present bit 0x01.
```text
### Index = 写组,presence = lane 子集
两个独立选择器控制 PopXrf,二者不可互换:
1. **`index`(`vres_unit`)是 XRF 写组选择器 —— 逐 OPCODE 常量。** 它*不是*从操作数派生的:消费分支会硬编码它。Opcode `0x10E9` 在断言 XRF 寄存器操作数 `getReg() == llvm::TPU::XRF0`(reg-id `0x150`,从 operand `+0x38` 读取;RetCheck `isa_emitter.cc:10302`)之后传入 `index = 0`;opcode `0x10EA` 在断言 `getReg() == llvm::TPU::XRF1`(reg-id `0x151`;RetCheck `:10318`)之后传入 `index = 1`。`index < 3` RetCheck 允许 `2`,但 glc 只接线 `{0,1}`(恰好两个 call sites,均在 `ghostlite::ConsumeOneTecBundleInstruction` glc `0x13a08e00` 中)。该 index 落在 `proto+0x18`,present bit 为 `0x01`。
2. **变体(写入哪些 lane)由三个结果操作数的 `isVoidOp` presence pattern 选择。** `isVoidOp`(`0x13a659a0`)对 void 操作数返回 1。`(op0,op1,op2)` 的模式选择 oneof tag 和被写入字段:
每一行中 index(`vres_unit`)都写入 `+0x18` 且 present bit 为 `0x1`;下列行只列出逐 lane 结果字段。每个 lane 的 present 操作数就是被写入的操作数 —— 注意在 `V P …` 行中,*第一个 present lane 是 op1*,因此其 vregno 落在 `+0x1c` 字段(字段偏移固定;源操作数是 present 的那个 lane):
| op0 | op1 | op2 | variant | oneof tag (`[proto+0x50]`) | result fields written (source operand → proto field, present bit) |
|:---:|:---:|:---:|---|---:|---|
| P | P | P | `WriteAll` | `0x7` | op0→`+0x1c` `GetVregno` (0x2), op1→`+0x20` `GetVregno` (0x4), op2→`+0x24` `GetVMDestregno` (0x8) |
| P | V | V | `PopXrfWritePartial0` | `0x8` | op0→`+0x1c` `GetVregno` (0x2) |
| P | V | P | `PopXrfWritePartial1` | `0x9` | op0→`+0x1c` `GetVregno` (0x2), op2→`+0x20` `GetVMDestregno` (0x4) |
| V | P | V | `PopXrfWritePartial2` | `0xa` | op1→`+0x1c` `GetVregno` (0x2) |
| V | P | P | `PopXrfWritePartial3` | `0xb` | op1→`+0x1c` `GetVregno` (0x2), op2→`+0x20` `GetVMDestregno` (0x4) |
| P | P | V | `PopXrfWritePartial4` | `0xc` | op0→`+0x1c` `GetVregno` (0x2), op1→`+0x20` `GetVregno` (0x4) |
| (any other) | | | — | — | `MakeError "Invalid operands for Pop XRF Result." (len 36, isa_emitter_base.h:3239)` |
Proto present-mask `[proto+0x10]` bits 为:bit0(`0x1`)= index/`+0x18`,bit1(`0x2`)= `+0x1c`,bit2(`0x4`)= `+0x20`,bit3(`0x8`)= `+0x24`。`WriteAll` 是完整 3-lane 提交(两个 vregs + 一个 VM-dest);`Partial*` 变体是由操作数 presence 决定的子集。每个结果 oneof 子消息在 `[proto+0x50]` tag 尚不匹配时被惰性 `DefaultConstruct` 到 `SparseCoreTecVectorResult` union 中。
> **陷阱 —— partial-write 的 “index” 是写组,不是 lane index。** 如果重新实现者把 `index` 理解为“哪个结果 lane”,就会错误建模 PopXrf:lane 子集来自操作数 presence,而 index 是 XRF 分区(写组 0 或 1)。操作名(`WriteAll`/`Partial0..4`)来源于 *presence pattern*,绝不来源于 `index`。把 `index` 和 variant 当作两个正交选择器。
>
> **注意 —— PopXrf index 值 2 有代码路径,但没有 glc caller。** `index < 3` RetCheck 允许 `vres_unit == 2`(第三个 XRF 写组),但 glc 的 TEC 编排只接线 opcodes `0x10e9`→0 和 `0x10ea`→1。vfc/gfc 是否接线第三个 PopXrf opcode(index 2)—— 即这些代上的 XRF 是否有 2 或 3 个写组 —— 尚未追踪。index-2 可到达性为 `LOW`;glc 接线集合 `{0,1}` 为 CONFIRMED。
---
## 三层 Emit 组合
operand→port 绑定横跨三层;本页负责第 2 和第 3 层(源端口一半)以及结果提交:
```text
VEX op emit — operand→port→bundle, three layers
1. DISPATCH ConsumeOneTecVexBundleInstruction opcode → op leaf (vectorextended-vex)
2. READ+ALLOC per-op emit body MCInst operands →
+ FindAndEmitToUnusedPort proto slot +0x1c+4*p ◄── THIS PAGE
present-mask +0x10
3. PACK SparseCoreTecVectorExtendedEncoder proto slot → bundle bit
+ BitCopy V0..V6 @ scattered 6b ◄── THIS PAGE
(+ sub-op/dest/mask) (vex-mask-destport-subopcode)
── result ──
COMMIT EmitXrfResultOp (PopXrf) index = write-group,
presence = lane subset ◄── THIS PAGE第 1 层(dispatch)选择操作 leaf,属于 VectorExtended 清单的职责。第 2 层读取每个 MCInst 操作数并为它分配物理读端口(本页)。第 3 层把每个 present proto 槽打包到其绝对 bundle bit(本页负责 7 个源选择器;sub-opcode/dest/mask 属于兄弟页面)。结果提交(PopXrf)是通过 VectorResult 槽进行的对称写回。
函数映射
| Symbol | Address | Role |
|---|---|---|
xla::ghostlite::GhostliteProtoUtils::GetVexSourcePortEncoding | glc 0x1c5ee280 | VregReadPort→VexSourcePortEncoding;identity [0,7],拒绝 8/9 |
xla::viperfish::ViperfishProtoUtils::GetVexSourcePortEncoding | vfc 0x1c5d2e80 | 同一 switch;“MISC_AUX not supported on VFC” |
…isa_emitter::FindAndEmitToUnusedPort<…SparsecoreVregReadPort,…VectorExtended_MinScanU32> | glc 0x13a4b840 | 7 端口贪心最低空闲分配器(代表实例) |
…isa_emitter::utils::FindAndEmitToUnusedPort<…> | gfc 0x13ab2aa0 | 代际相同分配器(结构孪生) |
…container_internal::btree_container<…>::erase | 0x13a0dd60 | 从 port_is_free 移除已分配端口 |
SparseCoreTecVectorExtendedEncoder::Encode | 0x1eb30ee0 | 逐操作编码器 dispatch(调用逐操作 Encode…<Op>) |
EncodeSparseCoreTecVectorExtendedAddScanF32 | 0x1eb32380 | sub-op 0x05;V0..V6 选择器 bit 锚点 |
EncodeSparseCoreTecVectorExtendedMaxScanF32 | 0x1eb32a80 | sub-op 0x07;相同端口 bits(op-invariant 证明) |
BitCopy(dst,dst_bit,src,src_bit,nbits) | 0x1fa0a900 | 逐字段 bit-pack 原语 |
…isa_emitter::EmitXrfResultOp<…PopXrfWriteAll,…Partial0..4,…VectorResult> | glc 0x13a14180 | PopXrf 结果提交(Ghostlite);index + presence 选择 |
EmitXrfResultOp<…> (gfc) | gfc 0x13ab8c60 | 6acc60406 PopXrf 实例(结构孪生) |
EmitXrfResultOp<…> (vfc) | vfc 0x139a8240 | Viperfish PopXrf 实例 |
…isa_emitter::isVoidOp | 0x13a659a0 | 驱动 variant select 的逐操作数 void 测试 |
…isa_emitter::GetVregno | 0x13a659c0 | reg band [0xd0,0x10f] → vregno id−0xd0 |
…isa_emitter::GetVectorMask | 0x13a33320 | reg band [0x5f,0x7e] → mask id−0x5f |
…isa_emitter::GetVMDestregno | 0x13a65b20 | reg band [0x5f,0x6e] → VM-dest id−0x5f |
注意事项
- 保持两个端口空间分离。
VregReadPort/VexSourcePortEncoding(逻辑,8 个源编码)是 seed/source 选择器;SparsecoreVregReadPort(物理,V0..V6)是逐 bundle 操作数分配。单一“端口”抽象会误解码 —— 逻辑编码命名总线,物理选择器携带 vregno。 - 将解析器复制为验证 switch。
GetVexSourcePortEncoding在[0,7]上是 identity,但必须拒绝V3_X(8)和MISC_AUX(9)。把它实现为返回Result/StatusOr的 switch,并带有两个拒绝消息,而不是encoding = port。 - 分配器是贪心最低空闲且与操作无关。 最低空闲 V0..V6 获胜;函数体在各操作和各代之间逐字节相同。把逐 bundle free set 建模为 btree/sorted set,并在分配时 erase;唯一逐操作差异是错误字符串。
- 为 7 个选择器使用绝对 bundle bits。 V0=
0x15a,V1=0x1bb,V2=0x1c7,V3=0x196,V4=0x1a2,V5=0x171,V6=0x17d(各 6 位)—— 分散布局,不是base + 6*p。 - PopXrf:index = 写组(逐 opcode 常量),presence = lane 子集。 variant 名称由
isVoidOppattern 派生,绝不由index派生;两个选择器正交。 - 未映射(LOW/inferred)。 V3 只贡献
Y_VREG(V3_X不可接受)的微架构原因 ——HIGH;btree free-set 插入/重置生命周期(谁按 bundle 填充 V0..V6,目标读端口如何选择)——LOW;vfc/gfc 上 PopXrf index-2 可到达性(第三 XRF 写组)——LOW。
相关组件
| Name | Relationship |
|---|---|
GetVexSourcePortEncoding(0x1c5ee280 glc / 0x1c5d2e80 vfc) | 源端口解析器;VregReadPort→8 个 VexSourcePortEncoding 值 |
FindAndEmitToUnusedPort(0x13a4b840 glc / 0x13ab2aa0 gfc) | 7 端口贪心最低空闲物理分配器;函数体与代际无关 |
SparseCoreTecVectorExtendedEncoder(0x1eb30ee0) | bit-packing 层,将 proto 端口槽 → 分散 bundle 选择器 bits |
EmitXrfResultOp(0x13a14180 glc / 0x13ab8c60 gfc / 0x139a8240 vfc) | PopXrf 结果提交;index = XRF 写组,presence = lane 子集 |
SparseCoreTecVectorResult(oneof tags 7..0xc) | PopXrf 变体被构造进入的 union |
交叉引用
- VectorExtended (VEX) —— scan/sort/dedup 操作清单(opcode→op dispatch 第 1 层),本页将其操作数绑定到端口。
- VEX 掩码 / 目标端口 / 子操作码 —— 共享
VectorExtendedEncoder的兄弟字段:5 位向量掩码(0x104)、3 位目标读端口(0x10c)和逐操作 6 位子操作码(0x10f)常量映射。 - VectorLoad Slot —— 其
SourceOneseed 字段携带本页解析器产生的VexSourcePortEncoding;load 数据通路与 VEX scan 之间的连接。 - TEC 向量操作码枚举 ——
VectorResult槽的 8 值操作集合(EupResult、PopXrfWriteAll、PopXrfWritePartial0..4、VresMove),PopXrf 是其中一部分;以及复用端口分配器的VectorAlu/combine_four_lanes槽。 - TEC(向量)引擎 —— 64 字节 bundle 几何、槽基址,以及 VEX 编码器接入的编码器 dispatch 模型。
- SparseCore 概览 —— 三类 SC engine、逐代存在性,以及 TEC 向量槽所在位置。
- 二进制:
extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d) - 索引条目: Part IX — SparseCore & BarnaCore / SparseCore ISA — 返回索引