Skip to content

Tile-ID 转换

本页中的每个地址、opcode 值、操作数索引和字符串字面量,都读取自 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(build-id 89edbbe81c5b328a958fe628a9f2207d;build libtpu_lts_20260413_b_RC00)。.text VA 在 0xe63c000 处等于文件偏移,.rodata0x84a0000 处等于文件偏移,二者都是恒等映射。地址空间整数按二进制使用方式同时以十进制/十六进制表示(0xc9=201 TileSpmem,0xca=202 Spmem)。其他版本会不同。

摘要

在 SparseCore 上,指向 TileSpmem(LLVM 地址空间 0xc9/201)的指针是片上的:它命名某个 tile 私有 scratch 中的一个字偏移,但不编码它属于哪个 tile。硬件 EXECUTE lane(TEC)通过运行时寄存器寻址该 tile,而不是通过指针位寻址。tile-id addrspacecast 是连接两者的 MLIR 方言 op 家族:它把片上 TileSpmem 指针重新标记为对等的、可片外寻址的 Spmem 空间(0xca/202),同时把它与从 STILEID 硬件寄存器读取的 tile-id 配对。本页记录 片上 2 操作数转换 lowering 主体,即 CastTileSpmemPointerToSpmem 驱动器,tile-id 组合方式(tile-id 和基指针如何成为一个 cast op 的两个操作数),以及 lowering 产生的发射节点序列

核心的、指令级的结论是:tile id 没有打包进指针位。 该转换在指针半边保持值不变(结果携带相同的 32 位字偏移),tile id 作为 cast op 的第二个 SSA 操作数随行,即由 tpu_tileidllvm_tpu.tileid,读取 STILEID.VRES 寄存器)产生的 i32。下游 lowered TileSpmem load/store 把 base 作为标量基寄存器 MCOperand 使用,并把 tile 上下文作为它所在的 sequencer-lane bundle 使用。数据布局为 AS7/8/9 保留的“胖指针”在这里不起作用;参见 胖指针(AS7/8/9),了解为什么该保留只是无效的 AMDGPU 样板,而不是 TPU 结构化指针。

对重新实现而言,契约是:

  • 两个操作数,一个结果。 TEC-lane addrspacecast 是 {base : !ptr<src_as>, tileId : i32} -> !ptr<dst_as>。trait 集为 ZeroRegions, OneResult, OneTypedResult<Type>, ZeroSuccessors, NOperands<2u>, MemoryEffectOpInterface。操作数 0 总是基指针;操作数 1 总是 tile id。
  • tile id 是寄存器读取,不是常量。 操作数 1 是 tpu_tileidSTILEID.VRES / stileid.u32)的 i32 SSA 结果。如果调用点尚未提供,lowering 会按需合成它。
  • sequencer 属性是门控。CastTileSpmemPointerToSpmem 中,只有当外围 LLVMFuncOp 携带 sc.sequencer == "execute"(TEC)时,lowering 才注入 tile id。在任何其他 sequencer 上,TileSpmem 源都会以硬诊断拒绝。(下面的元数分裂比这个单一驱动器更宽:op 级 tile-id 操作数存在于 TEC TAC 转换上,因为两条 lane 都寻址 per-tile 内存;它只在 SCS/TC 转换上缺失;参见元数表中的 per-op trait 读取。)
  • 组合是操作数配对,不是算术。 没有 add、shift 或 multiply 把 tile id 和 base 合并。“组合”在这里表示二者作为 cast 的操作数并排绑定;指针值原样重新标记,tile id 单独穿线到消费它的 load/store。
Lowering 驱动器xla::tpu::sparse_core::CastTileSpmemPointerToSpmem @ 0x135b8400
发射的 cast optpu_addrspacecast_spmem::create @ 0x146d67e0(TileSpmem 0xc9 → Spmem 0xca
Tile-id 来源tpu_tileid::create @ 0x149883a0llvm_tpu.tileid,0 操作数 / 1 结果 i32
Tile-id MLIR 来源TileIdOpReadIntegerRegisterOpLowering<TileIdOp,tpu_tileid>::matchAndRewrite @ 0x1359c7c0
硬件寄存器STILEID.VRES / stileid.u32SparseCoreScalarAlu1ReadRegisterTileid;vfc/glc/gfc)
操作数顺序op0 = 基指针 · op1 = tile-id i32(两次 addOperands(...,1,...) 调用)
Sequencer 门控sc.sequencer inherent attr == "execute"(immediates 0x63657865/0x65747563
片上谓词IsOffTileMemory @ 0x13d7ac00 = (ms & ~0x10) != 2 — 当且仅当 MS∈{2, 0x12} 时为片上
Dst 地址空间0xca(202,Spmem)写入输出 result-AS 字段
指针位打包? — 保值重新标记;tile id 是配对操作数(见 addrspacecast ISel
置信度已确认(lowering 主体、操作数顺序、元数分裂、sequencer 门控、片上谓词均来自反编译读取),除非某行另有说明

片上寻址问题

目的

本单元先说明该转换为什么存在,再说明它如何 lowering,使重新实现者理解 tile id 的用途。

SparseCore 内存空间分为片上和片外。片上谓词精确且很小:

c
// xla::tpu::sparse_core::lowering_util::IsOffTileMemory @ 0x13d7ac00
bool IsOffTileMemory(MemorySpace ms) {
    return (ms & 0xFFFFFFEF) != 2;     // (ms & ~0x10) != 2
}
```text

片外是** MS `2`(`tile_spmem`)和 MS `0x12`(`tile_spmem_cb`)之外的一切;`& ~0x10` 会把 circular-buffer 变体折叠到同一个片上类别。因此,TileSpmem 指针(LLVM AS `0xc9`)是片上指针:它是*当前 tile* scratch 中的字偏移,不携带 tile 索引。TEC EXECUTE lane 在许多 tile 上运行一个逻辑程序;若要从对等可寻址空间寻址某个特定 tile 的 TileSpmem,它必须提供运行时 tile 选择值。该选择值就是 `STILEID` 寄存器值,而 tile-id addrspacecast 是附加它的 IR 构造。

> **注意 — TileSpmem ⇄ Spmem 是片上/片外边界。** `0xc9`(TileSpmem)是片上视图;`0xca`(Spmem)是可片外寻址的对等视图。该转换把前者转换为后者,使 EXECUTE lane 可以显式命名 tile。这两个地址空间整数以及完整 SC AS 名册由地址空间页面负责;本页只使用 `0xc9`→`0xca` 这一对。

### 名称说明

同一个 op 出现了两个名称字符串,不能混淆:

| 名称 | 字符串(反编译) | 出现位置 |
|---|---|---|
| MLIR op 注册名 | `llvm_tpu.addrspacecast.spmem`(下划线 `llvm_tpu`) | `::create` 中的 `OperationState` ctor |
| MLIR op 注册名 | `llvm_tpu.tileid` | `tpu_tileid::create` 的 `OperationState` |
| 打印 / intrinsic 名称 | `llvm.tpu.addrspacecast.spmem`(点号 `llvm.tpu`) | 该 op lowering 到的 LLVM intrinsic |
| 打印 / intrinsic 名称 | `llvm.tpu.tileid` / `llvm.tpu.tileid.lock` | 打印出的 intrinsic / op-name |

从反编译 `::create` 主体读取的 `OperationState` ctor 字符串是 `llvm_tpu.`(下划线)注册名;`llvm.tpu.`(点号)名称是打印 intrinsic 形式。重新实现者注册方言时使用下划线名称;打印 IR 时看到点号名称。

---

## 2 操作数转换 Lowering 主体

### 目的

`CastTileSpmemPointerToSpmem` 是执行 tile-id 组合并发射转换的 lowering 驱动器。其主体很短且已完整解码;这是片上转换 lowering 的权威描述。

### 签名和控制流

```c
// xla::tpu::sparse_core::CastTileSpmemPointerToSpmem @ 0x135b8400
//   (ConversionPatternRewriter &rw, Operation *op, Value base, Value &outPtr, uint &outAS)
// Returns LogicalResult; on success writes outPtr (the re-tagged Spmem ptr) and outAS=202.

LogicalResult CastTileSpmemPointerToSpmem(rw, op, base, /*out*/ outPtr, /*out*/ outAS) {

  // (1) Walk block parents up to the enclosing LLVM::LLVMFuncOp.
  Operation *fn = op;
  for (;;) {
      Block *b = fn->block;                       // *(op+16)
      if (!b) { fn = nullptr; break; }
      fn = b->getParentOp();
      if (!fn) { fn = nullptr; break; }
      if (typeID(fn) == TypeID<LLVM::LLVMFuncOp>)  // *(*(fn+48)+16) == &LLVMFuncOp::id
          break;
  }

  // (2) Read the sequencer attribute off that function.
  Attribute seq = (fn && fn->numAttrs >= 0x1000000)
                    ? fn->getInherentAttr("sc.sequencer", 12)   // 0x1d8cc800
                    : DictionaryAttr::get(op+56, "sc.sequencer", 12);
  StringRef s = StringAttr::getValue(seq);

  // (3) Gate on "execute" (TEC): length 7 AND bytes == "execute".
  if (s.size() == 7 &&
      (s[0..3] == 0x63657865 /*"exec"*/) && (s[3..6] == 0x65747563 /*"cute"*/)) {

      // (4) Compose the tile id: synthesise tpu_tileid if the caller supplied none.
      Value tileId = providedTileId;               // a3
      if (!tileId) {
          Type i32 = rw.getI32Type();               // 0x1d853c40
          tileId   = tpu_tileid::create(rw, loc, i32);   // 0x149883a0  (STILEID read)
      }

      // (5) Build the destination pointer type in Spmem (0xca).
      Type spmemPtr = LLVM::LLVMPointerType::get(ctx, /*AS=*/0xca);   // 0x1746eb40

      // (6) Emit the 2-operand cast: {base, tileId} -> spmemPtr.
      outPtr = tpu_addrspacecast_spmem::create(rw, loc, spmemPtr, base, tileId); // 0x146d67e0
      outAS  = 202;                                 // 0xca, the result address space
      return success();
  }

  // (7) Not TEC: hard error — TileSpmem DMA is execute-only.
  return op->emitError("DMA to or from TileSpmem only allowed on TEC, but got ") << s;
}

分步说明

步骤动作反编译锚点
1沿 Block::getParentOp 向上走到 LLVM::LLVMFuncOp(TypeID 匹配)parent loop @ 0x135b8400TypeIDResolver<LLVMFuncOp> compare
2读取 sc.sequencer inherent attr(字符串,长度 12)getInherentAttr @ 0x1d8cc800,在 @0x135b8479 调用
3"execute" 比较:长度 7 且字节为 0x63657865/0x65747563compare @0x135b84c5
4如果未提供 tile id:getI32Type 然后 tpu_tileid::creategetI32Type @ 0x1d853c40tpu_tileid::create @ 0x149883a0 @0x135b85f6
5LLVMPointerType::get(ctx, 0xca) — Spmem ptr type0x1746eb40 @0x135b8610
6tpu_addrspacecast_spmem::create(rw, loc, spmemPtr, base, tileId)0x146d67e0 @0x135b8629
7设置 out AS = 2020xcastore @0x135b8639
非 TEC 路径发射 execute-only 诊断字符串 "DMA to or from TileSpmem only allowed on TEC, but got "

易错点 — lowering 会在未传入 tile id 时合成它。 驱动器把候选 tile id 作为 in/out 参数接收。如果调用点已经产生了 tpu_tileid,它会复用;否则它会就地构建一个(步骤 4)。两条分支最终发射同一个 op。重新实现者不能假设 tile id 总是预先构建好的;当源 IR 没有 tile id 时,lowering 负责在转换点物化 STILEID 读取。

注意 — sc.sequencer 位于函数上,而不是 op 上。 门控读取外围 LLVMFuncOp 的 inherent attribute,并先走出嵌套 block(步骤 1)。非 execute 函数中的 TileSpmem 转换根本不是 tile 转换,而是错误路径。这是下面元数分裂精确跟随 sequencer 的结构性原因。


Tile-ID 组合:i32 从哪里来

目的

“tile-id 组合”就是产生操作数 1。它不是算术;它是一次寄存器读取,lowering 为一个 i32 值。本单元记录该生产者。

TileIdOptpu_tileid

MLIR sc_tpu.tile_id op(TileIdOp)通过通用寄存器读取 pattern lowering:

c
// ReadIntegerRegisterOpLowering<TileIdOp, tpu_tileid>::matchAndRewrite @ 0x1359c7c0
LogicalResult matchAndRewrite(TileIdOp op, Adaptor, ConversionPatternRewriter &rw) {
    Type i32 = rw.getI32Type();                       // 0x1d853c40
    Value v  = tpu_tileid::create(rw, op->loc, i32);  // 0x149883a0
    rw.replaceOp(op, v);                              // vtable slot *(*rw+8)
    return success();
}
```text

`tpu_tileid::create` 构建一个**零操作数、一个 `i32` 结果**的 op:

```c
// mlir::sparse_core::tpu_tileid::create @ 0x149883a0
Value tpu_tileid::create(OpBuilder &b, Location loc, Type resultTy) {
    OperationState st(loc, "llvm_tpu.tileid", 15);    // NO addOperands calls
    st.addType(resultTy);                             // result type only
    return b.create(st);                              // typeID-checked
}

该 intrinsic 读取 SparseCore 标量 tile-id 寄存器:每代 MC op STILEID.VRES / stileid.u32SparseCoreScalarAlu1ReadRegisterTileid,存在于 vfc/glc/gfc)。因此,cast 的操作数 1 是运行时 i32,即正在执行的 tile 的索引,而不是编译时立即数。

为什么它不是算术

数据布局的 AS7/8/9 保留,在 AMDGPU 风格胖指针中会把 descriptor 和 offset 折叠进一个宽值。SparseCore 对 tile id 不做这种打包(参见 胖指针(AS7/8/9))。上面的 lowering 主体中没有把 tile id 合并进基指针的 imul/shl/or;base 原样流过,tile id 是作为操作数 1 绑定的独立 SSA 值。转换期间指针半边保持值不变(片外重新标记路径 lowering 为保值节点;见 addrspacecast ISel)。

怪癖 — “组合”是操作数绑定,不是位打包。 来自胖指针 ISA 的重新实现者会预期 tile id 占据宽指针的高位。这里 tile id 是兄弟操作数。唯一的“组合”是 {base, tileId} 操作数对(::create 中的两次 addOperands 调用)。硬件把它们作为两个物理事物消费:一个基寄存器和一个 sequencer-lane select;从不把它们当成一个地址字。


发射的节点序列

目的

本单元给出 lowering 端到端产生的精确 op 序列,并从 ::create 固定 cast op 的操作数结构。

tpu_addrspacecast_spmem::create 构建什么

c
// mlir::sparse_core::tpu_addrspacecast_spmem::create @ 0x146d67e0
//   (OpBuilder &b, Location loc, Type resultTy, Value base, Value tileId)
Value tpu_addrspacecast_spmem::create(b, loc, resultTy, base, tileId) {
    OperationState st(loc, "llvm_tpu.addrspacecast.spmem", 28);
    Value op0 = base;
    Value op1 = tileId;
    st.addOperands(&op0, 1);     // operand 0 = base pointer     (@0x146d67e0 first addOperands)
    st.addOperands(&op1, 1);     // operand 1 = tile-id i32      (second addOperands)
    st.addType(resultTy);        // result type (Spmem ptr)
    return b.create(st);         // typeID-checked
}
```text

两个不同的 `addOperands(..., 1, ...)` 调用,每个操作数一次,是操作数顺序的字节级证明:**base 在前,tile id 在后**。`_tec` 双生版本字节相同,只是名称为 `"llvm_tpu.addrspacecast.tec"`(长度 26)。

### 完整序列

对于 EXECUTE lane 上的 TileSpmem load/store,lowering 按顺序发射:

```text
1. %tid  = "llvm_tpu.tileid"() : () -> i32                      // tpu_tileid::create (if not already present)
                                                                //   = STILEID register read
2. %sp   = "llvm_tpu.addrspacecast.spmem"(%base, %tid)          // tpu_addrspacecast_spmem::create
              : (!ptr<0xc9 TileSpmem>, i32) -> !ptr<0xca Spmem>  //   op0=base, op1=tid; value-preserving on ptr
3.  ... downstream: the lowered TileSpmem load/store consumes
       %sp as a SparsecoreVectorBase MCOperand (scalar base reg)
       and the tile context as the SparseCoreTecBundle it sits in.
节点Op操作数结果
1tpu_tileidllvm_tpu.tileidi32(STILEID)
2tpu_addrspacecast_spmemop0 = %base0xc9),op1 = %tidi32!ptr<0xca>(Spmem)
3在 ISA 处消费base reg MCOperand + sequencer-lane bundle

消费者

lowered TileSpmem 访问由 SC isa_emitter 发射:

text
isa_emitter::EmitVectorLoadOrStore<SparsecoreVectorBase, SparsecoreVectorOffset,
    SparsecoreVectorMask, SparsecoreVectorStride,
    SparseCoreTecVectorLoad_TileSpmemLoad, SparseCoreTecBundle>
  plain-load glc instance @ 0x13a33de0  (+ gfc twin)
  the same EmitVectorLoadOrStore template is instantiated across the
  TileSpmem op family — e.g. the glc Store/Indexed/CircularBuffer slots
  at 0x13a378c0 / 0x13a362c0 / 0x13a36000 — proto op types include:

                  SparseCoreTecVectorLoad_TileSpmemLoad,
                  SparseCoreTecVectorStore_TileSpmemStore (+ Indexed/CircularBuffer/PostUpdate)
```text

base 作为 `SparsecoreVectorBase` `MCOperand` 流入,即一个标量基寄存器,并与 offset/stride/mask 并列。per-tile 上下文是 `SparseCoreTecBundle`(指令所在的 EXECUTE sequencer-lane),**不是**地址位字段。Mosaic 侧 verify `"failed to verify that all of {tile, base} have same element type"` 强制 tile 和 base 共享 pointee type。

> **注意 — operand→MCOperand 绑定是结构性的,不是由单个 getter 追踪得出(高)。** cast 的 `%base` SSA 结果*就是* lowered load 作为 `SparsecoreVectorBase` 读取的值,这一点依赖通过 LLVM 方言翻译的 SSA def-use,而不是 ISA 发射点的某个 getter。`::create` 操作数顺序(op0=base)和 emitter 读取基寄存器 `MCOperand` 都已确认;端到端绑定为高置信度。非 circular-buffer TileSpmem slot 的精确 `SparsecoreVectorBase`/`SparsecoreVectorOffset` 字段位位置未在此解码。

---

## 元数分裂:TEC/TAC 转换携带 Tile Id,SCS/TC 不携带

### 目的

tile-id 操作数存在于 TEC- 和 TAC-lane 转换上。本单元从 per-op `NOperands<N>` trait(从符号表中模板化 `mlir::Op<…>` 实例读取)固定该分裂,并在存在 `::create` 主体处用其签名元数佐证。

### 判别器

每个 cast op 携带固定元数 trait,即 `OpTrait::OneOperand`(一个操作数)或 `OpTrait::NOperands<2u>`(两个操作数)。存在 `::create` 主体时,其签名以 `…Value`(一个操作数)或 `…ValueValue`(mangled `ValueES6_`,两个操作数)结尾,与 trait 匹配。两个操作数集合是面向 per-tile lane 的转换,即 TEC(EXECUTE)lane 和 TAC lane;它们寻址 per-tile 内存,需要运行时 tile select。SCS 和 TC lane 对每个 core 是单一的,寻址其内存时不需要 per-tile 索引。

| Op 名称 | 元数 trait | `::create` (@VA) | Tile-id op? | Lane |
|---|---|---|:---:|---|
| `tpu_addrspacecast` | OneOperand | `0x146d5ea0` | no | generic |
| `tpu_addrspacecast_scs` | OneOperand | `0x146d5f80` | no | SCS |
| `tpu_addrspacecast_scs_sflag_scs` | OneOperand | `0x146d6060` | no | SCS |
| `tpu_addrspacecast_sflag_tile_scs` | OneOperand | `0x146d6140` | no | SCS |
| `tpu_addrspacecast_sflag_tile_sflag_scs` | OneOperand | `0x146d6220` | no | SCS |
| `tpu_addrspacecast_sflag_tile_sflag_tec` | NOperands<2u> | `0x146d6300` | **YES** | **TEC** |
| `tpu_addrspacecast_sflag_tile_tac` | **NOperands<2u>** | (no `::create` body) | **YES** | **TAC** |
| `tpu_addrspacecast_sflag_tile_tec` | NOperands<2u> | `0x146d6400` | **YES** | **TEC** |
| `tpu_addrspacecast_smem` | NOperands<2u> | `0x146d6500` | **YES** | **TEC** |
| `tpu_addrspacecast_smem_tile_scs` | OneOperand | `0x146d6600` | no | SCS |
| `tpu_addrspacecast_smem_tile_tec` | NOperands<2u> | `0x146d66e0` | **YES** | **TEC** |
| `tpu_addrspacecast_spmem` | NOperands<2u> | `0x146d67e0` | **YES** | **TEC** |
| `tpu_addrspacecast_tac` | **NOperands<2u>** | (no `::create` body) | **YES** | **TAC** |
| `tpu_addrspacecast_tc` | OneOperand | `0x146d68e0` | no | TC |
| `tpu_addrspacecast_tec` | NOperands<2u> | `0x146d69c0` | **YES** | **TEC** |
| `tpu_addrspacecast_tec_sflag_tec` | NOperands<2u> | `0x146d6ac0` | **YES** | **TEC** |

九个 2 操作数转换,即七个 TEC 变体(`_sflag_tile_sflag_tec`、`_sflag_tile_tec`、`_smem`、`_smem_tile_tec`、`_spmem`、`_tec`、`_tec_sflag_tec`)加两个 TAC 变体(`_tac`、`_sflag_tile_tac`),都携带 `NOperands<2u>`;七个 SCS/TC/generic 转换携带 `OneOperand`。存在 `::create` 主体时,2-op 签名以 `ValueES6_` 结尾,1-op 签名以 `ValueE` 结尾。`_tac` 和 `_sflag_tile_tac` 没有单独的 `::create` 主体;其元数仅从 `NOperands<2u>` trait 读取,因此对于这两行,*trait* 是权威来源。

> **怪癖 — `sflag.tile.*` 和 `addrspacecast.tac` 按 lane 分裂,而不是按名称分裂。** sflag-tile 转换以 `_sflag_tile_tec`/`_sflag_tile_tac`(2-op)和 `_sflag_tile_scs`(1-op)出现。同一个逻辑操作,不同 lane ⇒ 不同元数。`CastSflagPointerToSflagAny` 驱动器(`0x135b8a00`)读取外围函数的 `sc.sequencer`,并在存在对应分支时分派到 `_scs`/`_tc`/`_tec`(以及 `_sflag_scs`/`_sflag_tec` 源 AS 变体),对携带 tile 的分支像 `CastTileSpmemPointerToSpmem` 一样合成 `tpu_tileid`。重新实现者必须按外围函数的 sequencer 选择 cast 变体,绝不能只按 cast 的*名称片段*选择。

---

## 各代存在性

| 机制 | VF(vfc,Viperfish/v5) | GL(glc,Ghostlite/v6e) | GF(gfc,6acc60406/TPU7x) |
|---|:---:|:---:|:---:|
| `tpu_tileid` / `STILEID.VRES` 寄存器读取 | yes | yes | yes |
| 2 操作数 tile-id 转换(7 个 TEC + 2 个 TAC 变体) | yes | yes | yes |
| 1 操作数 SCS/TC 转换 | yes | yes | yes |
| `CastTileSpmemPointerToSpmem` 驱动器 + `sc.sequencer` 门控 | yes | yes | yes |
| `IsOffTileMemory` 谓词 `(ms & ~0x10) != 2` | yes | yes | yes |
| TileSpmem load/store 消费 `SparsecoreVectorBase` reg | yes | yes | yes |

`tpu_addrspacecast_*` op 家族和 `CastTileSpmemPointerToSpmem` 驱动器在各代 SC 间共享;tile-id `i32` 寄存器读取(`STILEID`)存在于三代全部。2 操作数/1 操作数 op 定义被编译进方言,与某代实际提供哪些 sequencer 无关,因此元数分裂是代际不变的。(TAC *sequencer* 在 gfc 上不存在:6acc60406 只提供 SCS+TEC;但 TAC-lane cast *ops* 在方言中仍定义为 `NOperands<2u>`。)

---

## 限制和未决项

| 项目 | 状态 |
|---|---|
| `CastTileSpmemPointerToSpmem` lowering 主体(gate、tile-id synth、emit、out-AS) | 完整主体已解码 |
| `tpu_tileid::create` 0 操作数 / 1 个 `i32` 结果 | 主体已解码;没有 `addOperands` |
| `TileIdOp` → `tpu_tileid` lowering(getI32Type + create + replaceOp) | 主体已解码 |
| 操作数顺序 op0=base,op1=tileId | 两次 `addOperands(...,1,...)` 调用,base 然后 tileId |
| 元数分裂:9 个 cast 为 2-op(7 个 TEC + 2 个 TAC),7 个 cast 为 1-op(SCS/TC/generic) | per-op `NOperands<2u>` vs `OneOperand` trait;存在主体处有 `::create` 签名(`ValueES6_` vs `ValueE`) |
| Sequencer 门控 == `"execute"`(长度 7,`0x63657865`/`0x65747563`) | 主体中的字节比较 |
| 片上谓词 `(ms & ~0x10) != 2` | 主体已解码 |
| spmem 转换的 Dst AS = `0xca`(202)Spmem | store `@0x135b8639` |
| Tile id 是配对操作数,不是指针位 | 主体中没有打包算术;保值重新标记 |
| `%base` SSA 结果 == load 的 `SparsecoreVectorBase` MCOperand | 结构性 def-use,不是由单个 getter 追踪 |
| `_tac` / `_sflag_tile_tac` 元数(2-op) | `mlir::Op<…>` 实例上的 `NOperands<2u>` trait;无 `::create` 主体 |
| SCS/TC 转换和 TAC 2-op 转换的精确 dst AS | 仅重新解码了 `_spmem`/`_smem`/`_tec`/`_scs`/`_tc`/`_tec_sflag_tec`/`_scs_sflag_scs` 生产者;其他依赖 sflag-promotion 表 |
| 非 CB TileSpmem slot 的 `SparsecoreVectorBase`/`Offset` 字段位布局 | 未在此解码 |
| SelectCode MatcherTable arm 将 op#1 穿线到 tile-select MCOperand | 未在此逐字节走查 |

---

## 交叉引用

- [胖指针(AS7/8/9)](fat-pointers-as789.md) — 160/128/192 位 non-integral 结构化指针保留(AMDGPU buffer-fat-pointer ABI),以及为什么没有 TPU op 构造它;`CircularBufferDescriptor`(SC 唯一的结构化指针)。本页的 tile id 特意**不**使用该保留。
- [addrspacecast ISel](addrspacecast-isel.md) — IR→ISD 转换、保值的 `LowerADDRSPACECAST` 节点(`MVT::i32`),以及把 tile-id 操作数配对进 lowered load/store 的 `SelectCode` MatcherTable。
- [SparseCore 架构](architecture.md) — SCS/TAC/TEC engine 角色;为什么 EXECUTE(TEC)lane 是寻址 per-tile TileSpmem 的 lane。
- [SparseCore 概览](overview.md) — 此片上寻址服务的 embedding datapath。
- [Stream Gather/Scatter](stream-gather-scatter.md) — 片外 indirect-DMA datapath;与这里闭合的片上寻址相对应的片外部分。
- [getSequencerType](getsequencertype.md) — SCS/TAC/TEC sequencer 如何决定,即门控此转换的同一个 `sc.sequencer` 区分。
- [Scalar Opcode 枚举](scalar-opcode-enum.md) — 周边标量名册中的 `SparseCoreScalarAlu1ReadRegisterTileid`(`STILEID`)opcode。
- [VectorLoad Slot](vectorload-slot.md) / [VectorStore Slot](vectorstore-slot.md) — 把重新标记后的指针作为 `SparsecoreVectorBase` 寄存器消费的 TileSpmem load/store slot。
- [CBREG Circular-Buffer 寄存器](cbreg.md) — `tile_spmem_cb`(MS `0x12`)片上变体,`& ~0x10` 折叠会把它归并到同一个片上类别。
- **二进制:** `extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so`(build-id `89edbbe81c5b328a958fe628a9f2207d`)
- **索引条目:** Part IX — SparseCore & BarnaCore / SparseCore pointers & DMA — [返回索引](../index.md)