Об одном известном макроассемблере

Вот есть такая dcraw.c, а в ней есть такие вот две строчки кода:
  unsigned int *data,pad[128],p;
  ...
  while (len--)
      *data++ ^= pad[p++ & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127];
И все было ничего в gcc 2.x...4.7, а вот 4.8 компилирует это место неправильно.

Я даже посмотрел генерируемые ассемблеры, увидел что разные, но за 30 секунд не разобрался (потому что константы 1 и 65 у 4.8 становятся 2 и 66 и надо внимательно смотреть в каком порядке там что инкрементится).

Переписал так:

  while (len--)
    {
      *data++ ^= pad[p & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127];
      p++;    
    }
Помогло. Но осадок - остался.

Вопросов у меня, собственно, два три:

  1. Куды жаловаться на gcc Действительно UB? Но ведь есть такое естественное знание, что сначала вычисление правой части, а потом уже присваивание к левой. Это ж поимеет в куче мест.
  2. Правильно ли я понимаю, что пересечение множеств "Linux с пакетами собранными 4.8" и "Фотограф в RAW" крайне мало отличается от пустого множества?
  3. А не отличается ли это место в C и в C++?
P.S. Помимо баланса белого, который поминается в в багрепорте, отваливается еще и декодирование файлов с Sony DSC-V3, Sony F828 и, возможно, еще каких-то (у меня таблички декодер - камера нету)

P.P.S. Если отвалится какая-нибудь криптуха или там MPEG-декодер - я совершенно не удивлюсь.

Comments

А не классический ли это UB, связанный с очередностью выполнения p++ и подсчетов p+1, p+65?

Этот UB нормально работал *везде*

Я не настолько хорошо знаю C/C++ (плюс, конечно, в новой ревизии C++ этот B могли D иначе), но вообще у меня такой код сомнений не вызывает.
Вот так же нормально:

array[p++] = array[p+1] + array[p+2];

?

Нет, это undefined. "p++" будет выполнено до ";", на этом гарантии заканчиваются. Короче говоря, говнокод.

Как хрупок окружающий нас мир.

В переписанном цикле - следует читать "pad[p & 127]", без "++"? Вообще я смотрю на этот код, и мне усиленно кажется, что в исходном цикле жеж undefined behaviour, нет?

Да, cut-n-paste меня погубит.
Два плюса лишних, исправил.

Насчет UB - я не уверен.

Ну вот я тоже (всмысле - доказать со стандартом в руках не возьмусь), но вроде как бы да:

http://en.wikipedia.org/wiki/Sequence_point

По стандарту это неопределённое поведение, вывод компилятора может зависеть от фазы луны. Нельзя применять p и p++ в одном выражении, без точки следования между ними.

Право и лево выражения - не точка следования?
Сначала вычисляем право, потом занимаемся лево?

Собственно, пардон, классика же:
*dest++ = ...
Означает банально что сначала *dest, а потом (dest++)

Нет, ну это-то пример - банальное использование результата выражения (dest++). Тут никаких гвоздей, просто этот код надо читать не так, как ты его читаешь. :)

э-э, а имеем ли мы при этом право справа от присваивания без side effects (то есть со знанием, что будет использовано значение до инкремента) тоже пользовать *dest? мне кажется, именно это undefined

Оператор присваивания не является точкой следования, так что _адресация_ для присваивания может быть вычислена как до, так и после правой части.

Ага, я почитал уже википедию.

Но ведь это место тогда многократно поимеет, закладываться на то, что лево раньше правы - это ж естественное программистское поведение.

Не имеем. Стандарт (ANSI C, если что) пишет:
Between the previous and next sequence point an object shall have
its stored value modified at most once by the evaluation of an
expression. Furthermore, the prior value shall be accessed only to
determine the value to be stored. /26/

И комментарий:
26. This paragraph renders undefined statement expressions such as
i = ++i + 1; while allowing i = i + 1;

Так что абсолютно явное UB.

Hmm, а не implementation ли это defined?

но вообще, obfuscated C contest какой-то а не код ;)

Ага, оно там все такое. Но при этом до gcc 4.8 - везде работало, я сомневаюсь что это UB.

memcpy() тоже везде работал до поры до времени

Ага, история похожая. Но другая.
memcpy - фигачил по closed source, а компилятор - по опенсорцу.

Быстро прочитал заголовок "Об одном известном мракобесе" :)))

Тебе привести пример из перловки, где добавление '+ 0' меняет результат вычисления?

PS: ++ (и --) - это зло, которое пришло из ассемблера, но там таких сложных выражений не предусмотрено.

Add new comment