Tile-ID 转换
本页中的每个地址、opcode 值、操作数索引和字符串字面量,都读取自
libtpu-0.0.40-cp314wheel 中的libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d;buildlibtpu_lts_20260413_b_RC00)。.textVA 在0xe63c000处等于文件偏移,.rodata在0x84a0000处等于文件偏移,二者都是恒等映射。地址空间整数按二进制使用方式同时以十进制/十六进制表示(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_tileid(llvm_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_tileid(STILEID.VRES/stileid.u32)的i32SSA 结果。如果调用点尚未提供,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 op | tpu_addrspacecast_spmem::create @ 0x146d67e0(TileSpmem 0xc9 → Spmem 0xca) |
| Tile-id 来源 | tpu_tileid::create @ 0x149883a0 — llvm_tpu.tileid,0 操作数 / 1 结果 i32 |
| Tile-id MLIR 来源 | TileIdOp → ReadIntegerRegisterOpLowering<TileIdOp,tpu_tileid>::matchAndRewrite @ 0x1359c7c0 |
| 硬件寄存器 | STILEID.VRES / stileid.u32(SparseCoreScalarAlu1ReadRegisterTileid;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 内存空间分为片上和片外。片上谓词精确且很小:
// 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 @ 0x135b8400;TypeIDResolver<LLVMFuncOp> compare |
| 2 | 读取 sc.sequencer inherent attr(字符串,长度 12) | getInherentAttr @ 0x1d8cc800,在 @0x135b8479 调用 |
| 3 | 与 "execute" 比较:长度 7 且字节为 0x63657865/0x65747563 | compare @0x135b84c5 |
| 4 | 如果未提供 tile id:getI32Type 然后 tpu_tileid::create | getI32Type @ 0x1d853c40;tpu_tileid::create @ 0x149883a0 @0x135b85f6 |
| 5 | LLVMPointerType::get(ctx, 0xca) — Spmem ptr type | 0x1746eb40 @0x135b8610 |
| 6 | tpu_addrspacecast_spmem::create(rw, loc, spmemPtr, base, tileId) | 0x146d67e0 @0x135b8629 |
| 7 | 设置 out AS = 202(0xca) | store @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 值。本单元记录该生产者。
TileIdOp → tpu_tileid
MLIR sc_tpu.tile_id op(TileIdOp)通过通用寄存器读取 pattern lowering:
// 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.u32(SparseCoreScalarAlu1ReadRegisterTileid,存在于 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 构建什么
// 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 | 操作数 | 结果 |
|---|---|---|---|
| 1 | tpu_tileid(llvm_tpu.tileid) | 无 | i32(STILEID) |
| 2 | tpu_addrspacecast_spmem | op0 = %base(0xc9),op1 = %tid(i32) | !ptr<0xca>(Spmem) |
| 3 | 在 ISA 处消费 | base reg MCOperand + sequencer-lane bundle | — |
消费者
lowered TileSpmem 访问由 SC isa_emitter 发射:
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)