Skip to content

addrspacecast ISel

本页中的每个地址、偏移、intrinsic ID 和字符串,均从 libtpu-0.0.40-cp314 wheel 中的 libtpu.so 逐字节读取(构建 libtpu_lts_20260413_b_RC00,BuildID md5 89edbbe81c5b328a958fe628a9f2207d)。其他版本会不同。地址是二进制自身的 VMA(text/rodata VMA == 文件偏移)。

摘要

SparseCore 需要十六种不同的地址空间重新标记操作,每一种对应它的指针可能落入的一个 (engine-scope, on-tile-pool) 组合。MLIR sc_tpu 层把每一种都表达为一个 @llvm.tpu.addrspacecast.<tag> intrinsic:一种保持值不变的 cast,其唯一效果是改变指针的 LLVM addrspace(N),而位本身保持相同。本页记录这十六个 intrinsic 在指令选择阶段的行为:它们在哪里(以及是否)转换为通用 ISD::ADDRSPACECAST(0xf4) SelectionDAG 节点,哪个 SelectCode MatcherTable 分支匹配它们,以及全部十六个 cast 的完整 from→to 地址空间映射。

最重要、也最反直觉的发现是:SparseCore cast intrinsic 不会转换为 ISD::ADDRSPACECAST(0xf4)。这个二进制中的 0xf4 节点恰好只来自一个来源:真正的 IR addrspacecast 指令,由标准 SelectionDAGBuilder::visitAddrSpaceCast 降低。十六个 SparseCore cast 会作为真正的 intrinsic 调用 存活进 LLVM-IR(call ptr addrspace(dst) @llvm.tpu.addrspacecast.X(ptr addrspace(src) %p [, i32 %tileid])),并作为由整数 intrinsic ID 键控的 ISD::INTRINSIC_WO_CHAIN 节点抵达 DAG。0xf4/ISD::ADDRSPACECAST 路径(经由 TPUTargetLowering::LowerADDRSPACECAST → 保持值不变的 0xf3 寄存器拷贝节点)服务于 TensorCore / 通用前端的真正 addrspacecast 指令,是一个与 SC intrinsic 家族分离的机制。把 SC cast intrinsic 接入 LowerADDRSPACECAST 的重新实现者会产出一个永远无法匹配、并以 CannotYetSelect 陷落的后端。

本页负责三件事:IR→ISD 转换点getAddrSpaceCastgetNode(0xf4) 生产者及其唯一真实指令调用方)、cast INTRINSIC_WO_CHAIN 节点落入的 MatcherTable / SelectCode 分支,以及 16 个 intrinsic 的 from→to AS 映射。这些 cast 重新标记的 160/128/192-bit fat-pointer struct 见 Fat Pointers (AS7/8/9);双操作数 (base, tileid) cast 主体见 Tile-ID Cast — 本页只链接,不重新推导二者。

对于重新实现,契约是:

  • 十六个 cast 是 LLVM intrinsic,不是 addrspacecast 指令。 Intrinsic ID 0x33b0..0x33bf(13232..13247)连续、按字母排序,夹在 llvm.tpu.addcarry0x33af)和 llvm.tpu.alloca.dreg0x33c0)之间。它们作为真正的 intrinsic 调用 存活过翻译。
  • ISD::ADDRSPACECAST 是 opcode 2440xf4,只由 SelectionDAG::getAddrSpaceCast0x192E2360)产生,而它唯一的真实调用方是 IR 指令处理器 visitAddrSpaceCast0x19333020)。没有 SC 代码发出 IR addrspacecast0xf4 节点。
  • cast ID 会进入 matcher 的 INTRINSIC_WO_CHAIN(opcode-48)分支,但那里没有它们的 pattern。TPUDAGToDAGISel::Select0x13B69640)中,全部十六个都会落入 SelectCodeCommon(MatcherTable 大小 0x37CAC);matcher 没有 cast-ID 分支,因此如果 cast 节点未被展开而抵达 matcher,就会 CannotYetSelect
  • from→to 映射编码在 intrinsic 名称后缀中。 每个 .<tag>(以及 .<src>.tile.<tag> 多段形式)命名目标 engine-scope / tile pool;该 cast 会把指针的 addrspace(N) 重新标记到该 pool 的 LLVM 地址空间。
  • 操作数个数由目标 sequencer scope 决定,而不是由 .tile. 中缀决定。 十六个中有九个,也就是目标为**可访问 tile 的 SC engine(TEC 或 TAC)**的 cast,带有 NOperands<2u> trait 并接收第二个 i32 tileid 操作数:七个 TEC 作用域的(.smem, .spmem, .tec, .smem.tile.tec, .sflag.tile.tec, .sflag.tile.sflag.tec, .tec.sflag.tec)加上两个 TAC 作用域的(.tac, .sflag.tile.tac)。另外七个(SCS/TC 作用域的 cast,包括带 .tile. 中缀的 .sflag.tile.scs / .smem.tile.scs / .sflag.tile.sflag.scs)带有 OneOperand,是单操作数重新标记。.tile. token 不会增加操作数;TEC 和 TAC 作用域才会。
Cast intrinsic ID0x33b0..0x33bf(13232..13247),16 个连续、按字母顺序
Intrinsic 名称范围llvm.tpu.addrspacecastllvm.tpu.addrspacecast.tec.sflag.tec
MLIR cast 来源MemorySpaceCastOpLowering::matchAndRewrite0x135A5C20)— elide-or-emit
IR→intrinsic 发出convertOperationImpl0x15140240,IDA VMA)→ createIntrinsicCall0x1683F440
ISD::ADDRSPACECASTopcode 2440xf4);生产者 SelectionDAG::getAddrSpaceCast0x192E2360
唯一 0xf4 调用方SelectionDAGBuilder::visitAddrSpaceCast0x19333020)— 真实 IR 指令处理器
TPU SelectTPUDAGToDAGISel::Select0x13B69640);cast ID → SelectCodeCommon 默认路径
MatcherTable大小 0x37CAC(228 524 B);opcode-48 = ISD::INTRINSIC_WO_CHAIN(无 cast 分支)
0xf4 loweringLowerOperation0x13B70AA0)opcode 244LowerADDRSPACECAST0x13B70480

两种 Address-Space-Cast 机制

目的

SparseCore addrspacecast ISel 的定义性事实是,存在两种互不相关的机制都携带 “address space cast” 这几个词,而混淆它们是本页唯一的陷阱。重新实现者必须从一开始就把它们分开,所以本单元在进入细节前先命名二者。

两条路径

text
MECHANISM A — the SparseCore cast-intrinsic family  (THIS PAGE owns the ISel)
─────────────────────────────────────────────────────────────────────────────
  sc_tpu.<op>                                        MLIR ScDialect op
    │  LowerToSparseCoreLlvm / LlvmTpu dialect

  @llvm.tpu.addrspacecast.<tag>(ptr %p [, i32 %tid]) REAL IR intrinsic CALL
    │  (16 IDs 0x33b0..0x33bf — survives into LLVM-IR; NOT an IR addrspacecast)

  ISD::INTRINSIC_WO_CHAIN  (DAG node keyed by intrinsic ID)
    │  TPUDAGToDAGISel::Select opcode-'0' (=48) arm

  SelectCodeCommon  (no cast-ID matcher pattern → consumed/folded, see below)

MECHANISM B — a generic IR `addrspacecast` instruction  (SEPARATE; not the SC intrinsic)
─────────────────────────────────────────────────────────────────────────────
  addrspacecast ptr addrspace(s) %p to ptr addrspace(d)   real IR instruction
    │  SelectionDAGBuilder::visitAddrSpaceCast 0x19333020

  SelectionDAG::getAddrSpaceCast 0x192E2360 → getNode( opcode 244 = 0xf4 )
    │  TPUTargetLowering::LowerOperation 0x13B70AA0  (else if v7 == 244)

  TPUTargetLowering::LowerADDRSPACECAST 0x13B70480  → value-preserving 0xf3 reg-copy
```text

> **GOTCHA —** MLIR `MemorySpaceCastOpLowering`(`0x135A5C20`)是通用 `llvm.addrspacecast` *指令*进入 SparseCore 流水线的*唯一*位置,并且只有在它的失败边上才会如此(§Conversion Site)。十六个 `@llvm.tpu.addrspacecast.*` *intrinsic* 由完全不同的机制发出,即 LlvmTpu 方言翻译,并且永远不会变成 `0xf4` 节点。把 cast intrinsic 通过 `LowerADDRSPACECAST` 降低的后端,是把机制 B 接到了只会走机制 A 的输入上。

### 为什么有两种机制

这种分裂存在,是因为两种 cast 含义不同。机制 B 在会*改变指针表示*的 LLVM 地址空间之间重新标记指针(例如 fat-pointer struct 与扁平整数),因此需要一个会物化表示变化的 lowering,也就是带 `MVT::i32` 载体、保持值不变的 `0xf3` 寄存器拷贝。机制 A 的十六个 cast 是*类型系统记账*:它们在表示完全相同的 SparseCore engine-scope / tile pool 之间重新标记指针,所以该 cast 是一个由消费它的 load/store 吸收的 no-op。把它们保留为不透明 intrinsic(而不是 `addrspacecast` 指令),使 SC 后端能在优化期间携带 engine-scope tag,而不被通用 `InferAddressSpaces` / `addrspacecast` 机制过早折叠掉。

---

## 转换点 — MLIR Op → Intrinsic,以及 `0xf4` 断开

### 目的

“IR→`ISD::ADDRSPACECAST` 转换点”有两半需要重新实现者追踪:(1)MLIR cast op 在哪里变成存活的 `@llvm.tpu.addrspacecast.*` intrinsic 调用,(2)证明这个调用*永远不会*转换为 `ISD::ADDRSPACECAST(0xf4)`。二者都在下方以字节地址锚定。

### MLIR cast op — elide 或 fall through

ScDialect cast op(`memref.memory_space_cast`,从 SparseCore 类型系统视角看)由 `MemorySpaceCastOpLowering::matchAndRewrite`(`0x135A5C20`)重写。它的主体是一个已按字节确认的三行 elide-or-emit 决策:

```c
function MemorySpaceCastOpLowering_matchAndRewrite(op, adaptor, rewriter):  // 0x135A5C20
    src_llvm = TypeConverter::convertType(srcPointerType)   // 0x135A5C20:22
    dst_llvm = TypeConverter::convertType(resultType)        // 0x135A5C20:23
    if src_llvm != dst_llvm:
        return failure                                       // → generic pattern emits a REAL llvm.addrspacecast
    rewriter.replaceOp(op, adaptor.operand)                  // ELIDE: identical LLVM ptr type → drop the cast
    return success

NOTE — 这个 MLIR pattern 是机制 B 的入口,而不是机制 A 的入口。当 convertType(src) == convertType(dst)(两个 SC 空间折叠为同一个 !llvm.ptr)时,cast 会被直接消去。当二者不同时,该 pattern 失败,通用 ConvertOpToLLVMPattern 会发出一个真正的 llvm.addrspacecast 指令,随后它走机制 B 到 0xf4。十六个 @llvm.tpu.addrspacecast.* intrinsic 源自这里;它们由 LlvmTpu 方言翻译发出,该翻译把每个 sc_tpu op 降低为它注册的 intrinsic。逐 ID 的 convertType 折叠映射(21 个 SC 地址空间 ID 中哪些共享一个 LLVM addrspace)由 Tile-ID Cast 负责。

Intrinsic 发出点(链接到逐 op 分派器)

十六个 SparseCore cast 会在 LlvmTpu 方言的 op→IR 翻译中变成真正的 IR intrinsic 调用convertOperationImpl(IDA 重基址 dump 中为 0x15140240)是一个约 1349 分支的 op-identity 分派器;每个 cast 分支都会尾调用共享跳板,该跳板用该 cast 的 intrinsic ID 调用 mlir::LLVM::detail::createIntrinsicCall0x1683F440)。createIntrinsicCall 是统一的 op→intrinsic-call 发出器,已按字节确认会物化真正的声明 + 调用:

c
function createIntrinsicCall(builder, modTrans, op, intrinsicID, numResults, ...):  // 0x1683F440
    operands = ModuleTranslation::lookupValues(op.operands)     // 0x1683F440:348 / :422 (base [, i32 tileid])
    decl     = Intrinsic::getOrInsertDeclaration(Module, intrinsicID, overloadTypes)  // 0x1683F440:656
    call     = IRBuilderBase::CreateCall(builder, decl, operands)                      // 0x1683F440:669
    return call    // %r = call ptr addrspace(dst) @llvm.tpu.addrspacecast.X(ptr addrspace(src) %base [, i32 %tid])
```text

> **NOTE —** op→intrinsic-ID 分派器机制(全部 1349 个分支、跳板、操作数/结果映射)属于方言翻译问题,不属于 ISel 问题;本页只引用它来确立 cast 会作为 `INTRINSIC_WO_CHAIN` 调用抵达 DAG,而不是 IR `addrspacecast`。双操作数 `(base, i32 tileid)` 调用,即九个 TEC 和 TAC 作用域的 cast,详见 [Tile-ID Cast](tile-id-cast.md)。

### `0xf4` 生产者及其唯一调用方

`ISD::ADDRSPACECAST` 是 SelectionDAG opcode **244**(`0xf4`)。唯一生产者是 `SelectionDAG::getAddrSpaceCast`(`0x192E2360`),已按字节确认会构造带该 opcode 的节点:

```c
function SelectionDAG_getAddrSpaceCast(loc, vt, ptr, srcAS, dstAS):   // 0x192E2360
    AddNodeIDNode(&id, 244 /* ISD::ADDRSPACECAST */, vtlist, &ops, 1)  // 0x192E2360:30
    ... // CSE lookup
    node->opcode = 244                                                 // 0x192E2360:70
    return node

它唯一有意义的调用方是 SelectionDAGBuilder::visitAddrSpaceCast0x19333020),也就是真正 IR addrspacecast 指令的处理器。没有 TPU/SparseCore 代码路径调用 getAddrSpaceCast、构造 AddrSpaceCastInst 或发出 0xf4 节点。因此,SparseCore cast intrinsic 只有在某个 pass 先把它重写成 IR addrspacecast 指令时,才可能变成 0xf4 节点,而没有任何 pass 会这样做。

0xf4 lowering 分支(机制 B 的目的地)

当真正的 addrspacecast 指令确实抵达 DAG 时,TPUTargetLowering::LowerOperation0x13B70AA0)会捕获 opcode 244 并转发它,已按字节确认:

c
function TPUTargetLowering_LowerOperation(op, dag):   // 0x13B70AA0
    v7 = op.getOpcode()
    ...
    else if (v7 == 244)                                // 0x13B70AA0:73  ISD::ADDRSPACECAST
        return TPUTargetLowering::LowerADDRSPACECAST(op, dag)   // 0x13B70480
```text

`LowerADDRSPACECAST`(`0x13B70480`)把 `0xf4` 节点重写为 SparseCore 中保持值不变的 `0xf3` 寄存器拷贝节点(合法性矩阵和 `0xf3` 寄存器拷贝 matcher 分支属于 TensorCore 前端问题,超出本页范围)。该分支存在;它只是永远不会被 cast-intrinsic 家族抵达。

### 函数映射

| 函数 | 地址 | 角色 |
|---|---|---|
| `MemorySpaceCastOpLowering::matchAndRewrite` | `0x135A5C20` | MLIR cast:若 `convertType` 相等则消去,否则失败 → 通用 `llvm.addrspacecast` |
| `convertOperationImpl` | `0x15140240`(IDA VMA) | LlvmTpu op→IR 分派器;cast 分支 → `createIntrinsicCall` |
| `mlir::LLVM::detail::createIntrinsicCall` | `0x1683F440` | 发出真正的 intrinsic 调用(`getOrInsertDeclaration` + `CreateCall`) |
| `SelectionDAG::getAddrSpaceCast` | `0x192E2360` | 唯一的 `getNode(244)` / `0xf4` 生产者 |
| `SelectionDAGBuilder::visitAddrSpaceCast` | `0x19333020` | `getAddrSpaceCast` 的唯一调用方 — 真实 IR 指令处理器 |
| `TPUTargetLowering::LowerOperation` | `0x13B70AA0` | opcode `244` → `LowerADDRSPACECAST`;opcode 48 不在此 lowering |
| `TPUTargetLowering::LowerADDRSPACECAST` | `0x13B70480` | `0xf4` → 保持值不变的 `0xf3` 寄存器拷贝节点 |

---

## MatcherTable / `SelectCode` 分支

### 目的

SparseCore cast intrinsic 会作为 `ISD::INTRINSIC_WO_CHAIN` 节点抵达 DAG,其 child-0 是整数 intrinsic ID。本单元记录该节点进入 `TPUDAGToDAGISel::Select` 的哪个分支,以及接下来发生什么:答案是 “`SelectCodeCommon` 默认路径,而那里没有 matcher pattern 认领它”。

### 入口点

```text
TPUDAGToDAGISel::Select (0x13B69640)            ── per-node ISel entry
  └─ switch on node opcode (low byte)
       case '0' (= 48 = ISD::INTRINSIC_WO_CHAIN)
         └─ switch on intrinsic ID v68 (= node->op0 constant)
              ├─ 13216..13222  ── ReplaceAllUses / cross-lane reg-class (special handlers)
              ├─ 13223..13465  ── (the big default group) ── goto LABEL_118
              │     ▲ the 16 cast IDs 13232..13247 live HERE
              ├─ 13227/13229/13293/13297/...  ── selectCrossLane / selectCMask / selectErf (special)
              └─ default       ── selectCrossLaneIntrinsic / selectErfIntrinsic
       LABEL_118: SelectCodeCommon(this, node, MatcherTable, 0x37CAC, OperandLists)

算法 — cast ID 落入 SelectCode 默认路径

TPUDAGToDAGISel::Select0x13B69640)首先按节点 opcode 分派(反编译器把 INTRINSIC_WO_CHAIN 分支渲染为 case '0',即 opcode 48 的低字节 ASCII)。在该分支内部,它读取 intrinsic ID 并对其 switch。十六个 cast ID 13232..132470x33b0..0x33bf)位于一个大型连续 fall-through 块中(cases 13225/13226/13231/13232 … 13465),其在 0x13B69640:422 的终止语句是 goto LABEL_118,也就是 SelectCodeCommon 尾调用:

c
function TPUDAGToDAGISel_Select(node):                    // 0x13B69640
    switch (node.opcode_low_byte):
      case 48 /* ISD::INTRINSIC_WO_CHAIN */:              // 0x13B69640:160 ('0')
          intNo = node.op0_constant                        // :167  v68
          switch (intNo):
            case 13216..13220: ReplaceAllUsesOfValueWith(...); return   // :171  special
            case 13221/13222:  reg_class = (cond ? XRFPR2 : XRFPR0);
                               goto selectCrossLane                       // :178
            // --- the big default group, INCLUDING all 16 cast IDs ---
            case 13225/13226/13231/13232/.../13247/.../13465:            // :221..:421
                goto LABEL_118                                            // :422
            case 13227/13229: ... selectCrossLane                         // :423  special
            case 13293:       selectCMask(node); return                  // :431  special
            ...
    LABEL_118:
        SelectCodeCommon(this, node,
                         MatcherTable /* &GOT - 521848896 */, 0x37CAC,    // :509
                         TPUDAGToDAGISel::SelectCode::OperandLists)
```text

因此,每个存活到 `Select` 的 cast intrinsic 节点都会交给通用 `SelectCodeCommon` MatcherTable 解释器,也就是处理每个没有专用 `Select` C++ handler 的 TPU intrinsic 的同一分支。cast ID **没有**任何特殊 C++ 处理(不同于例如 `13221/13222` cross-lane、`13293` `selectCMask` 或 `13227/13229`)。

### Matcher 没有 cast-ID pattern

`SelectCodeCommon` 遍历 MatcherTable(大小 `0x37CAC` = 228 524 字节)。其顶层 `OPC_SwitchOpcode` 有一个 opcode 48(`ISD::INTRINSIC_WO_CHAIN`)分支,会按 child-0 整数 ID 匹配约 150 个 TPU load/store/vector/scalar/sync intrinsic,但十六个 cast ID `0x33b0..0x33bf` **不在**其中(也不在任何其他分支中)。因此,未被展开的 cast `INTRINSIC_WO_CHAIN` 节点若抵达 matcher,就会 `CannotYetSelect`。

> **QUIRK —** matcher 中的 opcode 48 *确实*是 `ISD::INTRINSIC_WO_CHAIN`,并且*确实*存在(一个多 KB 的分支,匹配约 150 个 intrinsic);只是没有列出 cast ID。缺失是 cast ID 特有的,不是 opcode 缺失。期望这些 cast intrinsic 像 load 一样匹配、因而添加 TableGen pattern 的重新实现者会发现没有保留槽位;这是有意为之,因为 cast 应该在抵达 matcher 前被消费。

### Cast 如何在 matcher 前被清除

十六个 cast 都保持值不变:结果指针等于操作数指针,只是 `addrspace` tag 不同。预期的清除方式是,**消费它的** SparseCore load/store ISel pattern 会读*穿过*该 cast 的指针操作数(并且对 TEC/TAC 作用域的双操作数 cast,读取独立的 `i32 tileid` 操作数),因此 cast 节点变成 dead 并在 `Select` 看到它之前被 DCE;或者它会由通用 DAGCombiner 的 `visitINTRINSIC_WO_CHAIN` 指针透传折叠。无论哪种方式,cast 都不需要 matcher pattern。准确的清除阶段(consumer-pattern fold 还是通用 combiner DCE)未在此按字节追踪;负面结论则是牢固的:没有 `0xf4` 转换、没有 matcher 分支、没有特殊 `Select` handler。

### 函数映射

| 函数 | 地址 | 角色 |
|---|---|---|
| `TPUDAGToDAGISel::Select` | `0x13B69640` | 逐节点 ISel;opcode-48 intrinsic 分派 |
| `SelectionDAGISel::SelectCodeCommon` | (tail-called) | MatcherTable 解释器;`0x37CAC` 字节表 |
| MatcherTable opcode-48 分支 | (在表中) | `ISD::INTRINSIC_WO_CHAIN`;约 150 个 ID,**** cast ID |
| `selectCrossLaneIntrinsic` / `selectCMask` / `selectErfIntrinsic` | `0x13B6B940` / `0x13B6C6E0` / `0x13B6B480` | 特殊处理器 intrinsic(不是 cast) |

---

## 16 个 Intrinsic 的 from→to AS 映射

### 目的

每个 cast intrinsic 的行为完全由其名称后缀决定,后缀命名目标 engine-scope,并且(对带 `.tile.` 中缀的形式)命名 per-tile pool 重新标记。本单元给出全部十六个的完整 from→to 地址空间映射,并锚定到 SparseCore 地址空间 ID 表。

### 命名语法

`llvm.tpu.addrspacecast` 之后的后缀是**目标 tag**。确认自二进制中的十六个名称字符串,共出现三种形状:

```text
llvm.tpu.addrspacecast                       no suffix  →  generic/default re-tag
llvm.tpu.addrspacecast.<engine|pool>         1 segment  →  re-tag to that engine-scope / pool
llvm.tpu.addrspacecast.<srcpool>.tile.<eng>  3 segment  →  per-tile-pool re-tag, named src + dst scope

engine tag 是 scs / tac / tec(指针被查看时所在作用域的 SparseCore 子引擎)和 tc(TensorCore)。pool tag 是 smem / spmem / sflag.tile. 中缀标记一个 per-tile-pool 重新标记(指针会被重新标记进 tile-private pool,并在命名的目标 engine 作用域中查看)。.tile. token 本身意味着第二个操作数;操作数个数由目标 sequencer scope 控制:可访问 tile 的引擎 TEC 和 TAC 接收 tileid,SCS 和 TC 不接收(见下一个标注)。

QUIRK — 操作数个数跟随目标 sequencer,而不是名称的 .tile. 中缀。 cast 仅当其目标作用域是可访问 tile 的 SC engine,即 TEC 或 TAC 时,才接收第二个 i32 tileid 操作数(九个带 NOperands<2u> trait 的 cast,已从二进制中每个 op class 的 trait list 按字节确认)。SCS/TC 作用域的 cast 带 OneOperand,即使带 .tile. 也是单操作数,例如 .sflag.tile.scs0x33b3)和 .smem.tile.scs0x33b9)是单操作数,而 .smem0x33b8)、.spmem0x33bb)、.tec0x33be)以及两个 TAC cast .tac0x33bc)/ .sflag.tile.tac0x33b6)是双操作数。双操作数 cast 的 lowering 主体由 Tile-ID Cast 负责。

QUIRK — 后缀命名的是目标作用域,不是源。cast 保持值不变,所以源地址空间就是操作数指针携带的任何地址空间;intrinsic 只断言新 tag。ID 纯粹按字母顺序分配(它是 Intrinsic 名称表位置),所以相邻 ID 在语义上相邻:0x33b6.sflag.tile.tac)和 0x33b7.sflag.tile.tec)只差目标 engine,而 0x33bb.spmem)和 0x33bc.tac)互不相关。

完整映射

目标地址空间读自 SparseCore 地址空间 ID 表(该表由地址空间目录负责;这里只复现这些 cast 目标涉及的 ID)。“AS” 列是 LLVM 地址空间编号;engine-scope tag 会重新标记到某个 pool 的逐作用域别名,而不是单一固定编号,所以这些行给出作用域而不是一个 ID。

Operands 列从每个 op class 的 operand trait 按字节确认(NOperands<2u> ⇒ 双操作数,OneOperand ⇒ 单操作数)。九个双操作数 cast 正好是 TEC 和 TAC 作用域集合。

IDhexIntrinsic 名称操作数from(src AS)to(dst scope / AS)
132320x33b0llvm.tpu.addrspacecastptranygeneric / default pointer re-tag
132330x33b1…​.scsptranySCS engine scope(scalar-sequencer view)
132340x33b2…​.scs.sflag.scsptrsflagSFLAG, SCS scope(sflag_scs, AS 223)
132350x33b3…​.sflag.tile.scsptrsflag-tileSFLAG tile pool, SCS view(sflag_tile AS 217)
132360x33b4…​.sflag.tile.sflag.scsptrsflag-tileSFLAG tile → sflag_scs(AS 223), SCS scope
132370x33b5…​.sflag.tile.sflag.tecptr, i32 tidsflag-tileSFLAG tile → sflag, TEC scope
132380x33b6…​.sflag.tile.tacptr, i32 tidsflag-tileSFLAG tile pool, TAC view(sflag_tile AS 217)
132390x33b7…​.sflag.tile.tecptr, i32 tidsflag-tileSFLAG tile pool, TEC view(sflag_tile AS 217)
132400x33b8…​.smemptr, i32 tidanySMEM(AS 0 / smem), TEC scope
132410x33b9…​.smem.tile.scsptrsmem-tileper-tile SMEM(smem_tile/TileSmem AS 219), SCS scope
132420x33ba…​.smem.tile.tecptr, i32 tidsmem-tileper-tile SMEM(smem_tile/TileSmem AS 219), TEC scope
132430x33bb…​.spmemptr, i32 tidanySPMEM(chip-shared SC SRAM, AS 202), TEC scope
132440x33bc…​.tacptr, i32 tidanyTAC engine scope(tile-access-core view)
132450x33bd…​.tcptranyTensorCore scope(cross-engine handoff)
132460x33be…​.tecptr, i32 tidanyTEC engine scope(tile-execute view)
132470x33bf…​.tec.sflag.tecptr, i32 tidsflagSFLAG, TEC scope(.tec engine + sflag pool)

NOTE — intrinsic 名称及其连续 ID 分配(0x33b0..0x33bf,夹在 0x33af llvm.tpu.addcarry0x33c0 llvm.tpu.alloca.dreg 之间)逐字节读自二进制的 Intrinsic 名称表。操作数个数读自每个 op class 的 operand trait(九个 NOperands<2u> 双操作数 cast 正是 TEC 和 TAC 作用域集合;七个 OneOperand cast 是 SCS/TC 作用域)。pool 地址空间编号 217sflag_tile)、219smem_tile/TileSmem)、223sflag_scs)、202spmem)、0smem)来自 cast-lowering 驱动(CastSflagPointerToSflagAny 0x135b8a00CastTileSmemPointerToSmem 0x135b86e0)。对于裸 engine tag(.scs/.tac/.tc),给定 engine-scope tag 解析到的确切 LLVM 地址空间编号是由 Fat Pointers (AS7/8/9) 和地址空间目录负责的逐作用域别名选择;本页解析的是 pool,不是逐作用域数字别名。

双操作数(TEC/TAC 作用域)子集

十六个中有九个接收第二个 i32 tileid 操作数,也就是目标为可访问 tile 的 SC engine 的 cast。七个是 TEC 作用域:0x33b5.sflag.tile.sflag.tec)、0x33b7.sflag.tile.tec)、0x33b8.smem)、0x33ba.smem.tile.tec)、0x33bb.spmem)、0x33be.tec)、0x33bf.tec.sflag.tec);两个是 TAC 作用域:0x33b6.sflag.tile.tac)、0x33bc.tac)。全部九个都带有 NOperands<2u> op trait;另外七个带有 OneOperand。剩余七个是单操作数重新标记,包括三个 SCS 作用域且带 .tile. 中缀的形式(.sflag.tile.scs.sflag.tile.sflag.scs.smem.tile.scs)以及裸 .scs / .scs.sflag.scs / .tc cast。双操作数 cast 主体,即 (base, tileid) 如何解析成 tile-local 物理地址,完整记录在 Tile-ID Cast;本页只记录 TEC 或 TAC 目标作用域是操作数个数判别条件,并且第二个操作数是 tile selector。

GOTCHA — 不要从 .tile. token 推断操作数数量。…​.sflag.tile.scs0x33b31 个操作数)和 …​.sflag.tile.tec0x33b72 个操作数)都带 .tile.,也都指向 SFLAG-tile pool,但只有 TEC 作用域的那个接收 tileid.sflag.tile.tac0x33b6)和裸 .tac0x33bc)cast 也是 2 操作数,尽管 TAC 的裸形式没有 .tile.。反过来,…​.smem0x33b8)和 …​.spmem0x33bb)完全没有 .tile.,却是 2 操作数。操作数个数判别条件是目标 sequencer scope 是否为可访问 tile 的 engine,即 TEC 或 TAC ⇒ NOperands<2u>(base, i32 tileid);SCS/TC ⇒ OneOperand,而不是字面 .tile. token。


注意事项

构建 SparseCore addrspacecast ISel 的重新实现者必须按顺序做这些事,并避开名称中内置的陷阱:

  • 声明十六个 intrinsic,而不是一个 addrspacecast 指令。 在字母顺序位置保留连续 Intrinsic ID(在本构建中位于 addcarryalloca.dreg 之间),并从方言翻译中把它们作为普通 intrinsic 调用发出,绝不要作为 IR addrspacecast 指令。把它们标记为保持值不变(IntrNoMem,cast 结果等于其指针操作数),使 combiner 能折叠它们。
  • 不要把它们接到 LowerADDRSPACECAST 该分支只用于通用 IR addrspacecast 指令(机制 B)。把 cast intrinsic 路由到那里是最可能犯的错误,并会产生一个永远无法选择这些 cast 的后端。
  • 不要给它们 matcher pattern;依赖消费者吸收。 cast 应被折叠进消费它的 load/store(后者读穿过指针和独立 tileid)。如果某个 cast 以某种方式存活到 matcher,正确行为是 CannotYetSelect,这表示缺少消费者折叠,而不是缺少 cast pattern。
  • 用名称后缀驱动 from→to 映射,用目标作用域驱动操作数个数。 .scs/.tac/.tec/.tc engine tag 选择目标 pool 的逐作用域别名;pool tag(smem/spmem/sflag)命名物理层级。第二个 i32 tileid 操作数仅当目标作用域是可访问 tile 的 engine,即 TEC 或 TAC(九个 NOperands<2u> cast)时存在,并通过 tile 地址解析器路由;它不是.tile. token 决定的,因为该 token 也出现在单操作数 SCS 作用域 cast 上。

存活 cast 的清除阶段(consumer-pattern fold 还是通用 visitINTRINSIC_WO_CHAIN DCE)未在此按字节追踪,是唯一的未闭合链接;它不影响上面的 ISel 契约,因为两种情况下 cast 都不需要 matcher pattern,也永远不会变成 0xf4 节点。


相关组件

名称关系
MemorySpaceCastOpLowering::matchAndRewrite0x135A5C20MLIR cast op:消去或 fall through 到通用 llvm.addrspacecast(机制 B 的入口)
createIntrinsicCall0x1683F440把十六个 cast 作为存活的 intrinsic 调用发出(机制 A 的发出点)
SelectionDAG::getAddrSpaceCast0x192E2360唯一的 ISD::ADDRSPACECAST(0xf4) 生产者,SC cast 永远不会抵达
TPUDAGToDAGISel::Select0x13B69640把 cast INTRINSIC_WO_CHAIN 节点路由到 SelectCodeCommon 默认路径
TPUTargetLowering::LowerADDRSPACECAST0x13B70480通用 IR addrspacecast0xf40xf3 lowering(机制 B 的主体)

交叉引用

  • Fat Pointers (AS7/8/9) — 这些 cast 重新标记的 160/128/192-bit 结构化指针,以及逐作用域地址空间别名编号。
  • Tile-ID Cast — 双操作数 (base, i32 tileid) cast 主体(这十六个中的九个 TEC 和 TAC 作用域 cast)。
  • SparseCore Hardware Architecture — cast tag 命名的四层内存模型和 SCS/TAC/TEC engine scope。
  • SparseCore Overview — Part IX 的导航入口;engine 名称、逐代存在性、数据路径。
  • Stream Gather/Scatter — 读取穿过重新标记 SparseCore 指针的 indirect-DMA 消费者。
  • Binary: 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 — 返回索引