SCTypeConverter
本页中的所有地址、符号名和表值都逐字节精确读取自
libtpu-0.0.40-cp314wheel 中的libtpu.so(构建libtpu_lts_20260413_b_RC00,BuildID md589edbbe81c5b328a958fe628a9f2207d)。.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 就是MemorySpaceCastlowering 查询的 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 样板后如下:
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。SCMemorySpaceAttr不会存活到 LLVM dialect 中;存活的只有其数值 address-space ID,它以i64整数属性携带,供基础转换器在构造ptr<ID>时消费。试图原样传递源 dialect 属性的重新实现,将无法匹配基础转换器实际构造的描述符。
表 A — MemorySpaceToAddressSpace 反向表 (0xaf36ce8)
SCTypeConverter 对 !llvm.ptr<N> 的结果是 N = MemorySpaceToAddressSpace(MS)。索引 = MS − 1;MemorySpace 8 是空洞(无效 → LOG(FATAL))。flatten 列是 lowerFunc 覆盖(下一节);lowerAsserts lambda 省略它。
| MS | pool | → addrspace (= ptr<N>) | sequencer flatten |
|---|---|---|---|
| 1 | smem | 0 (0x00) | — |
| 2 | tile_spmem | 201 (0xC9) | — |
| 3 | spmem | 202 (0xCA) | — |
| 4 | hbm | 203 (0xCB) | — |
| 5 | sflag | 204 (0xCC) | — |
| 6 | vmem | 205 (0xCD) | — |
| 7 | dreg | 208 (0xD0) | — |
| 8 | (gap) | invalid → LOG(FATAL) | — |
| 9 | smem_any | 212 (0xD4) | — |
| 10 | hbm_any | 213 (0xD5) | — |
| 11 | timem | 214 (0xD6) | — |
| 12 | simem | 215 (0xD7) | — |
| 13 | iova | 216 (0xD8) | — |
| 14 | sflag_tile | 217 (0xD9) | → 204 if !execute-seq |
| 15 | spmem_any | 218 (0xDA) | — |
| 16 | smem_tile | 219 (0xDB) | → 0 if !execute-seq |
| 17 | mar | 220 (0xDC) | — |
| 18 | tile_spmem_cb | 501 (0x1F5) | — |
| 19 | smem_cb | 502 (0x1F6) | — |
| 20 | sflag_scs | 223 (0xDF) | → 204 if !core-seq |
| 21 | smem_scs | 224 (0xE0) | → 0 if !core-seq |
| 22 | sflag_tc | 204 (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 的函数中捕获的两个闭包布尔值门控:
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 |
|---|---|---|
| 1 | tgt.SupportsTileSmemDma() | vtable +0xd8; VF = false, GL = false |
| 2 | enclosing llvm.func's sc.sequencer inherent attr == "scs" | getInherentAttr("sc.sequencer", 12) → 3-char cmp |
| 3 | srcAS != 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") → failure | error 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 目标上都返回 false:ViperfishSparseCoreTarget (0x1d49c8e0, xor eax,eax) 和 GhostLiteSparseCoreTarget (0x1d499460, xor eax,eax),它们分别通过 vtable 的 R_X86_64_RELATIVE 加数 0x21cc9078 和 0x21cc86f8 到达。因此在每个当前世代上,条件 (1) 都是死的,有效规则是条件 (2) ∨ (3):触及 SMEM 的 simple DMA 只在 SCS 上合法。 这是 SCS 作为 SparseCore 标量控制引擎这一事实在 IR 侧的对应物;见 SCS Engine。
唯一调用点与 address-space 规范化
唯一调用者是 DmaSimpleStartOpLowering::matchAndRewrite (0x135a9100),调用位于 0x135a977a。关键点是,该入口看到的 srcAS/dstAS 是post-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。_macrointrinsic 是 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,然后 repack(PackResults<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 @VA | source op | → EUP macro intrinsic | EUP selector |
|---|---|---|---|
0x1357e2c0 | math::TanhOp | tpu_tanh_macro (0x14988180) | Tanh 0x13/0x1b |
0x1357e540 | sparse_core::RsqrtOp | tpu_rsqrt_macro (0x14735840) | ReciprocalSqrt 0x10/0x0c |
0x1357e880 | math::Log2Op | tpu_log2_macro (0x14730640) | LogTwo 0x12/0x1a |
0x1357eb00 | sparse_core::ReciprocalOp | tpu_rcp_macro (0x147346c0) | Reciprocal 0x15/0x1d |
0x1357ef40 | sparse_core::Log2Op | tpu_log2_macro (0x14730640) | LogTwo 0x12/0x1a |
0x1357f380 | sparse_core::Pow2Op | tpu_pow2_macro (0x147339c0) | PowTwo 0x11/0x19 |
0x1357f6c0 | math::SinOp | tpu_sin_macro (0x14736880) | Sinq 0x17/0x1e |
0x1357f840 | math::CosOp | tpu_cos_macro (0x146d8540) | Cosq 0x18/0x1f |
0x1357fac0 | sparse_core::VsinqOp | tpu_sin_macro (0x14736880) | Sinq 0x17/0x1e |
0x1357ff00 | sparse_core::VcosqOp | tpu_cos_macro (0x146d8540) | Cosq 0x18/0x1f |
0x13580240 | math::ErfOp | tpu_erf_macro (0x1472efa0) | Erf 0x0e/0x0f |
0x135804c0 | sparse_core::VsigshftOp | tpu_sigshft (0x147365e0) | ShiftedSigmoid 0x14/0x1c |
八种不同 macro,即 rsqrt、rcp、tanh、sin、cos、erf、log2、pow2,外加裸 tpu_sigshft。math:: 和 sparse_core:: 源 op 会扇入同一 macro:math.tanh 和一个 sc.tanh 都到达 tpu_tanh_macro;sc::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 @VA | source op | re-emitted Compute | pack | class |
|---|---|---|---|---|
0x135de780 | math::RsqrtOp | math::RsqrtOp | F | transcendental |
0x135df200 | math::ExpOp | math::ExpOp | F | transcendental |
0x135dfca0 | math::Log2Op | math::Log2Op | F | transcendental |
0x135e0740 | math::TanhOp | math::TanhOp | F | transcendental |
0x135e11e0 | math::FloorOp | math::FloorOp | F | rounding |
0x135e1c80 | sparse_core::RsqrtOp | sparse_core::RsqrtOp | F | transcendental |
0x135e2720 | sparse_core::ReciprocalOp | sparse_core::ReciprocalOp | F | transcendental |
0x135e31c0 | math::AbsFOp | math::AbsFOp | F | abs |
0x135e3c60 | arith::FPToSIOp | arith::FPToSIOp | F→SI | convert |
0x135e4700 | math::CeilOp | math::CeilOp | F | rounding |
0x135e51a0 | sparse_core::Pow2Op | sparse_core::Pow2Op | F | transcendental |
0x135e5c40 | sparse_core::Log2Op | sparse_core::Log2Op | F | transcendental |
0x135e66e0 | arith::AddFOp | arith::AddFOp | F | binary float |
0x135e7160 | arith::DivFOp | arith::DivFOp | F | binary float |
0x135e7c00 | math::CopySignOp | math::CopySignOp | F | binary float |
0x135e86a0 | arith::MaximumFOp | arith::MaximumFOp | F | binary float |
0x135e9140 | arith::MinimumFOp | arith::MinimumFOp | F | binary float |
0x135e9be0 | arith::MulFOp | arith::MulFOp | F | binary float |
0x135ea680 | arith::NegFOp | arith::NegFOp | F | unary float |
0x135eb120 | arith::SubFOp | arith::SubFOp | F | binary float |
0x135ebbc0 | sparse_core::ClampFOp | sparse_core::ClampFOp | F | clamp |
0x135ec660 | arith::MaxSIOp | arith::MaxSIOp | SI | binary int |
0x135ed100 | arith::MinSIOp | arith::MinSIOp | SI | binary int |
0x135edba0 | arith::MaxUIOp | arith::MaxUIOp | UI | binary uint |
0x135ee640 | arith::MinUIOp | arith::MinUIOp | UI | binary uint |
0x135ef0e0 | arith::MulIOp | arith::MulIOp | SI | binary int |
0x135efb80 | arith::AddIOp | arith::AddIOp | SI | binary int |
0x135f0620 | arith::SubIOp | arith::SubIOp | SI | binary int |
0x135f10c0 | sparse_core::AddSIOp | arith::AddIOp | SI | int alias |
0x135f1b20 | sparse_core::AddUIOp | arith::AddIOp | UI | uint alias |
模板签名固定了两个结构事实:
- 两个
sc::int alias 会重新发出arith::AddIOp。sparse_core::AddSIOp(UnpackSIOp/PackSIOp)和sparse_core::AddUIOp(UnpackUIOp/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 层:
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::RsqrtOp在0x1357e540处的 UnaryFloatVector 和0x135e1c80处的 AluEp),当两者都可匹配时必然存在某种仲裁。动态合法性谓词决定适用性(packed 操作数 → AluEp;适配 lane 的操作数 → 1:1 macro),但显式PatternBenefit整数和 conversion-target 合法性标记顺序只做了结构追踪,没有逐字节解码;本页把动态合法性谓词视为有效选择器。
当前世代都提供 EUP
SupportsScEupOps 在 ViperfishSparseCoreTarget (0x1d49c8c0, mov $1, al) 和 GhostLiteSparseCoreTarget (0x1d499420, mov $1, al) 上都返回 true;两者都提供 EUP transcendental unit,因此 UnaryFloatVector macro lowerings 在 IR 层面普遍可用。(是否存在成本模型上廉价的单 µop sinq/cosq 路径,是本 lowering 下游的另一个逐世代问题,而不是这些 pattern 决定的。)
相关组件
| Name | Relationship |
|---|---|
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-Cast —
CheckAddressSpaces所在的两阶段 DMA lowering。 - The
tpuMLIR Dialect — 这些 pattern 重写到的tpu/sparse_coreop 的 op-registration ABI。 - Compiler Overview — Part V 导览;SC lowering 在五阶段下降中的位置。
- Fat Pointers (AS7/8/9) — 此转换器消费的 AS-id ↔
MemorySpace↔ pool 表(不要重复)。 - addrspacecast ISel — flatten 折叠集合驱动的
MemorySpaceCast→llvm.addrspacecast转换。 - Tile-ID Cast —
CastTileSmemPointerToSmem发出的 2 操作数{base, tileId}cast,用于规范化 tile 指针。 - SCS Engine —
sc.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-id89edbbe81c5b328a958fe628a9f2207d) - Index entry: Part V — Compiler: Lowering & Optimization Passes / MLIR lowering chain — back to index