C++ GCC和Clang都不会通过编译时已知的函数指针数组内联调用;为什么?

C++ GCC和Clang都不会通过编译时已知的函数指针数组内联调用;为什么?,c++,gcc,C++,Gcc,编译器资源管理器上的示例代码: 我试图使用函数指针数组作为跳转表,而不是开关,因为我发现它更干净。然而,令我惊讶的是,无论是GCC还是clangcompiler似乎都不能内联这个 有没有具体的原因 死链接情况下的示例代码: namespace{ template<int N> int bar(){ return N; } int foo1(int n){ if(n < 0 || n > 5){ __builtin_unreachable

编译器资源管理器上的示例代码:

我试图使用函数指针数组作为跳转表,而不是开关,因为我发现它更干净。然而,令我惊讶的是,无论是GCC还是clangcompiler似乎都不能内联这个

有没有具体的原因

死链接情况下的示例代码:

namespace{
template<int N>
int bar(){
    return N;
}

int foo1(int n){
     if(n < 0 || n > 5){
        __builtin_unreachable();
    }
    #if __clang__
    __builtin_assume(n >= 0 && n <= 5);
    #endif
    static int (* const fns[])() = {
        bar<0>, bar<1>, bar<2>, bar<3>, bar<4>, bar<5>
    };
    return fns[n]();
}

int foo2(int n){
    #if __clang__
    __builtin_assume(n >= 0 && n <= 5);
    #endif
    switch(n){
        case 0:
            return bar<0>();
        case 1:
            return bar<1>();
        case 2:
            return bar<2>();
        case 3:
            return bar<3>();
        case 4:
            return bar<4>();
        case 5:
            return bar<5>();
        default:
            __builtin_unreachable();
    }
}
}

int main(int argc, char** argv){
    volatile int n = foo1(argc);
    volatile int p = foo2(argc);
}
名称空间{
模板
整型条(){
返回N;
}
int foo1(int n){
如果(n<0 | | n>5){
__内置的不可访问();
}
#如果叮当作响__

__内置\u假设(n>=0&&n=0&&n编译器无法在foo1中内联调用,因为调用未使用编译时常量被调用方。如果编译器知道在编译时通过内联将常量参数传递给foo1,它将内联正确的函数

考虑这个例子:

namespace{
template<int N>
int bar(){
    return N;
}

int foo1(int n){
     if(n < 0 || n > 5){
        __builtin_unreachable();
    }
    #if __clang__
    __builtin_assume(n >= 0 && n <= 5);
    #endif
    static int (* const fns[])() = {
        bar<0>, bar<1>, bar<2>, bar<3>, bar<4>, bar<5>
    };
    return fns[n]();
}
}

int main(int argc, char** argv){
    int n = foo1(3);

    return n;
}
在foo2的例子中,编译器从5个不同的调用开始,使用常量被调用方,所有这些调用都是内联的。然后它进一步优化生成的代码,如果它认为有利,则生成自己的跳转表


我猜编译器可以尝试从跳转表中提取一个开关,然后内联所有内容,但这将非常复杂,在一般情况下不太可能产生性能改进,因此gcc和clang似乎都不能做到这一点。

查找表的平均效率高于处理器t上的5个测试和分支操作hat可能支持流水线?这可能是我在这方面的知识不足,但您希望函数内联到哪里?您专门设置了函数指针,指向要在运行时解析的函数。在开关情况下,开关解析所需的操作,函数内联到案例代码块中。@dani否,你会用常数时间查找来换取对数。你会用下降。查找表只需要一次内存提取。它们非常便宜,特别是当目标代码已经在缓存中时。@dani上述两种方法都相当于使用向量。如果你查看优化的汇编程序输出,你会看到这一点。如果你删除foo1代码,它确实变成了一个查找表。如果我们通过添加足够的constexpr来计算n的值,它就会得到优化。因此,我怀疑只是缺少一些优化过程或一些意外的排序。
main:
        mov     eax, 3
        ret