【发布时间】:2015-01-18 16:49:48
【问题描述】:
如何识别 AST 的结构上常见的子树,以便将它们分解为单独的函数?
例如给定这个伪代码(假设该语言只允许纯的终止函数):
f(a, b, c) {
return (a + b) * c * 6;
}
g(x[4], k) {
var y[4];
for (i in 0..3)
y[i] = f(x[i], 1, k);
return y;
}
varying arr[4];
result = g(arr, 1);
...在完全特化和内联之后,我们将得到以下表示程序结果值的原始操作树:
(make-vec4
(* (arr 0) 6)
(* (+ (arr 1) 1) 6)
(* (+ (arr 2) 1) 6)
(* (+ (arr 3) 1) 6) )
(这是一个糟糕的例子,因为扩展结果在结构上仍然与输入非常相似...假设更改可以传播到输入代码的结构边界)
人眼很明显,结果树包含三个相似的表达式,我们现在可以将它们重构为对 fn(i) { return 6 * (arr[i] + 1); } 之类的函数的调用,因为 instruction cache size mumble mumble etc(或更现实地利用例如map 或fold 原语)。但是编译器如何将它们识别为相似,以便将它们视为提取的候选对象?
消除 相同的 子表达式应该很容易,使用类似 hash-consing 的方法。但是你不能用它来解决这个问题,因为从叶子向上移动构建的哈希不会以任何方式相互关联。有没有办法从根节点“构建”并确定两个表达式树之间的分歧点,看看分支在哪里成为参数? (没有使用任何关于原始程序形式的知识,假设它已经扩展得面目全非,而且无论如何可能没有被最佳分割)
感觉应该有办法通过排序子树和比较邻居来做到这一点,但这需要某种与元素位置无关的排序......?
【问题讨论】:
标签: compiler-construction inline compiler-optimization