Об одном известном макроассемблере
lexa - 20/Май/2013 17:20
Вот есть такая dcraw.c, а в ней есть такие вот две строчки кода:
И все было ничего в gcc 2.x...4.7, а вот 4.8 компилирует это место неправильно.
Помогло. Но осадок - остался.
unsigned int *data,pad[128],p;
...
while (len--)
*data++ ^= pad[p++ & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127];
...
while (len--)
*data++ ^= pad[p++ & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127];
Я даже посмотрел генерируемые ассемблеры, увидел что разные, но за 30 секунд не разобрался (потому что константы 1 и 65 у 4.8 становятся 2 и 66 и надо внимательно смотреть в каком порядке там что инкрементится).
Переписал так:
while (len--)
{
*data++ ^= pad[p & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127];
p++;
}
{
*data++ ^= pad[p & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127];
p++;
}
Вопросов у меня, собственно, два три:
Куды жаловаться на gccДействительно UB? Но ведь есть такое естественное знание, что сначала вычисление правой части, а потом уже присваивание к левой. Это ж поимеет в куче мест.- Правильно ли я понимаю, что пересечение множеств "Linux с пакетами собранными 4.8" и "Фотограф в RAW" крайне мало отличается от пустого множества?
- А не отличается ли это место в C и в C++?
P.P.S. Если отвалится какая-нибудь криптуха или там MPEG-декодер - я совершенно не удивлюсь.
Comments
А не классический ли это UB,
А не классический ли это UB, связанный с очередностью выполнения p++ и подсчетов p+1, p+65?
Этот UB нормально работал
Этот UB нормально работал *везде*
Я не настолько хорошо знаю C/C++ (плюс, конечно, в новой ревизии C++ этот B могли D иначе), но вообще у меня такой код сомнений не вызывает.
Вот так же нормально:
array[p++] = array[p+1] + array[p+2];
?
Нет, это undefined. "p++"
Нет, это undefined. "p++" будет выполнено до ";", на этом гарантии заканчиваются. Короче говоря, говнокод.
Как хрупок окружающий нас
Как хрупок окружающий нас мир.
В переписанном цикле - следует читать "pad[p & 127]", без "+
В переписанном цикле - следует читать "pad[p & 127]", без "++"? Вообще я смотрю на этот код, и мне усиленно кажется, что в исходном цикле жеж undefined behaviour, нет?
Да, cut-n-paste меня погубит. Два плюса лишних, исправил. Н
Да, cut-n-paste меня погубит.
Два плюса лишних, исправил.
Насчет UB - я не уверен.
Ну вот я тоже (всмысле - доказать со стандартом в руках не в
Ну вот я тоже (всмысле - доказать со стандартом в руках не возьмусь), но вроде как бы да:
http://en.wikipedia.org/wiki/Sequence_point
По стандарту это неопределённое поведение, вывод компилятора
По стандарту это неопределённое поведение, вывод компилятора может зависеть от фазы луны. Нельзя применять p и p++ в одном выражении, без точки следования между ними.
Право и лево выражения - не точка следования? Сначала вычисл
Право и лево выражения - не точка следования?
Сначала вычисляем право, потом занимаемся лево?
Собственно, пардон, классика же:
*dest++ = ...
Означает банально что сначала *dest, а потом (dest++)
Нет, ну это-то пример - банальное использование результата в
Нет, ну это-то пример - банальное использование результата выражения (dest++). Тут никаких гвоздей, просто этот код надо читать не так, как ты его читаешь. :)
э-э, а имеем ли мы при этом право справа от присваивания без
э-э, а имеем ли мы при этом право справа от присваивания без side effects (то есть со знанием, что будет использовано значение до инкремента) тоже пользовать *dest? мне кажется, именно это undefined
Оператор присваивания не является точкой следования, так что
Оператор присваивания не является точкой следования, так что _адресация_ для присваивания может быть вычислена как до, так и после правой части.
Ага, я почитал уже википедию. Но ведь это место тогда много
Ага, я почитал уже википедию.
Но ведь это место тогда многократно поимеет, закладываться на то, что лево раньше правы - это ж естественное программистское поведение.
Не имеем. Стандарт (ANSI C,
Не имеем. Стандарт (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? но вообще, obfusca
Hmm, а не implementation ли это defined?
но вообще, obfuscated C contest какой-то а не код ;)
Ага, оно там все такое. Но при этом до gcc 4.8 - везде работ
Ага, оно там все такое. Но при этом до gcc 4.8 - везде работало, я сомневаюсь что это UB.
memcpy() тоже везде работал до поры до времени
memcpy() тоже везде работал до поры до времени
Ага, история похожая. Но другая. memcpy - фигачил по closed
Ага, история похожая. Но другая.
memcpy - фигачил по closed source, а компилятор - по опенсорцу.
http://c-faq.com/expr/seqpoints.html
http://c-faq.com/expr/seqpoints.html
Быстро прочитал заголовок "Об одном известном мракобесе" :))
Быстро прочитал заголовок "Об одном известном мракобесе" :)))
Тебе привести пример из перловки, где добавление '+ 0' меняе
Тебе привести пример из перловки, где добавление '+ 0' меняет результат вычисления?
PS: ++ (и --) - это зло, которое пришло из ассемблера, но там таких сложных выражений не предусмотрено.