Skip to content

Region → Sequencer 提取器

本页上的每个函数地址、op 名字符串、属性名称/长度以及错误消息字符串,都读取自 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(build-id 89edbbe81c5b328a958fe628a9f2207d;build libtpu_lts_20260413_b_RC00)——来源包括 TileTaskOutliningPass::runOnOperation@0x13606220)、它的逐 TileTaskOp walk 回调(@0x136066e0),以及 LaunchTileTaskOp builders 的反编译 C++。其他版本会有所不同。

摘要

SparseCore 后端把每个卸载计算运行在三个子引擎上:SCS(标量控制)、TAC(tile-access / DMA)、TEC(向量计算),并把这种拆分表示为普通 MLIR 函数:每个引擎一个函数,每个函数都带有一个 sc.sequencer StringAttr。创建这些函数的 pass 是 TileTaskOutliningPass。它是一个经典的 region outliner,形态是 getUsedValuesDefinedAboveFuncOp::createRegion::cloneInto → replace-with-launch,和 LLVM/MLIR 把并行 region 提取成 kernel 的形状相同;它为 SparseCore 做了专门化:捕获内容(只捕获静态 memref)、盖章内容(sc.sequencer = "execute"),以及留下的内容(一个 sc_tpu.launch_tile_task op;在 overlayer 路径上还会有一个 PrefetchTileTaskOp 和一个 overlay memref.alloc)。

本页负责说明pass 机制和 launch 发射sc_tpu.tile_task region 如何变成独立的 func.func,它的 live-in 如何变成函数参数,body 如何被克隆,原始 op 如何被 launch 替换,以及提取出的函数携带哪些属性。本页刻意重新推导逐 op→engine 的选择谓词,也就是决定某个 lowered op 是 Stream 还是 DMA、以及它落入哪个 TileTask region 的规则。该谓词由 getSequencerType 负责(GetTransferKind + sc.sequencer 回读);这里把 engine 字符串视为 pass 的输出,并按字节确认在 6acc60406(gfc)上为 "execute"

重新实现者必须内化的单个结构事实是:TileTaskOutliningPass 对每个 sc_tpu.tile_task op 精确产生一个提取出的函数,标记为 "execute"(TEC),并把该 op 替换为一个 sc_tpu.launch_tile_task,其 execute_func 符号指向该函数。 外层函数,也就是发出 tile_task 的函数,是 SCS 控制程序(sc.sequencer = "scs")。在 Viperfish/Ghostlite 上,同一个按 Target 参数化的 pass 还会拆出一个 "access"(TAC)函数;在 6acc60406 上没有 TAC,所以 tile-fetch 工作折叠进 "execute"。本页记录 gfc(6acc60406)单 "execute" 路径,也就是反编译回调发射的路径,并把 TAC 拆分标为 LOW。

对重新实现来说,契约是:

  • 一个 pass,一次 walk,每个 task 一个 launch。 runOnOperation 先运行 Timem 冲突 gate,解析 SparseCoreTarget,然后 walk 每个 sc_tpu.tile_task op。每次匹配都独立提取;提取出的函数之间没有跨 task 共享。
  • 捕获项是 region live-in,且必须是静态 memref。 参数来自 getUsedValuesDefinedAbove;每个参数都必须是形状完全静态的 MemRefType,否则 pass 会以 "Tile tasks only support capture of static memrefs" 中止(tile_task_outlining_pass.cc:62)。
  • 函数名是 "execute" 加逐 pass 计数器。 前缀 "execute" 会和十进制计数器(APInt::toString)一起渲染成唯一符号;属性值 sc.sequencer 则是裸 "execute"(7 个字符)。
  • 替换 op 是 sc_tpu.launch_tile_task(23 字符 op 名),携带一个 FlatSymbolRefAttr execute_func、一个 clear_ibuf UnitAttr、overlay-alloc 操作数,以及捕获的 ValueRange。原始 tile_task 会被 erase
Passxla::tpu::sparse_core::(anon)::TileTaskOutliningPass::runOnOperation @0x13606220
FactoryCreateTileTaskOutliningPass(const jellyfish::Target&) @0x13605fe0
基类mlir::sparse_core::impl::TileTaskOutliningBase<…>(TableGen pass base)
逐 op 回调walk<TileTaskOp> callback @0x136066e0;嵌套 terminator walk @0x136071c0
被提取的 opsc_tpu.tile_taskmlir::sparse_core::TileTaskOp)→ 提取出的 func::FuncOp
Launch opsc_tpu.launch_tile_taskmlir::sparse_core::LaunchTileTaskOp);create(FuncOp overload,本处调用)@0x145dd0e0build @0x1459c060
Engine 标记setAttr("sc.sequencer", StringAttr "execute") — 名称 12 字符,值 7 字符
下游读取方LowerSequencerFunctionsPass::runOnOperation @0x13532120
置信度CONFIRMED(反编译已验证),除非某行或 callout 另有说明

关于 engine-selection 决策(Stream/DMA、sc.sequencer 谓词),见 getSequencerType;关于此 pass 在十二个 pass 的 codegen pipeline 中的位置,见 SC Backend Pipeline;关于提取出的函数最终编码进入的逐 engine bundle,见 Per-Engine Bundle Slot-Base Map


Pass 所在位置

Outlining 运行在十二个 pass 的 SparseCore codegen pipeline(CustomKernelEmitter::RunPasses之前,而不是其中。RunPasses 消费的 module 已经存在逐 engine 函数且已经打上标记;它的第一个 pass(ConvertIntegerMemrefs)作用在此 pass 生成的 func.func 上。因此顺序是:

text
  tpu-dialect module with sc_tpu.tile_task ops

        ▼  TileTaskOutliningPass::runOnOperation        @0x13606220   ── THIS PAGE
        │     for each sc_tpu.tile_task:
        │        outline region → func.func (sc.sequencer="execute")
        │        replace op with sc_tpu.launch_tile_task

        ▼  module: SCS control program (sc.sequencer="scs")
        │          + one "execute" func per task, launched from it

        ▼  CustomKernelEmitter::RunPasses  @0x13202780  ── see sc-backend-pipeline.md
        │     12 single-pass managers, ending in LowerToMlo

        ▼  LowerSequencerFunctionsPass     @0x13532120  ── per-engine body lowering
        │     reads sc.sequencer via the ScDialect predicates

        ▼  per-engine bundle codec (SCS / TAC / TEC)   ── see bundle-slot-base-map.md
```text

与 [getSequencerType](getsequencertype.md) 的分工是精确的:该页的 *Layer 2* 把这个 outliner 命名为“写入 `sc.sequencer` 的地方”,并明确把 pass 机制转交给本页;本页把 `"scs"`/`"access"`/`"execute"` 字符串集合和逐 op region 分配规则视为已经决定的输入,只记录把它们物化出来的 IR 手术。

---

## `runOnOperation` — Pass 驱动器(`@0x13606220`)

### 目的

在整个 `ModuleOp` 上驱动 outlining:先检查 Timem/tile-task 冲突,解析 SparseCore target,然后访问每个 `sc_tpu.tile_task` op 并提取它。该 pass 作用于 module(它是 module-level pass,挂在顶层,不嵌在 func 内)。

### 算法

```c
// TileTaskOutliningPass::runOnOperation(this)            @0x13606220
void runOnOperation(TileTaskOutliningPass *this):
    ModuleOp module = this->getOperation();               // this+5 (masked ptr)

    // 1. Timem conflict gate.
    if (overlayer::ContainsExplicitTimemAccess(module)) {  // @0x1395bb60
        // only a problem if the module ALSO launches tile tasks:
        if (walk<TileTaskOp>(module, ContainsTileTask) == found) {  // @ ContainsTileTask lambda
            emitOpError(module)
              << "programs that launch tile tasks while also explicitly accessing "
              << "Timem are not supported";                // two appended literals
            report();
            this->signalPassFailure();                     // this+40 |= 4
            return;
        }
    }

    // 2. Resolve the SparseCore target (gen / geometry) and cache it.
    this->target_ = xla_mlo_util::SparseCoreTargetForModule( // @0x14a8b5c0
                        this->target_arg_, module);          // stored at this+43

    // 3. A naming/context seed: the module body's first op anchor + an
    //    MLIRContext handle (this+352 holds the monotonic func counter).
    ctx = module.getContext();

    // 4. Walk every sc_tpu.tile_task op; outline each via the per-op callback.
    if (walk<TileTaskOp>(module, &outline_one /* @0x136066e0 */) == interrupted)
        this->signalPassFailure();                          // this+40 |= 4

重新实现者必须复现两个驱动层事实:

  • Timem gate 是硬错误,不是跳过。 如果一个 module 既显式读/写 Timem,又发起 tile task,就会用两个片段的诊断拒绝它("programs that launch tile tasks while also explicitly accessing " + "Timem are not supported")。检查分两次 walk:Timem 侧为 ContainsExplicitTimemAccess@0x1395bb60),launch 侧为 ContainsTileTask walk lambda;只有二者同时为真才报错。这保护了 tile-overlay 机制(见下文),因为后者会复用 Timem 区域。
  • target 在 walk 前解析一次。 SparseCoreTargetForModule@0x14a8b5c0)把 module 映射到 SparseCoreTarget,结果缓存到 pass 对象上(this+43)。逐 op 回调会读回它来驱动 overlayer 决策。target 参数化了 6acc60406 与 VF/GL 的行为差异(单 "execute" vs "access"+"execute" 拆分)。

函数映射

FunctionVA作用
TileTaskOutliningPass::runOnOperation0x13606220pass 驱动器:gate → target → walk
CreateTileTaskOutliningPass(Target&)0x13605fe0pass factory(绑定 Target
overlayer::ContainsExplicitTimemAccess0x1395bb60Timem 冲突 gate
xla_mlo_util::SparseCoreTargetForModule0x14a8b5c0module → SparseCoreTarget
walk<TileTaskOp> callback(outline one)0x136066e0逐 op outlining body
nested terminator walk(OutlineSequencerFunction lambda)0x136071c0重接克隆 region 的 terminator

逐 op 回调(@0x136066e0) {#the-per-op-callback-0x136066e0}

这是 pass 的核心:给定一个 sc_tpu.tile_task op,它构建提取出的函数、克隆 body、把 op 替换为 launch,并打上 engine 属性。反编译回调按以下顺序执行。

算法

c
// walk<TileTaskOp> callback "outline one"                @0x136066e0
WalkResult outline_one(TileTaskOp op):
    if (op == null || op.typeID != TileTaskOp::id)        // line 126 — TypeID gate
        return advance;                                   // (walk visits all ops; skip non-tasks)

    Region &region = op.getRegion();                      // op + body offset

    // 1. Live-ins → future function arguments.
    SetVector<Value> liveIns;
    getUsedValuesDefinedAbove(region, liveIns);           // @0x1c974440 (line 155)

    // 2. Read the per-task overlay budget attribute (inherent → dictionary).
    Attribute hwm = op.getInherentAttr("sc.execute_alloc_high_water_mark", 32);
    if (!hwm) hwm = op.getDictionaryAttr().get("sc.execute_alloc_high_water_mark", 32);

    // 3. Build the function name: "execute" + decimal(pass.funcCounter++).
    name = "execute";
    APInt(64, this->funcCounter++).toString(name, /*radix=*/10);   // pass+352 counter

    // 4. Argument types: each live-in must be a STATIC memref.
    SmallVector<Type> argTypes;
    for (Value v : liveIns) {
        MemRefType m = dyn_cast<MemRefType>(v.getType());
        // CHECK(memref.hasStaticShape()) — abort otherwise:
        if (!m || !m.hasRank() || hasDynamicDim(m.getShape()))     // @0x1d896e20 / @0x1d8921e0
            LOG(FATAL) << "Tile tasks only support capture of static memrefs"; // .cc:62
        argTypes.push_back(m);
    }
    FunctionType fty = FunctionType::get(ctx, argTypes, /*results=*/{});  // @0x1d891c80

    // 5. Create the func and its entry block.
    FuncOp fn = func::FuncOp::create(builder, loc, name, fty);     // @0x1d8006a0
    Block *entry = fn.addEntryBlock();                             // @0xea4b680

    // 6. Map each live-in Value → the matching entry block argument.
    IRMapping map;
    for (i in 0..liveIns.size())
        map.map(liveIns[i], entry->getArgument(i));    // DenseMap<Value,Value>

    // 7. Clone the tile_task body into the function under the mapping.
    region.cloneInto(fn.getBody(), map);               // @0x1d8dfa60

    // 8. Wire the synthetic entry to the cloned first block.
    cf::BranchOp::create(builder, clonedEntry, /*operands=*/{});   // @0x17bd69a0

    // 9. Fix up the cloned region's terminators (sequencer-function form).
    walk<TerminatorOp>(fn, OutlineSequencerFunction_terminatorCb); // @0x136071c0

    // 10. Stamp the engine + budget attributes on the outlined func.
    fn.setAttr("sc.sequencer", StringAttr::get(ctx, "execute"));   // @0xea37860, value 7 chars
    if (hwm)
        fn.setAttr("sc.alloc_high_water_mark", hwm);               // 24-char name

    // 11. Tile-overlayer path (gated on the target/module).
    Value overlayAlloc = null;
    if (overlayer::IsTileOverlayerEnabled(target)) {               // @0x1395d880
        int sz = overlayer::GetTileOverlaysSize(target);           // @0x1395ba20
        fn.setAttr("sc.func_size_limit", getI32IntegerAttr(sz));   // 18-char name
        Type ovTy = overlayer::GetTileOverlayMemRefType(target,…); // @0x1395b960
        overlayAlloc = memref::AllocOp::create(builder, loc, ovTy);// @ memref alloc
        sparse_core::PrefetchTileTaskOp::create(builder, loc, fn, overlayAlloc);
    }

    // 12. Replace the original op with a launch of the outlined func, then erase it.
    Value task = op.getOperand(0);                     // the tile-task descriptor operand
    LaunchTileTaskOp::create(builder, loc, /*execute=*/fn /*FuncOp*/, overlayAlloc, task,
                             /*captures=*/liveIns, /*clear_ibuf=*/true);   // @0x145dd0e0 (FuncOp overload)
    op.erase();                                        // @0x1d8ccd20
    return advance;
```text

### 步骤注记

编号步骤各自带有一个值得说明的重新实现细节。

- **步骤 1 — live-in 就是捕获集合。** `getUsedValuesDefinedAbove(region, SetVector)`(`@0x1c974440`)返回 region 使用但在其外定义的每个 SSA value。这些 value 会按*迭代顺序*成为函数参数,因此 launch 必须按相同顺序传递它们(回调会对同一个 `SetVector` 迭代两次:一次构建参数类型,一次构建 IRMapping 和 launch 操作数)。顺序就是 launch site 与函数签名之间的契约。
- **步骤 4 — 静态 memref 捕获会被强制执行,并且是 fatal。** 参数类型循环要求每个捕获 value 都是 `MemRefType`(`TypeIDResolver<MemRefType>` 检查),带 rank,且形状完全静态(shape walk 把 `0x8000000000000000` = `ShapedType::kDynamic` 作为失败哨兵)。动态维度会在 `tile_task_outlining_pass.cc:62` 触发带 `"Tile tasks only support capture of static memrefs"` 的 `LOG(FATAL)`。重新实现者不能提取捕获动态形状 buffer 的 tile task,因为 SC tile 模型没有对应的运行时大小 descriptor。
- **步骤 7 — 使用 `cloneInto`,不是移动。** `Region::cloneInto(funcBody, IRMapping)`(`@0x1d8dfa60`)会在 value mapping 下*克隆* body;原始 op 不会被移动,而是在步骤 12 擦除 op 时删除。这就是为什么步骤 6 需要新 IRMapping(live-in → block-arg):clone 内部的每个外部 use 都会被重写为函数 block 参数。
- **步骤 9 — terminator fix-up 是“sequencer function”形态。** 克隆后,会对新函数进行第二次 `walk`(`@0x136071c0`),把它的 region terminator 重写成 sequencer-function 形态(嵌入的 lambda 名为 `OutlineSequencerFunction`)。这一步把通用克隆 region 变成良构的 `func.func` body。它没有逐行 bit-trace(HIGH)。
- **步骤 1011 — 三类属性。** 提取出的函数携带:(a) engine 标记 `sc.sequencer = "execute"`;(b) 从 op 的 `sc.execute_alloc_high_water_mark` inherent attr 复制来的逐 task 分配预算 `sc.alloc_high_water_mark`;并且仅当 tile-overlayer 启用时,携带 (c) `sc.func_size_limit`(由 `GetTileOverlaysSize` 给出的 i32)。overlayer 路径还会在 launch 之前注入 overlay buffer 的 `memref.alloc` 和一个 `PrefetchTileTaskOp`。

> **NOTE — high-water-mark 属性从 op 读取,并以不同名称重新盖到 func 上。** 回调从 `tile_task` op 读取 `sc.execute_alloc_high_water_mark`(32 字符名称),然后在提取出的函数上把该值写回为 `sc.alloc_high_water_mark`(24 字符名称)。早期原始分析把它记录为 pass *设置* `sc.execute_alloc_high_water_mark`;反编译显示该名称是*来源*(read),而 `sc.alloc_high_water_mark` 是*目的地*(write)。把 32 字符名称视为 task 上的输入属性,把 24 字符名称视为函数上的输出。
>
> **GOTCHA — 函数名和属性值不是同一个字符串。** 符号是 `"execute" + N`(例如 `execute0`、`execute1`、…),由逐 pass 计数器通过 `APInt::toString` 唯一化。`sc.sequencer` 属性值是裸 `"execute"`(精确 7 字符,也就是 `@0x1459a020` 的 `HasExecuteSequencerTypeAttribute` 谓词检查的长度)。如果重新实现者把 `sc.sequencer` 设成*符号名*,会无法通过 [getSequencerType](getsequencertype.md) 中下游的 length-7 字节比较。符号名可自由命名;engine 标记必须使用裸词。

### 函数映射

| Function | VA | 作用 |
|---|---|---|
| `getUsedValuesDefinedAbove` | `0x1c974440` | region live-in 收集 → arg/capture set |
| `BaseMemRefType::hasRank` | `0x1d896e20` | 捕获 rank 检查 |
| `MemRefType::getShape` | `0x1d8921e0` | 捕获静态形状检查 |
| `FunctionType::get` | `0x1d891c80` | 构建提取出的 func type |
| `func::FuncOp::create` | `0x1d8006a0` | 创建提取出的函数 |
| `FunctionOpInterface::addEntryBlock` | `0xea4b680` | entry block + block args |
| `Region::cloneInto` | `0x1d8dfa60` | 在 IRMapping 下克隆 task body |
| `cf::BranchOp::create` | `0x17bd69a0` | 连接 synthetic entry |
| `StringAttr::get` | `0x1d85dda0` | 构建 attr name/value StringAttrs |
| `Operation::setAttr` | `0xea37860` | 盖上 `sc.sequencer` / budget attrs |
| `Operation::erase` | `0x1d8ccd20` | 删除原始 `tile_task` |
| `overlayer::IsTileOverlayerEnabled` | `0x1395d880` | gate overlay 路径 |
| `overlayer::GetTileOverlaysSize` | `0x1395ba20` | `sc.func_size_limit` 的 i32 size |
| `overlayer::GetTileOverlayMemRefType` | `0x1395b960` | overlay-buffer memref type |
| `memref::AllocOp::create` | `0x183015a0` | overlay buffer 分配 |
| `sparse_core::PrefetchTileTaskOp::create` | `0x145f4cc0` | launch 前的 overlay prefetch |
| `LaunchTileTaskOp::create`(FuncOp overload) | `0x145dd0e0` | 回调发射的 launch |

---

## Launch Op — `sc_tpu.launch_tile_task`

### 目的

该 op 留在提取出的 `tile_task` 原位置。它用符号命名提取出的 `"execute"` 函数,并携带函数运行时将接收的操作数。后续 lowering(`LaunchTileTaskOpLowering` `@0x135901c0`,LLVM conversion pattern)会把它变成实际 launch 序列;arguments-spill pass 会把它的 `execute_func` 符号解析回 `func::FuncOp`。

### Op 形态(来自 `build` / `create`)

存在两个 `create` overload:接收 `func::FuncOp` 的版本(`@0x145dd0e0`,签名 `create(OpBuilder&, Location, func::FuncOp, Value, Value, ValueRange, bool)`)——**也就是 outliner 回调调用的那个**(在 call site `136070ec0x145dd0e0` 已验证)——以及预先形成的 `Value` 变体(`@0x145dcfa0`,签名 `create(OpBuilder&, Location, Value, Value, ValueRange, bool)`)。FuncOp overload 转发到规范 builder `LaunchTileTaskOp::build`(`@0x1459c060`,签名 `build(OpBuilder&, OperationState&, Value, Value, UnitAttr, FlatSymbolRefAttr, ValueRange)`),后者把 func 解析为其 `FlatSymbolRefAttr`,并把 `clear_ibuf` 设为 `UnitAttr`。两个 overload 固定同一个 op 表面:

```c
// LaunchTileTaskOp::create(builder, loc, fn /*FuncOp*/, alloc, task, captures, clear_ibuf)  @0x145dd0e0
op = OperationState("sc_tpu.launch_tile_task", 23);   // op-name literal, 23 chars
// forwards to build(@0x1459c060):
op.addOperands(alloc);         // the overlay-alloc Value (overlayer path)
op.addOperands(task);          // the tile-task descriptor Value
op.addOperands(captures);      // N — the ValueRange of region live-ins
if (clear_ibuf)
    properties.clear_ibuf = builder.getUnitAttr();    // UnitAttr present ⇔ true
properties.execute_func = FlatSymbolRefAttr(fn.getSymName());  // symbol of the outlined func
return builder.create(op);     // verified TypeID == LaunchTileTaskOp::id
元素种类来源
op namestring "sc_tpu.launch_tile_task" (23)create @0x145dd0e0 / @0x145dcfa0
execute_funcFlatSymbolRefAttrbuild @0x1459c060;accessor getExecuteFunc @0x145dcf40
clear_ibufUnitAttr(存在 ⇔ true)create bool arg;accessor getClearIbuf @0x145dcf20
operand 0(taskValueaddOperands site 1
operand 1(allocValue(overlay buffer)addOperands site 2
trailing operandsValueRange(captures)addOperands site 3

execute_func 符号往返

launch 不嵌入函数,而是通过符号引用函数。LaunchTileTaskOp::getExecuteFunc@0x145dcf40)返回 FlatSymbolRefAttr 的 root-reference StringAttr 值(函数名 "execute<N>")。自由函数 GetExecuteFunc@0x136054e0)会解析它:它在 launch 的 parent op 上构建 SymbolTablelookup launch.getExecuteFunc(),并在 tile_task_arguments_spill.cc:70"execute_func != nullptr" 断言结果非空。这是二进制给出的保证:每个 launch 都指向同一 module 中真实存在的提取函数。

QUIRK — outliner 无条件以 clear_ibuf = true 调用 create 在回调(@0x136066e0)中,launch 构建时 bool 参数被置位,因此产生的 op 总是携带 clear_ibuf UnitAttr。该属性存在是为了让下游消费者可以抑制 instruction-buffer clearing,但 outliner 自身从不产生 cleared-false 形式;镜像此 pass 的重新实现者应发射存在的 unit attribute。


Engine 分配 — 字符串的含义

该 pass 在 6acc60406(gfc)上只写 "execute"。完整的三值映射("scs" / "access" / "execute")以及决定 op 落入哪个 region 的逐 op 规则由 getSequencerType 负责;本节只记录 outliner 产生什么以及逐 generation 的形态。

sc.sequencerEngine产生方出现于此 pass 发射
"scs"SCS(标量控制)外层函数(发出 launch 的程序)VF · GL · GF间接(parent func)
"access"TAC(tile-access / DMA)同一 pass 的 VF/GL TAC 拆分仅 VF · GL不在 gfc 回调中
"execute"TEC(向量计算)此回调(fn.setAttr,值 7 字符)VF · GL · GF是 — 每个 task

NOTE — 6acc60406 回调只盖 "execute" 反编译的逐 op 回调(@0x136066e0)设置 sc.sequencer = "execute",没有设置其他值;这个 gfc build 中没有发射 "access" 的分支,并且 gfc 二进制无法到达 TAC 拆分(6acc60406 上没有 SparseCoreTacCodecBase,见 getSequencerType)。逐 op Access-vs-Execute 规则由上游负责(此处 LOW confidence)。

NOTE — parent function 的 "scs" 标记不是此 pass 设置的。 提取出的 "execute" 函数在此处创建;外层控制程序上的 "scs" 标记单独附加(它是此 pass 运行后 sc_tpu.launch_tile_task ops 所在的函数)。重新实现者必须确保控制程序携带 sc.sequencer = "scs",这样 launch 及其周围的 sync/addressing 代码才会 lower 到 SCS codec。


下游回读

提取出的函数由 LowerSequencerFunctionsPass::runOnOperation@0x13532120)消费。该 pass walk 每个 LLVM::LLVMFuncOp,通过 ScDialect 谓词读取其 sc.sequencer,并 lower 逐 engine body。字符串到 engine 的谓词已按字节确认:

  • ScDialect::HasCoreSequencerTypeAttribute@0x14599ec0)— 匹配 "scs"(len 3)。
  • ScDialect::HasExecuteSequencerTypeAttribute@0x1459a020)— 匹配 "execute"(len 7)。
  • ParentHasSequencerTypeAttribute@0x1353e980)— 沿 Block::getParentOp 走到外层 LLVMFuncOp,并要求它标为 "scs""execute"TileTaskOp / LaunchTileTaskOp / PrefetchTileTaskOp / TileTaskWaitOp family 携带此 trait。

完整谓词解码(little-endian 字节字面量、"access" 没有谓词的不对称性、trait enforcement)在 getSequencerType 中,本页不重复。交接契约是:此 pass 写字符串;那个 pass 读字符串。 选中的 TpuSequencerType codec 模板参数({SCS=3, TAC=4, TEC=5})随后驱动逐 engine bundle encoder;见 Per-Engine Bundle Slot-Base Map


重新实现清单

要复现 SparseCore region→sequencer outliner:

  1. 作为 codegen 前的 module-level pass 运行。 检查 Timem/tile-task 冲突(硬错误),解析一次 SparseCoreTarget,然后 walk 每个 sc_tpu.tile_task op。
  2. 按标准 region-outline 配方提取。 live-in 通过 getUsedValuesDefinedAbove 获取 → arg types → FuncOp::create + addEntryBlockIRMapping(live-in → block-arg)Region::cloneIntocf::BranchOp entry wiring → terminator fix-up。
  3. 强制静态 memref 捕获。 拒绝(fatal)任何不是静态形状 MemRefType 的捕获 value。
  4. 符号命名为 "execute"+N;属性标记为裸 "execute" 保持二者不同;下游 reader 会对裸 7 字符值做长度检查。
  5. 盖上 budget 属性。 把 op 的 sc.execute_alloc_high_water_mark 复制到 func 的 sc.alloc_high_water_mark;在 overlayer 路径上添加 sc.func_size_limit(i32)、一个 memref.alloc overlay buffer,以及一个 PrefetchTileTaskOp
  6. 替换为 sc_tpu.launch_tile_task 并擦除原始 op。 launch 通过 FlatSymbolRefAttr execute_func 引用 func,按 live-in 顺序以 ValueRange 携带 captures,并设置 clear_ibuf UnitAttr
  7. 推迟 engine decision 此 pass 物化 engine membership;它不做决策。在 TAC generation 上,应从上游 Stream/DMA classifier 驱动 Access-vs-Execute 拆分(见 getSequencerType)。

置信度摘要

ClaimEvidence
runOnOperation gates on Timem+tile-task conflict, resolves target, walks TileTaskOpdecompile @0x13606220: ContainsExplicitTimemAccess + two error fragments + SparseCoreTargetForModule + walk
Per-op callback outlines via live-ins → FuncOp → cloneInto → launch → erasedecompile @0x136066e0 (lines 126–476)
Captures = getUsedValuesDefinedAbove, must be static memrefs@0x1c974440; hasRank/getShape + LOG(FATAL) tile_task_outlining_pass.cc:62
Func name = "execute" + decimal counter (APInt::toString)callback: qmemcpy("execute",7) + counter pass+352 + APInt::toString
sc.sequencer stamped "execute" (12-char name, 7-char value) via setAttrsetAttr @0xea37860 with StringAttr "sc.sequencer"/"execute"
Budget attr read from sc.execute_alloc_high_water_mark (32), written as sc.alloc_high_water_mark (24)callback getInherentAttr(…,32) read + setAttr(…,24) write
Overlayer path adds sc.func_size_limit (18) + memref.alloc + PrefetchTileTaskOpIsTileOverlayerEnabled/GetTileOverlaysSize/GetTileOverlayMemRefType + create calls
Launch op name "sc_tpu.launch_tile_task" (23 chars); execute_func FlatSymbolRef; clear_ibuf UnitAttrboth create overloads (@0x145dd0e0 / @0x145dcfa0) carry the op-name literal; build @0x1459c060 signature; getExecuteFunc/getClearIbuf accessors
Outliner always sets clear_ibuf = truecallback passes bool=1 to create
GetExecuteFunc resolves the symbol via SymbolTable::lookup, asserts non-null@0x136054e0 tile_task_arguments_spill.cc:70
6acc60406 callback emits only "execute"; VF/GL also emit "access"gfc callback has no "access" branch; cross-gen pass is Target-parameterized (not traced)
Callback calls the func::FuncOp LaunchTileTaskOp::create overload 0x145dd0e0 (the Value-only overload 0x145dcfa0 is a sibling, not the call here)disasm call site 136070ec → 0x145dd0e0; 0x145dd0e0 forwards to build @0x1459c060
Terminator fix-up walk @0x136071c0 (OutlineSequencerFunction lambda) reshapes the cloned regioncallback nested walk call site; lambda name in symbol
Per-op Access-vs-Execute region-selection rulenot bit-traced; owned by getSequencerType

交叉引用

  • getSequencerType — engine-selection decision(Stream/DMA classifier + sc.sequencer 回读谓词),其输出会流入此 pass;逐 op region-assignment 规则的负责人。
  • SC Backend Pipeline — 在此 outliner 之后、作用于它生成的 func.func 的十二个 pass codegen pipeline。
  • SparseCore Overview — 三个 engine class(SCS/TAC/TEC)、逐 generation 存在性,以及 SCv0 弃用背景。
  • SCS (Scalar) Engine — 发出 sc_tpu.launch_tile_task ops 的 "scs" 控制程序。
  • TAC Engine"access" tile-fetch engine 及其在 6acc60406 上的移除(解释为什么 gfc 回调只发射 "execute")。
  • TEC (Vector) Engine — 提取出的函数面向的 "execute" 向量 engine。
  • Per-Engine Bundle Slot-Base Map — 提取并 lower 后的函数最终编码进入的逐 engine bundle。
  • Binary: extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id 89edbbe81c5b328a958fe628a9f2207d
  • Index entry: Part IX — SparseCore & BarnaCore / SparseCore engines — back to index