Skip to content

SCTypeConverter

本页中的所有地址、符号名和表值都逐字节精确读取自 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(构建 libtpu_lts_20260413_b_RC00,BuildID md5 89edbbe81c5b328a958fe628a9f2207d)。.symtab 未被剥离;每个结论都锚定到一个 demangled 符号、一个重定位加数或一个反编译函数体。其他版本会不同。

摘要

当 SparseCore (SC) lowering 将一个 mlir::sparse_core (ScDialect) 函数下降到 LLVM dialect 时,它需要为接触到的每个 memref 回答一个结构性问题:lowered memref 描述符结构体内部的 !llvm.ptr 应使用哪个 LLVM address-space 整数? SCTypeConverter 回答了这个问题。它不是一个独立类,而是 LowerToSparseCoreLlvmPass::lowerFunc (0x13568280) 构造的 LLVMTypeConverter,并且只额外添加了一个 registerTypeAttributeConversion lambda (0x135763c0);这个 lambda 将 memref 的 MemorySpaceAttr 转换成一个 64 位 IntegerAttr,其中携带原始 SparseCore address-space ID。核心结论可以写成一行:LLVM address space 是 MemorySpaceToAddressSpace(MS),也就是后端其他部分使用的同一个正向映射;因此 memref<…, #sc.memory_space<spmem>> 会下降为一个指针字段为 !llvm.ptr<202> 的描述符。这里没有重新编号;SC address-space ID 就是 LLVM addrspace

本页负责说明三件事,LowerToSparseCoreLlvm 的 rewrite 函数体和 DMA bridge-cast 都建立在它们之上:

  • address-space → !llvm.ptr 类型映射MemorySpaceAttr → IntegerAttr(addrspace) 转换 lambda、对所有 21 个命名 ID 逐字节转储的 MemorySpaceToAddressSpace 反向表,以及sequencer-context flatten 覆盖逻辑;当函数不在 execute-/core-sequencer 上下文中时,该逻辑会把 per-tile 和 per-SCS 空间折叠到其通用基空间。这个 flatten 就是 MemorySpaceCast lowering 查询的 elide-vs-emit 决策。
  • CheckAddressSpaces 合法性矩阵 — SC simple-DMA 层的唯一合法性入口 (0x135b8e00):“触及通用 SMEM 的 simple DMA 只在 Scalar Core Sequencer 上合法”这一契约,并从上到下解码它的 SupportsTileSmemDma 目标能力旁路和 sc.sequencer=="scs" 逃逸条件。
  • EUP 实例化清单 — 42 个经过类型转换的 elementwise lowering,分为两个家族:12 个 UnaryFloatVectorOpLowering(1:1 EUP push+pop macro)和 30 个 AluEpOpLowering(1:N unpack → compute → repack),每个都从源 op 映射到其 EUP intrinsic 或重新发出的 compute op,并说明在两条路径之间选择的 IsDynamicallyLegal 谓词。

AS-id 表本身,即 ID ↔ MemorySpace enum ↔ memory pool ↔ on/off-tile,并不在这里重新推导;它位于 Fat Pointers (AS7/8/9),而每个 cast 的 addrspacecast ISel 位于 addrspacecast ISel。本页记录类型转换器如何消费那张表来给指针类型盖章,以及合法性层和 EUP 层如何依托转换后的类型运行。

类型转换器位置LowerToSparseCoreLlvmPass::lowerFunc @ 0x13568280 (LLVMTypeConverter ctor @ 0x13568369)
AS-attr 转换安装点TypeConverter::registerTypeAttributeConversion @ 0x135685e8
转换规则!llvm.ptr<MemorySpaceToAddressSpace(MemorySpaceAttr::getValue())>
转换 lambda (lowerFunc)0x135763c0 — 带有 sequencer-context flatten 覆盖
转换 lambda (lowerAsserts)0x135b6f80 — 同一映射,没有 flatten 覆盖
正向映射mlir::sparse_core::MemorySpaceToAddressSpace(MemorySpace) @ 0x14b78780(表 0xaf36ce8,掩码 0x3fff7f
Flatten 门控布尔值ScDialect::HasExecuteSequencerTypeAttribute @ 0x1459a020 · HasCoreSequencerTypeAttribute @ 0x14599ec0
指针构造器LLVM::LLVMPointerType::get(ctx, ID) @ 0x1746eb40(ID = 原始 SC address-space)
合法性入口CheckAddressSpaces(SparseCoreTarget&, Operation*, int, int) @ 0x135b8e00
合法性调用者DmaSimpleStartOpLowering::matchAndRewrite @ 0x135a9100(调用 @ 0x135a977a
EUP 清单12 个 UnaryFloatVectorOpLowering(1:1 macro)+ 30 个 AluEpOpLowering(1:N unpack/compute/pack)
1:1-vs-1:N 门控IsDynamicallyLegal @ 0x135ddd20
置信度CONFIRMED(以反编译为锚点),除非某行或标注另有说明

Address-Space → !llvm.ptr 类型映射

转换器在哪里构建

SparseCore 类型转换器不是一个独立的 C++ 类;不存在 SCTypeConverter 符号。它是在 LowerToSparseCoreLlvmPass::lowerFunc (0x13568280) 内部构造的普通 mlir::LLVMTypeConverter:构造函数在 0x13568369 触发,在其上添加的唯一 SC 特定行为,是 0x135685e8 处的一次 TypeConverter::registerTypeAttributeConversion 调用,它安装 0x135763c0 处的 BaseMemRefType × MemorySpaceAttr lambda。其他所有内容,即 memref → {alloc-ptr, align-ptr, offset, [sizes], [strides]} 描述符结构体、函数签名转换、标量/向量类型透传,都是上游标准 LLVMTypeConverter

这种分离就是重新实现契约:不要继承类型转换器;注册一个属性转换 lambda。 该 lambda 的职责很窄,即把 memref 的源 dialect MemorySpaceAttr 转成一个整数,让基础转换器把它烘入描述符的指针字段;其余工作由基础转换器完成。

转换规则是一行

0x135763c0 处的 lambda 去掉 MLIR 样板后如下:

text
AttributeConversionResult convertSCMemorySpace(BaseMemRefType, MemorySpaceAttr msAttr):
    MemorySpace ms = msAttr.getValue()                    // 0x145929e0  (1-based enum)
    int id        = MemorySpaceToAddressSpace(ms)         // 0x14b78780  (the AS-id table)
    id            = applySequencerFlatten(id, ms, fn)     // override band — see below
    IntegerType i64 = IntegerType::get(ctx, 64, Signless) // 0x1d8c60c0
    return IntegerAttr::get(i64, id)                       // 0x1d859f00 — the new memory-space attr
```text

结果是一个 64 位 signless `IntegerAttr`,携带原始 address-space ID。随后基础 `LLVMTypeConverter` 在下降 `MemRefType` 时读取这个整数属性,并通过 `LLVM::LLVMPointerType::get(ctx, ID)` (`0x1746eb40`) 把它盖到描述符结构体的每个 `!llvm.ptr` 字段上。因此 `memref<…, #sc.memory_space<spmem>>` 会变成 `!llvm.struct<(ptr<202>, ptr<202>, i64, …)>`。这条路径已通过 `CircularBufferDescriptor::GetMemRefType` (`0x135c6020`) 逐字节端到端确认:带 `MemorySpaceAttr` 的 `MemRefType::get` → `convertType` (`0x1c956740`) → `mlir::StructBuilder` (`0x171c1640`, `extractPtr`/`setPtr`)。

`MemorySpaceToAddressSpace` (`0x14b78780`) 是 [fat-pointers 页面](../sparsecore/fat-pointers-as789.md)记录的同一个正向映射;其反编译函数体为:

```c
__int64 MemorySpaceToAddressSpace(unsigned int ms) {
  if (ms - 1 > 0x15 || ((0x3FFF7Fu >> (ms - 1)) & 1) == 0)
    LOG(FATAL) << "Unsupported memory space: " << ms;   // sc_enums.cc:110
  return dword_AF36CE8[ms - 1];                          // 22-entry reverse table
}

范围检查 ms - 1 > 0x15 允许 MemorySpace ∈ 1..22;位掩码 0x3FFF7F 拒绝 MemorySpace 8 空洞(以及任何未置位 bit),并在无效空间上 LOG(FATAL)。下面复现表 0xAF36CE8,它是 fat-pointers 页面中 AS-id 表的精确逆表

陷阱 — 结果属性是普通的 64 位 IntegerAttr不是重新发出的 MemorySpaceAttr。SC MemorySpaceAttr 不会存活到 LLVM dialect 中;存活的只有其数值 address-space ID,它以 i64 整数属性携带,供基础转换器在构造 ptr<ID> 时消费。试图原样传递源 dialect 属性的重新实现,将无法匹配基础转换器实际构造的描述符。

表 A — MemorySpaceToAddressSpace 反向表 (0xaf36ce8)

SCTypeConverter 对 !llvm.ptr<N> 的结果是 N = MemorySpaceToAddressSpace(MS)。索引 = MS − 1MemorySpace 8 是空洞(无效 → LOG(FATAL))。flatten 列是 lowerFunc 覆盖(下一节);lowerAsserts lambda 省略它。

MSpool→ addrspace (= ptr<N>)sequencer flatten
1smem0 (0x00)
2tile_spmem201 (0xC9)
3spmem202 (0xCA)
4hbm203 (0xCB)
5sflag204 (0xCC)
6vmem205 (0xCD)
7dreg208 (0xD0)
8(gap)invalid → LOG(FATAL)
9smem_any212 (0xD4)
10hbm_any213 (0xD5)
11timem214 (0xD6)
12simem215 (0xD7)
13iova216 (0xD8)
14sflag_tile217 (0xD9)204 if !execute-seq
15spmem_any218 (0xDA)
16smem_tile219 (0xDB)0 if !execute-seq
17mar220 (0xDC)
18tile_spmem_cb501 (0x1F5)
19smem_cb502 (0x1F6)
20sflag_scs223 (0xDF)204 if !core-seq
21smem_scs224 (0xE0)0 if !core-seq
22sflag_tc204 (0xCC)always 204

address-space ID 会被直接用作 LLVM addrspace,没有二次重映射。扫描 SC lowering 区间 0x13530000..0x135c0000 内的 LLVMPointerType::get 立即数,可以恢复出完全相同的这些 ID 字面量:0xCA (Spmem)、0xCB (HBM)、0xCC (Sflag, ×5)、0xD0 (Dreg)、0xD3 (SflagAny, ×4)、0xD4 (SmemAny)、0xD5 (HBMAny)、0xDB (TileSmem)、0xE1 (SflagAnySynctile)。每个 ID 的 pool/MemorySpace/on-tile 语义属于 fat-pointers 页面

sequencer-context flatten — elide-vs-emit 折叠集合

MemorySpaceToAddressSpace 产生基础 ID 后,lowerFunc lambda 会应用一个覆盖跳转表(0xae4633c,按 MS − 14 索引,9 项),该表由从正在 lowering 的函数中捕获的两个闭包布尔值门控:

text
b0 = !ScDialect::HasExecuteSequencerTypeAttribute(fn)   // 0x1459a020  (TEC execute-lane context?)
b1 = !ScDialect::HasCoreSequencerTypeAttribute(fn)      // 0x14599ec0  (core-sequencer context?)
```text

从跳转表目标逐字节解码出的逐 `MemorySpace` 覆盖如下:

| MS | space | base ID | override |
|---|---|---|---|
| 14 | `sflag_tile` | 217 | → 204 (Sflag) if `b0` (not execute-seq) |
| 16 | `smem_tile` | 219 | → 0 (Smem) if `b0` (not execute-seq) |
| 20 | `sflag_scs` | 223 | → 204 (Sflag) if `b1` (not core-seq) |
| 21 | `smem_scs` | 224 | → 0 (Smem) if `b1` (not core-seq) |
| 22 | `sflag_tc` | 204 | **always** 204 (TC sflag is generic sflag) |
| 15/17/18/19 | `spmem_any`/`mar`/`tile_spmem_cb`/`smem_cb` | — | **no override** (keep base) |

意图是上下文敏感的 flatten:在 execute-sequencer 或 core-sequencer 函数**之外**,per-tile(`sflag_tile`、`smem_tile`)和 per-SCS(`sflag_scs`、`smem_scs`)空间会折叠为通用 `Sflag` (204) / `Smem` (0) 指针类型;因为代码没有运行在该 sequencer 上时,并不存在可寻址的 per-tile 或 per-SCS bank。`tc`-sflag (MS 22) 无条件是通用 `Sflag`。

这个 flatten *就是* [`MemorySpaceCast`](../sparsecore/addrspacecast-isel.md) 的 elide-vs-emit 决策。当 cast lowering 的源和目标 `MemorySpace` 在**post-flatten** 后映射到*相同* ID 时,它会省略 `addrspacecast`;这恰好发生在非 sequencer 函数中的 `{sflag_tile, sflag_scs, sflag_tc, sflag} → 204` 和 `{smem_tile, smem_scs, smem} → 0`,其他情况从不发生。`lowerAsserts` 中的伴随 lambda (`0x135b6f80`) 是相同映射但**没有**覆盖区间;assertion lowering 始终使用未 flatten 的(per-tile/per-SCS)ID,因为 assert 文本需要命名精确 bank。

> **怪癖 —** 转换器在布尔值 `b0`/`b1` 上是有状态的:*同一个* `MemorySpace` 会根据它出现在哪个 sequencer 的函数中而 lowering 成*不同*的 `ptr<N>`。`smem_tile` 在 TEC execute-lane 函数内是 `ptr<219>`,在其他所有地方是 `ptr<0>`;`smem_scs` 在 SCS 函数内是 `ptr<224>`,在其他所有地方是 `ptr<0>`。重新实现必须在转换任何 memref 之前捕获外层函数的 sequencer-type 属性。这两个布尔值存储在 `lowerFunc` 的 `-0xf0(rbp)`(设置 @ `0x135685d3`)。sequencer-type 属性本身记录在 [GetSequencerType](../sparsecore/getsequencertype.md)。

---

## `CheckAddressSpaces` 合法性矩阵

### 唯一的 simple-DMA 门控

`CheckAddressSpaces(SparseCoreTarget& tgt, Operation* op, int srcAS, int dstAS)` (`0x135b8e00`) 是 SC lowering 运行的*唯一* address-space 合法性入口,并且它只有一个调用者。它强制 SparseCore simple-DMA 数据搬运契约:**源或目标为通用 SMEM 空间(address-space 0)的 simple-tier DMA 只有从 Scalar Core Sequencer (SCS) 发出时才合法**,除非硬件声明原生 tile/SMEM DMA。反编译函数体可归约为三个条件的短路 OR;如果三个都失败,它会发出错误并返回 failure:

```c
__int64 CheckAddressSpaces(SparseCoreTarget *tgt, Operation *op, int srcAS, int dstAS) {
  result = 1;                                          // assume legal
  if (!tgt->vtable[+0xd8]()) {                         // (1) SupportsTileSmemDma() ?
    fn = walkParentsTo<LLVM::LLVMFuncOp>(op);          //     enclosing llvm.func
    attr = fn.getInherentAttr("sc.sequencer", 12);     // (2) sc.sequencer attribute
    s    = StringAttr::getValue(attr);
    bool isScs = (s.size == 3) &&                       //     "scs": 0x6373='sc', 0x73='s'
                 ((s[0..1] ^ 0x6373) | (s[2] ^ 0x73)) == 0;
    if (!isScs && (srcAS == 0 || dstAS == 0)) {        // (3) neither endpoint is SMEM ?
      op->emitError("Simple DMAs on SMEM only supported on SCS");  // 41 chars
      result = failure;
    }
  }
  return result;
}

表 D — 合法性条件(从上到下短路 OR)

#condition (any one ⇒ legal)source
1tgt.SupportsTileSmemDma()vtable +0xd8; VF = false, GL = false
2enclosing llvm.func's sc.sequencer inherent attr == "scs"getInherentAttr("sc.sequencer", 12) → 3-char cmp
3srcAS != 0 AND dstAS != 0 (neither endpoint is generic SMEM)the two int params (post-cast Table-A IDs)
else → emitError("Simple DMAs on SMEM only supported on SCS") → failureerror string @ 0x91b1ca3

反编译固定了每个分支:vtable 槽 +216 (= 0xd8) 是 SupportsTileSmemDma(行门控在 *(this+216)(this));父级遍历停在 mlir::detail::TypeIDResolver<mlir::LLVM::LLVMFuncOp>(外层 llvm.func);"scs" 比较是带 s.size == 3 的字面量 (s[0..1] ^ 0x6373) | (s[2] ^ 0x73);failure 分支构造 41 字节字符串 "Simple DMAs on SMEM only supported on SCS"

当前两代都没有原生 tile/SMEM DMA

SupportsTileSmemDma 在两个已发布的 SparseCore 目标上都返回 falseViperfishSparseCoreTarget (0x1d49c8e0, xor eax,eax) 和 GhostLiteSparseCoreTarget (0x1d499460, xor eax,eax),它们分别通过 vtable 的 R_X86_64_RELATIVE 加数 0x21cc90780x21cc86f8 到达。因此在每个当前世代上,条件 (1) 都是死的,有效规则是条件 (2) ∨ (3):触及 SMEM 的 simple DMA 只在 SCS 上合法。 这是 SCS 作为 SparseCore 标量控制引擎这一事实在 IR 侧的对应物;见 SCS Engine

唯一调用点与 address-space 规范化

唯一调用者是 DmaSimpleStartOpLowering::matchAndRewrite (0x135a9100),调用位于 0x135a977a。关键点是,该入口看到的 srcAS/dstASpost-cast 规范化后的 ID:在调用之前,CastTileSmemPointerToSmem (0x135b86e0) 会把任何 tile-resident 指针向下 cast 到通用 SMEM(通过接受 tpu_tileid 窗口操作数的 tpu_addrspacecast_smem;见 Tile-ID Cast),并把得到的 address-space 整数写入该入口消费的 out-param。因此一个 tile_spmem resident DMA 端点会先 flatten 为 SMEM,然后应用“SMEM ⇒ 仅 SCS”的门控。承载此门控的两阶段 DMA lowering 记录在 LowerToMlo DMA bridge-cast 页面。

陷阱 — 该门控关心的是通用 SMEM 空间(address-space 0),而不是整个 SMEM-family 空间。两个非零 ID(spmem/hbm/tile_spmem/…)之间的 DMA 会无条件合法,不受 sequencer 影响;只要两个端点都是非零,条件 (3) 就通过。“仅 SCS”限制专门针对 generic-SMEM (ptr<0>) 情况,这就是为什么先运行 CastTileSmemPointerToSmem 很重要:正是它创建了该门控随后保护的 ptr<0> 端点。


EUP 实例化清单

两个家族,一个源 op 扇入

SparseCore EUP (Extended Unary Processor) lowering 表面是42 个类型转换后的实例化,分属两个模板化 pattern 家族;它们都由同一个 LowerToSparseCoreLlvm pass 注册,并都运行在转换后的(LLVM-dialect)类型上:

  • UnaryFloatVectorOpLowering<Src, tpu_*_macro> — 12 个实例化。1:1 路径:操作数已经适配单个 EUP lane 宽度,因此函数体发出一个 EUP push+pop macro intrinsic。代数形式:获取操作数 + 结果类型 → FilterLLVMAttributes(丢弃 access_groups, 0x135b7a20)→ tpu_X_macro::create(b, loc, {resT}, {operand}, attrs)replaceOp_macro intrinsic 是 EUP VALU3 push + result pop 对。
  • AluEpOpLowering<Src, Compute, UnpackF, PackF> — 30 个实例化。1:N 路径:操作数是 packed sub-element vector(例如一个 32 位 lane 中的两个 bf16),因此函数体会把宽 vreg unpack 为一组窄 sub-element 值(UnpackOperand<UnpackOp>, 0x1360fac0),对每个片段重新发出 ComputeOp,然后 repackPackResults<PackOp>, 0x13610940)。

同一个源 op 可以同时出现在两个家族中:sparse_core::RsqrtOp 既有一个 UnaryFloatVector 实例化 (0x1357e540),也有一个 AluEp 实例化 (0x135e1c80)。下面的 IsDynamicallyLegal 谓词决定给定操作数类型触发哪条路径。因此 AluEp 家族是用于任何 elementwise math/arith op 的通用 sub-element staging 包装器;transcendental 只是其中同时也拥有快速 1:1 macro 形式的子集。

表 B — UnaryFloatVectorOpLowering 清单(12;1:1 EUP push+pop macro)

每一行的源 op 都扇入函数体尾部的 macro intrinsic。所有 12 个 matchAndRewrite 符号及其模板参数都已在反编译中确认。

matchAndRewrite @VAsource op→ EUP macro intrinsicEUP selector
0x1357e2c0math::TanhOptpu_tanh_macro (0x14988180)Tanh 0x13/0x1b
0x1357e540sparse_core::RsqrtOptpu_rsqrt_macro (0x14735840)ReciprocalSqrt 0x10/0x0c
0x1357e880math::Log2Optpu_log2_macro (0x14730640)LogTwo 0x12/0x1a
0x1357eb00sparse_core::ReciprocalOptpu_rcp_macro (0x147346c0)Reciprocal 0x15/0x1d
0x1357ef40sparse_core::Log2Optpu_log2_macro (0x14730640)LogTwo 0x12/0x1a
0x1357f380sparse_core::Pow2Optpu_pow2_macro (0x147339c0)PowTwo 0x11/0x19
0x1357f6c0math::SinOptpu_sin_macro (0x14736880)Sinq 0x17/0x1e
0x1357f840math::CosOptpu_cos_macro (0x146d8540)Cosq 0x18/0x1f
0x1357fac0sparse_core::VsinqOptpu_sin_macro (0x14736880)Sinq 0x17/0x1e
0x1357ff00sparse_core::VcosqOptpu_cos_macro (0x146d8540)Cosq 0x18/0x1f
0x13580240math::ErfOptpu_erf_macro (0x1472efa0)Erf 0x0e/0x0f
0x135804c0sparse_core::VsigshftOptpu_sigshft (0x147365e0)ShiftedSigmoid 0x14/0x1c

八种不同 macro,即 rsqrtrcptanhsincoserflog2pow2,外加裸 tpu_sigshftmath::sparse_core:: 源 op 会扇入同一 macro:math.tanh 和一个 sc.tanh 都到达 tpu_tanh_macrosc::Vsinq/Vcosq/Vsigshft op 是 EUP-native dialect 形式。

表 C — AluEpOpLowering 清单(30;1:N unpack → compute → pack)

每个 AluEpOpLowering<Src, Compute, Unpack, Pack> 都会 unpack 操作数,对每个 sub-element 片段重新发出 Compute op,然后 repack。第 3 列是重新发出的 Compute op;pack 家族(F=float,SI=signed-int,UI=unsigned-int)是 Unpack/Pack 模板对。所有 30 个模板签名都已在反编译中确认。

matchAndRewrite @VAsource opre-emitted Computepackclass
0x135de780math::RsqrtOpmath::RsqrtOpFtranscendental
0x135df200math::ExpOpmath::ExpOpFtranscendental
0x135dfca0math::Log2Opmath::Log2OpFtranscendental
0x135e0740math::TanhOpmath::TanhOpFtranscendental
0x135e11e0math::FloorOpmath::FloorOpFrounding
0x135e1c80sparse_core::RsqrtOpsparse_core::RsqrtOpFtranscendental
0x135e2720sparse_core::ReciprocalOpsparse_core::ReciprocalOpFtranscendental
0x135e31c0math::AbsFOpmath::AbsFOpFabs
0x135e3c60arith::FPToSIOparith::FPToSIOpF→SIconvert
0x135e4700math::CeilOpmath::CeilOpFrounding
0x135e51a0sparse_core::Pow2Opsparse_core::Pow2OpFtranscendental
0x135e5c40sparse_core::Log2Opsparse_core::Log2OpFtranscendental
0x135e66e0arith::AddFOparith::AddFOpFbinary float
0x135e7160arith::DivFOparith::DivFOpFbinary float
0x135e7c00math::CopySignOpmath::CopySignOpFbinary float
0x135e86a0arith::MaximumFOparith::MaximumFOpFbinary float
0x135e9140arith::MinimumFOparith::MinimumFOpFbinary float
0x135e9be0arith::MulFOparith::MulFOpFbinary float
0x135ea680arith::NegFOparith::NegFOpFunary float
0x135eb120arith::SubFOparith::SubFOpFbinary float
0x135ebbc0sparse_core::ClampFOpsparse_core::ClampFOpFclamp
0x135ec660arith::MaxSIOparith::MaxSIOpSIbinary int
0x135ed100arith::MinSIOparith::MinSIOpSIbinary int
0x135edba0arith::MaxUIOparith::MaxUIOpUIbinary uint
0x135ee640arith::MinUIOparith::MinUIOpUIbinary uint
0x135ef0e0arith::MulIOparith::MulIOpSIbinary int
0x135efb80arith::AddIOparith::AddIOpSIbinary int
0x135f0620arith::SubIOparith::SubIOpSIbinary int
0x135f10c0sparse_core::AddSIOparith::AddIOpSIint alias
0x135f1b20sparse_core::AddUIOparith::AddIOpUIuint alias

模板签名固定了两个结构事实:

  • 两个 sc:: int alias 会重新发出 arith::AddIOp sparse_core::AddSIOpUnpackSIOp/PackSIOp)和 sparse_core::AddUIOpUnpackUIOp/PackUIOp)都把其 Compute op lowering 为 arith::AddIOp;它们是整数加法的 SI/UI 风格别名,不是不同的 compute kernel。
  • Exp 没有 EUP macro。 math::ExpOp 出现在 AluEp 中(0x135df200,重新发出 math::ExpOp);不存在 tpu_exp_macro。Exp 始终是 polynomial/pow2 构建形式,而不是单个 EUP push,这与 Exp 不在 EUP-native 函数集合中一致。

共享 unpack/pack 层:

text
UnpackOperand<UnpackFOp>  0x1360fac0      PackResults<PackFOp>   0x13610940
UnpackOperand<UnpackSIOp> 0x13610080      PackResults<PackSIOp>  0x13611280
UnpackOperand<UnpackUIOp> 0x136104e0      PackResults<PackUIOp>  0x13610de0
GetUnpackResultElementType                0x1360ff20
```text

1:N 函数体是在 `AluEp<math::ExpOp>` (`0x135df200`) 上解码的:`math::ExpOp::create` (`0x1782be40`) 被调用两次,一次作为类型探测,一次作为逐片段循环体;两次调用由 `UnpackOperand` 和 `PackResults` 包围。

### 1:1-vs-1:N 决策 — `IsDynamicallyLegal`

两个家族之间的选择器是 `addDynamicallyLegalOp` 谓词 `IsDynamicallyLegal(Operation*, SparseCoreTarget&, int)` (`0x135ddd20`),它由 `PackedOperandsLowering::AddDynamicallyLegalAluEpOps<Op, UnpackF, PackF>` 按 op 安装。当操作数**不是** packed sub-element vector 时,它会把 AluEp pattern 标记为*原样合法*(因此 AluEp **不会**触发;运行 1:1 `UnaryFloatVector` macro 路径);当操作数需要 sub-element staging 时,它会标记为*非法*(AluEp **会**触发)。该谓词调用:

```text
ForceBF16ALUOperationsToUnpack(op, type)   0x135dd6e0   (force-unpack BF16 ALU ops)
IsPackedVectorType(type, target, bool)     0x13611720   (packed sub-element layout, e.g. 2×bf16 / 32-bit lane?)
lowering_util::GetVpackFormat(type)        0x13dad800   (VPACK format enum)
element bit-width == 0x10                    @0x135dddc9  (16-bit → bf16/fp16 packed-pair detect)
target vtable *0x780                         @0x135ddda5  (lane-width / pack capability)
target vtable *0x260                         @0x135dde75  (EUP / format support)

NOTE (HIGH) — 谓词的结构,即“当且仅当操作数是 packed sub-element vector 时触发 AluEp”,已通过 IsPackedVectorType / ForceBF16ALUOperationsToUnpack / GetVpackFormat 调用以及 element-width == 16 测试由反编译确认。没有解析出名称的是目标 vtable 槽 *0x780(lane width)和 *0x260(EUP-format support)这两个访问器在各世代上的精确返回;已确认它们被调用,但其逐世代数值,即会让 unpack 迭代次数数字化的值,没有逐字节解码。这是本页唯一低于 CONFIRMED 的地方。

NOTE (INFERRED) — 因为一个 transcendental 源 op 会注册在两个家族中(例如 sc::RsqrtOp0x1357e540 处的 UnaryFloatVector 和 0x135e1c80 处的 AluEp),当两者都可匹配时必然存在某种仲裁。动态合法性谓词决定适用性(packed 操作数 → AluEp;适配 lane 的操作数 → 1:1 macro),但显式 PatternBenefit 整数和 conversion-target 合法性标记顺序只做了结构追踪,没有逐字节解码;本页把动态合法性谓词视为有效选择器。

当前世代都提供 EUP

SupportsScEupOpsViperfishSparseCoreTarget (0x1d49c8c0, mov $1, al) 和 GhostLiteSparseCoreTarget (0x1d499420, mov $1, al) 上都返回 true;两者都提供 EUP transcendental unit,因此 UnaryFloatVector macro lowerings 在 IR 层面普遍可用。(是否存在成本模型上廉价的单 µop sinq/cosq 路径,是本 lowering 下游的另一个逐世代问题,而不是这些 pattern 决定的。)


相关组件

NameRelationship
LowerToSparseCoreLlvmPass::lowerFunc (0x13568280)构建转换器并安装 AS-attr lambda
MemorySpaceToAddressSpace (0x14b78780)转换 lambda 调用的正向映射(表 A)
LLVM::LLVMPointerType::get (0x1746eb40)把原始 ID 盖到描述符的 !llvm.ptr<ID>
CheckAddressSpaces (0x135b8e00)simple-DMA 的 SMEM-on-SCS 合法性门控(表 D)
CastTileSmemPointerToSmem (0x135b86e0)在门控看到端点之前将其规范化为 SMEM
IsDynamicallyLegal (0x135ddd20)两个 EUP 家族之间的 1:1-vs-1:N 选择器

交叉引用

  • LowerToSparseCoreLlvm — 建立在本页产生的转换类型之上的逐类 rewrite 函数体。
  • LowerToMlo DMA Bridge-CastCheckAddressSpaces 所在的两阶段 DMA lowering。
  • The tpu MLIR Dialect — 这些 pattern 重写到的 tpu/sparse_core op 的 op-registration ABI。
  • Compiler Overview — Part V 导览;SC lowering 在五阶段下降中的位置。
  • Fat Pointers (AS7/8/9) — 此转换器消费的 AS-id ↔ MemorySpace ↔ pool 表(不要重复)。
  • addrspacecast ISel — flatten 折叠集合驱动的 MemorySpaceCastllvm.addrspacecast 转换。
  • Tile-ID CastCastTileSmemPointerToSmem 发出的 2 操作数 {base, tileId} cast,用于规范化 tile 指针。
  • SCS Enginesc.sequencer=="scs" 合法性逃逸所命名的 Scalar Core Sequencer。
  • TEC Engine — 其 sequencer 上下文解锁 per-tile flatten 的 per-tile execute lane。
  • GetSequencerType — flatten 布尔值(b0/b1)读取的 sequencer-type 属性。
  • Binary: extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id 89edbbe81c5b328a958fe628a9f2207d
  • Index entry: Part V — Compiler: Lowering & Optimization Passes / MLIR lowering chain — back to index