1

[编辑:这是动机:将变量的指针传递给外部函数可能会意外地破坏对“相邻”变量的一些优化,因为有可能获得指向由外部函数从原始指针计算的相邻变量的指针。以下为原帖,volatile是为了模拟当前编译单元无法访问的外部函数,例如虚函数调用、闭源库函数等]

我想知道return t.a;以下代码中的是否会优化为return 0;.

//revision 1
struct T
{
    int a;
    int b;
};

void f_(int * p)
{
    *p = 1;
}
auto volatile f = f_;

int main()
{
    T t;
    t.a = 0;
    t.b = 0;
    for (int i = 0; i < 20; ++i)
    {
        f(&t.b);
    }
    return t.a;
}

好吧,它不是。很公平,因为函数中的代码f可以offsetof用来获取指向t然后 change的指针t.a。所以优化off的负载是不安全的t.a

[编辑:再想一想,offsetof这里还不够。我们需要container_of,似乎没有办法在标准 C++ 中实现。]

offsetof不能用于非标准布局类型。所以我尝试了以下代码:

//revision 2
#include <type_traits>

struct T
{
private:
    char dummy = 0;
public:
    int a;
    int b;
};
static_assert(!std::is_standard_layout_v<T>);

void f_(int * p)
{
    *p = 1;
}
auto volatile f = f_;

int main()
{
    T t;
    t.a = 0;
    t.b = 0;
    for (int i = 0; i < 20; ++i)
    {
        f(&t.b);
    }
    return t.a;
}

不幸的是,它仍然无法正常工作。

我的问题是:

  • t.a在上述情况下优化 away 的负载是否安全(修订版 2)
  • 如果不是,是否存在一些安排/建议使其成为可能?(例如,制作T更特殊的类型,或为成员bin制作一些属性说明符T

PS 下面的代码针对 进行了优化return t.a;,但是生成的循环代码效率有点低。而且,临时变量杂耍很麻烦。

//revision 3
struct T
{
    int a;
    int b;
};

void f_(int * p)
{
    *p = 1;
}
auto volatile f = f_;

int main()
{
    T t;
    t.a = 0;
    t.b = 0;
    for (int i = 0; i < 20; ++i)
    {
        int b = t.b;
        f(&b);
        t.b = b;
    }
    return t.a;
}
4

1 回答 1

1

使用offsetoftoreach T::afromT::b是非法的,因为不存在可以与from相互转换的对象指针。在另一个方向上,可以从到达,因为后者可以与 指针互转换。Contra Peter 在评论中(尽管在例如 Linux 内核中存在宏),不会产生指向 的指针,因为并且不是指针可互转换的。T::bT::aT::bT::aTcontainer_of&t.b - 1t.aT::bT::a

请注意,给定一个指向T::a您的指针仍然需要使用std::launder来访问T::b

auto p = &t.a;
std::launder(reinterpret_cast<T*>(p))->b = 1;

f因此,一个足够激进的编译器确实能够得出结论,t.a给定一个指向t.b. 但是,目前似乎没有主流编译器执行此优化。

于 2020-10-31T15:26:16.423 回答