ConvertToSSA Pass¶
将非 SSA IR 转换为静态单赋值 (SSA) 形式,包含变量重命名、phi 节点和 iter_args。
概述¶
此 Pass 将具有对同一变量多次赋值的中间表示 (IR) 转换为 SSA 形式,使每个变量恰好被赋值一次。它处理以下情况:
- 直线代码:对同一变量的多次赋值
- If 语句 (Statement):在一个或两个分支中修改的变量
- For 循环:在循环体内修改的变量
- 混合 SSA/非 SSA:保留现有的 SSA 结构,同时转换非 SSA 部分
需要:TypeChecked 属性 (Property)(需在运行本 Pass 之前已建立,可通过属性验证/VerificationInstrument 等机制检查)。
使用时机:在任何需要 SSA 形式的优化或分析之前运行此 Pass(如 OutlineIncoreScopes、内存优化 Pass)。
API¶
| C++ | Python | 级别 |
|---|---|---|
pass::ConvertToSSA() |
passes.convert_to_ssa() |
函数级 |
工厂函数:
Python 用法:
from pypto.pypto_core import passes
ssa_pass = passes.convert_to_ssa()
program_ssa = ssa_pass(program)
算法¶
- 变量重命名:为每次赋值添加 SSA 后缀(x -> x__ssa_v0, x__ssa_v1, x__ssa_v2)
- If 的 Phi 节点:为在 if 分支中修改的变量添加 phi 节点(return_vars + YieldStmt),包括在两个分支中独立定义的变量
- 循环的 Iter_args:将循环中修改的变量转换为 iter_args + return_vars 模式,带有 YieldStmt
- 逃逸变量提升:通过前向使用分析,将首次在循环体内定义但在循环后使用的变量提升为 iter_args + return_vars
- 作用域跟踪:跨嵌套作用域跟踪变量定义
- 跨作用域的 escaping 防护:除
RuntimeScopeStmt外的ScopeStmt子类(HierarchyScopeStmt/InCoreScopeStmt/ClusterScopeStmt/SpmdScopeStmt),ConvertScope在进入 body 前把future_needs_裁剪到仅包含 scope 之前已经存在的变量。这样可以阻止 scope body 内的嵌套循环把 scope-local 新变量根据 scope 之后的引用错误地提升成init_values=(foo__FREE_VAR,)。scope body 内首次定义、然后在 scope 之外引用的变量仍然能正常替换(OutlineIncoreScopes等后续 pass 依赖这一行为)。RuntimeScopeStmt(pl.scope())只是 codegen 包装节点,保持完全透传。 - 保留:保持现有 SSA 构造不变
关键变换:
x = 1; x = 2->x__ssa_v0 = 1; x__ssa_v1 = 2- 具有分歧赋值的 If -> 在两个分支中添加 return_vars 和 YieldStmt
- 具有循环携带依赖的 For 循环 -> 添加 iter_args/return_vars/YieldStmt
- 循环逃逸变量 -> 提升为 iter_args,以匹配的 Out 参数作为初始值
示例¶
直线代码¶
变换前:
变换后:
If 语句¶
变换前:
变换后:
x__ssa_v0 = 1
if condition:
x__ssa_v1 = 2
yield (x__ssa_v1,) # Yield modified variable
else:
yield (x__ssa_v0,) # Yield original variable
return_vars = (x__phi_v2,) # Phi node
z = x__phi_v2 + 3
For 循环¶
变换前:
变换后:
sum__ssa_v0 = 0
for i in range(10):
iter_args = (sum__iter_v1,)
init_values = (sum__ssa_v0,)
# Loop body
sum__ssa_v2 = sum__iter_v1 + i
yield (sum__ssa_v2,)
return_vars = (sum__rv_v3,)
循环逃逸变量¶
变换前(变量 out 首次在循环内定义,在循环后使用):
for i in range(4):
tile_c = tile.add(tile_a, tile_b)
out = tile.store(tile_c, [offset, 0], c) # first assignment inside loop
return out # used after loop
变换后(提升为 iter_arg + return_var):
for i in range(4):
iter_args = (out__iter_v0,)
init_values = (c__ssa_v0,) # Out parameter as initial value
tile_c__ssa_v0 = tile.add(tile_a__ssa_v0, tile_b__ssa_v0)
out__ssa_v1 = tile.store(tile_c__ssa_v0, [offset__ssa_v0, 0], out__iter_v0)
yield (out__ssa_v1,)
return_vars = (out__rv_v2,)
return out__rv_v2
实现¶
头文件:include/pypto/ir/transforms/passes.h
实现文件:src/ir/transforms/convert_to_ssa_pass.cpp
- 使用手动语句分派和显式作用域管理
- 维护变量重命名的版本映射
- 插入 YieldStmt 并管理 return_vars/iter_args
Python 绑定:python/bindings/modules/passes.cpp
测试:tests/ut/ir/transforms/test_convert_to_ssa_pass.py
- 测试直线代码重命名
- 测试 if 语句 phi 节点
- 测试 for 循环 iter_args
- 测试嵌套作用域
- 测试混合 SSA/非 SSA