Re[9]: каждый раз, когда вы пишете i++ + ++i...
От: TarasB  
Дата: 19.06.14 13:16
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Причём на 32-битной машине он просто немножко потупит, когда в своп залезет,


А может и не немножко. Я вот забыл перевести миллиметры в метре, нажал на запуск, программа запросила в 1000 раз больше памяти, чем надо, система встала раком, ctrl+shift+esc не работает, потому что комбинация вызова диспетчера задач имеет тот же приоритет, что и вызов просмотрщика фотографий котиков, охеренно мудрое решение от микрософт, жму ресет, посылаю лучи ненависти микрософту.
Re[6]: каждый раз, когда вы пишете i++ + ++i...
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.14 13:17
Оценка:
Здравствуйте, Tilir, Вы писали:

N>>Не должно быть никакого "что угодно". Должно быть примерно следующее (и это должно быть в стандарте): в случае, если результат не помещается в целевой тип целого со знаком, компилятор и среда исполнения могут, по выбору:

N>>* выполнить операцию в соответствии с правилами платформы
N>>* выполнить операцию в соответствии с общепринятой арифметикой в дополнительном коде
N>>* выполнить операцию, отдав в качестве результата зависящее от реализации значение
N>>* по зависящим от реализации правилам выполнить действие, предусмотренное для особой ситуации типа "целочисленное переполнение"

T>Без возможности сделать на этапе компиляции arithmetical reassociations (а ваши условия их убивают) вы платите производительностью за то, чего не заказывали. Это противоречит философии C++.


http://www.rsdn.ru/forum/cpp/5654364.1
Автор: netch80
Дата: 19.06.14

Вы почему-то предполагаете, что типы значений внутренних промежуточных действий должны быть точно такие же, как в случае явно определённых автором кода операций. Это ограничение насильственно и неадекватно.

T> Убирать такие вещи под опции -- верное решение. Если вы сомневаетесь и производительность не критична -- подавайте -fwrapv и будет счастье.


Если Вы посмотрите на исходное, увидите, что там не было никакой видимой причины выставлять wrapv поведение, никакой арифметики по модулю 2**32 не предполагалось. Это как раз совершенно неожиданный удар от компилятора туда, где не предполагалось защиты.
The God is real, unless declared integer.
Re[5]: каждый раз, когда вы пишете i++ + ++i...
От: TarasB  
Дата: 19.06.14 13:18
Оценка:
Здравствуйте, TarasB, Вы писали:

TB>Здравствуйте, watchmaker, Вы писали:


W>>Так это же суть UB. Или ты не видел тот пример с форматированием диска и clang?


TB>Не могу вообще понять логику компилятора.

Ааа, дошло, указатель на функцию изначально нулевой, а вызов нулевого указателя — это УБ, поэтому компилятор вправе поставить туда что угодно? Весело получается. Компилятор вправе запустить ядерную ракету, если увидел в коде УБ, а что — всё по стандарту.
Re[4]: каждый раз, когда вы пишете i++ + ++i...
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 19.06.14 13:18
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Здравствуйте, netch80, Вы писали:


N>>Непонятно, почему это UB вообще ломает всё, а не только одно вычисленное выражение.

W>Так это же суть UB. Или ты не видел тот пример с форматированием диска и clang?

Это из-за того, что Do не инициализирована?
Какой добрый компилятор
Маньяк Робокряк колесит по городу
Re[5]: каждый раз, когда вы пишете i++ + ++i...
От: Don Reba Канада https://stackoverflow.com/users/49329/don-reba
Дата: 19.06.14 13:31
Оценка:
Здравствуйте, TarasB, Вы писали:

TB>Не могу вообще понять логику компилятора.


Компилятор видит, что есть только один способ придать этому указателю валидное значение. Поэтому, когда компилятор видит вызов, он предполагает, что программист не использует неопределённое поведение вызова нулевого указателя, а присвоил валидное значение где-то ранее; затем, следующим проходом, компилятор инлайнит функцию.
Ce n'est que pour vous dire ce que je vous dis.
Re[5]: каждый раз, когда вы пишете i++ + ++i...
От: watchmaker  
Дата: 19.06.14 13:39
Оценка:
Здравствуйте, Marty, Вы писали:

W>>Так это же суть UB. Или ты не видел тот пример с форматированием диска и clang? :)


M>Это из-за того, что Do не инициализирована?


Нет. Тем более, что в данном случае переменная Do гарантированно инициализирована перед запуском программы (значением NULL).
Re[7]: каждый раз, когда вы пишете i++ + ++i...
От: Tilir Россия http://tilir.livejournal.com
Дата: 19.06.14 13:41
Оценка:
Здравствуйте, netch80, Вы писали:

N>http://www.rsdn.ru/forum/cpp/5654364.1
Автор: netch80
Дата: 19.06.14

N>Вы почему-то предполагаете, что типы значений внутренних промежуточных действий должны быть точно такие же, как в случае явно определённых автором кода операций. Это ограничение насильственно и неадекватно.

Это совсем не банально. GCC для reassociations работает с SSA-представлением, там нет никакого различия между переменными которые программист завел лично и переменными, которые ему сделал компилятор как временные.

Мало того, я могу эквивалентно переписать этот код, добавив туда явное определение:

#include <iostream>

int main()
{
    int x = 27;
    for(int i=0; i < 10; ++i)
    {
        int t = i*1000000000;
        std::cout << i << " : " << t << " : " << x << std::endl;
        if(x==1) break;
        x = x%2 ? x*3+1 : x/2;
    }
}


Gimple для этого кода будет идентичен для приведенного в начале топика.
Если компилятор будет себя вести не идентично в первом и втором случае -- это откроет двери ада.

N>Если Вы посмотрите на исходное, увидите, что там не было никакой видимой причины выставлять wrapv поведение, никакой арифметики по модулю 2**32 не предполагалось


Умножение явно переполняющее int -- достаточно серьёзное основание.
Re[5]: каждый раз, когда вы пишете i++ + ++i...
От: wander  
Дата: 19.06.14 15:45
Оценка:
Здравствуйте, netch80, Вы писали:

N>Что ты назвал "оптимизацией"? Такая опция, наоборот, устраняет оптимизации. Если ты её имел в виду, то используй, пожалуйста, более адекватные термины.

N>Далее, с чего это тут дорога в ад?

Вообще-то он с тобой согласился
Т.е. ответ про "горе от ума" был не в противовес твоему, а в подтверждение. Фразой "дорога в ад" характеризовались разработчики компилятора, а не ты.

PS.
Все-таки, замечаю и тут, и в том числе и на себе: у программеров больное самолюбие, чуть что не так — сразу в штыки — надо с этим как-то бороться.
Re[6]: каждый раз, когда вы пишете i++ + ++i...
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 19.06.14 15:56
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>>>Так это же суть UB. Или ты не видел тот пример с форматированием диска и clang?


M>>Это из-за того, что Do не инициализирована?


W>Нет. Тем более, что в данном случае переменная Do гарантированно инициализирована перед запуском программы (значением NULL).


Ну, я подразумевал, что она явно не инициализирована каким-то значением. То, что она инициализируется нулем, я в курсе. То, что разыменование 0 это UB, я тоже.
Беда именно поэтому происходит, или я что-то не увидел еще?

Кстати, баловался с реализацией property:
    #define CONTAINING_RECORD(address, type, field) ((type *)( \
                                                      (PCHAR)(address) - \
                                                      (ULONG_PTR)(&((type *)0)->field)))


    #define DECLARE_PROPERTY_RW(OWNERNAME, TYPE, NAME)                   \
    struct propclass_##NAME {                                                \
        DWORD dummy;                                                         \
        inline operator TYPE() {                                             \
            return CONTAINING_RECORD(this, OWNERNAME, NAME)-> get_##NAME ();    \
        }                                                                    \
        inline void operator=(const TYPE &src) {                             \
            CONTAINING_RECORD(this, OWNERNAME, NAME)-> set_##NAME (src); \
        }                                                                    \
    } NAME;

struct SomeStruct{
                UINT get_Uint( )
                   {
                    return (UINT)1;
                   }
                
                void set_Uint( UINT Uint
                                 )
                   {
                    // ...
                   }
                
                DECLARE_PROPERTY_RW(SomeStruct, UINT, Uint );
};

SomeStruct s;
s.Uint = 3; // set_Uint() called


Это тоже UB и компилятор может придумать все, что ему угодно? Или будет работать?

Такая обманка не поможет?
    #define CONTAINING_RECORD(address, type, field) ((type *)( \
                                                      1+(PCHAR)(address) - \
                                                      (ULONG_PTR)(&((type *)1)->field)))
Маньяк Робокряк колесит по городу
Re[6]: каждый раз, когда вы пишете i++ + ++i...
От: Кодт Россия  
Дата: 19.06.14 15:59
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Нет. Тем более, что в данном случае переменная Do гарантированно инициализирована перед запуском программы (значением NULL).


Вот именно, что она инициализирована нулём. А разыменование нуля (вызов функции по этому адресу) — неопределённое поведение.
Перекуём баги на фичи!
Re: каждый раз, когда вы пишете i++ + ++i...
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.14 16:03
Оценка: 45 (2)
Здравствуйте, Кодт, Вы писали:

Тут с коллегой прокопали ещё пару фактов.

1. Кроме -fno-strict-overflow, проблема лечится -fno-aggressive-loop-optimizations. Последний появился в 4.8 и формально включен на всех уровнях (а реально — RTFS'ить надо).

2. А вот ещё более суровый вариант:

#include <stdio.h>       

int main()
{
    int i, j, x = 27;
    for (i=0, j=0; i < 10; ++i, ++j)
    {
        printf("%d : %d : %d\n", i, j*1000000000, x);
        if (x==1) break;
        x = x%2 ? x*3+1 : x/2;
    }
    return 0;
}


Запускаем (4.8 или 4.9, -O2):

$ ./kodt 
0 : 0 : 27
1 : 1000000000 : 82
2 : 2000000000 : 41


это всё, оно остановилось.
The God is real, unless declared integer.
Re[8]: каждый раз, когда вы пишете i++ + ++i...
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.14 16:11
Оценка:
Здравствуйте, Tilir, Вы писали:

N>>http://www.rsdn.ru/forum/cpp/5654364.1
Автор: netch80
Дата: 19.06.14

N>>Вы почему-то предполагаете, что типы значений внутренних промежуточных действий должны быть точно такие же, как в случае явно определённых автором кода операций. Это ограничение насильственно и неадекватно.
T>Это совсем не банально. GCC для reassociations работает с SSA-представлением, там нет никакого различия между переменными которые программист завел лично и переменными, которые ему сделал компилятор как временные.

Вот это и не адекватно.

T>Мало того, я могу эквивалентно переписать этот код, добавив туда явное определение:

[...]
T> int t = i*1000000000;
T>Gimple для этого кода будет идентичен для приведенного в начале топика.
T>Если компилятор будет себя вести не идентично в первом и втором случае -- это откроет двери ада.

В этом примере по сравнению с предыдущим я согласен, что они должны вести себя идентично — потому что в любом случае i*1000000000 написал автор кода.
А вот для сгенерированных компилятором промежуточных переменных (например, p[i] превращается в t1=p+i; *t1) это уже не так, нет никакой причины требовать от них принадлежности тем же доменам.

N>>Если Вы посмотрите на исходное, увидите, что там не было никакой видимой причины выставлять wrapv поведение, никакой арифметики по модулю 2**32 не предполагалось

T>Умножение явно переполняющее int -- достаточно серьёзное основание.

Тут — да. А если константа в миллиард пришла из заголовочного файла цепочкой в 10 включений и константные вычисления — нет, недостаточно.
The God is real, unless declared integer.
Re[6]: каждый раз, когда вы пишете i++ + ++i...
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.14 16:12
Оценка:
Здравствуйте, wander, Вы писали:

N>>Что ты назвал "оптимизацией"? Такая опция, наоборот, устраняет оптимизации. Если ты её имел в виду, то используй, пожалуйста, более адекватные термины.

N>>Далее, с чего это тут дорога в ад?
W>Вообще-то он с тобой согласился
W>Т.е. ответ про "горе от ума" был не в противовес твоему, а в подтверждение. Фразой "дорога в ад" характеризовались разработчики компилятора, а не ты.

Ну это было совсем не очевидно.

W>PS.

W>Все-таки, замечаю и тут, и в том числе и на себе: у программеров больное самолюбие, чуть что не так — сразу в штыки — надо с этим как-то бороться.

Вообще-то я не вышел за пределы просьбы уточнения в заведомо сомнительном контексте.
The God is real, unless declared integer.
Re[7]: каждый раз, когда вы пишете i++ + ++i...
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 19.06.14 16:16
Оценка:
Здравствуйте, Кодт, Вы писали:

W>>Нет. Тем более, что в данном случае переменная Do гарантированно инициализирована перед запуском программы (значением NULL).


К>Вот именно, что она инициализирована нулём. А разыменование нуля (вызов функции по этому адресу) — неопределённое поведение.


Ну это не оправдывает "хитрый" компилятор, который видя нулевой указатель, подсовывает ему первую попавшуюся похожую функцию. Лучше бы уж честно вызывал код по адресу 0. Так бы просто SEGFAULT получился. Что за умники там этот clang и gcc пишут?
Маньяк Робокряк колесит по городу
Re[6]: каждый раз, когда вы пишете i++ + ++i...
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.14 16:18
Оценка:
Здравствуйте, Кодт, Вы писали:

N>>Не должно быть никакого "что угодно". Должно быть примерно следующее (и это должно быть в стандарте): в случае, если результат не помещается в целевой тип целого со знаком, компилятор и среда исполнения могут, по выбору:

К>То есть, поменять undefined на unspecified, а если заплатить комитету стандартизации много пожертвований, то вообще на implementation-defined (чтобы авторы компиляторов были вынуждены внести эффекты ещ1 и в свою документацию).

Ну в общем-то да. Только не заплатить, а шантажировать.
И документировать лучше за счёт включения определений, проверяемых хотя бы через #if, какая локальная политика (например, что платформа с дополнительным кодом и переполнения в явно заданных юзером операциях приводят к усечению).

К>>>Горе от ума какое-то. Такая агрессивная преждевременная оптимизация, точно по заветам Дейкстры, вымостила дорогу в ад.

N>>Что ты назвал "оптимизацией"? Такая опция, наоборот, устраняет оптимизации. Если ты её имел в виду, то используй, пожалуйста, более адекватные термины.
N>>Далее, с чего это тут дорога в ад?
К>Оптимизация, конечно: вместо test+jnz сделал jmp

Понятно, спасибо. А в случае последнего посланного мной примера (где добавлена переменная j), очевидно, сработало в противоположную сторону.
The God is real, unless declared integer.
Re[7]: каждый раз, когда вы пишете i++ + ++i...
От: watchmaker  
Дата: 19.06.14 16:38
Оценка: :)
К>Вот именно, что она инициализирована нулём. А разыменование нуля (вызов функции по этому адресу) — неопределённое поведение.

Кодт, тебя КО укусил? Что там UB было написано изначально. Прямо теряюсь, когда мне объясняют моё же пример — как автор, я то уже знаю в чём там секрет
Re[7]: каждый раз, когда вы пишете i++ + ++i...
От: watchmaker  
Дата: 19.06.14 16:45
Оценка:
Здравствуйте, Marty, Вы писали:

M> То, что разыменование 0 это UB, я тоже.

M>Беда именно поэтому происходит, или я что-то не увидел еще?
Именно так.

M>Это тоже UB и компилятор может придумать все, что ему угодно? Или будет работать?


Используй стандартный offsetof. Ну и прочитай про границы его применимости. В принципе, на некоторых компиляторах offsetof реализован практически как и в твоём коде. Соответственно и работать, и ломаться будут они примерно в одинаковых ситуациях.
Re[2]: каждый раз, когда вы пишете i++ + ++i...
От: watchmaker  
Дата: 19.06.14 16:52
Оценка:
Здравствуйте, Xeor, Вы писали:

X>Обещают в 4.8.3 пофиксить: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58143


Не, это совершенно другой баг. Там проблема в том, что UB просачивается через unreachable code. Формально это означает, что компилятор находит UB там где стандарт говорит, что поведение определено. Конечно, это не связано с исходным примером, где UB есть уже по стандарту.
Re[6]: каждый раз, когда вы пишете i++ + ++i...
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.14 16:54
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, netch80, Вы писали:


N>>-O2 это не один уровень на всех, это описанный в документации набор опций, которыми можно управлять и отдельно. Данная конкретная опция называется "strict-overflow", вот описание из info:


J>о, ты уже написал про это Какой удар от классика


Да, но акценты у меня совсем другие. Компилятор не должен гнать бред только потому, что -O2.
The God is real, unless declared integer.
Re: каждый раз, когда вы пишете i++ + ++i...
От: Шахтер Интернет  
Дата: 19.06.14 17:11
Оценка:
Здравствуйте, Кодт, Вы писали:

К>... где-то форматируется маленький несчастный одинокий винчестер.


Мне представляется, что в современных C/C++ слишком много UB не по делу.
Это следовало бы пофиксить. Т.е. разумным образом доопределить семантику языка, что бы убрать ряд дурацких UB.

В данном конкретном случае всё-таки следует доопределить свойства знаковых целочисленных типов, так что бы они реализовывали обычную 2'c арифметику.
А всякую экзотику типа 1'c, 0'c и арифметику с насыщением сделать опциональной, если она поддерживается железом, с помощью соответствующих встроенных типов
типа int_1c, int_0c, int_sat и.т.п.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.