由于仍然有些概念不是很清晰,这里仅当学习记录。
1. 支配关系
我们说d dom i,节点d支配节点i,如果从entry到节点i的所有可能执行路径都包含d。
所以dom关系是自反的,传递的,反对称的
- 自反性:a dom a,每个节点都支配它自己
- 传递性:a dom b, b dom c, 则a dom c
- 反对称性:a dom b, b dom a, 则a与b相等
根据自反偏序集的定义,dom关系是自反偏序(简称偏序)的。那么我们可以用Hasse图(哈塞图)表示支配关系,也叫支配树。
2. 立即支配
dom关系的一个子关系叫做 idom (immediate dominance, 立即支配)。a idom b, 当a dom b并且a != b,不存在节点c,使得c != a, c != b, a dom c, c dom b
记idom(b)为b的立即支配者,显然,节点的立即支配者是唯一的
3. 严格支配
d sdom i, 当d dom i, 且d != i
4. 求节点的支配者
一个基本的方法就是:a dom b, 当且仅当:
- a = b;或者
- a 是 b的唯一前驱节点;或者
- b有多个直接前驱,b的所有直接前驱(记为c)都满足,c != a, 并且a dom c
第3点的主要思想就是dom关系的传递性:a dom c,c dom b,则a dom b
如果某个节点a能够支配b的所有直接前驱节点c,那么a一定支配b。
-
(从entry到b的所有前驱节点都经过a,那么从entry到b肯定也都经过a)
-
(也可以理解为,如果a支配b的所有前驱,那么b的所有前驱可以简化成一个节点c,a一定支配此节点c,而c为b的唯一前驱节点,c dom b,则a dom b)
这种计算支配者的方法也叫数据流计算方法。
Node entry; // entry 为cfg的entry
Set<Node> N; // N 为cfg的所有节点集合
Map<Node, Set<Node>> pred; // pred映射: 节点 -> 该节点的直接前驱
Map<Node, Set<Node>> dom; // DOM为映射: 节点 -> 该节点的支配者集合
// 基于第1点,a = b, 则a dom b
dom.put(entry, {entry});
for each n in (N - {entry}) {
dom.put(n, N)
}
boolean changed = true;
while(changed) { // 迭代
changed = false;
for each n in (N - {entry}) {
Set<Node> t = N;
// 求所有直接前驱的支配者交集
for each p in pred.get(n) {
Set<Node> pDom = dom.get(p);
t = t.intersect(pDom);
}
// n 的支配者 为 前驱的支配者交集 U 自己
Set<Node> nDom = t.union({n});
// 如果求出来的nDom与之前的不一样, 更新dom映射; 并继续迭代
if not equals(nDom, dom.get(n)) {
changed = true;
dom.put(n, nDom);
}
}
}
5. Soot中支配者(支配树)的计算
配置Soot,使其生成SSA,这样它就会计算节点的支配者,以及CFG的支配树。
Options.v().set_whole_shimple(true);
通过调用栈可以知道Dominators的计算是通过SimpleDominatorsAnalysis类分析出来的。再细看此类的源码:
/**
* Calculate dominators for basic blocks.
* <p>
* Uses the algorithm contained in Dragon book, pg. 670-1.
*
* <pre>
* D(n0) := { n0 }
* for n in N - { n0 } do D(n) := N;
* while changes to any D(n) occur do
* for n in N - {n0} do
* D(n) := {n} U (intersect of D(p) over all predecessors p of n)
* </pre>
**/
class SimpleDominatorsAnalysis<N> extends ForwardFlowAnalysis<N, FlowSet<N>> { ... }
它是一个经典的前向数据流分析算法,通过注释可以看到,这个算法在龙书的670页有。
数据流的节点类型为N,节点的 数据流的数据类型为节点的集合FlowSet<N>
再看这个类的构造函数的字段。
// 数据流的空集
private final BoundedFlowSet<N> emptySet;
// 数据流的全集
private final BoundedFlowSet<N> fullSet;
SimpleDominatorsAnalysis(DirectedGraph<N> graph) {
super(graph);
// define empty set, with proper universe for complementation
List<N> nodes = new ArrayList<N>(graph.size());
for (N n : graph) {
nodes.add(n);
}
this.emptySet = new ArrayPackedSet<N>(new CollectionFlowUniverse<N>(nodes));
this.fullSet = (BoundedFlowSet<N>) emptySet.clone();
this.fullSet.complement();
doAnalysis();
}
entry节点数据流初始化,对应:D(n0) := { n0 }
/**
* OUT(Start) contains all head nodes at initialization time.
**/
@Override
protected FlowSet<N> entryInitialFlow() {
FlowSet<N> initSet = emptySet.clone();
for (N h : graph.getHeads()) {
initSet.add(h);
}
return initSet;
}
非entry节点数据流初始化,对应:for n in N - { n0 } do D(n) := N;
/**
* All OUTs are initialized to the full set of definitions OUT(Start) is tweaked in customizeInitialFlowGraph.
**/
@Override
protected FlowSet<N> newInitialFlow() {
return fullSet.clone();
}
数据流分析body, 添加自身到目标数据流中。(自己的支配者为自己)
/**
* We compute out straightforwardly.
**/
@Override
protected void flowThrough(FlowSet<N> in, N block, FlowSet<N> out) {
// Perform generation
in.copy(out);
out.add(block);
}
所有路径的数据流输出应该作交集。
/**
* All paths == Intersection.
**/
@Override
protected void merge(FlowSet<N> in1, FlowSet<N> in2, FlowSet<N> out) {
in1.intersection(in2, out);
}
合并in(可能有新的数据流事实)到最终结果
@Override
protected void mergeInto(N block, FlowSet<N> inout, FlowSet<N> in) {
inout.intersection(in);
}
6. 求支配树
Soot在求解完节点的支配者后,开始构建支配树。
SimpleDominatorsAnalysis实现Dominator计算后,得到的结果通过SimpleDominatorsFinder包裹起来。最后通过DominatorTree这个类来实现DominatorTree的构造。
DominatorTree#buildTree方法为核心代码:
protected void buildTree() {
// hook up children with parents and vice-versa
for (N gode : graph) {
// 获取节点
DominatorNode<N> dode = fetchDode(gode);
// 获取父节点
DominatorNode<N> parent = fetchParent(gode);
// 连接树的父子
if (parent == null) {
heads.add(dode);
} else {
parent.addChild(dode);
dode.setParent(parent);
}
}
...
}
protected DominatorNode<N> fetchDode(N gode) {
DominatorNode<N> dode = godeToDode.get(gode);
if (dode == null) {
dode = new DominatorNode<N>(gode);
godeToDode.put(gode, dode);
}
return dode;
}
protected DominatorNode<N> fetchParent(N gode) {
// 通过已经有的支配关系获取立即支配节点
N immediateDominator = dominators.getImmediateDominator(gode);
return (immediateDominator == null) ? null : fetchDode(immediateDominator);
}
SimpleDominatorsFinder#getImmediateDominator
@Override
public N getImmediateDominator(N node) {
// root node
if (getGraph().getHeads().contains(node)) {
return null;
}
// avoid the creation of temp-lists
FlowSet<N> head = nodeToDominators.get(node).clone();
head.remove(node);
for (N dominator : head) {
// 核心
// 支配树节点越靠下,它的支配者越多。
// 只有node在DominatorTree上的parent才会满足这种情况
if (nodeToDominators.get(dominator).isSubSet(head)) {
return dominator;
}
}
return null;
}