MaterializeSemanticAliases Pass¶
将语义要求必须是同一块分配的 buffer 归一到同一个 MemRef —— 通过把每个循环
carried 的 iter_arg/initValue MemRef 沿 yield/producer 链向下传播来实现。
概述¶
内存规划区分两种 buffer 共享:
- 强制别名(语义要求): 循环累加器、或原地算子的结果必须落在同一块 buffer——写"下一个"值必须更新 carried buffer,否则循环无法累加。这是正确性, 不是优化。
- 机会别名(可选): 生命周期不冲突的两块独立 buffer 可以共享存储以省内存, 属于优化。
本 pass 只处理强制别名。它从 MemoryReuse 中拆出
(原来是那个 pass 的 "Step 0"),以便机会性的生命周期复用可以被独立跳过:
MemoryPlanner.DSA_RP保留独立分配身份,交给进程内 DSA-RP 求解器放置。MemoryPlanner.PTOAS把生命周期复用和地址分配交给 ptoas。
使用时机:在 InitMemRef(创建 MemRef)之后、所选内存
规划器之前运行。它总是运行。PYPTO 随后运行
MemoryReuse;DSA_RP 在
AllocateMemoryAddr 中消费这些分配身份。
API¶
| C++ | Python | 级别 |
|---|---|---|
pass::MaterializeSemanticAliases() |
passes.materialize_semantic_aliases() |
函数级 |
算法¶
InitMemRef 已经让循环 carried 的 iter_arg 和 return_var 与 initValue
(累加器 buffer)共享同一 MemRef,但 yield 值的生产者 —— 例如计算 acc_next
的 tile.add —— 仍被分配了自己的新 MemRef。本 pass 补上这个缺口:
- 自顶向下重定向(
TopDownRetargeter):对每个ForStmt,取每个iter_arg的规范 MemRef 作为目标,推送到 yield 值及其 producer 链上(跟随原地output-reuses-input算子与 view 输入)。IfStmt的返回值被推送到两个分支的 yield,然后应用收集到的类型改写。 - 规范化 peeled accumulator phi:按后序访问嵌套
IfStmt,同时识别直接的 in-place accumulator producer,以及由分支外 accumulator seed 驱动的分支内 loop。当且仅当一个分支是 accumulator continuation 时,把另一个分支的局部 seed、phi result、alias 和嵌套 loop carry 重定向到 reused input 的规范Accallocation。accumulator loop 和 sibling seed 都必须位于各自分支内,而且 target 在 seed 分支剩余部分必须已 dead。无论 continuation 是直接的tile.matmul_acc还是分支内 loop,它复用的 input 及所有 bare/metadata alias 在if之后都不能存在绕过 phi 的独立读取;否则在 continuation 不执行的 sibling 路径上,重定向后的 producer 会覆盖仍可观察的值。 - 规范化语义 identity chain(
NormalizeIdentityCopyBuffersMutator):让 bare SSA copy 与 source 共享 allocation,并让每个注册的 in-place result 与其 reused input 共享 allocation。这样可在任何 memory planner 观察 lifetime 或 PTOAS 发射 tile handle 之前消除 lowering 引入的类型漂移。
当没有可重定向的内容时(Compute 返回空)本 pass 是 no-op,并跳过
Orchestration 函数(无 TileType 变量)。
与 codegen 的关系¶
PTO codegen 把解析到同一物理 MemRef window(base + byte_offset + size +
pipeline-slot 元数据)的变量渲染成同一个 tile_buf handle,因此本 pass 之后,
循环累加器会发出原地的
pto.tadd ins(%acc, %t) outs(%acc),而不是写到独立的 %acc_next。
memory_planner=DSA_RP 会把每个所得分配身份变成一个 DSA buffer;
memory_planner=PTOAS 则让 codegen 不带物理地址发射该身份,交给 ptoas
PlanMemory。
参见 PTO 代码生成 — 由谁规划内存。
说明¶
- view / 部分 view 保留各自的
byte_offset/size元数据。在DSA_RP下,共享 同一base的所有成员属于同一个物理分配;规划器整体移动该分配,并在回写时保留 每个成员的相对偏移。仅共享base不足以建立 must-alias 关系:互不相交的 byte window 和不同 pipeline slot 会保持独立,直到 producer 被安全地重定向到精确的 canonical window。 - 在默认(
PYPTO)流水线里,本 pass 加上MemoryReuse组合起来等于原来单个MemoryReusepass 的行为。 DSA_RP与PTOAS都跳过这里的机会性 MemRef 合并;二者都不能撤销本 pass 建立的强制别名关系。- accumulator-phi 规范化会在 lifetime planning 之前对所有 memory planner 运行。
legacy
PYPTO路径在机会性 reuse 之后会再运行一次,因为 reuse 可能引入新的 carry/phi mismatch。 - 新的 matmul accumulator 推荐使用单个
tile.matmul_acc(..., init_cond=...)。为兼容已有手写 kernel,peeledmatmul/matmul_acc分支仍受支持,并由本 pass 规范化。