jsn: (common)
jsn ([personal profile] jsn) wrote2013-02-14 05:58 pm

Несколько слов о языке C

[несколько слов пропущено, потому что они все нецензурные]

jason@jsn c $ cat r.c
#include <stdio.h>

#define B (sizeof(int) * 8)
int _ = B ;
int f(void) { return _ ; }

unsigned g(unsigned x) {
    printf("x == %d, ", x) ;
    return 1 << (B - x) ;
}

int main(int ac, const char *av[]) {
    printf("g(x) == %d\n", g(0)) ;
    printf("g(x) == %d\n", g(B - f())) ;
    return 0 ;
}
jason@jsn c $ gcc -o r r.c && ./r
x == 0, g(x) == 1
x == 0, g(x) == 1
jason@jsn c $ gcc -O2 -o r r.c && ./r
x == 0, g(x) == 0
x == 0, g(x) == 1
jason@jsn c $ clang -o r r.c && ./r
x == 0, g(x) == 1
x == 0, g(x) == 1
jason@jsn c $ clang -O2 -o r r.c && ./r
x == 0, g(x) == 0
x == 0, g(x) == 1
jason@jsn c $ 

Это, что, считается нормально теперь? Там, что, в стандарте действительно сказано, что overflowing left shift result is undefined? И "undefined" следует интерпретировать как "ведёт себя по-разному в соседних строчках кода?"

[identity profile] dmarck.livejournal.com 2013-02-14 02:36 pm (UTC)(link)
"undefined" != "implementation-defined", ain't it?

Но, да, некрасиво, хоть и неудивительно. а на -Wall что компиляторы говорят?

[identity profile] jsn.livejournal.com 2013-02-14 02:41 pm (UTC)(link)
Ничего не говорят, конечно. (1 << 32) == 1, причём только при -O2 и выше и только иногда даже при этом -- это не implementation defined как-то :)

[identity profile] ilya-dogolazky.livejournal.com 2013-02-14 02:43 pm (UTC)(link)
Ответ на все три вопроса "ну типа да". Ответ "ноль" получен, видимо, прямо во время компиляции, и является "верным" (ну то есть не по правилам языка, а по нашему преставлению о том, сколько будет 1<<32). Ответ "1" честно вычислен микропроцессорной командой сдвига во время исполнения, на i386 процессорах (по крайней мере на некоторых, за все не скажу) длина сдвига берётся только из младших пяти битиков.

[identity profile] jsn.livejournal.com 2013-02-14 02:49 pm (UTC)(link)
Мощно, спасибо. [livejournal.com profile] sgt, впрочем, сообщает, что у него на clang-е:
x == 0, g(x) == 4195907
x == 0, g(x) == 1

... но да, если уж undefined и всё так ужасно, то чего мелочиться-то. Что ж, придётся усыпать код омерзительными if-ами.

[identity profile] alexott.livejournal.com 2013-02-14 03:51 pm (UTC)(link)
у меня на 64-bit линуксе и clang (с -O2, clang 3.0):
x == 0, g(x) == 4195973
x == 0, g(x) == 1

[identity profile] jsn.livejournal.com 2013-02-14 03:52 pm (UTC)(link)
У меня clang 3.2; 4195973 всё ж похоже на баг в clang-е.

Гы. промежуточное

[identity profile] dmarck.livejournal.com 2013-02-14 04:19 pm (UTC)(link)
marck@castor:~/tmp/jsn-shift> clang -O2 -o r r.c && ./r
x == 0, g(x) == 4196043
x == 0, g(x) == 1
marck@castor:~/tmp/jsn-shift> clang --version
FreeBSD clang version 3.1 (branches/release_31 156863) 20120523
Target: x86_64-unknown-freebsd9.0
Thread model: posix
marck@castor:~/tmp/jsn-shift> uname -a
FreeBSD castor.rinet.ru 9.1-STABLE FreeBSD 9.1-STABLE #0 r245742: Mon Jan 21 21:29:07 MSK 2013
     marck@castor.rinet.ru:/usr/obj/usr/src/sys/CASTOR  amd64
Edited 2013-02-14 16:27 (UTC)

[identity profile] trurle.livejournal.com 2013-02-14 03:03 pm (UTC)(link)
Это, что, считается нормально теперь?

Не только теперь, но с 1973 года. C language: enabling shooting yourself in the foot for 40 bloody years!

[identity profile] alexott.livejournal.com 2013-02-14 03:07 pm (UTC)(link)
"1972 - Dennis Ritchie invents a powerful gun that shoots both forward and backward simultaneously. Not satisfied with the number of deaths and permanent maimings from that invention he invents C and Unix."

[identity profile] ilya-dogolazky.livejournal.com 2013-02-14 03:24 pm (UTC)(link)
Я вот не понимаю этих шуточек, пример со сдвигом как раз показывает очень большое достоинство языка Си --- он выполняет сдвиг одной единственной инструкцией (где может). Сделать это (на i386 к примеру) с различением сдвига на 0 и сдвига на 32 разряда невозможно, вот он и не делает, причём честно об этом говорит в стандарте.

[identity profile] jsn.livejournal.com 2013-02-14 03:36 pm (UTC)(link)
Беспокоит собственно не это, а неконсистентность. g() [вполне себе const unsigned g(x)], которая не зависит ни от чего, кроме x, тем не менее возвращает разные значения при одном и том же x. Нехорошо это.

[identity profile] alexott.livejournal.com 2013-02-14 03:52 pm (UTC)(link)
а потом ты проводишь часы медитируя над кодом, поскольку не всегда можешь понять откуда ты получил такое число, и почему оно работает на соседней машине...

[identity profile] ilya-dogolazky.livejournal.com 2013-02-14 04:02 pm (UTC)(link)
Есть альтернатива: (а) медитировать часами (б) компилировать КАЖДЫЙ сдвиг в два сравнения с ветвлением плюс одну инструкцию собственно сдвига. Вам какой вариант кажется подходящим?

[identity profile] juan-gandhi.livejournal.com 2013-02-14 04:54 pm (UTC)(link)
Какое ж это достоинство, это дефект.

[identity profile] jsn.livejournal.com 2013-02-14 08:48 pm (UTC)(link)
Для ассемблера, например, это очевидное достоинство.

[identity profile] kika.livejournal.com 2013-02-14 10:53 pm (UTC)(link)
это дефект для тех, кому язык С не нужен. педальный привод велосипеда тоже дефект для автомобиля.

[identity profile] cherry-merry.livejournal.com 2013-02-14 09:28 pm (UTC)(link)
о боже, о чем вы? :-)
с праздничком тебя! Любви!

[identity profile] jsn.livejournal.com 2013-02-14 09:53 pm (UTC)(link)
спасибо; и тебя :)

[identity profile] ifp5.livejournal.com 2013-02-14 10:41 pm (UTC)(link)
И "undefined" следует интерпретировать как "ведёт себя по-разному в соседних строчках кода?"


Ну скорее как "может вести себя по разному в ...". Может стереть данные на диске, сжечь материнскую плату, устроить ядерную войну... А какие еще варианты то?

[identity profile] ifp5.livejournal.com 2013-02-14 10:47 pm (UTC)(link)
Очень, кстати, хочу компилятор и пр. чтобы в случае UB программа сразу же валилась.

[identity profile] helvegr.livejournal.com 2013-03-29 07:49 pm (UTC)(link)
В clang есть -fsanitize=undefined.
http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
Вот отчёт о его применении к libc++:
http://cplusplusmusings.wordpress.com/2013/03/26/testing-libc-with-fsanitizeundefined/

[identity profile] kika.livejournal.com 2013-02-14 10:52 pm (UTC)(link)
undefined на то и undefined, что его можно как угодно интерпретировать. представь себе что популярная реализация в месте undefined оставляет верхушку стека? вот и будет что попало

[identity profile] jsn.livejournal.com 2013-02-14 11:02 pm (UTC)(link)
право же, помандейтить, чтобы const funcs по крайней мере вели себя как const func (== результат не зависит ни от чего, кроме аргументов) -- это меньшее, что разумно было бы сделать.

[identity profile] kika.livejournal.com 2013-02-15 12:47 am (UTC)(link)
не. она ведет себя одинаково же. undefined == undefined, whatever value of undefined is. а ты выходишь за рамки этого понимания и требуешь чтобы у тебя было undefined0 и undefined1, а этого тебе никто не обещал.

[identity profile] jsn.livejournal.com 2013-02-14 11:07 pm (UTC)(link)
...впрочем, да, в Си const func-и -- одна вывеска, и это оверкилл. но помандейтить, чтобы left shift хотя бы вёл себя консистентно-то можно было б.

[identity profile] kika.livejournal.com 2013-02-15 12:49 am (UTC)(link)
только ценой дополнительных проверок и инструкций. С - это работа на открытом сердце, дополнительные проверки не входят.

ты, кстати, http://www.amazon.com/gp/product/B009RAOZ8M/ref=docs-os-doi_0 читал?

[identity profile] jsn.livejournal.com 2013-02-15 12:55 am (UTC)(link)
да не, каких проверок-инструкций-то, в данном случае? всего лишь достаточно, чтобы compiler precomputed lshift был реализован той же инструкцией, что и compiled lshift.

читал, кажется; не особо впечатлило, по-моему.

edit: ну то есть понятно, что это требует довольно продвинутой модели target-а при кросс-компиляции; но в собственно порождаемом коде это никакого оверхеда не приносит.
Edited 2013-02-15 01:02 (UTC)

[identity profile] kika.livejournal.com 2013-02-15 01:04 am (UTC)(link)
ну то есть тебя устроит если константный шифт будет вести себя адекватно, а не константный - хер знает как?

[identity profile] jsn.livejournal.com 2013-02-15 01:09 am (UTC)(link)
наоборот, как раз сейчас оно так и делает, и меня это сбивает с ритма. меня устроит, чтобы константный шифт вёл себя так, как если бы его скомпилили и запустили.

[identity profile] evolver.livejournal.com 2013-02-17 03:53 am (UTC)(link)
У меня есть несколько иная точка зрения на происходящее. Она основана на чтении результата трансляции с флажком "-S". Очень вероятно, что никакого константного шифта при опции "-O2" не cлучается. Компилятор инлайнит функцию g() в тело main(), причем делает это двумя разными способами. Для случая вызова g(0) он оптимизирует все до состояния, где в printf для g(0) попадает мусор после printf для x. Забавно наблюдать результат двух или более последовательных вызовов бинарника, скомпилированного с -O2. Кажый раз мы видим новый результат.

Фактическая инструкция "shll" выполняется только для второго инлайна функции g() в теле main(). Смотри строку 30 в этом фрагменте:
http://codepad.org/XBCaHfIo

Она как раз возвращает стабильный результат.

Вариант без "-O2" честно геренрирует и дважды вызывает функцию g(), которая честно содержит "shll". Наверно это объясняет наблюдаемое поведение.

Для меня загадкой остается то, зачем компилятор сохраняет в коде отдельную реализацию g(), которая никогда не вызывается, но это уже другая тема.

[identity profile] jsn.livejournal.com 2013-02-18 09:13 am (UTC)(link)
никакого константного шифта при опции "-O2" не cлучается ... Для случая вызова g(0) он оптимизирует все до состояния, где в printf для g(0) попадает мусор после printf для x

Это не называется "оптимизирует" -- это баг. Константный шифт на 32, будучи undefined, сочтён nop-ом и несэмичен.

Фактическая инструкция "shll" выполняется только для второго инлайна функции g() в теле main().

Конечно; так и должно быть в случае precomputed vs emitted.

[identity profile] jsn.livejournal.com 2013-02-18 09:51 am (UTC)(link)
Для меня загадкой остается то, зачем компилятор сохраняет в коде отдельную реализацию g()

Очевидно, потому что она не static, и поэтому может вызываться из других файлов, с которыми будет слинкован этот.

[identity profile] evolver.livejournal.com 2013-02-18 02:55 pm (UTC)(link)
Да, точно. Я как-то про это не подумал. Выходит, загадок больше не осталось :)