Skip to content

Rolled / Strided / General 发射器

本页所有地址均适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(构建 libtpu_lts_20260413_b_RC00,build-id md5 89edbbe81c5b328a958fe628a9f2207d)。该镜像 strip;反混淆后的 C++ 符号名按原文引用。.text VMA 等于文件偏移(.text 基址 0xe63c000);.rodata VMA 等于文件偏移;.data.rel.ro 带有 0x200000 的 VMA→文件 delta。其他版本会不同。

摘要

当 Mosaic TPU lowering pass 已经对一个 EnqueueDMAOp 做了 tiling、选定传输类型,并决定 tile grid 的形状之后,它必须发射实际的 SparseCore DMA / Stream op。这正是 lower_pass_base.cclower_memref_to_mlo.cc 中三个函数的职责:issueRolledTransfer 将剩余 tile grid 卷入一个 scf.forissueStridedTransfer 是逐 tile 回调,把一个 DmaParameters 包转换成一个具体的 sc_tpu.* op;issueGeneralDma 组装跨 core / 多 stride 的 GeneralDma 形式。本页覆盖这三个发射器主体,以及 DmaSimpleStart / DmaSingleStridedStart / DmaStridedStream / DmaGeneralStart 的 ODS builder 槽位字段映射,也就是 create-arg → Properties-offset → ODS-attr → slot-bit 链。

已经熟悉 MLIR 的 OpBuilder / ODS create() / build() 模式的读者会认出其形状:每个发射器都是一次 *StartOp::create 调用,它的按值传递 bool、IntegerAttrStringAttrCoreTypeAttr 会写入一个 getOrAddProperties<> Properties blob,并且一个固定的 +0x40 delta 将每个原始 Properties 偏移映射到相对 op 的 getter 偏移。本页记录的两个意外点是:(1) issueRolledTransfer 不会每个 tile 发射一个 op,而是先运行维度合并优化器,在循环之前展平连续的 tile 维度,然后只把剩余的 leading dimension 卷入单个 scf.for,其 body 通过手动填充(调用 ForOp::create 时传入 NULL body builder);(2) 形式选择完全由一个整数驱动,即 DmaParameters.src_byte_strides.size()(0 / 1 / >1)与 TransferKind(kDma / kStream)的组合,一个 3×2 网格精确映射到六个 op,其中两个格子在 emitOpError 处 trap。

负责决定 Simple-vs-Strided-vs-General 并运行前置 dim-coalescing 的 selector 是 DmaParameters 选择器 的主题;这些 op 最终写入的 descriptor 字段布局以及 mem_id/core_id/dma_type 枚举见 Intra-Chip DMA 描述符;产生地址 operand 的 tile-index→flat-offset 代数见 Tile-Index 展开。本页是它们之间的发射器层。

对重实现而言,契约是:

  • rolled SCF 循环issueRolledTransfer 的 coalesce-then-roll 结构:三个并行的逐维度 ArrayRef、contiguity-merge predicate、带 NULL body builder + 手动 body 填充的单个 scf.for、每次迭代的 index × stride 偏移,以及可整除性 emitOpError
  • 逐 tile 回调issueStridedTransfer 的二维分派(TransferKind × src_byte_strides.size())、DmaParameters 一致性 invariant、kStream gather/scatter striding gate、granularity divisor,以及绑定到每个 *StartOp::create 的 operand。
  • general 路径issueGeneralDma 的 remote-vs-local target base、双边 sync CHECK、destination_id 拓扑算术、多级 stride descriptor,以及 StringAttr selector。
  • ODS builder 槽位映射DmaSimpleStartDmaSingleStridedStartDmaStridedStreamDmaGeneralStart 的 create-arg → Properties-offset → ODS-attr → slot-bit 链,包括不变量 +0x40 raw↔getter delta。
Rolled 发射器mlir::tpu::LowerPassBase::issueRolledTransfer @ 0x13516ca0 (0x1614 B)
Strided 发射器mlir::tpu::LowerMemrefToMlo::issueStridedTransfer @ 0x1350cb60 (0x37cd B)
General 发射器mlir::tpu::(anonymous namespace)::issueGeneralDma @ 0x1350b3a0 (0x16ff B)
源文件lower_pass_base.cc (rolled), lower_memref_to_mlo.cc (strided/general)
逐 tile 回调目标lowerEnqueueDma::$_0 policy-func 0x13516720issueStridedTransfer
六个发射出的 opDmaSimpleStart 0x6, DmaSingleStridedStart 0x7, GeneralDma 0x8, LinearStream 0x3b, StridedStream 0x3a, + 2 个 error cell
ODS raw↔getter delta+0x40(raw Properties offset + 0x40 == op-relative getter offset)
证据等级重实现级 / 已按所有三个主体的 IDA decompile + ODS create/build 对逐字节确认

1. issueRolledTransfer — SCF 循环的 rolled 路径

目的

issueRolledTransfer 是多 tile descriptor 路径:当一个 tiled DMA 覆盖的 tile grid 无法表示为一次连续传输时,该函数发射 tile-grid 遍历。它不是递归,也不是完全展开的“每个 tile 一个 DmaSimpleStart”发射器。相反,它 (a) 将连续 tile 维度合并为一个维度,然后 (b) 把单个剩余 leading dimension 卷入一个 scf.for,手动填充 loop body,并通过 LowerPassBase virtual 发出逐 tile 传输。issueRetiledTransfer (0x13519480) 总是把多 tile 情况路由到这里(0x13519f3d / 0x1351a1ea 的两个 call 0x13516ca0 位置);unrolled-vs-rolled 选择位于该函数内部的 coalescing loop 中。

入口点

text
issueRetiledTransfer (0x13519480)             ── tile-grid driver
  └─ issueRolledTransfer (0x13516ca0, 0x1614 B) ── coalesce + roll
       ├─ $_0 (0x135182c0)                     ── per-axis tile-stride product table
       ├─ scf::ForOp::create (0x17866d60)      ── the rolled loop (NULL body builder)
       ├─ $_1 (0x13518480)                     ── arith.constant n : i32
       ├─ vtable+0x20 [LowerPassBase]          ── getConstantIntValue probe (coalescing)
       └─ vtable+0x18 [LowerPassBase]          ── per-tile sub-memref / offset generator

算法

该函数接收三个并行的逐维度数组:basesArrayRef<Value>,来自 getTiledDynamicShape 的 tile-grid extent)、srcTileStridesArrayRef<long>)和 dstTileStridesArrayRef<long>),每个 tile-grid 维度一个条目。prologue 对它们的长度做 CHECK_EQ;主体执行 coalesce、roll 和 issue。

c
// mlir::tpu::LowerPassBase::issueRolledTransfer   sub_13516CA0
function issueRolledTransfer(b, op, bases, srcTileStrides, dstTileStrides,
                             v1, v2, L1, L2, v3, v4, devIds, prio, kind,
                             perTileCallback):
    // 0. three parallel arrays, one entry per tile-grid dim
    CHECK_EQ(bases.size(), srcTileStrides.size())   // "tiled_dynamic_shape.size() == src_tile_strides.size()" @0xa166591
    CHECK_EQ(bases.size(), dstTileStrides.size())   // "== dst_tile_strides.size()" @0xa16655b

    // 1. per-axis suffix-product multipliers (row-major), one per side
    srcMul = $_0(srcTileStrides)        // sub_135182C0: factor*shape[k] imul, SmallVector<unsigned>
    dstMul = $_0(dstTileStrides)        // same, dst side

    // 2. COALESCE contiguous striding dims (see §2 for the predicate)
    coalesceStridingDims(bases, srcTileStrides, dstTileStrides)   // backward scan @0x13517070..0x13517301

    // 3. emit ONE scf.for over the residual leading dim
    dim = residual_leading_dim
    forOp = scf::ForOp::create(b, op.getLoc(),
                lb   = arith.constant 0 : i32,          // $_1(0)
                ub   = bases[dim],                      // the tile-count Value
                step = arith.constant 1 : i32,          // $_1(1)
                inits = {},                             // empty ValueRange
                body = NULL,                            // function_ref left null
                false)                                  // sub_17866D60 @0x13517477
    b.setInsertionPointToStart(forOp.body[0])           // manual body population

    // 4. per-iteration base offset, per side
    for side in {src, dst}:
        idx = inductionVar-derived index
        off = arith.muli(idx, tileStride)               // MulIOp<V,V> 0xfaaae00 / <V,ConstOp> 0x1351ab00
        // 5. issue the per-tile transfer through the LowerPassBase virtual
        base = this->vtable[+0x18](b, op.getLoc(), tileStrideConst, idx, accum)  // per-tile sub-memref (HIGH)

    b.restoreInsertionPoint(savedIP)

    // 6. divisibility guard
    if (L1 % perIterStride) != 0:                       // idiv @0x13517726
        return op.emitOpError("Tile size (%d bytes) not divisible by %d bytes. …")  // @0xa01cd05
    return success

QUIRK — 调用 scf::ForOp::create 时传入的是 NULL function_ref body builder(在 0x13517433 清零的 {callee=0, callable=0} 16 字节结构)。这会使 ForOp::create 只构造循环外壳(一个携带 induction variable 的 body block)。随后会手动填充 body:OpBuilder insertion point 被重定向到 for-op 的 region-0 block(在 0x135174ac 写入 OpBuilder+0x10/+0x18),在那里发射每次迭代的 op,之后恢复 insertion point(SpecificNodeAccess::getNodePtr @ 0x1d8ccbc0, 0x1351763b)。传入真实 body builder lambda 的重实现会发射相同 IR,但该 binary 采用手动填充路线。(Decompile @ 0x13516ca0 line 419: mlir::scf::ForOp::create(v173, v118, v119);CONFIRMED。)

函数映射

函数地址角色
issueRolledTransfer0x13516ca0rolled 发射器
issueRolledTransfer::$_00x135182c0逐 axis tile-stride suffix-product 表(SmallVector<unsigned>
issueRolledTransfer::$_10x13518480arith.constant n : i32 builder
mlir::scf::ForOp::create0x17866d60rolled loop shell
MulIOp<Value,Value>::createOrFold0xfaaae00每次迭代的 index × stride(两边都是 Values)
MulIOp<Value,ConstantOp>::createOrFold0x1351ab00每次迭代的 index × stride(折叠常量)
OpState::emitOpError0x1d8cefa0可整除性诊断
LowerPassBase vtable +0x18(no symbol)逐 tile sub-memref / offset generator
LowerPassBase vtable +0x20(no symbol)getConstantIntValue 风格 probe(coalescing)

NOTE — 逐 tile op-emission helper(vtable +0x18)和 constant-int probe(vtable +0x20)是抽象 LowerPassBase virtual,没有独立符号;具体 pass LowerMemrefToMlo 是匿名命名空间类,没有发射出的 ZTV 表。调用参数以及周围的 index/offset 算术已按字节锁定;方法名称是从这些参数推断出来的,因此是 HIGH,而非 CONFIRMED。


2. 维度合并 predicate(rolled prologue)

目的

coalescing pass 是 rolled 形式高效的原因:在发射 scf.for 之前,连续或退化的 tile 维度会被展平为一次更大的传输(更少 descriptor、更少循环迭代),因此循环只覆盖无法展平的剩余维度。作为 selector 概念的 dim-coalescing 优化器由 DmaParameters 选择器 负责;这里记录在 issueRolledTransfer prologue 中运行的逐字节精确 merge predicate,因为它控制 rolled loop。

算法

一次反向相邻对扫描(0x13517150)从最后一个维度向第一个维度遍历。对每一对(outer d,inner d+1),它在每个 base 上通过 LowerPassBase vtable +0x20 查询 getConstantIntValue probe;该 probe 返回打包为 {bit32 = present | low32 = value}optional<int64>,因此标记 0x100000001 表示“present AND value == 1”。

c
// coalescing scan in issueRolledTransfer   @0x13517070..0x13517301
function coalesceStridingDims(bases, srcStride, dstStride):
    for d = N-2 downto 0:                              // backward adjacent-pair scan
        innerC = getConstantIntValue(bases[d+1])       // vtable+0x20 @0x13517176
        outerC = getConstantIntValue(bases[d])         // vtable+0x20 @0x13517160
        merge = false
        // COND (i): degenerate inner tile-count == 1
        if (innerC & 0x1ffffffff) == 0x100000001:      // @0x13517186
            merge = true
        // COND (ii): row-major contiguity, per side
        elif (outerC & 0x100000000) != 0:              // outer base is a constant int @0x1351721a
            if srcStride[d] == srcStride[d+1] * outerC  // imul; cmp @0x13517243
               && dstStride[d] == dstStride[d+1] * outerC:  // imul; cmp @0x1351726a
                merge = true
        if merge:
            bases[d] = arith.muli(bases[d], bases[d+1])         // MulIOp 0xfaaae00 @0x135171e5
            VLOG(1) << "Flattening striding dimension " << d    // lower_pass_base.cc:123, @0xa1f260b
            memmove(bases    + d+1, bases    + d+2, …)  // 8-byte entries  @0x135170b1
            memmove(srcStride+ d+1, srcStride+ d+2, …)  // 4-byte entries  @0x135170ea
            memmove(dstStride+ d+1, dstStride+ d+2, …)  // 4-byte entries  @0x1351711c
            N -= 1                                       // dec all three counts

一对维度会被折叠,当且仅当要么 inner 维度的 tile-count 是常量 1(COND i,总是可折叠,跳过 stride 检查),要么 outer base 是一个常量整数,且 outer stride 等于 inner stride 乘以 outer extent,并且在两侧都成立(COND ii,真正的 row-major 连续性)。如果两者都不成立,该对会保持不变,并成为剩余 loop/stride 维度。当 VLOG 路径无法折叠某个 stride 时,它会记录 "Attempted to combine non-constant stride: "@0xa27e861)并跳过。

Predicate 步骤测试 (@VA)含义
outer-const probegetConstantIntValue(bases[d]) @ 0x13517160vtable+0x20 constant-int probe
inner-const probegetConstantIntValue(bases[d+1]) @ 0x13517176同上,inner dim
COND (i)(rax & 0x1ffffffff) == 0x100000001 @ 0x13517186inner tile-count 是常量 1
COND (ii) gatetest r13, 0x100000000 @ 0x1351721aouter base 是常量整数
COND (ii) srcsrcStride[d] == srcStride[d+1] × outerC @ 0x13517243源端 row-major 连续性
COND (ii) dstdstStride[d] == dstStride[d+1] × outerC @ 0x1351726a目标端 row-major 连续性
MERGEmuli + VLOG + 3× memmove + dec @ 0x13517070(d, d+1) 折叠为一个 dim

GOTCHA — 两个 merge 条件是 binary 中不同的分支目标。COND (i)(inner count == 1)完全跳过 stride 检查。对 count-1 dim 来说,它是 COND (ii) 的保正确超集,而不是冗余特例。只在 stride-contiguity 测试(COND ii)上折叠的重实现,将无法折叠退化的 count-1 维度,并会发射浪费的 1-trip loop level。(两个不同 jump target 已按字节锁定;(i) 对 zero-stride inner dim 是否在逻辑上被 (ii) 涵盖尚未证明,binary 将它们保持为分离。)


3. issueStridedTransfer — 逐 tile 回调 {#3-issuestridedtransfer--the-per-tile-callback}

目的

issueStridedTransfer 是统一的逐 tile DMA-op 发射器,也是 tiled-DMA 分派通过 lowerEnqueueDma::$_0 policy-func 0x13516720 每个 tile 调用一次的 std::function 回调目标。它读取一个 DmaParameters 包,并精确发射一个具体的 sc_tpu.* op。它是一个二维 selector:轴 1 是 TransferKind 参数(kDma=0 / kStream=1 / 其他 → emitOpError),轴 2 是 DmaParameters.src_byte_strides.size()(0 / 1 / >1)。这个 3×2 网格映射到六个 SparseCore off-tile op。

入口点

text
lowerEnqueueDma::$_0  (policy-func 0x13516720)        ── std::function callback target
  └─ issueStridedTransfer (0x1350cb60, 0x37cd B)      ── per-tile emitter
       ├─ DmaSimpleStartOp::create        (0x145b9740, opcode 0x6)
       ├─ DmaSingleStridedStartOp::create (0x145bcd20, opcode 0x7)
       ├─ issueGeneralDma                 (0x1350b3a0, → GeneralDma 0x8)   [§5]
       ├─ LinearStreamStartOp::create     (0x145e3440, opcode 0x3b)
       ├─ StridedStreamStartOp::create    (0x1460b8e0, opcode 0x3a)
       ├─ isGather                        (0x14afb1e0)  ── stream gather/scatter classifier
       └─ $_0 (0x13510340)                              ── per-side stride-level validator

算法

c
// mlir::tpu::LowerMemrefToMlo::issueStridedTransfer   sub_1350CB60
function issueStridedTransfer(b, op, src, dst, p /*DmaParameters&*/,
                              devIds, priority, kind, force):
    CHECK_EQ(priority, 0)                              // "priority == 0" @0x9fc7181 (lower_memref_to_mlo.cc:1142)
    srcSpace = GetMemorySpace(p.src)                   // 0x1459c7e0 (from memref @p+0x00)
    dstSpace = GetMemorySpace(p.dst)                   // (from memref @p+0x08)
    trace = op.getInherentAttr("sc.inside_trace_region") != null   // @0x85fd7e8

    n = p.src_byte_strides.size()                      // p+0x18 — the form selector

    switch kind:
      case kDma (0):
        if n == 0 && !force:                           // contiguous
            len = ceil_div(p.length, Target::GranuleBytes())   // 0x1d617f80
            return DmaSimpleStartOp::create(b, loc, src, {0}, dst, {0}, len, sflagIdx, sflag)  // 0x6
        // Strided/General arm — consistency invariant first:
        CHECK_EQ(p.src_byte_strides.size(), p.tgt_byte_strides.size())   // @0xa166501 (:843)
        CHECK_EQ(p.src_byte_strides.size(), p.steps_per_stride.size() - 1)  // @0x9e79ab6 (:844)
        if n == 1:
            return DmaSingleStridedStartOp::create(…, srcStride, dstStride, innerVecLen, elemsPerStride)  // 0x7
        else: // n > 1
            return issueGeneralDma(b, locGen, target, op, src, dst, len, A, B, C, …)  // GeneralDma 0x8

      case kStream (1):
        isG = isGather(op)                             // 0x14afb1e0
        // gather/scatter striding gates (per side, via $_0 0x13510340 → vtable+0x20):
        if isG && $_0(p.tgt_byte_strides) > 0: return op.emitOpError("Gather streams do not support destination striding. …")  // @0xa06d0da
        if isScatter && $_0(p.src_byte_strides) > 0: return op.emitOpError("Scatter streams do not support source striding. …")  // @0xa06d17f
        gran = TransferGranularityInBytes(target, dstSpace, …)  // 0x14a89ea0 via target->[+0x948]
        if n == 0: return LinearStreamStartOp::create(b, loc, false, dstIsHbm, trace, 0, …)  // 0x3b
        if n == 1: return StridedStreamStartOp::create(b, loc, false, dstIsHbm, trace, …, stride0, stride1, …)  // 0x3a
        else:      return op.emitOpError("Streams support up to 1 level of striding. Got %d levels of …")  // @0xa092a49

      default:
        return op.emitOpError("Unsupported transfer kind: %d")   // @0x872e1dd

形式选择网格

src_byte_strides.size()kDma (0)kStream (1)
0(连续)DmaSimpleStart 0x6LinearStream 0x3b
1(一个 stride level)DmaSingleStridedStart 0x7StridedStream 0x3a
>1(multi-stride)issueGeneralDmaGeneralDma 0x8emitOpError(仅 ≤1 level)
(其他 TransferKindemitOpError "Unsupported transfer kind: %d"(同上)

GOTCHA — kStream 最多支持一个 stride level。decompile 中有两个不同的 "Streams support up to 1 level of striding" 诊断:一个 keyed on source striding(src_byte_strides.size() <= 1, lower_memref_to_mlo.cc:1189),另一个 keyed on steps per stride:1193),外加单独的 gather/scatter gate。对收到的任意 n >= 1 都发射 StridedStream 的重实现,会错误编码任何流路径收到的 2+ level stride;只有 kDma 会把 n > 1 路由到 GeneralDma 形式。

DmaParameters 一致性 invariant

Strided/General 分支会 CHECK_EQ 包中的三个 size 字段(其 offset map 由 Tile-Index 展开 负责):|src_byte_strides| == |tgt_byte_strides| == |steps_per_stride| − 1。因此一个连续(0-stride)descriptor 具有 steps_per_stride.size() == 1steps_per_stride vector 为 N 个 stride level 携带 N+1 个累计 step。

包字段偏移issueStridedTransfer 中的角色
src(memref Value+0x00GetMemorySpace → src endpoint
dst(memref Value+0x08GetMemorySpace → dst endpoint
src_byte_strides {data,size}+0x10 / +0x18形式 selector+0x18
tgt_byte_strides {data,size}+0x50 / +0x58CHECK_EQ vs +0x18
length/sflag Value+0x90create len/sflag 参数
steps_per_stride {data,size}+0x98 / +0xa0CHECK size == +0x18 + 1

NOTE — granularity divisor 因 kind 而异:kDma 使用带 ceil-div 惯用法的 Target::GranuleBytes()0x1d617f80);kStream 使用通过 target()->[+0x948] 到达的 xla_mlo_util::TransferGranularityInBytes(SparseCoreTarget const&, MemorySpace, bool)0x14a89ea0),即逐 memory-space 的 SPMEM-stripe / DMA-word 单元。选定的 granularity 会变成一个 arith.ConstantIndexOp divisor,在构建 op 之前折叠进 DivUIOp/idiv


4. ODS builder 槽位字段映射(contiguous + single-stride 形式)

目的

每个 *StartOp::create 都是一个薄 wrapper,它按参数顺序原样转发到 build();后者添加 operand group(AttrSizedOperandSegments),并把 inherent attribute 写入 getOrAddProperties<…Properties>() blob。整个家族的不变量是 +0x40 delta:op getter 会在原始 Properties offset 上加常量 0x40(op-relative getter offset = raw + 0x40)。本节映射 contiguous(DmaSimpleStart 0x6)和 single-stride(DmaSingleStridedStart 0x7DmaStridedStream 0x3a)形式的 create 参数;GeneralDma 映射在 §6

DmaSimpleStart (0x6)

DmaSimpleStartOp::create0x145b9740)是一个 wrapper,它提供 enable_trace=false、两个 null IntegerAttrStringRef="";它转发到 build0x1459ac00),后者添加 7 个 operand segment,并写入一个 0x40 字节的 Properties(TypeID @0x224e95c0)。7 个 create operand 参数是 (srcBase, srcIdx, dstBase, dstIdx, length, sflagIdx, sflag)

create 参数 / wrapper 默认值ODS getter(op-rel)SparseCoreDma SimpleDma 槽位字段
enable_trace = false(默认)getEnableTrace+0x50TraceEn(slot +0x18 bit 41)
srcMemIndex IntegerAttr = nullgetSrcMemIndex+0x58dma_sreg_source_offset
dstMemIndex/opcode IntegerAttr = 0getDstMemIndex+0x40) / getDstOpcode+0x48dst_opcode(slot +0x18 >>39 &0x3
srcBase ValuegetSrcBufferIndicesMutablesrc_mem_{core,mem}_id(通过 GetMemorySpace
dstBase ValuegetDstBufferIndicesMutabledst_mem_{core,mem}_id(通过 GetMemorySpace
length Value(operand seg)dma_length(slot +0x18 >>42 &0x3f
sflagIdx, sflaggetDstSflagIndicesMutabledest_sync_flags(slot +0x18 >>15 &0x3f

dst_opcode string-attr → 2-bit code 映射(write_4b / read_and_add / atomic_add)及其 memory-space gate 由 Intra-Chip DMA 描述符 §2 负责;GetDstOpcode<DmaSimpleStartOp> accessor(0x135aaa60)读取 this+0x48 处可选的 dst_opcode attribute。

DmaSingleStridedStart (0x7)

DmaSingleStridedStartOp::create0x145bcd20)的反混淆签名是 (b, loc, V, VR, V, VR, V, VR, V, V, V, V, V),它是 DmaSimpleStart operand 集再加上 4 个尾随的 Value stride 参数,这些参数由 src_byte_strides[0]tgt_byte_strides[0]steps_per_stride span 和 inner-vector-length(通过 arith.ConstantIndexOp/IndexCastOp 物化)构建。按位置,这 4 个 stride 绑定到 SingleStridedDma {source_stride, destination_stride, inner_vector_length, elements_per_stride} 槽位字段(slot +0x10 >>41/35/—/29,以及 +0x18 >>10)。create arity(4 个 stride)和 SmallVector 读取已 CONFIRMED;精确的 1-to-1 stride-arg → slot-field 顺序按位置推定(HIGH,其 build body 未独立解码)。

DmaStridedStream (0x3a)

StridedStreamStartOp::create0x1460b8e0)的反混淆签名是 (b, loc, b, b, b, V, VR, V, VR, V, V, V, IntegerAttr, V, VR),它是 LinearStream operand 集再加上 2 个尾随 stride Valuestride0stride1)以及 IntegerAttr tile_local length-per-stride。前三个 bool 镜像 LinearStream{upd, hbm4b/dstIsHbm, enable_trace}。这 2 个 stride 映射到 StridedStream {stride0, stride1} operand;create-arg 形状已 CONFIRMED,slot-bit join 是结构性推断(HIGH)。

NOTE — LinearStream0x3b)contiguous-stream 形式的完整 create→Properties→slot 映射(upd / hbm4b / enable_trace / opcode / tile_local_length_per_stride 绑定)由 SparseCore stream slot-encoding 页面负责;在本页它是 §3 网格中的 n==0/kStream 格。issueContiguousTransfer 的 stream 调用是 (false, dstIsHbm, TraceLocalDma, 0, …)upd=false, hbm4b=dstIsHbm, enable_trace=TraceLocalDma, opcode=0


5. issueGeneralDma — general-DMA 路径

目的

issueGeneralDmaGeneralDma0x8)形式的组装器,也是唯一携带 >1 stride level 的 DMA 形式,以及跨 core / 跨 device 形式。issueStridedTransfer 将 kDma 的 n > 1 情况路由到这里。这个自由函数(匿名命名空间,lower_memref_to_mlo.cc)计算双边 sync、来自 device topology 的显式 destination_id,以及多级 stride descriptor,然后发射四个 DmaGeneralStartOp::create overload 中最丰富的一个。

入口点

text
issueStridedTransfer (0x1350cb60)             ── kDma, src_byte_strides.size() > 1
  └─ issueGeneralDma (0x1350b3a0, 0x16ff B)   ── GeneralDma assembler
       ├─ lowering_util::GetRemoteMemBase (0x13d88660)   ── remote target base
       ├─ AllocateAtOffsetOp::create      (0x145a5aa0)   ── local sflag memref
       ├─ TpuChipConfig::Megacore         (0x20afca00)   ── topology gate
       ├─ {Tensor,Sparse}CoresPerLogicalDevice (0x111f6020 / 0x135159c0)
       ├─ CoreIndexOp::create             (0x145aba00)   ── runtime core index
       └─ DmaGeneralStartOp::create       (0x145b1b80)   ── GeneralDma 0x8 (28-arg overload)

算法

c
// mlir::tpu::(anonymous namespace)::issueGeneralDma   sub_1350B3A0
function issueGeneralDma(b, locGen, target, op, src, dst, len,
                         A, B, C /*ArrayRef<Value> stride lists*/,
                         isRemote, srcSpace, dstSpace, devIds, v1, v2, syncBool):
    // 1. remote-vs-local target base (the SOURCE side of two-sided sync)
    if isRemote && devIds.present:
        base = GetRemoteMemBase(b, locGen, …)            // 0x13d88660 → remote base
    elif isRemote:                                       // remote but no source semaphore
        return MakeError("Source semaphore must be provided for a remote DMA.")  // @0xa0c8904 (:915)
    else:
        base = AllocateAtOffsetOp::create(ChipIdOp, MemorySpaceAttr(sflag=5), MemRefType, ConstantIndex)  // local sflag

    // 2. target-semaphore core-type CHECK (the SET side)
    ct = op.operand(3).getType().getMemorySpace().getCoreType()   // getCoreType 0x14a9e320
    if !ct.present:
        return MakeError("Target semaphore is missing a core type in its memory space")  // @0x86e65f5 (:948)

    // 3. destination_id from topology
    if Megacore(target) && target.coresGate >= 2:        // [target+0x3b8]+0x7c >= 2
        if srcSpace == vmem || dstSpace == vmem:
            return MakeError("Targeting VMEM on MegaCore targets is ambiguous.")  // @0x9ffb522 (:968)
    divisor = (dstCoreType == TENSOR_CORE) ? SparseCoresPerLogicalDevice * chipMeta[+0x90]
            : (dstCoreType == 1)           ? SparseCoresPerLogicalDevice
            :                                TensorCoresPerLogicalDevice
    destId = arith.addi(arith.{rem,div}ui(index_cast(CoreIndexOp), ConstantIndex(divisor)),
                        ConstantIndex(offset))            // RemUI 0x1cb20800 / DivUI 0x1cb06d00 / AddI 0x1caf0b00

    // 4. multi-level (>1) stride descriptor
    shape = SmallVector<unsigned>(inline cap 6)          // header 0x600000000
    fill shape per stride-dim (cmp r15,N je ladder N=1..6; grow_pod for >=7)
    nondefault_stride_dimensions = strideDimCount        // ≤8, 3-bit

    // 5. attribute selectors
    dst_opcode   = (srcSpace == smem || dstSpace == smem) ? "write_4b" : null   // @0x879a767
    enable_trace = (same smem condition) ? UnitAttr : null
    sync_mode    = syncBool ? "count_dones" : "count_words"   // cmovne @0x8562648 / @0x8570908
    dma_ordering = "relaxed"                              // @0x86ff2e0

    // 6. emit
    return DmaGeneralStartOp::create(b, loc, enable_trace, src, srcIdx, base, …,
                                     A, B, C, ct1..ct4, dst_opcode, sync_mode, dma_ordering)  // 0x145b1b80

函数映射

函数地址角色
issueGeneralDma0x1350b3a0GeneralDma 组装器
lowering_util::GetRemoteMemBase0x13d88660跨 device remote target base
AllocateAtOffsetOp::create0x145a5aa0本地 sflag memref
MemorySpaceAttr::getCoreType0x14a9e320target-semaphore core type
TpuChipConfig::Megacore0x20afca00megacore gate
Target::SparseCoresPerLogicalDevice0x135159c0dest_id divisor(SC)
Target::TensorCoresPerLogicalDevice0x111f6020dest_id divisor(TC)
CoreIndexOp::create0x145aba00runtime core index
DmaGeneralStartOp::create0x145b1b80emit(28-arg overload)

QUIRK — GeneralDma 的 descriptor attribute 会以 StringAttr 发射,而不是 slot decode 显示的整数枚举:dst_opcode = "write_4b"sync_mode = "count_dones"/"count_words"dma_ordering = "relaxed"。codec 在 encode 时把这些符号名映射到 bit field。直接把整数 enum 写入 op 的重实现会跳过 binary 使用的 string 层;硬件看到的 descriptor 相同,但 MLIR op 携带的是 string。除非 srcdstsmem,否则 dst_opcodenull(若是则 "write_4b"WRITE_4B=1,4 字节 scalar write)。(全部四个 StringAttr selector 均在 decompile @ 0x1350b3a0 lines 762/1025/1027/1033 中 CONFIRMED。)

GOTCHA — destination_id 根据 device topology 计算,不是常量:dest_id = base + (core_index op divisor),其中 divisor 由目标 core type 选择 TensorCoresPerLogicalDeviceSparseCoresPerLogicalDevice,且在 megacore 路径上 opRemUI,在非 megacore 路径上是 idiv 折叠常量。硬编码 routing id 的重实现会在任何非默认 chip geometry 上误路由。精确代数公式从 op 顺序重建(HIGH);按 core type 的分支选择已 CONFIRMED;[target+0x3b8]+0x7c(megacore core-count gate,119*8+124)和 [target+0x948]+0x90(SparseCore multiplier,297*8+144)背后的 chip-metadata 字段名尚未解析为命名的 TpuChipConfig accessor。


6. DmaGeneralStart ODS 槽位映射 {#6-dmageneralstart-ods-slot-map}

DmaGeneralStartOp::create0x145b1b80,28-arg overload)转发到 build0x1459b2c0),后者添加 operand group(AttrSizedOperandSegments,segment-size array 位于 op+0x80),并把 8 个 inherent attribute 写入一个 0x88 字节的 Propertiesoperator new(0x88),TypeID @0x224e95b0)。与更简单形式相同的 +0x40 raw↔getter delta 仍然成立。build 的 attr-pointer 写入已做 data-flow trace;6 个 typed getter 已逐字节确认。

raw Propertiesop-rel gettergetterattr 名称类型
+0x00+0x40(仅 getInherentAttr)dma_orderingStringAttr
+0x08+0x48getDstMemCoreTypedst_mem_core_typeCoreTypeAttr
+0x10+0x50getDstOpcodedst_opcodeStringAttr
+0x18+0x58getDstSyncFlagCoreTypedst_sync_flag_core_typeCoreTypeAttr
+0x20+0x60getEnableTraceenable_traceUnitAttr
+0x28+0x68getSrcMemCoreTypesrc_mem_core_typeCoreTypeAttr
+0x30+0x70getSrcSyncFlagCoreTypesrc_sync_flag_core_typeCoreTypeAttr
+0x38+0x78(仅 getInherentAttr)sync_modeStringAttr

create-arg → GeneralDma slot-field join:

  • src/dst data buffer → {src,dst}_mem_{core,mem}_id(slot +0x18 >>26/28/34/36,通过 buffer memref 的 MemorySpace)。
  • dst_opcode "write_4b"DstOpcode(slot +0x18 >>39 &0x3 = WRITE_4B=1)。
  • enable_trace UnitAttrTraceEn(slot +0x18 bit 41)。
  • dst_sync_flag_core_type CoreTypeAttr → 提供给 dest_sync_flags_vs1 的 sync-flag core type。
  • destination_id Value(topology AddIOp)→ DestinationId(slot +0x10 >>29 &0x1f)+ DestinationIdValid
  • source semaphore → SourceSyncFlagNumber WAIT(slot +0x10 >>41 &0x1f)+ valid bit。
  • dst semaphore base → DestSyncFlagsVs1 SET(slot +0x10 >>35 &0x1f)+ valid bit。
  • stride-dim count → NondefaultStrideDimensions(slot +0x18 >>10 &0x7);三个 ArrayRef<Value> → 逐级 stride operand。

NOTE — 两个没有 typed getter 的 StringAttr 槽位,即 +0x00dma_ordering)和 +0x38sync_mode),通过排除法以及调用方的 StringAttr 构造顺序配对到这些名称(6 个 typed getter 精确固定了其他槽位)。槽位 offset 已按字节锁定;这两个的 name↔offset 配对是一次推断 join(HIGH)。getInherentAttr name→offset bit-trace 可以确认它。

GeneralDma opcode 是 0x8,编码在 bundle bit 0xb5=181gfc SparseCoreDmaEncoder::Encode 0x1eb5a3a0,oneof bound cmp rax,0xa,case 10)。


相关组件

组件关系
issueRetiledTransfer (0x13519480)tile-grid driver;总是将多 tile 情况路由到 issueRolledTransfer
issueContiguousTransfer (0x1350a3e0)单次连续传输 peer(直接发射 DmaSimpleStart/LinearStream
lowerEnqueueDma::$_0 (0x13516720)每个 tile 调用 issueStridedTransferstd::function policy-func
getTiledDynamicShape (0x13516840)产生 rolled loop 迭代的 bases ArrayRef<Value>

交叉引用

  • DmaParameters 选择器 — Simple-vs-Strided-vs-General selector,以及决定这些发射器产出哪种形式的前置 dim-coalescing
  • Intra-Chip DMA 描述符 — descriptor 字段布局,以及发射出的 op 最终写入的 mem_id/core_id/dma_type/dst_opcode 枚举
  • Tile-Index 展开getTiledDynamicShapeDmaParameters 包(src_byte_strides/tgt_byte_strides/steps_per_stride),其 size() 驱动形式选择
  • Host↔Device DMA — host infeed/outfeed leg;DMA_TYPE_CHIP_TO_HOST/DMA_TYPE_LOCAL_OR_HOST 路径
  • OCI Command DMA-ID — 与已发射 descriptor 的 begin/end trace point 配对的 trace_id_header DMA-id
  • net_router 发射器流水线 — 发射这些 intra-chip transfer 的 collective router local leg;issueGeneralDmaGetRemoteMemBase 路径对应的 cross-chip remote-endpoint encoding