控制流的支配者关系分析以及Soot中的实现

由于仍然有些概念不是很清晰,这里仅当学习记录。

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值