【问题标题】:What compilers can inline indirect function calls?哪些编译器可以内联间接函数调用?
【发布时间】:2010-08-21 22:24:06
【问题描述】:
LLVM 的 LDC D 编译器可以在某些情况下内联间接函数调用,前提是它可以证明目标是静态已知的。这是可能发生这种情况的玩具示例(在 D 中):
void main() {
uint num;
void incNum() {
num++;
}
auto myDelegate = &incNum;
myDelegate();
}
在这种情况下,尽管myDelegate() 调用名义上是间接调用,但目标对于人类读者和 LLVM/LDC 的流分析来说是显而易见的,因此它被内联。
在现代编译器中,将间接函数调用内联到静态可知目标的能力有多广泛? LLVM 是唯一可以做到这一点的提前编译器吗? JIT 编译器更常见吗?
【问题讨论】:
标签:
performance
language-agnostic
delegates
compiler-construction
compiler-optimization
【解决方案1】:
如果大多数 C++ 编译器都进行这种优化,我不会感到惊讶,至少是其中的一些变体。实际上,这是非常特定于语言和编译器的。
我不能代表 D 语言,但是对于 C/C++,由于指针运算,上面的这种优化可能很难进行。例如,如果它是这样的,你能优化代码吗?
++myDelegate;
myDelegate();
这在很大程度上取决于myDelegate 的类型。以上可以是有效的 C/C++,但内联 myDelegate() 可能不是编译器可以保证的。
其他语言(例如 Java、C# 等)没有指针运算,因此可以做出更多假设。例如,Sun JVM 可以将间接的多态调用转换为直接调用,恕我直言,这非常酷。示例:
public class A2 {
private final B2 b;
public A2(B2 b) {
this.b = b;
}
public void run() {
b.f();
}
}
public interface B2 {
public void f();
}
public class C2 implements B2 {
public void f() {
}
}
A2 a2 = new A2(new C2()); 实际上可以优化,Sun JVM 可以接受。
我从Java Specialists newletter 157 得到了这个例子,我推荐阅读它来了解 WRT Java 的这种东西。
【解决方案2】:
我认为大多数优化编译都可以做类似的事情(考虑到他们无论如何都会做各种流分析)。
Jit 编译器有时甚至可以更进一步,内联间接调用,它们无法证明目标是静态已知的(或者根本不知道)。对于这种情况,如果目标是函数调用之前的预期目标,他们会插入一个测试,并且仅在不是时才使用间接调用(并根据每条路径的使用频率重新调整)。 IIRC .Net Jit 会这样做,我认为 java 也会这样做。