Pull to refresh

Comments 11

Предложенный способ избавления от UB не избавляет от всех UB. Остаются UB растущие из многопоточности и модели памяти, для их устранения нужно либо полностью запретить многопоточность, либо ввести в язык понятие владения (как в Rust). До тех пор пока в языке есть многопоточность и разделяемая память — UB не избежать, потому что корни UB — в железе.


Но даже в однопоточной программе избавиться от UB не так-то просто: UB потенциально может дать любая операция обращения по указателю, если только в языке нет способа гарантировать что указатель всегда указывает на валидный объект, т.е. ручное управление памятью в таком языке будет под запретом.


Наконец, если все получится и мы сделаем язык без UB — то задача оптимизации программы на этом языке будет сводиться к проблеме останова...

Я с вами абсолютно согласен. Поэтому автор и пишет, что можно сделать без UB игрушечный язык или подмножество реального языка, но на практике наличие UB является неизбежным.
Автор пишет: «Но это проблема чисто практическая, теоретически проблемы не существует.»

Но это не так. Проблема синхронизации потоков рассматривается не только практиками, она и в теории существует.

Часть 1 — какой-то странный поток сознания.
Неужели у кого-то были претензии к наличию UB в LLVM? Да ладно. Казалось бы, это промежуточный язык, в котором может быть что угодно — лишь бы он позволял эффективно перевести код на ЯВУ (в котором UB есть) в машинный.


Опять же, заголовок откровенно желтушный. UB — это безусловно небезопасное программирование. Вся суть UB сводится к тому, что его быть не должно, и компилятор может на это полагаться. Так что если вы видите UB в безопасной (а значит, корректной) программе — значит, его обходит какой-нибудь if или ещё что случается, чтобы мы не попали в эту ситуацию. There is no spoon.

Здесь речь вот о чём. Допустим, что в программе на определённом ЯВУ нет UB, и каждая операция, например, сложения имеет проверку на переполнение. Но оптимизирующий компилятор может выбросить эти проверки, если он видит, что переполнение невозможно, и сгенерировать код на IR, в котором есть UB.
И в этом ничего плохого нет, поэтому и получается, что UB != Unsafe.

Тут терминологическая ошибка. "Есть UB" != "Есть операция имеющая UB-случаи"


Простейший пример: разыменование указателя имеет два UB-случая: мусор в указателе и пустой указатель. Но в выражении *(new int) нет никакого UB, потому что оператор new всегда возвращает корректный непустой указатель.

Это я понимаю. UB — это такая штука, которой в программе не должно быть, и гарантировать это должен программист. В случае LLVM-кода — программист даёт программу без UB, фронтэнд-компилятор делает из неё LLVM-код без UB (к примеру [насколько я понял], если на входе программа без UB — LLVM-код не сгенерирует poison value), дальше он транслируется в машинный код. На каждом этапе UB нет, или мы имеем GIGO.


Иначе говоря — не может быть UB в LLVM-коде, если во входном коде его нет.

> не может быть UB в LLVM-коде, если во входном коде его нет.
Могут быть ооперации, обладающие UB, но сам компилятор при этом гарантирует, что случаев UB не будет.

Именно. Он получил на входе код, не генерирующий UB — выдал на выходе код, не генерирующий UB.
И если даже где-то в этом коде инструкция, которая может привести к UB — то транслятор следующего уровня может полагаться на то, что она в таком виде не вызовется, и смело оптимизировать.


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

Часть UB для конкретного процессора и конкретного компилятора вполне определена. Это такой compiler specific, который в стандарте прописан как UB, чтобы не сковывать авторов компилятора. Если мы пишем драйвер для конкретного процессора и конкретной ОС — вполне безопасно будет использовать некоторые UB для ускорения обработки прерываний.

Собственно об этом и статья: большинство UB — это всего лишь незадокументированное поведение. Ну как пример: "If the implementation does not support negative zeros, the behavior of the &, |, ^, ~, <<, and >> operators with operands that would produce such a value is undefined.". Такой код будет процессорно-специфичным (непереносимым), но вполне безопасным для конкретного процессора и компилятора.
Sign up to leave a comment.

Articles

Change theme settings