Skip to main content

第八章:控制流中的支配关系与 SSA Phi 指令

未完成的章节

本章是一个未完成的章节,将会基本介绍控制流和支配的关系,并且会介绍 SSA 的 Phi 节点问题。

控制流与基本块

控制流中的支配关系

在接下来的内容中,我们重点将会讨论控制流中基本块之间的支配问题。

控制流分析中,支配是一个非常重要的概念,他描述了基本块之间的依赖关系,我们以

来表示基本块
支配基本块

我们可以进一步形式化定义支配关系:

其中:

  • 是支配者(dominator)
  • 是被支配者(dominee)

CFG 中的基本块支配边界

在用户了解完支配问题之后,接下来我们提出一个问题,“一个数据节点的支配的范围有多大?”

要回答这个问题,我们需要定义一个概念叫支配边界。考虑任何离开块 B 的路径。最初路径上的块由 B 支配。最终到达一个不由 B 支配的块。除非路径返回到 B,否则之后的所有块都不受 B 支配。不被 B 支配的第一个块是重要的,因为它指示了 B 支配的块的范围,并使用有关 B 中的计算的信息指示了优化的限制。考虑到所有路径,拥有该特征的块的集合称为支配 B 的边界。

让我们首先给出支配边界的形式化定义:

其中:

  • 表示节点X的支配边界
  • 表示被X支配的节点集合
  • Y是X的支配边界中的一个节点
  • Z是被X支配的某个节点
  • Y是Z的直接后继节点
  • X不支配Y

让我们用一个具体的例子来说明:

我们给出图中各节点的支配边界分析表格:

节点支配的节点集合 Dom(X)直接后继节点支配边界 DF(X)
1{1,2,3,4,5,6,7,8,9,10,11,12,13}{2,5,9}
2{2,3}{3}{4}
3{3}{4}{4}
4{4}{13}{13}
5{5,6,7,8}{6}{4,12,13}
6{6,7,8}{4,7,8}{4,12,13}
7{7}{8,12}{8,12}
8{8}{13}{13}
9{9,10,11}{10,11}{12}
10{10}{12}{12}
11{11}{12}{12}
12{12}{13}{13}
13{13}

给出支配边界计算算法的形式化表示:

其中:

  • 表示节点X的局部支配边界
  • 表示从X的支配树子节点上传递来的支配边界
  • 表示X的直接后继节点集合
  • 表示X在支配树中的直接子节点集合

下面是计算支配边界的完整算法伪代码:

// 计算所有节点的支配边界
function computeDominanceFrontiers(cfg) {
let DF = new Map() // 存储每个节点的支配边界

// 按照支配树的后序遍历节点
for (let X of postorderTraversal(cfg.domTree)) {
DF.set(X, new Set()) // 初始化空集

// 计算局部支配边界
for (let Y of X.successors) {
if (X.immediateDominator !== Y.immediateDominator) {
DF.get(X).add(Y)
}
}

// 计算来自子节点的支配边界
for (let Z of X.domTreeChildren) {
for (let Y of DF.get(Z)) {
if (!dominates(X, Y) || X === Y) {
DF.get(X).add(Y)
}
}
}
}
return DF
}

// 判断X是否支配Y
function dominates(X, Y) {
return Y.dominators.has(X)
}