Skip to content

VEX 操作数端口绑定

本页中的每个 enum 值、switch case、proto 偏移、present-mask bit 和 bundle 位位置,都逐字节读取自 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(build-id 89edbbe81c5b328a958fe628a9f2207d,build libtpu_lts_20260413_b_RC00)—— 来源包括 GetVexSourcePortEncoding switch(glc/Ghostlite 0x1c5ee280,vfc 0x1c5d2e80)、FindAndEmitToUnusedPort 7 端口分配器函数体(glc 0x13a4b840)、逐操作 SparseCoreTecVectorExtendedEncoder BitCopy 立即数,以及 EmitXrfResultOp PopXrf 结果提交(glc 0x13a14180,gfc 0x13ab8c60)。地址适用于此 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 个,端口 8V3_X)和端口 9MISC_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 == 8V3_X)和 port == 9MISC_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 bit 1<<(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 < 3vres_unit ∈ {0,1,2}),从逐 opcode 常量取得写组索引,并纯粹根据三个结果操作数的 isVoidOp presence 选择 WriteAll/Partial0..4 —— 每个 present lane 被提交为 vreg(GetVregno)或 vector-mask-dest(GetVMDestregno)。
64 字节 TEC bundleVEX 槽的 operand→port 绑定
逻辑端口空间VregReadPort 0..9 → VexSourcePortEncoding 0..7(解析器,8 个合法值)
解析器GetVexSourcePortEncoding — glc 0x1c5ee280,vfc 0x1c5d2e80StatusOr,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 选择器 bitsV0=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。二进制保留了两个:

text
 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,并对 89 构造 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.ccV3_X 消息(66 字节)在二者中完全相同。合法 {0..7} 集合和两个拒绝端口跨代稳定;消息字符串是唯一差异。二代均 CONFIRMED。

8 个源端口编码 {#the-8-source-port-encodings}

该编码把向量操作数 V0..V3 的 Y_VREGX 子端口交错排列,并把 VST(vector-store)源总线放在 0。与 VectorLoad SourceOne seed 交叉确认,后者在 bundle 中携带同一个 enum:

valueenum namesource bus
0VEX_SOURCE_PORT_ENCODING_VST_SOURCEVST(vector-store)源总线
1VEX_SOURCE_PORT_ENCODING_V0_Y_VREGV0 操作数,Y_VREG 子端口
2VEX_SOURCE_PORT_ENCODING_V0_XV0 操作数,X 子端口
3VEX_SOURCE_PORT_ENCODING_V1_Y_VREGV1 操作数,Y_VREG 子端口
4VEX_SOURCE_PORT_ENCODING_V1_XV1 操作数,X 子端口
5VEX_SOURCE_PORT_ENCODING_V2_Y_VREGV2 操作数,Y_VREG 子端口
6VEX_SOURCE_PORT_ENCODING_V2_XV2 操作数,X 子端口
7VEX_SOURCE_PORT_ENCODING_V3_Y_VREGV3 操作数,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 0x13a4b840SegmentedAddScanF32/MaxScanU32 兄弟实例)。反编译的 MinScanU32 实例:

c
// 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. **边界 + 槽写入。** 7switch 分派 `[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..0x3f64 个 vregs) |
| `GetVectorMask` (`0x13a33320`) | `[0x5f, 0x7e]` | `id − 0x5f` | mask 0..0x1f32 个 vmasks) |
| `GetVMDestregno` (`0x13a65b20`) | `[0x5f, 0x6e]` | `id − 0x5f` | VM-dest 0..0xf16|

如果 `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 → bundle 0x10c,3 位)、向量掩码(+0x38 → bundle 0x104,5 位)以及逐操作 6 位子操作码常量(bundle 0x10f)。这三个字段和完整子操作码清单由 VEX 掩码 / 目标端口 / 子操作码 负责;本页只负责 7 个源端口选择器。它们共享编码器,而不是共享页面。

怪癖 —— 同一个物理子操作码 0x1b 在 VEX 外被复用。 子操作码 0x1b(十进制 27)在 VEX 槽中是 UniquifyFloat(已验证:EncodeSparseCoreTecVectorExtendedUniquifyFloat glc 0x1eb3c58027 写入 bundle bit 0x10f 处的 6 位子操作码字段)。combine_four_lanes cross-lane fold 是 gxc-family VectorAlu 操作(存在于 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

c
// 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 分区(写组 01)。操作名(`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 是否有 23 个写组 —— 尚未追踪。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 槽进行的对称写回。


函数映射

SymbolAddressRole
xla::ghostlite::GhostliteProtoUtils::GetVexSourcePortEncodingglc 0x1c5ee280VregReadPortVexSourcePortEncoding;identity [0,7],拒绝 8/9
xla::viperfish::ViperfishProtoUtils::GetVexSourcePortEncodingvfc 0x1c5d2e80同一 switch;“MISC_AUX not supported on VFC”
…isa_emitter::FindAndEmitToUnusedPort<…SparsecoreVregReadPort,…VectorExtended_MinScanU32>glc 0x13a4b8407 端口贪心最低空闲分配器(代表实例)
…isa_emitter::utils::FindAndEmitToUnusedPort<…>gfc 0x13ab2aa0代际相同分配器(结构孪生)
…container_internal::btree_container<…>::erase0x13a0dd60port_is_free 移除已分配端口
SparseCoreTecVectorExtendedEncoder::Encode0x1eb30ee0逐操作编码器 dispatch(调用逐操作 Encode…<Op>
EncodeSparseCoreTecVectorExtendedAddScanF320x1eb32380sub-op 0x05;V0..V6 选择器 bit 锚点
EncodeSparseCoreTecVectorExtendedMaxScanF320x1eb32a80sub-op 0x07;相同端口 bits(op-invariant 证明)
BitCopy(dst,dst_bit,src,src_bit,nbits)0x1fa0a900逐字段 bit-pack 原语
…isa_emitter::EmitXrfResultOp<…PopXrfWriteAll,…Partial0..4,…VectorResult>glc 0x13a14180PopXrf 结果提交(Ghostlite);index + presence 选择
EmitXrfResultOp<…> (gfc)gfc 0x13ab8c606acc60406 PopXrf 实例(结构孪生)
EmitXrfResultOp<…> (vfc)vfc 0x139a8240Viperfish PopXrf 实例
…isa_emitter::isVoidOp0x13a659a0驱动 variant select 的逐操作数 void 测试
…isa_emitter::GetVregno0x13a659c0reg band [0xd0,0x10f] → vregno id−0xd0
…isa_emitter::GetVectorMask0x13a33320reg band [0x5f,0x7e] → mask id−0x5f
…isa_emitter::GetVMDestregno0x13a65b20reg 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 名称由 isVoidOp pattern 派生,绝不由 index 派生;两个选择器正交。
  • 未映射(LOW/inferred)。 V3 只贡献 Y_VREGV3_X 不可接受)的微架构原因 —— HIGH;btree free-set 插入/重置生命周期(谁按 bundle 填充 V0..V6,目标读端口如何选择)—— LOW;vfc/gfc 上 PopXrf index-2 可到达性(第三 XRF 写组)—— LOW

相关组件

NameRelationship
GetVexSourcePortEncoding0x1c5ee280 glc / 0x1c5d2e80 vfc)源端口解析器;VregReadPort→8 个 VexSourcePortEncoding
FindAndEmitToUnusedPort0x13a4b840 glc / 0x13ab2aa0 gfc)7 端口贪心最低空闲物理分配器;函数体与代际无关
SparseCoreTecVectorExtendedEncoder0x1eb30ee0bit-packing 层,将 proto 端口槽 → 分散 bundle 选择器 bits
EmitXrfResultOp0x13a14180 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 —— 其 SourceOne seed 字段携带本页解析器产生的 VexSourcePortEncoding;load 数据通路与 VEX scan 之间的连接。
  • TEC 向量操作码枚举 —— VectorResult 槽的 8 值操作集合(EupResultPopXrfWriteAllPopXrfWritePartial0..4VresMove),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-id 89edbbe81c5b328a958fe628a9f2207d
  • 索引条目: Part IX — SparseCore & BarnaCore / SparseCore ISA — 返回索引