Gcc, строгие сглаживания и ужасы
В gcc-strict-aliasing-and-casting-through-a-union Я спросил, кто-то сталкивался с проблемами с профсоюзом, набирая указатели. Пока что ответ кажется No.
Этот вопрос шире: есть ли у вас какие-то ужасные истории о gcc и строгом псевдониме?
Справочная информация: цитата из Ответ AndreyT в c99-strict-aliasing-rules-in-c-gcc:
"Строгие правила псевдонимов основаны на части стандарта, которые присутствовали на C и С++ с начала [стандартизованных] раз. Предложение, запрещающее доступ к объекту одного типа через lvalue другого типа, присутствует в C89/90 (6.3), а также в С++ 98 (3.10/15).... То, что не все компиляторы хотели (или посмели) обеспечить его выполнение или полагаться на него".
Ну, gcc теперь смеет это делать, с его переключателем -fstrict-aliasing
. И это вызвало некоторые проблемы. См., Например, отличную статью http://davmac.wordpress.com/2009/10/ об ошибке Mysql, а также отличное обсуждение в http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html.
Некоторые другие менее релевантные ссылки:
Итак, чтобы повторить, есть ли у вас ужасная история? Конечно, проблемы не, обозначенные -Wstrict-aliasing
, были бы предпочтительнее. И другие компиляторы C также приветствуются.
Добавлено 2 июня. Первая ссылка в ответе Майкла Барра, которая действительно квалифицируется как история ужасов, возможно, немного устарела (с 2003 года). Я сделал быстрый тест, но проблема, очевидно, исчезла.
Источник:
#include <string.h>
struct iw_event { /* dummy! */
int len;
};
char *iwe_stream_add_event(
char *stream, /* Stream of events */
char *ends, /* End of stream */
struct iw_event *iwe, /* Payload */
int event_len) /* Real size of payload */
{
/* Check if it possible */
if ((stream + event_len) < ends) {
iwe->len = event_len;
memcpy(stream, (char *) iwe, event_len);
stream += event_len;
}
return stream;
}
Конкретная жалоба:
Некоторые пользователи жаловались, что, когда код [выше] скомпилирован без -fno-strict-aliasing, порядок записи и memcpy инвертируется (что означает, что фиктивная линя копируется в поток).
Скомпилированный код, используя gcc 4.3.4 на CYGWIN wih-O3 (пожалуйста, поправьте меня, если я ошибаюсь - мой ассемблер немного ржавый!):
_iwe_stream_add_event:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $20, %esp
movl 8(%ebp), %eax # stream --> %eax
movl 20(%ebp), %edx # event_len --> %edx
leal (%eax,%edx), %ebx # sum --> %ebx
cmpl 12(%ebp), %ebx # compare sum with ends
jae L2
movl 16(%ebp), %ecx # iwe --> %ecx
movl %edx, (%ecx) # event_len --> iwe->len (!!)
movl %edx, 8(%esp) # event_len --> stack
movl %ecx, 4(%esp) # iwe --> stack
movl %eax, (%esp) # stream --> stack
call _memcpy
movl %ebx, %eax # sum --> retval
L2:
addl $20, %esp
popl %ebx
leave
ret
И для второй ссылки в Майкле ответьте,
*(unsigned short *)&a = 4;
gcc будет обычно (всегда?) давать предупреждение. Но я считаю, что правильное решение для этого (для gcc) заключается в использовании:
#define CAST(type, x) (((union {typeof(x) src; type dst;}*)&(x))->dst)
// ...
CAST(unsigned short, a) = 4;
Я спросил, правильно ли это в gcc-strict-aliasing-and-casting-through-a-union, но пока никто не согласен.
Ответы
Ответ 1
Нет моей ужасной истории, но вот некоторые цитаты из Линуса Торвальдса (извините, если они уже находятся в одной из связанных ссылок в вопросе):
http://lkml.org/lkml/2003/2/26/158:
Дата Ср, 26 фев 2003 09:22:15 -0800 Тема Re: Неверная компиляция без -fno-strict-aliasing От Жана Туррилеса < >
Вкл, 26 февраля 2003 года в 04:38:10 +0100, Хорст фон Бранд писал (а):
Jean Tourrilhes < > сказал:
Похоже на ошибку компилятора... Некоторые пользователи жаловались, что, когда следующий код скомпилированный без -fno-strict-aliasing, порядок записи и memcpy перевернута (что означает, что фиктивная len mem-скопирована в поток). Код (из linux/include/net/iw_handler.h):
static inline char *
iwe_stream_add_event(char * stream, /* Stream of events */
char * ends, /* End of stream */
struct iw_event *iwe, /* Payload */
int event_len) /* Real size of payload */
{
/* Check if it possible */
if((stream + event_len) < ends) {
iwe->len = event_len;
memcpy(stream, (char *) iwe, event_len);
stream += event_len;
}
return stream;
}
IMHO, компилятор должен иметь достаточный контекст, чтобы знать, что переупорядочение опасно. Любое предложение сделать этот простой код более bullet proof приветствуется.
Компилятор может свободно принимать поток char * и struct iw_event * iwe point для разделения областей памяти из-за строгого сглаживания.
Это правда, и это не проблема, о которой я жалуюсь.
И Линус Торвальд прокомментировал сказанное выше:
Жан Туррильес писал: >
Похоже на ошибку компилятора...
Почему вы думаете, что ядро использует "-fno-strict-aliasing"?
Люди gcc больше заинтересованы в том, чтобы выяснить, что может быть разрешенных спецификациями c99, чем о том, чтобы заставить вещи действительно работать. код сглаживания, в частности, даже не стоит включать, это просто не можно здраво сказать gcc, когда некоторые вещи могут быть псевдонимом.
Некоторые пользователи жаловались, что, когда следующий код скомпилированный без -fno-strict-aliasing, порядок записи и memcpy перевернута (что означает, что фиктивная len mem-скопирована в поток).
"Проблема" заключается в том, что мы встраиваем memcpy(), в какой точке gcc не будет заботиться о том, что он может быть псевдонимом, поэтому они просто переупорядочат все и утверждать это по собственной вине. Даже если нет здравого смысла Кстати, нам даже сказать gcc об этом.
Я пытался получить здравый смысл несколько лет назад, и разработчики gcc действительно не заботился о реальном мире в этой области. Я был бы удивлен, если бы это изменилось, судя по ответам, которые я уже видел.
Я не собираюсь бороться с этим.
Линус
http://www.mail-archive.com/[email protected]/msg01647.html:
Наложение на основе типов глупо. Это так невероятно глупо, что это даже не смешно. Он сломан. И gcc взял сломанное понятие и сделал его более тем, сделав его "по буквам закона", что не имеет смысла.
...
Я знаю, что gcc переписал бы записи, которые явно (статически) совпадали с адресом. Gcc вдруг подумает, что
unsigned long a;
a = 5;
*(unsigned short *)&a = 4;
может быть переупорядочено, чтобы сначала установить его на 4 (потому что явно они не являются псевдонимами - читая стандарт), а затем, поскольку теперь присвоение 'a = 5' было позже, назначение 4 могло бы быть полностью исключено! И если кто-то жалуется, что компилятор сумасшедший, люди-компиляторы скажут: "nyaah, nyaah, стандарты, люди сказали, что мы можем это сделать", абсолютно без самоанализа, чтобы спросить, сделал ли он какой-либо SENSE.
Ответ 2
SWIG генерирует код, который зависит от того, что строгий псевдоним отключен, что может вызвать всевозможные проблемы.
SWIGEXPORT jlong JNICALL Java_com_mylibJNI_make_1mystruct_1_1SWIG_12(
JNIEnv *jenv, jclass jcls, jint jarg1, jint jarg2) {
jlong jresult = 0 ;
int arg1 ;
int arg2 ;
my_struct_t *result = 0 ;
(void)jenv;
(void)jcls;
arg1 = (int)jarg1;
arg2 = (int)jarg2;
result = (my_struct_t *)make_my_struct(arg1,arg2);
*(my_struct_t **)&jresult = result; /* <<<< horror*/
return jresult;
}
Ответ 3
gcc, aliasing и 2-D массивы переменной длины: Следующий пример кода копирует матрицу 2x2:
#include <stdio.h>
static void copy(int n, int a[][n], int b[][n]) {
int i, j;
for (i = 0; i < 2; i++) // 'n' not used in this example
for (j = 0; j < 2; j++) // 'n' hard-coded to 2 for simplicity
b[i][j] = a[i][j];
}
int main(int argc, char *argv[]) {
int a[2][2] = {{1, 2},{3, 4}};
int b[2][2];
copy(2, a, b);
printf("%d %d %d %d\n", b[0][0], b[0][1], b[1][0], b[1][1]);
return 0;
}
С gcc 4.1.2 на CentOS я получаю:
$ gcc -O1 test.c && a.out
1 2 3 4
$ gcc -O2 test.c && a.out
10235717 -1075970308 -1075970456 11452404 (random)
Я не знаю, известно ли это вообще, и я не знаю, является ли это ошибкой или функцией. Я не могу дублировать проблему с gcc 4.3.4 на Cygwin, поэтому она может быть исправлена. Некоторые обходы:
- Используйте
__attribute__((noinline))
для copy().
- Используйте gcc-переключатель
-fno-strict-aliasing
.
- Измените третий параметр copy() с
b[][n]
на b[][2]
.
- Не используйте
-O2
или -O3
.
Дальнейшие примечания:
- Это ответ, через год и день, по моему собственному вопросу (и я немного удивлен, есть только два других ответа).
- Я потерял несколько часов с этим по моему фактическому коду, фильтру Калмана. По-видимому, небольшие изменения окажут резкие последствия, возможно, из-за изменения автоматической установки gcc (это предположение, я все еще не уверен). Но это, вероятно, не квалифицируется как ужасная история.
- Да, я знаю, что вы не напишете
copy()
, как это. (И, как в сторону, я был слегка удивлен, увидев, что gcc не разворачивает двойной цикл.)
- Нет предупреждающих переключателей gcc, включая
-Wstrict-aliasing=
, ничего здесь.
- 1-D массивы переменной длины кажутся ОК.
Обновление. Вышеописанное не отвечает на вопрос OP, так как он (т.е. я) спрашивал о случаях, когда строгое наложение "законно" нарушило ваш код, тогда как выше это, похоже, садоводческий компилятор.
Я сообщил об этом GCC Bugzilla, но они не интересовались старым 4.1.2, хотя (я полагаю) это ключ к $1-миллиарду RHEL5. Это не происходит в 4.2.4 вверх.
И у меня есть несколько более простой пример подобной ошибки, только с одной матрицей. Код:
static void zero(int n, int a[][n]) {
int i, j;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
a[i][j] = 0;
}
int main(void) {
int a[2][2] = {{1, 2},{3, 4}};
zero(2, a);
printf("%d\n", a[1][1]);
return 0;
}
дает результаты:
gcc -O1 test.c && a.out
0
gcc -O1 -fstrict-aliasing test.c && a.out
4
Кажется, это комбинация -fstrict-aliasing
с -finline
, которая вызывает ошибку.
Ответ 4
Следующий код возвращает 10, в соответствии с gcc 4.4.4. Что-то не так с методом union или gcc 4.4.4?
int main()
{
int v = 10;
union vv {
int v;
short q;
} *s = (union vv *)&v;
s->v = 1;
return v;
}
Ответ 5
вот мой:
http://forum.openscad.org/CGAL-3-6-1-causing-errors-but-CGAL-3-6-0-OK-tt2050.html
он вызвал неправильные рисунки определенных фигур в программе САПР. Благодарим за то, что руководители проекта работают над созданием набора регрессионных тестов.
ошибка обнаруживалась только на некоторых платформах, с более старыми версиями GCC и более ранними версиями некоторых библиотек. а затем только при включенном -O2. -fno-strict-aliasing решил.
Ответ 6
Правило общей исходной последовательности C, которое интерпретируется как его создание
можно написать функцию, которая могла бы работать над ведущей частью
большое разнообразие типов структуры, при условии, что они начинаются с элементов соответствия
типы. В разделе C99 правило было изменено так, что оно применяется только в том случае, если структура
типы были членами одного и того же союза, чья полная декларация была видна в пункте использования.
Авторы gcc настаивают на том, что рассматриваемый язык применим только тогда, когда
доступ осуществляется через тип объединения, несмотря на факты
что:
-
Не было бы причин указывать, что полное объявление должно быть видимым, если доступ должен выполняться через тип объединения.
-
Хотя правило СНГ описывалось с точки зрения профсоюзов, его основной
полезность заключается в том, что она подразумевала о том, как структурированы
выложены и доступны. Если S1 и S2 были структурами, разделяющими СНГ,
не было бы способа, чтобы функция, которая принимала указатель на S1
и S2 от внешнего источника может соответствовать правилам C89 СНГ
не позволяя тому же поведению быть полезным с указателями на
структуры, которые на самом деле не были внутри объекта объединения; с указанием СНГ
Таким образом, поддержка структур была бы излишней, поскольку она была
уже указано для объединений.