c++ 为什么编译器在内联时不对final类的调用进行反虚拟化呢?

cidc1ykv  于 2023-06-25  发布在  其他
关注(0)|答案(1)|浏览(128)
struct base {
    virtual void vcall() = 0;
};

struct foo final : base {
    void vcall() final;
};

void call_base(base& b) {
    b.vcall();
}

void call_foo(foo& f) {
    call_base(f);
}

void call_foo_directly(foo& f) {
    f.vcall();
}

clang 16产生:

call_base(base&):
        mov     rax, qword ptr [rdi]
        jmp     qword ptr [rax]
call_foo(foo&):
        mov     rax, qword ptr [rdi]
        jmp     qword ptr [rax]
call_foo_directly(foo&):
        jmp     foo::vcall()@PLT

GCC和MSVC产生相同的结果,所以这不是仅限于clang的问题。难道call_foo不应该也包含对foo::vcall()的非虚拟调用吗?这是一个错过的优化,还是呼叫可能是虚拟的?
参见live example on Compiler Explorer

mnemlml8

mnemlml81#

编译器确实尝试了,但是需要内联一些东西,如果一个函数没有实现,它只是一个空调用,这就是编译的内容;添加final只是防止以后使用override。为了编译它,优化volatile是有点需要的,所以一切都不会被优化掉。
在bodbolt上查一下。

struct base {
    volatile int num = 111;
    virtual void vcall() = 0;
};

struct foo final : base {
    void vcall() {
        num += 222;
    };
};

void call_base(base& b) {
    b.vcall();
}
void call_foo(foo& f) {
    call_base(f);
}

void call_foo_directly(foo& f) {
    f.vcall();
}

void main_func(void) {
    foo val;
    call_foo(val);
    call_foo_directly(val);
}

这是带有-O3部分拆卸的叮当-15(与-O2相同); vs无法内联call_foo

main_func():                          # @main_func()
        mov     dword ptr [rsp - 8], 111
        add     dword ptr [rsp - 8], 222
        add     dword ptr [rsp - 8], 222
        ret

相关问题