符合要求的编译器应能够对这些指针比较中的哪一个进行优化以使其“始终为假”?

时间:2019-05-12 15:41:44

标签: c++ compiler-optimization strict-aliasing

为了更好地理解优化过程中指针别名不变量的表现方式,我plugged some code into the renowned Compiler Explorer,在此重复:

#include <cstring>

bool a(int *foo, int *bar) {
    (void) *foo, (void) *bar;
    return foo == bar;
}

bool b(int *foo, float *bar) {
    (void) *foo, (void) *bar;
    return foo == reinterpret_cast<int *>(bar);
}

bool c(int *foo, int *bar) {
    (void) *foo, (void) *bar;
    // It's undefined behavior for memcpyed memory ranges to overlap (i.e. alias)
    std::memcpy(foo, bar, sizeof(int));
    return foo == bar;
}

bool d(int *__restrict foo, int *__restrict bar) {
    (void) *foo, (void) *bar;
    return foo == bar;
}

当前的Clang版本和GCC都不会编译这些函数中的任何一个以始终返回false,所以我的问题是,这些函数中的哪一个在仍然符合C ++标准的情况下,可以 已编译为始终返回false?我(非常有限)的理解是,bcd应该都以这种方式被优化,但是我不确定(我也认识到__restrict不是标准,而是假装它具有它在任一编译器下定义的语义。

更新

我在每个函数的顶部都包含了对两个指针的取消引用(以便它们不能为nullptr),并使std::memcpy调用实际上复制了int的一个实例。

更新2

添加了一条注释,解释了我对std::memcpy的意图。

1 个答案:

答案 0 :(得分:1)

对于a,这是显而易见的。对于b,该代码实际上是正确的,编译器无法做出任何假设。考虑以下对b的调用:

int x[2]{};
b(x,reinterpret_cast<float*>(x+1));

如果您要访问两个参数的值,则编译器可能会做出假设:

bool b(int *foo, float *bar) {
    *foo=10;  //*foo is an int (or unsigned int) 
              //and if foo is a member of a union 
              //*foo is the active member
    *bar+0.f; //bar must be a float within its lifetime so it cannot be
              //in the same union as *foo
    return foo == reinterpret_cast<int *>(bar);//so always false
    }

对于c,我同意您的分析,一个非常聪明的编译器可以优化比较。

对于d,根据C标准restrict仅涉及对象的访问方式,而不涉及指针的值,请参见N1570中的§6.7.3

  

通过限制限定的指针访问的对象与该指针具有特殊的关联。这种关联在下面的6.7.3.1中定义,要求对该对象的所有访问都直接或间接使用该特定指针的值。

b而言,如果访问了指向对象,那么智能编译器可以做出以下假设:

bool d(int *__restrict foo, int *__restrict bar) {
  *foo=10;
  *bar=12;//So foo and bar point to different objects
  return foo == bar;//always false
  }