如果可以证明目标是静态已知的,则LLVM的LDC D编译器可以在某些情况下内联间接函数调用。这是一个可能发生这种情况的玩具示例(在D中):
void main() {
uint num;
void incNum() {
num++;
}
auto myDelegate = &incNum;
myDelegate();
}
在这种情况下,即使myDelegate()
调用名义上是间接调用,目标对于人类读者和LLVM / LDC的流分析也是显而易见的,因此它被内联。
一个功能在现代编译器中内联间接函数调用静态可知目标的能力有多广泛? LLVM是唯一能够做到这一点的提前编译器吗? JIT编译器更常见吗?
答案 0 :(得分: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这类事情。
答案 1 :(得分:0)
我认为最好的优化编译可以做类似的事情(考虑到它们无论如何都会进行各种流量分析)。
Jit编译器有时甚至可以更进一步内联间接调用,因为它们无法证明目标是静态知道的(或者根本就不知道)。对于这种情况,如果目标是函数调用之前的预期目标,则插入测试;如果不是,则使用间接调用(并根据每个路径的使用频率重新调用)。 IIRC .Net Jit这样做,我认为java也是如此。