为什么gcc无法取消虚拟化此函数调用? [英] Why can't gcc devirtualize this function call?

查看:96
本文介绍了为什么gcc无法取消虚拟化此函数调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

#include <cstdio>
#include <cstdlib>
struct Interface {
    virtual void f() = 0;
};

struct Impl1: Interface {
    void f() override {
        std::puts("foo");
    }
};

// or __attribute__ ((visibility ("hidden")))/anonymous namespace
static Interface* const ptr = new Impl1 ;

int main() {
    ptr->f();
}

使用g ++-7编译时 -O3 -flto -fdevirtualize-at-ltrans -fipa-pta -fuse-linker-plugin ,上面的 ptr-> f()调用不能取消虚拟化。

When compiled with g++-7 -O3 -flto -fdevirtualize-at-ltrans -fipa-pta -fuse-linker-plugin, the above ptr->f() call cannot be devirtualized.

似乎没有外部库可以修改 ptr 。这是否是GCC优化程序的不足,还是因为在这种情况下无法使用其他虚拟化?

It seems that no external library can modify ptr. Is this a deficiency of GCC optimizer, or because some other sources make devirtualization unavailable in this case?

Godbolt链接

更新:
似乎带有 -flto -O3 -fwhole-program-vtables -fvisibility = hidden 的clang-7是唯一的编译器+标志(如在2018/03年)可以对该程序进行虚拟化

UPDATE: It seems that clang-7 with -flto -O3 -fwhole-program-vtables -fvisibility=hidden is the only compiler+flags (as in 2018/03) that can devirtualize this program.

推荐答案

如果将ptr移到主函数中,结果非常有说服力,并且为为什么gcc不想对指针进行虚拟化提供了有力的暗示。

If you move the ptr into the main function, the result is very telling and offers a strong hint as to why gcc doesn't want to de-virtualize the pointer.

对此的反汇编显示,如果具有静态已初始化标志为false,它将初始化静态,然后立即跳回到虚拟函数调用,即使两者之间什么也没发生。

The disassembly for this shows that if the 'has the static been initialized flag' is false, it initializes the static and then jumps right back to the virtual function call, even though nothing could possibly have happened to it in between.

这告诉我gcc很难相信任何类型的全局持久性指针都必须始终被视为指向未知类型的指针。

This tells me that gcc is hard-wired to believe that any kind of globally persistent pointer must always be treated as a pointer to an unknown type.

实际上,甚至比这更糟。如果添加局部变量,则在创建局部变量和调用 f 的调用很重要> f 与否。显示 f 插入案例的程序集在这里:另一个Godbolt链接;并且很容易在站点上自行重新排列,以查看在没有插入其他调用的情况下该程序集如何转换为 f 的内联。

In fact, it's even worse than this. If you add in a local variable, it matters whether the call to the f on the static pointer occurs between the creation of the local variable and the call to f or not. The assembly showing the f interposed case is here: Another godbolt link; and it is simple to re-arrange it yourself on the site to see how the assembly turns into an inline of f once the other call isn't interposed.

因此,gcc必须假定,只要控制流出于任何原因离开该函数,指针所指的实际类型就可能发生变化。以及是否将其声明为 const 都无关紧要。地址是否曾被占用或任何其他方式也没有关系。

So, gcc must assume that the actual type a pointer refers to may change whenever control flow leaves the function for any reason. And whether or not it's declared const is irrelevant. Nor is it relevant if it's address is ever taken, or any number of other things.

clang也是一样。对我来说,这似乎过于谨慎,但我不是编译器作家。

clang does the same thing. This seems overly cautious to me, but I'm not a compiler writer.

这篇关于为什么gcc无法取消虚拟化此函数调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆