将 (let ((x rhs)) body) 中 rhs 为 (if cond then else) 的绑定展平为基本块后, 两条分支各自向同一个变量 x 赋值。编译到 SSA 时该变量必须有唯一定义点, 因此分支内需要用 provide(声明提供值)、汇合点用 use(创建唯一定义), 替代普通的 copy。
为什么只有 let + rhs-if 需要 phi?
只有 if 会造成控制流分裂, 而只有 let 的 rhs 位置会让分裂的两条分支向同一个变量赋值。
tail 位置各分支独立返回,begin 位置丢弃结果,都不形成 merge。
原理
伪代码:
scheme
(provide ((x rhs)) body)
(use (x) body)用标记 use-sites 集合区分两类情况:
x已因 rhs-if 被标记时,后续处理中遇到x的绑定,翻译为provide而非copy;- 内层嵌套的
if汇合点不重复发use。
带有嵌套的例子
scheme
(let ((x (if a (if b c d) e)))
(f x))将 rhs 中的 if 提升出来,两侧分支各自绑定 x:
scheme
(if a
(provide ((x (if b c d)))
(goto body))
(provide ((x e))
(goto body)))
body:
(use (x)
(f x))继续内层 if:
scheme
(if a
(if b
(provide ((x c))
(goto inner-body))
(provide ((x d))
(goto inner-body)))
(provide ((x e))
(goto body)))
inner-body:
(goto body)
body:
(use (x)
(f x))两条内层分支跳到 inner-body,else 分支直接跳到 body。 inner-body 仅转发跳转,不放 use,因为 x 的初次标记发生在外层 if, use 必须放在所有 provide 路径收束之处。
三条路径(c、d、e)各 provide 一次, 在 body 汇合为一个 use。 x 从 use 往后唯一定义,满足 SSU(static single use)。
总结
只有 let 中 rhs 为 (if ...) 时才需要 phi, 因为只有该场景让两条分支向同一变量提供值。
标记集 use-sites 在展平过程中传递:
- 首次遇到 let-rhs-if 时标记变量;
- 内层嵌套跳过
use; - 绑定时据此选择
provide或copy。