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 СНГ не позволяя тому же поведению быть полезным с указателями на структуры, которые на самом деле не были внутри объекта объединения; с указанием СНГ Таким образом, поддержка структур была бы излишней, поскольку она была уже указано для объединений.