Какие из этих сравнений указателей должен оптимизировать соответствующий компилятор для «всегда ложного»? - PullRequest
10 голосов
/ 12 мая 2019

Чтобы лучше понять, как проявляются псевдонимы-указатели при оптимизации, я подключил некоторый код к известному Проводнику компилятора , который я здесь повторю:

#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?Мое (очень ограниченное) понимание говорит, что b, c и d должны быть оптимизированы таким образом, но я не уверен (я также признаю, что __restrict не в стандарте, но притворяетсячто это было с семантикой, которую он определил для любого компилятора).

Обновление

Я включил разыменование обоих указателей в верхней части каждой функции (чтобы они не могли быть nullptr) и заставил вызов std::memcpy фактически скопировать один экземпляр int.

Обновление 2

Добавлен комментарий, объясняющий мои намерения с std::memcpy.

1 Ответ

2 голосов
/ 12 мая 2019

Для 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 влияет только на способ доступа к объекту, а не на значение указателей, см. §6.7.3 в N1570

Объект, доступ к которому осуществляется через ограниченный указатель, имеет специальную связь с этим указателем. Эта ассоциация, определенная в 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
  }
...