Контекст
"Строгое псевдонижение", названное в честь оптимизации GCC, является предположением компилятора о том, что значение в памяти не будет доступно через значение l типа ( "объявленный тип" ), сильно отличающийся от типа, написанный с ( "эффективным типом" ). Это предположение допускает неправильные преобразования кода, если необходимо учитывать возможность того, что запись указателю на float
может изменить глобальную переменную типа int
.
Как GCC, так и Clang, извлекающие наибольшее значение из стандартное описание, полное темных углов, и имеющие предвзятость для выполнения сгенерированного кода на практике предположим, что указатель на int
первый член a struct thing
не имеет указателя на первый элемент int
элемента struct object
:
struct thing { int a; };
struct object { int a; };
int e(struct thing *p, struct object *q) {
p->a = 1;
q->a = 2;
return p->a;
}
Оба GCC и Clang выводят, что функция всегда возвращает 1, то есть p
и q
не могут быть алиасами для та же ячейка памяти:
e:
movl $1, (%rdi)
movl $1, %eax
movl $2, (%rsi)
ret
До тех пор, пока вы согласитесь с аргументацией для этой оптимизации, неудивительно, что p->t[3]
и q->t[2]
также считаются непересекающимися lvalues в следующем фрагменте (или, скорее, вызывающий вызывает UB, если они псевдоним):
struct arr { int t[10]; };
int h(struct arr *p, struct arr *q) {
p->t[3] = 1;
q->t[2] = 2;
return p->t[3];
}
GCC оптимизирует указанную выше функцию h
:
h:
movl $1, 12(%rdi)
movl $1, %eax
movl $2, 8(%rsi)
ret
До сих пор так хорошо, если вы видите p->a
или p->t[3]
как-то доступ к целому struct thing
(соответственно struct arr
), можно утверждать, что создание псевдонима местоположения нарушит правила изложенных в 6.5: 6-7. Аргументом, что это подход GCC, является это сообщение, часть длинной нити, которая также обсуждала роль профсоюзов в строгих правилах псевдонимов.
Вопрос
У меня есть сомнения, однако, в следующем примере, в котором нет struct
:
int g(int (*p)[10], int (*q)[10]) {
(*p)[3] = 1;
(*q)[4] = 2;
return (*p)[3];
}
GCC версии 4.4.7 через текущий снимок версии 7 на Matt Godbolt полезный веб-сайт оптимизирует функцию g
, как если бы (*p)[3]
и (*q)[4]
не могли быть псевдонимом (точнее, как если бы программа вызывала UB, если они это сделали ):
g:
movl $1, 12(%rdi)
movl $1, %eax
movl $2, 16(%rsi)
ret
Есть ли какое-либо чтение стандарта, которое оправдывает этот очень строгий подход к строгому псевдониму? Если оптимизация GCC здесь может быть оправдана, применимы ли аргументы также к оптимизации функций f
и k
, которые не оптимизированы GCC?
int f(int (*p)[10], int (*q)[9]) {
(*p)[3] = 1;
(*q)[3] = 2;
return (*p)[3];
}
int k(int (*p)[10], int (*q)[9]) {
(*p)[3] = 1;
(*q)[2] = 2;
return (*p)[3];
}
Я готов обсудить это с разработчиками GCC, но я должен сначала решить, не сообщая об ошибке правильности для функции g
или пропущенной оптимизации для f
и k
.