DmaParameters 选择器
本页所有地址均适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(构建libtpu_lts_20260413_b_RC00,build-id md589edbbe81c5b328a958fe628a9f2207d)。该镜像未剥离符号;反混淆后的 C++ 符号名按原样引用。.textVMA 等于文件偏移(.text基址0xe63c000)。所有+0xNN偏移都进入DmaParameters包(内存中的 C++ 结构体,不是线描述符)。其他版本会不同。
摘要
一旦 LowerPassBase 将一个平铺的 enqueue_dma 拆成逐 tile 传输,每个 tile 都会交给一个单独的逐 tile 发射器:LowerMemrefToMlo::issueStridedTransfer(0x1350cb60,lower_memref_to_mlo.cc)。它的工作是选择能够表达该 tile 访问模式的成本最低的具体 LLO DMA-start op。该决策是一个二维选择器:一条轴是 TransferKind(kDma vs kStream),另一条轴是 tile 有多少级 stride,直接从 DmaParameters::src_byte_strides.size()(+0x18)读取。零级 stride 表示平坦连续区间(DmaSimpleStart / LinearStream 快路径);一级 stride 表示规则的二维矩形(DmaSingleStridedStart / StridedStream);超过一级表示需要完整描述符的不规则 gather(GeneralDma,或者对于 stream 是硬错误,因为 stream 最多支持一级 striding)。
只根据 src_byte_strides.size() 选择形式会很幼稚:一个 4-D tile 的内侧三个维度如果恰好是 row-major 连续的,应该发射为平坦拷贝,而不是 4 级 general DMA。因此选择之前会先运行一个维度合并优化器,该优化器位于调用方 LowerPassBase::issueRolledTransfer(0x13516ca0,lower_pass_base.cc:123)内部。它从最内侧维度向外遍历逐维 stride 数组,并扁平化任意相邻对 (d, d+1),只要该对退化(内侧 extent 是常量 1),或者确实是 row-major 连续(外侧 stride 正好等于内侧 stride 乘以外侧 extent,并分别对 source 和 destination 检查)。每次合并都会通过 arith.muli 将两个维度折叠成一个更大的 stride,用 memmove 将三个并行数组各缩短一项,并记录 VLOG(1) "Flattening striding dimension <d>"。合并先运行;只有未能扁平化的残余维度会成为逐 tile 的 DmaParameters,随后选择器据此分派。净效果是:传输越连续,编译器落到的描述符形式越便宜。
本页负责两件事:issueStridedTransfer 内部的形式选择规则(TransferKind × src_byte_strides.size() 的 3×2 网格、入口检查强制的 DmaParameters 大小不变量、stream gather/scatter 门控,以及 length→granularity 除数),以及维度合并优化器(反向相邻对扫描和精确的连续性合并谓词)。六个发射器主体,也就是每个 ::create 如何将操作数绑定到描述符,见 Rolled / Strided / General 发射器;描述符字段布局和 memory-space/opcode 枚举见 片上 DMA 描述符;构造 DmaParameters 数组的 tile-index→stride 代数见 Tile-Index 展开。
重新实现时,契约是:
- 3×2 选择网格:
TransferKind(kDma=0/kStream=1/ 其他→OpError)与src_byte_strides.size()(0/1/>1)交叉,映射到六个具体 SparseCore op 加两个错误分支。 DmaParameters大小不变量:|src_byte_strides| == |tgt_byte_strides| == |steps_per_stride| - 1,由 strided 分支入口的两个CHECK_EQ强制,并且形式根据|src_byte_strides|选择。- 合并谓词:当且仅当
inner_extent == 1或者(outer_base是编译期常量 并且 每侧stride[outer] == stride[inner] × outer_extent)时折叠(d, d+1),并执行arith.muli+ 3 次memmove折叠。 - stream 门控:
kStream传输最多只能携带一级 stride(两个不同的<= 1断言,每侧一个);gather 不允许 destination 带 stride;scatter 不允许 source 带 stride。
| 逐 tile 发射器(选择器) | mlir::tpu::LowerMemrefToMlo::issueStridedTransfer(OpBuilder&, EnqueueDMAOp, Value src, Value dst, DmaParameters const&, optional<DeviceAndCoreIds>, int priority, …::TransferKind, bool) @ 0x1350cb60 |
| 合并优化器 | mlir::tpu::LowerPassBase::issueRolledTransfer(…) @ 0x13516ca0 — 合并循环主体,lower_pass_base.cc:123 |
| 逐侧 stride 验证器 | issueStridedTransfer::$_0::operator()(ArrayRef<int>) @ 0x13510340 |
| general 形式 helper | mlir::tpu::(anonymous namespace)::issueGeneralDma(…) @ 0x1350b3a0 |
| 常量整数探测 | LowerPassBase vtable +0x20((*(this)+0x20)(this, value))— getConstantIntValue 风格的 fold |
| 形式键 | DmaParameters::src_byte_strides.size() @ +0x18 |
| 源文件 | lower_memref_to_mlo.cc(选择器),lower_pass_base.cc(合并) |
| 证据等级 | 重新实现级 / 经 IDA 反编译字节确认(issueStridedTransfer 主体,issueRolledTransfer 合并循环) |
1. 选择网格(TransferKind × stride-count)
目的
issueStridedTransfer 是抽象逐 tile 传输变成具体硬件 op 的唯一位置。它不决定哪些 tile存在(那是上游 rolled-loop 机制的工作),而是针对一个已经平铺好的传输决定描述符形式。具体形式正好有六种,每个 TransferKind 三种,而选择完全由两个整数决定:kind,以及 stride 级数。
两条轴
轴 1 — TransferKind 是第八个参数(一个字节,位于 int priority 之后)。分派是三路 switch:
TransferKind | 值 | 路径 |
|---|---|---|
kDma | 0 | DMA 形式(Simple / SingleStrided / General) |
kStream | 1 | Stream 形式(Linear / Strided / 错误) |
| (任何其他值) | ≥2 | emitOpError("Unsupported transfer kind: %d") |
默认分支通过 absl::str_format_internal::FormatPack("Unsupported transfer kind: %d", 29, …) 将原始整数格式化进 InFlightDiagnostic,并返回失败的 LogicalResult。这是面向用户的错误,不是 CHECK,因为 kind 来源于正在 lowering 的 op。
轴 2 — stride 级数 是 DmaParameters::src_byte_strides.size(),从包偏移 +0x18 读取为 u32。在每种 kind 内,选择器按 0 / 1 / >1 分支:
// issueStridedTransfer @ 0x1350cb60 — form selection (reconstructed)
n = p.src_byte_strides.size(); // *(u32*)(p + 0x18)
switch (kind) {
case kDma: // TransferKind == 0
if (n == 0 && !force_descriptor) // also gated by the bool arg / cross-device presence
emit DmaSimpleStartOp; // contiguous
else if (n == 1)
emit DmaSingleStridedStartOp; // one stride level
else /* n > 1 */
issueGeneralDma(...); // multi-stride -> GeneralDma
break;
case kStream: // TransferKind == 1
if (n == 0)
emit LinearStreamStartOp;
else if (n == 1)
emit StridedStreamStartOp; // CHECK: steps_per_stride.size() == 2
else /* n > 1 */
// hard error: streams support <= 1 stride level (see §4)
break;
default:
return emitOpError("Unsupported transfer kind: %d", kind);
}
```text
### 3×2 网格
从 `(kind, src_byte_strides.size())` 到发射 op 的完整映射如下(每个 op 的操作数绑定见 [Rolled / Strided / General 发射器](rolled-strided-general.md),op 填充的描述符见 [片上 DMA 描述符](intra-chip-descriptor.md)):
| `src_byte_strides.size()` | `kDma` (0) | `kStream` (1) |
|---|---|---|
| **0**(连续) | `DmaSimpleStartOp::create` @ `0x145b9740` | `LinearStreamStartOp::create` @ `0x145e3440` |
| **1**(一级 stride) | `DmaSingleStridedStartOp::create` @ `0x145bcd20` | `StridedStreamStartOp::create` @ `0x1460b8e0` |
| **>1**(多级 stride) | `issueGeneralDma` @ `0x1350b3a0` → `GeneralDma` | **错误** — `"Streams support up to 1 level of striding"` |
| (否则 `TransferKind`) | `emitOpError("Unsupported transfer kind: %d")` | 同左 |
> **注意 —** `kDma`/`n==0` 的 Simple 快路径还会被尾随 `bool` 参数(`[rbp+0x38]`)以及 `optional<DeviceAndCoreIds>` 跨设备 id 的存在性额外门控:只要任一者被设置,连续情形就会被强制进入 Strided/General 分支,而不是采用 Simple 形态。门控算术已按字节确认;该标志的精确语义为 **HIGH**,与“is-remote / force-descriptor”角色一致(跨设备传输不能使用本地 Simple 描述符)。重新实现者可以将规则视为:*只有纯本地、零 stride 的 tile 才采用 Simple。*
### priority 前置条件
该函数做的第一件事就是断言其 `int priority` 参数为零:
```c
// @ entry — lower_memref_to_mlo.cc:1142
if (priority != 0)
LogMessageFatal("priority == 0"); // MakeCheckOpString<long,long>这是硬 CHECK(absl::log_internal::LogMessageFatal),不是可恢复错误;逐 tile 发射器只会以 priority 0 调用。priority 参数是为了匹配与更高层 enqueue 路径共享的 API 形状;在这里它必须为 0。
2. DmaParameters 包
目的
DmaParameters 是选择器消费的逐 tile 描述符,也就是传入的内存中 C++ 结构体(以 const& 传递),不同于它最终产生的线格式 OciDescriptorCommonIssuedFromTcs。它由 rolled-loop 机制逐 tile 构建(构造点在 issueRetiledTransfer;见 Tile-Index 展开),并在这里逐字段读取。选择器只读取它,从不修改它。
布局
字段名来自 CHECK_EQ 字符串字面量(p.src_byte_strides / p.tgt_byte_strides / p.steps_per_stride);偏移和访问宽度来自反编译读取:
| 偏移 | 大小 | 字段 | 类型 |
|---|---|---|---|
+0x00 | 8 | src | mlir::Value |
+0x08 | 8 | dst | mlir::Value |
+0x10 | 8 | src_byte_strides.data() | int*(4-B 条目) |
+0x18 | 4 | src_byte_strides.size() | u32 |
+0x50 | 8 | tgt_byte_strides.data() | int*(4-B 条目) |
+0x58 | 4 | tgt_byte_strides.size() | u32 |
+0x90 | 8 | transfer length / sflag | mlir::Value |
+0x98 | 8 | steps_per_stride.data() | Value*(8-B 条目) |
+0xA0 | 4 | steps_per_stride.size() | u32 |
注意 —
+0x90是作为len/sflagcreate 参数使用的Value;精确字段名(transfer length 还是 sync-flag)不在任何CHECK字符串中,因此是推断。偏移、8 字节Value类型以及作为 create 参数的用途均由字节钉牢。
大小不变量
在进入 strided/general 分支时,两个 CHECK_EQ 会强制三个 vector 耦合(lower_memref_to_mlo.cc:843/:844):
CHECK_EQ(p.src_byte_strides.size(), p.tgt_byte_strides.size());
// *(u32*)(p+0x18) == *(u32*)(p+0x58)
CHECK_EQ(p.src_byte_strides.size(), p.steps_per_stride.size() - 1);
// *(u32*)(p+0x18) == *(u32*)(p+0xA0) - 1
```text
因此,格式良好的包拥有长度匹配的 source/target stride vector,并且 `steps_per_stride` 条目数正好比 stride 级数**多一**;`steps_per_stride` 是 `N` 级 stride 对应的 `N+1` 累积 extent vector。连续(`N=0`)描述符因此有 `steps_per_stride.size() == 1`。随后形式由 `N = src_byte_strides.size()` 选择:`0` 为 Simple/Linear,`1` 为 SingleStrided/Strided,`>1` 为 General/错误。这是整个 3×2 网格据以分派的唯一整数,也正因如此,会缩小 `N` 的合并 pass(§5)必须先运行。
---
## 3. Length → Granularity 除数
在构建 op 之前,传输长度会从字节转换为硬件*粒度*单位(描述符携带的是粒度计数,而不是原始字节计数;见 [片上 DMA 描述符](intra-chip-descriptor.md) 中的 `(length, length_granule)` 对)。两种 kind 使用不同的 granularity 来源:
- **`kDma`** 除以 `xla::jellyfish::Target::GranuleBytes()`(`0x1d617f80`),使用普通截断整数除法(`len / gran`;`(gran | len) >> 32` 测试只是在 64 位有符号和 32 位无符号 `idiv` 宽度之间选择,**不是**向上取整)。精确可整除性由 `isGuaranteedDivisible(…, 128, …)` 检查保证,失败时抛出 `emitOpError("Inner DMA transfer size divisible by DMA's inner vector length (%d). Got %s")`(通过 `:878` 处的 `MakeErrorImpl<3>` 构建),所以无需舍入。
- **`kStream`** 除以 `xla::tpu::sparse_core::xla_mlo_util::TransferGranularityInBytes(SparseCoreTarget const&, MemorySpace, bool)`(`0x14a89ea0`),通过 `target()->[+0x948]` 到达;这是 SPMEM-stripe / DMA-word 单位,取决于 memory space,同样使用普通截断除法。
granule 值物化为 `arith.ConstantIndexOp`;stream strided 路径将除法发射为 `DivUIOp`(`createOrFold<arith::DivUIOp>`),而 kDma 路径会将商内联为已 fold 的常量。HBM endpoint 测试(`srcSpace == HBM (4)` / `dstSpace == HBM (4)`,通过与 `4` 比较计算)会馈入 Linear/Strided stream `::create` 接收的 `dstIsHbm` bool。
---
## 4. Stream Striding 门控
stream 比 DMA 更受限制:硬件 stream 引擎最多处理**一级** striding,并且 gather/scatter stream 会禁止其中一侧带 stride。`issueStridedTransfer` 会在发射 `LinearStream`/`StridedStream` 前强制全部三个约束。
### 一级限制(两个断言)
对于 `kStream` strided 路径,二进制按**每侧**断言级数边界,带有两个不同的 `CHECK` 和两个不同的诊断字符串:
```c
// lower_memref_to_mlo.cc:1189
CHECK(src_byte_strides.size() <= 1)
else "Streams support up to 1 level of striding. Got %d levels of source striding.";
// lower_memref_to_mlo.cc:1193
CHECK(tgt_byte_strides.size() <= 1)
else "Streams support up to 1 level of striding. Got %d levels of steps per stride.";单 stride stream 发射本身还由 CHECK_EQ(off_tile_byte_strides.size(), 1)(:1263)和 CHECK_EQ(p.steps_per_stride.size(), 2)(:1264)守护,也就是正好一级 stride,以及匹配的两个累积 step。
陷阱 — “>1 stride”的 stream 情形是致命
CHECK,不是kDma>1情形路由到issueGeneralDma时的可恢复emitOpError。不存在“general stream”形式:必须为 stream 的多 stride 传输到达这里时已经是编译器 bug,因此二进制会 trap。重新实现者必须保证上游 tiler 永远不会把 >1-stride 包交给kStream分支(§5 中的合并正是让连续内侧维度满足该保证的机制)。
Gather / scatter 侧约束
用 mlir::tpu::isGather(0x14afb1e0)分类 op 后,stream 分支会运行逐侧连续性移除 lambda $_0(0x13510340,operator()(ArrayRef<int>)),source 侧和 destination 侧各一次。$_0 首先断言 CHECK_EQ(p.steps_per_stride.size(), 2)(:1208),读取 steps_per_stride(+0x98),并通过 LowerPassBase vtable +0x20 的 getConstantIntValue 探测折叠 steps_per_stride[1],也就是合并循环使用的同一个探测(§5)。当且仅当该级别折叠为编译期常量,且等于传入该侧的 byte-stride extent 时,它返回 true;也就是说,单 stride 级别描述的是连续区间,可以被丢弃。当它返回 true 时,调用方将该侧 stride count 置零,并记录 VLOG(1) "Removing the source striding representing a contiguous access pattern."(:1220)或 target 侧变体(:1225)。随后分类器会基于残余逐侧 stride count 拒绝不对称情形:
| 条件 | 诊断(emitOpError) |
|---|---|
isGather 且 destination 带 stride | "Gather streams do not support destination striding. Got %d level(s) of target striding." |
isScatter 且 source 带 stride | "Scatter streams do not support source striding. Got %d level(s) of source striding." |
gather 将分散的 source 元素拉入 packed destination,因此 destination 本质上是连续的;scatter 则相反。对“packed”侧加 stride 没有意义,并会以面向用户的错误拒绝。
5. 维度合并优化器
目的
选择器(§1)以 src_byte_strides.size() 为键,因此 tile 的 stride 级数越少,落到的形式越便宜。合并 pass 的存在是为了最小化该计数:它在包到达选择器之前,将每个连续(或退化)的相邻维度对折叠成一个更大的 stride。访问模式在数学上是平坦区间的传输会被扁平化到 N=0,并发射为 DmaSimpleStart,即使它最初用多个名义维度描述。合并位于调用方 issueRolledTransfer(lower_pass_base.cc)中;无法扁平化的残余 leading dim 会成为单个 scf.for 循环,内侧已扁平化维度则成为选择器消费的逐迭代 DmaParameters。
反向相邻对扫描
issueRolledTransfer 维护三个并行逐维数组:bases(逐维 extent,8 字节 Value 条目)、src_byte_strides(4 字节 int)和 dst_byte_strides(4 字节 int),并带有三个独立计数。合并循环从最内侧维度向外遍历相邻对,索引 r15 → bases[outer],r12 = r15−8 → bases[inner]。对每一对,它都会通过常量整数探测((*(this)+0x20)(this, base))fold 两个 base,该探测返回打包为 { bit32 = present | low32 = value } 的 optional<int64>:
v47 = getConstantIntValue(bases[outer]); // (*(a1)+0x20)(a1, &bases[outer])
v48 = getConstantIntValue(bases[inner]); // same probe on the inner dim
```text
### 精确连续性合并谓词
当且仅当如下条件成立时,相邻对 `(outer d, inner d+1)` 会被折叠(字节精确,`issueRolledTransfer` 反编译第 317–320 行;`0x100000001` = present-bit | value 1):
```c
if ( (v48 & 0x1FFFFFFFF) == 0x100000001 // (i)
|| (v47 & 0x100000000) != 0 // (ii) gate
&& src_stride[outer] == (int)v47 * src_stride[inner] // (ii) src contiguity
&& dst_stride[outer] == dst_stride[inner] * (int)v47 ) // (ii) dst contiguity- 条件 (i) — 退化内侧维度。
getConstantIntValue(bases[inner])存在且等于1。内侧 extent 为 1 不贡献迭代,因此该对总是 fold,无需检查 stride。((x & 0x1ffffffff) == 0x100000001掩码在一次比较中测试“present 且 value == 1”。) - 条件 (ii) — row-major 连续性。
getConstantIntValue(bases[outer])存在(编译期常量 extent,即test r13, 0x100000000present-bit 门控)并且按每侧,外侧 stride 等于内侧 stride 乘以外侧 extent:src_stride[outer] == src_stride[inner] × outer_extent且dst_stride[outer] == dst_stride[inner] × outer_extent。这正是两个 row-major 维度无间隙相邻时的代数;它们描述一个更大的连续 stride。
若两者都不成立,该对会保持原样,并成为残余 stride/loop 维度。
| 步骤 | 测试 | 含义 |
|---|---|---|
| a | v47 = getConstantIntValue(bases[outer]) | 对外侧 extent 进行 vtable +0x20 fold |
| b | v48 = getConstantIntValue(bases[inner]) | 对内侧 extent 做同样 fold |
| c | (v48 & 0x1ffffffff) == 0x100000001 | (i) 内侧 extent 是常量 1 |
| d | v47 & 0x100000000 | (ii) 门控:外侧 extent 是常量 |
| e | src_stride[outer] == (int)v47 × src_stride[inner] | (ii) source 连续性 |
| f | dst_stride[outer] == dst_stride[inner] × (int)v47 | (ii) destination 连续性 |
| MERGE | (c) 或 (d && e && f) | 折叠 (d, d+1) → 一个维度 |
注意 — 条件 (i) 是刻意的快路径,不是 (ii) 的子集:count-1 维度会在不查询任一侧 stride 的情况下 fold。当内侧 stride 为 0 时,(i) 是否在逻辑上被 (ii) 包含是推断;二进制中的两个分支目标不同,而 (i) 对退化维度是保持正确性的 (ii) 超集。
折叠
合并时,内侧 extent 会乘入外侧以形成合并后维度的 extent,诊断会被记录,三个数组会越过被消费的槽向下移动一位:
// merged extent
merged = OpBuilder::createOrFold<arith::MulIOp>(bases[outer], bases[inner]);
VLOG(1) << "Flattening striding dimension " << d; // lower_pass_base.cc:123
bases[d] = merged;
memmove(bases + d*8 ..., 8 * (count - 1)); --bases_count; // 8-B Value entries
memmove(src_strides + d*4 ..., 4 * (count - 1)); --src_count; // 4-B int entries
memmove(dst_strides + d*4 ..., 4 * (count - 1)); --dst_count; // 4-B int entries
```text
`arith.muli` 是 `createOrFold`(两个 `Value` 上的 `mlir::arith::MulIOp`),因此两个编译期常量 extent 会立即 fold 为常量。三个 `memmove` 以 lock-step 缩短并行数组(8 字节 bases、4 字节 source strides、4 字节 destination strides),并分别递减各自计数。随后扫描在缩短后的数组上继续。当它耗尽时,残余 leading dim 成为单个 `scf::ForOp`,扁平化后的包被交给逐 tile 回调,也就是交给 `issueStridedTransfer` 和 §1 的选择器。
---
## 6. 成本排序
选择器编码了一套成本层级:只要访问模式允许,就优先选择更便宜的描述符。从最便宜到最昂贵:
| 排名 | 形式(`kDma` / `kStream`) | 选择时机 | 成本依据 |
|---:|---|---|---|
| 1 | `DmaSimpleStart` / `LinearStream` | `src_byte_strides.size() == 0`(且本地) | 一个连续区间;无 stride 操作数,描述符最小 |
| 2 | `DmaSingleStridedStart` / `StridedStream` | `src_byte_strides.size() == 1` | 一级 stride;单个二维矩形,四个额外 stride `Value` |
| 3 | `GeneralDma`(仅 `kDma`) | `src_byte_strides.size() > 1` | 多级 / 跨 core;完整 stride list + 双侧 sync |
| — | 错误 | `kStream` 带 `> 1` 级,或未知 kind | 无法表示为单个 op |
合并(§5)是将传输*上移*到该排序中更便宜位置的机制:通过扁平化连续维度,它将 `src_byte_strides.size()` 推向 0,因此同一个逻辑传输会用其实际内存布局允许的最便宜形式发射。选择规则本身随后只是对合并后计数做平坦查表;没有成本模型或搜索,计数*就是*成本类别。
---
## 7. 诊断字符串
两个函数可能打印的所有字符串,以及 trap 种类和源码位置。`CHECK`/`LogMessageFatal` 是*编译器内部*不变量(违反表示上游 tiler 中的 bug);`emitOpError` 是*面向用户*的(违反表示输入 op 无法表示)。`VLOG(1)` 是信息性日志。
| 字符串 | 种类 | 位置 | 触发条件 |
|---|---|---|---|
| `priority == 0` | `CHECK`(致命) | `lower_memref_to_mlo.cc:1142` | 入口处 `priority != 0` |
| `p.src_byte_strides.size() == p.tgt_byte_strides.size()` | `CHECK_EQ`(致命) | `:843` | source/target stride vector 长度不同 |
| `p.src_byte_strides.size() == p.steps_per_stride.size() - 1` | `CHECK_EQ`(致命) | `:844` | 对 `N` 个 stride,`steps_per_stride` 不是 `N+1` |
| `off_tile_byte_strides.size() == 1` | `CHECK_EQ`(致命) | `:1263` | 单 stride stream 的 off-tile stride 数不等于 1 |
| `p.steps_per_stride.size() == 2` | `CHECK_EQ`(致命) | `:1264` | 单 stride stream 的 cumulative step 数不等于 2 |
| `src_byte_strides.size() <= 1` | `CHECK`(致命) | `:1189` | stream 上 source stride 级数 >1 |
| `tgt_byte_strides.size() <= 1` | `CHECK`(致命) | `:1193` | stream 上 target stride 级数 >1 |
| `Unsupported transfer kind: %d` | `emitOpError` | (kind 分派默认分支) | `TransferKind` ∉ {`kDma`, `kStream`} |
| `Streams support up to 1 level of striding. Got %d levels of source striding.` | `emitOpError` | (stream 门控) | stream source stride 过多 |
| `Streams support up to 1 level of striding. Got %d levels of steps per stride.` | `emitOpError` | (stream 门控) | stream target stride 过多 |
| `Gather streams do not support destination striding. Got %d level(s) of target striding.` | `emitOpError` | (gather 门控) | `isGather` 且 destination 带 stride |
| `Scatter streams do not support source striding. Got %d level(s) of source striding.` | `emitOpError` | (scatter 门控) | `isScatter` 且 source 带 stride |
| `Flattening striding dimension <d>` | `VLOG(1)` | `lower_pass_base.cc:123` | 合并 pass 合并了维度对 `(d, d+1)` |
---
## 8. 函数映射
| 地址 | 符号 | 角色 |
|---|---|---|
| `0x1350cb60` | `LowerMemrefToMlo::issueStridedTransfer` | 逐 tile 选择器(本页 §1–§4) |
| `0x13510340` | `issueStridedTransfer::$_0::operator()(ArrayRef<int>)` | 逐侧 stream stride-level 验证器(§4) |
| `0x13516ca0` | `LowerPassBase::issueRolledTransfer` | 承载合并 pass 的 rolled-loop 调用方(§5) |
| `0x1350b3a0` | `(anonymous)::issueGeneralDma` | `>1`-stride / 跨 core 的 `GeneralDma` 发射器 |
| `0x145b9740` | `DmaSimpleStartOp::create` | 连续 DMA 发射器 |
| `0x145bcd20` | `DmaSingleStridedStartOp::create` | 单 stride DMA 发射器 |
| `0x145e3440` | `LinearStreamStartOp::create` | 连续 stream 发射器 |
| `0x1460b8e0` | `StridedStreamStartOp::create` | 单 stride stream 发射器 |
| `0x14afb1e0` | `mlir::tpu::isGather` | gather/scatter 分类器(§4) |
| `0x1d617f80` | `Target::GranuleBytes` | `kDma` length 除数(§3) |
| `0x14a89ea0` | `xla_mlo_util::TransferGranularityInBytes` | `kStream` length 除数(§3) |
| `0xfaaae00` | `OpBuilder::createOrFold<arith::MulIOp, Value&, Value&>` | 合并 base-merge(§5) |
| `LowerPassBase` vtable `+0x20` | (抽象)`getConstantIntValue` 探测 | `$_0` 和合并循环共同使用的 constant-fold |
---
## 交叉引用
- [Rolled / Strided / General 发射器](rolled-strided-general.md) — 六个 `::create` 发射器主体(`DmaSimpleStart` / `DmaSingleStridedStart` / `issueGeneralDma` / `LinearStream` / `StridedStream`),以及各自如何将 `DmaParameters` 操作数绑定进描述符
- [片上 DMA 描述符](intra-chip-descriptor.md) — `OciDescriptorCommonIssuedFromTcs` 线布局、`(mem_id, core_id, opcode)` endpoint 枚举,以及 granularity 除数(§3)馈入的 `(length, length_granule)` size 对
- [Tile-Index 展开](tile-index-expansion.md) — `ExpandTiledMemRefs` / `expandTiledIndices`,它们构造此选择器据以分派的 `DmaParameters` stride 数组
- [Continuation Queue](continuation-queue.md) — 已发射 DMA/stream op bump 的运行时 SFLAG completion 协议
- [返回索引](../index.md) — Part X — On-Chip Memory & DMA / DMA