LINUX.ORG.RU

LLVM. Зачем он вообще нужен?

 ,


3

6

Я понимаю, зачем его используют: чтобы не писать 100500 раз в каждом компиляторе одни и те же алгоритмы оптимизации.

Я не понимаю, почему не использовать просто компиляцию через Си или Си++. Оптимизации сделает компилятор Си. Семантика у LLVM всё равно совпадает с Си, по объёму кода компилятора тоже выигрыша практически нет. Зато если использовать Си, можно использовать любой из компиляторов Си и компилировать для платформ, для которых нет реализации LLVM.

★★★★★

Ответ на: комментарий от Taetricus

Поскольку при компиляции C в LLVM IR, clang производит оптимизации, которые нужно будет делать компилятору гипотетического языка, а этот тест этого не учитывает.

То есть компилятор в LLVM всегда будет сложнее. Надо сделать преобразование в SSA, надо сделать часть оптимизаций. Переписать кусок сишного компилятора или получить размеры системных типов через libffi. И всё ради того, чтобы получить практически тот же результат, который был бы при трансляции в Си++ вместо LLVM.

monk ★★★★★
() автор топика
Ответ на: комментарий от Taetricus

https://en.cppreference.com/w/c/string/multibyte/mbrtoc32

Спасибо, интересный случай. API POSIX / {g,}libc довольно внимательно следят за знаковостью возвращаемых значений.

Неизвестный размер стандартных типов данных в битах в Си

саму проблему неизвестного размера типов данных это не решает.

Её и не возможно решить в рамках C / POSIX - вся система спроектирована как работа с некоторыми условными целочисленными значениями, чьи диапазоны задаются конкретной аппаратно-программной реализацией. Теоретически, некие целочисленные вычисления в памяти без обращений к API OS можно реализовать благодаря stdint.h, который завезли только в C99, но плавающая точка точно так же условная как int.

Если нужны гарантии на мат. операции от стандарта языка, то С - вообще мимо кассы.

LamerOk ★★★★★
()
Ответ на: комментарий от monk

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

но нарушается семантический инвариант класса и то свойство что конструктор по умолчанию или вполне конкретный конструктор, также как и абстрактная фабрика – создаёт объект в гарантированно корректном преднастроенном состоянии.

а если обошли корректную инициализацию (с аллокацией допустим нормально, хотя могут быть связанные из этого объекта указатели) – то и состояние объекта обойдённое исключением будет некорректное.

Сигнальный протокол и перезапуски для C++ — это передача обработчика ошибок параметром. А через исключения мы ловим результат.

в этом смысле кортежи вида (obj,err) := initCObj(....) в Go даже более корректны чем исключения.

то есть: нет никакого запутанного потока управления, есть вполне корректные мультизначения, если ошибка то объект не инициаллизирован (а может и не аллоцирован полностью).

то есть: неявный поток обработки ошибок делаем явным.

ну и зачем тогда исключения?

опять же, нетранзитивность константности в С++ vs. immutable в D и гарантия компилятором.

const это просто read only view в параметрах этой функции/этого конструктора – но не факт, что того, который в другом потоке например.

то есть, если бросить исключения в мультитредовой программе, в каком состоянии окажутся основной поток и остальные треды?

ну то есть, опять управление тредами нужно делать явным.

бросить исключения в конструкторе/деструкторе/многопоточной программе в С++ – это отличный способ выстрелить себе в ногу.

а вот ещё и корутины в новом стандарте добавили :))) теперь можно стрелять рикошетом и асинхронно

anonymous
()
Ответ на: комментарий от monk

в этом смысле отдельные ключевые слова task/entry/task type и управляемая/контролируемая память, синхронизация и рандеву в аде – более корректный и честный подход, чем многозадачность библиотекой.

в модуле-2 например библиотекой, модулями были реализованы и исключения и корутины.

результат: в одних языках компилятор понимает что здесь будет многозадачность и может проверить какие-то дополнительные гарантии корректности (не лайвлоки конечно), в других – компилятор просто такой информации не имеет в принципе и новая фича добавленная библиотекой плохо интегрирована в язык (может конфликтовать с другими его фичами).

anonymous
()
Ответ на: комментарий от Taetricus

и правда, почему Why undefined behavior for infinite loops?

можно ещё volatile переменных подобавлять чтобы не пытался их выоптимизировать нахрен

The question of whether or not side-effect-free infinite loops have defined semantics impacts primarily compiler back-ends.

а какую же он имеет – неопределённую, undefined/unspecified? :)

здрасти, С++. UB не отходя от кассы сразу в бесконечном цикле? в mainloop приложения? :)

во всяких хаскелях через монады с коэффектами например можно вполне конкретно доопределить.

а тут что, получается что нельзя ? :)))

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

On a purely technical basis, there are solid arguments both for the current wording, and for the N1509 position. We hope that this document, together with N1509 present those properly.

он согласен с обоями? :))

anonymous
()

LLVM родился как исследовательский проект Криса Латнера (тогда ещё студента-магистра в Университете штата Иллинойс в Урбана-Шампейн) и Викрама Адве (тогда и по сию пору профессора в том же университете). Целью проекта было создание промежуточного представления (intermediate representation, IR) программ, позволяющего проводить «агрессивную оптимизацию в течение всего времени жизни приложения» — что-то вроде Java байт-кода, только круче. Основная идея — сделать представление, одинаково хорошо подходящее как для статической компиляции (когда компилятор получает на вход программу, написанную на языке высокого уровня, например C++, переводит её в LLVM IR, оптимизирует, и получает на выходе быстрый машинный код), так и динамической (когда runtime система получает на вход машинный код вместе с LLVM IR, сохранённым в объектном файле во время статической компиляции, оптимизирует его — с учётом собранного к этому времени динамического профиля — и получает на выходе ещё более быстрый машинный код, для которого можно продолжать собирать профиль, оптимизировать, и так до бесконечности).

Если понравилось, тогда сюда https://habr.com/ru/companies/huawei/articles/511854/

saufesma
()
Ответ на: комментарий от anonymous

а если обошли корректную инициализацию (с аллокацией допустим нормально, хотя могут быть связанные из этого объекта указатели) – то и состояние объекта обойдённое исключением будет некорректное.

Если в конструкторе исключение, то объект не создан. Никакого инварианта не нарушается.

то есть: нет никакого запутанного потока управления, есть вполне корректные мультизначения, если ошибка то объект не инициаллизирован (а может и не аллоцирован полностью).

Здесь семантика абсолютно идентична

try {
   auto obj = initCObj(....);
} catch(exception &e) {
   auto err = e.what;
}

ну и зачем тогда исключения?

В конструкторах незачем. Исключения нужны, если ошибка проброшена через несколько уровней.

Типа

void editor::read_file(string & s)
{
   try {
      ...
      f = open(...);
      ...
      while(...) { ... p += f.readline(...) ... }
      ...
      process(p);
      ...
   } catch (...) {
      message("Не удалось прочитать файл");
   }
}

И если где-то в потрохах process ошибку не обработали, то программа не упадёт, но в ошибочном состоянии не будет попыток что-то сделать (как если в Go проигнорировать err).

Почти идеальный вариант в Zig. Исключений нет, но проигнорированные ошибки прерывают работу и гарантированно поднимаются в результат выше.

опять же, нетранзитивность константности в С++ vs. immutable в D и гарантия компилятором.

В С++ вместо immutable есть constexpr.

а вот ещё и корутины в новом стандарте добавили :))) теперь можно стрелять рикошетом и асинхронно

Поэтому на Си++ не надо писать, а надо в него компилировать. Как на ассемблере.

monk ★★★★★
() автор топика
Ответ на: комментарий от anonymous

а какую же он имеет – неопределённую, undefined/unspecified? :)

UB для бесконечного цикла, чтобы код типа

while(i.next_entry()) {
   if(logging) log << i.data();
}

можно было выкидывать целиком, если logging ложно. А не выполнять на всякий случай next_entry() для списка ошибок, если компилятор не смог доказать, что количество ошибок за время выполнения цикла конечно.

Но вроде разрешить хотят не любой бесконечный цикл, а тривиальный.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Поэтому на Си++ не надо писать, а надо в него компилировать. Как на ассемблере.

чушь вообще. самоисключающий паразитизм в чистом виде. если не писать на с++, то зачем кто-то будет писать его компилятор?

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

логика типа - пусть одни в шахте работают, «а я у буду памидор прадават, пять раз больше имет».

alysnix ★★★
()
Ответ на: комментарий от alysnix

если не писать на с++, то зачем кто-то будет писать его компилятор?

Кто-то же пишет компилятор LLVM и ассемблеров.

а другие, левой ногой будут писать свои колхозные поделки, типа продвинутые «языки», компилируя прям в с++.

То есть, компилировать в LLVM надо, так как разработчики Си++ мучались, поэтому всё остальные для справедливости тоже обязаны мучиться?

Так эта логика и к LLVM применима. Почему одни должны мучаться разрабатывая LLVM компилятор с мегаоптимизациями и на_все_случаи_кодогенерациями , а другие, левой ногой будут писать свои колхозные поделки, типа Rust, компилируя прям в LLVM.

Всех надо обязать писать собственные компиляторы в машинные коды и ни в коем случае не переиспользовать чужие исходники?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Почему одни должны мучаться разрабатывая LLVM компилятор с мегаоптимизациями и на_все_случаи_кодогенерациями , а другие, левой ногой будут писать свои колхозные поделки, типа Rust, компилируя прям в LLVM.

потому что llvm - это отдельный проект, со своим академическим интересом - разработка методов оптимизации и кодогенерации.

в нем нет всякого мусора, связанного с написанием ручками машинного кода, всяких там доктрин программирования, навроде ооп или функциональщины, или метапрограмирования и проч фигни. его не волнует 300-летняя совместимость и легаси с царем горохом.

у llvm вообще задачи разработки инфраструктуры для создания компиляторов и около того, а не преобразование конкретного человекоориентированного синтаксиса в байтовый код.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

у llvm вообще задачи разработки инфраструктуры для создания компиляторов и около того, а не преобразование конкретного человекоориентированного синтаксиса в байтовый код.

Си тоже когда-то был «переносимый ассемблер». И разработчики S-выражений лиспа не предполагали, что на них писать будут.

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

monk ★★★★★
() автор топика
Ответ на: комментарий от alysnix

логика типа - пусть одни в шахте работают, «а я у буду памидор прадават, пять раз больше имет».

Эту аналогию можно понять так, что с помощью «колхозной поделки» можно написать проект в 5 раз быстрее. Но мы из каких-то религиозных соображений будем работать в 5 раз медленнее.

Ну и тут всегда классический пример с сайтом. Можно современный сайт писать на C++. Корпорации могут себе такое позволить, наверное. Но одиночке или маленькой команде зачем так делать, если есть более подходящие языки?

Kogrom
()
Последнее исправление: Kogrom (всего исправлений: 1)
Ответ на: комментарий от monk

И разработчики S-выражений лиспа не предполагали, что на них писать будут.

если читать изначальную работу Джона Маккарти recursive.pdf про rationale, чего он на самом деле хотел и почему определил именно так.

Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I John McCarthy, Massachusetts Institute of Technology, Cambridge, Mass. ∗ April 1960

то там есть вполне чёткое, вполне доопределённое поведение :) несмотря на то что это математизированное определение поведения лиспа, его семантики выполнено в частично-рекурсивных функциях, а не в тотальных.

изначально его бы вполне устраивал алгол 58/60 в котором можно было бы задавать свой eval, свою семантику вычислений (например: ленивую а не жадную, символьную а не значения; грубо говоря, он хотел чего-то типа call by name, только расширяемую произвольным (т.е., вполне доопределённым образом)

там 34 страницы, на которых он последовательно: многостадийно и метациклически определяет такой вычислитель, его семантику вычислений.

  1. базовые кирпичики – Cond-выражения. которые частично вычисляют if p1 then e1 else if p2 then e2 else .... else if p_n then e_n

логично, что если какая-то ветка не упомянута – то Cond-выражение это частично а не тотально определённая функция.

это 1c, а 1а – частично определённые/вычисленные функциии (partitial evaluation), и 1b – propositional Expressions and predicates и соотв. логика 0 порядка

  1. e. функции и формы, f. выражения для рекурсивных функций.

вводится λ-нотация и λ-выражения. λ-нотация неадекватна для именования рекурсивно определённых функций, поэтому вводится конструктор label(a,ε) вдобавок к λ((x1,x2,…xn),ε)

  1. далее в 3 разделе со стр. 8 определяются символьные выражения, S-exprs.

через частично определённые функции выше. для чего вводятся

3a. классы S-выражений

  1. атомы (atomic symbols) – S-выражения

  2. если e1 и e2 – S-выражения, то (e1 .e2) – тоже

    то есть, через COND-выражения частично определённые

3b. функции над S-выражениями и выражениями их представляющими

M-выражения для вычислений S-выражений

3c. элементарные S-функции и предикаты.

  1. atom [X] = T; atom [(X . A)] = F

  2. eq: eq[X;X]=T; eq[X;A]=F; eq[X;(X . A)] – неопределено (то есть, опять частично определённый предикат)

  3. car: … , car[X] – неопределено, car[x]-определено когда x не атом

  4. cdr: … , cdr[X] – неопределено, cdr[x]-определено когда x не атом

  5. cons: cons[x;y] – тотально, определено везде

3d. рекурсивные S-функции над S-выражениями и cond-выражениями.

  1. ff[x]
  2. subst[x;y;z]
  3. equal[x;y]

3e. представление S-функций через S-выражения. S-функции описывались через M-выражения. тут определили правило трансляции M-выражений в S-выражения.

3f. универсальная S-функция apply

определена рекурсивно через

apply[f;args] = eval[cons[f;appq[args]];NIL], где appq = … cond-выражение
и eval = … пошёл метациклический вычислитель через вспомогательные функции и cond-выражения, вплоть до базовых примитивов.

eval через atom, assoc, eq, car, caar, cdr, cadr, caddr, caddar, list, cons, append, evcon, evlis

ну и т.п.

anonymous
()
Ответ на: комментарий от anonymous

то есть что я хочу сказать. хотя в этом математизированном метациклическом частично рекурсивном определении лиспа через cond-выражения,λ-выражения, S-выражения, M-выражения – в какой-то мере кажется, что присутствует возможный UB, undefined behaviour.

ибо ну как же так: функции определены частично, а не тотально везде.

но раскрутка устроена таким образом, что никаких неопределённых значений в реальности не возникает.

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

то есть, если бы тут потребовать определить через строго формальную семантику того же алгола 58/60 – чтобы были полностью определены строго везде.

то не факт что и получится. собственно, об этом и писал МакКарти – когда его рацпредложение, proposal отвергли в комитете по стандартизации алгола, он выкатил вот такое математизированное определение с обоснованием для чего нужны все эти разные χζ-выражения.

интересно подумать, а возможно ли было бы расширить семантику алгола с его call by reference, call by value, а также call by name в CTFE CTMP (в духе converge pl, template haskell и прочего MetaLua, или того же D с string mixin, template mixin) для вычисления символов.

вполне возможно что тут хватило бы и какого-то продвинутого интерфейса между compile time/runtime, который компилятор бы просто вызывал в рантайме как CTFE интерпретируемые функции, определённые по тексту выше.

возможно, что и алгола бы хватило. ну и макросы бы были более продвинутые (модульные AST CTFE например).

то есть: наличие кажущегося UB в языке в реальности не мешает этим эффективно пользоваться.

хотя частично определённые функции, а не тотально – это не совсем UB.

скажем так, недостаточно доопределённое поведение — которое потом нужно доопределять, уточнять.

anonymous
()
Ответ на: комментарий от anonymous

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

иначе говоря, вот пытается конпелятор С++ определять наличие UB чтобы что? а можно проще? если всего этого – не делать, а просто определения доопределять другим более правильным способом?

anonymous
()
Ответ на: комментарий от monk

но нарушается семантический инвариант класса и то свойство что конструктор по умолчанию или вполне конкретный конструктор, также как и абстрактная фабрика – создаёт объект в гарантированно корректном преднастроенном состоянии.

скорее, нарушается семантический инвариант метакласса.

абстрактную фабрику можно же реализовать через метакласс.

то есть, «семантический инвариант» (с) интерфейса абстрактной фабрики по его использованию кагбе намекает нам на то, что она обработает и оставит объект в гарантированно корректно сконфигурированном состоянии. или не отработает и оставит NULL ? или какой-то пустой объект?

Если в конструкторе исключение, то объект не создан. Никакого инварианта не нарушается.

а если он не создан, но мы потом пользуемся кагбе намёком на то что он создан?

то есть, что не NULL, не пустой, и создан какой-то вполне конкретный, корректный объект?

это UB – или недостаточно доопределённое поведение? :))

anonymous
()
Ответ на: комментарий от anonymous

расширим класс метапрогов помимо

  • тотально определённых полностью везде функций имеющих определение согласно светлой стороны ян-стандарту;

  • частично определённых конпелируемых подфункций функций и метафункций согласно тёмной стороны инь-стандарту;

  • UB-неопределённо определённых :)) функций согласно мета-инь- или дао-стандарту;

  • не вполне доопределёнными конпелируемыми подфункциями согласно дзен-стандарту определения метаязыка метапрогового.

остаётся вопрос к кластеру метапарадигм: как же их следует корректно доопределять и эффективно конпелировать.

дабы познать дзен.

anonymous
()
Ответ на: комментарий от anonymous

иначе говоря, вот пытается конпелятор С++ определять наличие UB чтобы что? а можно проще? если всего этого – не делать, а просто определения доопределять другим более правильным способом?

Чтобы можно было сгенерировать эффективный код. Например встречается

if(x + n > x) { ... }

И компилятор видит, что в каком-то контексте n всегда положительное. Тогда, если x имеет знаковый целый тип, компилятор может выкинуть сложение и вычисление. Программа станет работать быстрее.

Или

for(int i = n; i < n + a; i++) p += i;

можно свернуть в p = a * (n + n + a -1) / 2.

А если считать, что знаковое переполнение допустимо, то в этих случаях надо делать дополнительные проверки.

monk ★★★★★
() автор топика
Ответ на: комментарий от anonymous

а если он не создан, но мы потом пользуемся кагбе намёком на то что он создан?

Мы не можем им пользоваться, если в конструкторе произошло исключение. Значение ведь будет недоступно в обработчике исключения и коде выше уровнем.

monk ★★★★★
() автор топика

Семантика у LLVM всё равно совпадает с Си, по объёму кода компилятора тоже выигрыша практически нет.

Это ты сам придумал?

Зато если использовать Си, можно использовать любой из компиляторов Си и компилировать для платформ, для которых нет реализации LLVM.

Нельзя. Стандартный Си реализуют примерно два с половиной компилятора, и то между ними можно найти довольно ощутимую разницу. Если же у тебя платформа, для которой нет LLVM, то там стопудов и нормального стандартного Си нет.

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

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Если же у тебя платформа, для которой нет LLVM, то там стопудов и нормального стандартного Си нет.

HPPA, arc600, ETRAX CRIS, OpenRISC, s390, … : есть GCC, нет LLVM.

Эльбрус: Есть стандартный Си (совместим с GCC 9) и оптимизируется заметно лучше, чем LLVM.

monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Семантика у LLVM всё равно совпадает с Си

Я же приводил выше пример. Бывает семантика ассемблера, где любая операция что-то сделает, возможно влезет в чужие данные. Бывает семантика Паскаля, где ошибочная операция должна аварийно завершить программу. Бывает семантика Си/Си++ с понятием неопределённого поведения: компилятор может делать что угодно с ветками, которые к нему приводят. Функции бывают Сишные, бывают Фортовские, бывают Лисповые. У LLVM сишные функции с полностью сишным ABI.

по объёму кода компилятора тоже выигрыша практически нет

GCC и LLVM почти одинаковы по объему.

monk ★★★★★
() автор топика

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

anonymous
()
Ответ на: комментарий от monk

HPPA, arc600, ETRAX CRIS, OpenRISC, s390, … : есть GCC, нет LLVM.

А живых из них сколько?

Эльбрус

Мне кажется, шансы разраба LLVM или GCC компилировать что-то на Эльбрусе стремятся к нулю. Оно мертво, Джим (c).

Бывает семантика ассемблера, где любая операция что-то сделает, возможно влезет в чужие данные.

Это семантика вообще любого языка, кроме ультрачистых штук типа Haskell.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Вообще, если ты хотя бы почитаешь спеки LLVM IR, у тебя такие странные вопросы отпадут в принципе. Си – слишком высокоуровневый язык, чтобы в него что-то адекватно компилировать. Как промежуточная цель для компиляции, он подходит только когда ты хочешь портировать код на платформу, где кроме него ничего нет. А дальше уже проще нормальный бэкенд использовать.

К слову, у GCC тоже есть аналог LLVM IR под названием GIMPLE. Просто он убогий и мозолеед был категорически против того, чтобы позволять его использование во внешнем тулинге, потому что ЗЛЫЕ ПРОПРИЕТАРЩИКИ СДЕЛАЮТ ИЗ GCC ЗОКРЫТЫЕ КОМПЕЛЯТОРЫ. Короче, типичные гнутые шизофрения и говнокод.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от monk

заменять

if(x+n>x){...}
на
{...}
в указанных вами ограничениях нельзя

ибо вы неявно ожидаете отсутсвие переполнения

LLVM это современный трансмогифер https://en.wikipedia.org/wiki/TMG_(language)

c большим сохранением семантики чем «обычные»(гдето и когдато) прямые компиляторы С/C++ ->бинарь ибо сама идеология С - нулевой цены рантайма по умолчанию - т.е если деву чёт нужно он прикручивает сам - закат солнца и прочие прелести

qulinxao3 ★★
()
Ответ на: комментарий от qulinxao3

ибо вы неявно ожидаете отсутсвие переполнения

В случае Си или LLVM мы явно ожидаем отсутствие знакового переполнения, так как оно UB. То есть если оно случилось, уже неважно что будет делать программа. Значит можно компилировать, постулируя, что его никогда не бывает.

LLVM это современный трансмогифер https://en.wikipedia.org/wiki/TMG_(language)

Тогда как на LLVM будет выглядеть это:

program: [alpha = letter | digit] parse(expr);
expr: <(>/exp1 expr operator expr <)> = { 3 1 2 };
exp1: ident = { <LOAD > 1 < > };
operator:
op0: <+>/op1 = { <ADD > };
op1: <->/op2 = { <SUB > };
op2: <*>/op3 = { <MPY > };
op3: </>     = { <DIV > };

ident: smark any(letter) string(alpha) scopy = { 1 };

letter: <<abcdefghijklmnopqrstuvwxyz>>
        <<ABCDEFGHIJKLMNOPQRSTUVWXYZ>>;
digit:  <<0123456789>>;
alpha:  0;

?

monk ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Бывает семантика ассемблера, где любая операция что-то сделает, возможно влезет в чужие данные.

Это семантика вообще любого языка

Так у Си, Си++ и LLVM другая семантика. Операция может быть проигнорирована, если её выполнение свидетельствует о том, что где-то может быть UB.

Например

int foo(int *ptr) 
{
  int d = *ptr;
  if (ptr == NULL)
    return 1;
  return 0;
}

int main()
{
    printf("%d", foo(NULL));
}

с ассемблерной семантикой может аварийно завершиться, если чтение по 0 адресу запрещено ОС, или может вывести 1, если чтение не запрещено. А программа на Си выведет 0, так как и чтение по адресу NULL и сравнение с NULL не будет выполнено, так как при этом может быть UB.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Операция может быть проигнорирована, если её выполнение свидетельствует о том, что где-то может быть UB.

Ты опять не понимаешь, что такое UB в Си.

В случае undefined behaviour имеется ввиду behaviour компилятора, которое undefined. Т.е. при возникновении вот таких-то вот паттернов в коде, компилятор волен выдать тебе любое говно вместо бинарника. Или не выдать. Ты вот знаешь, например, что в Си в списке UB есть отсутствие переноса строки в конце файла и отсутствие пары у кавычек? Как вот это ложится в твою картину мира о семантике языка, а?

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

А живых из них сколько?

Это не важно. Если наполеоны из сумасшедшего дома обсуждают, как им руководить миром, то такие тонкости роли не играют. А если подходить к задаче более рационально, то дилемма тут такая:

  1. Если для своего язычка использовать C++, то получишь дополнительную практику использования C++, компиляторов, систем сборки. Это может пригодиться по работе.
  2. Если используешь LLVM, то сможешь в курилке сказать: «поковырял я тут LLVM, интересная игрушка, мужики». И они может быть отвлекутся от обсуждения баб и машин по пару минут.
Kogrom
()
Ответ на: комментарий от hateyoufeel

например, что в Си в списке UB есть отсутствие переноса строки в конце файла и отсутствие пары у кавычек? Как вот это ложится в твою картину мира о семантике языка, а?

Также. У нормального языка это ошибка компиляции, а не UB.

monk ★★★★★
() автор топика
Ответ на: комментарий от Kogrom

В случае undefined behaviour имеется ввиду behaviour компилятора, которое undefined.

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

monk ★★★★★
() автор топика
Ответ на: комментарий от Kogrom

В случае undefined behaviour имеется ввиду behaviour компилятора, которое undefined.

Надо на 1 апреля язык придумать с такой фичей: если код не понравился, компилятор может делать что угодно от форматирования диска до отправки содержимого домашнего каталога в ФСБ.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

LLVM ( ща MLIR и в целом промежуточные представления с тегированием для дальнейшего трансформирования) в том смысле современные TMG ( а точнее ТЕ треугольные пазлы где вместо генерации для n входов и m хостов nm трансляторов делают n+m трансляторов где n трансляторы входа в IR(какой либо) и m трансляторов из IR в хостовые битовые последовательности

TMG тогда универсальный транслятор трансляторов который сводит написание n+m трасляторов в «конфиги» для TMG и «никакоВОмошенства»

преимущество IR (в первоисточниках как тут уже указывали лучше смотреть там конкретные трэйдоффы и их обоснования) в как всегда «отложенной» потери семантики исходных кодов в отличии от обычной трансляции в бинарь

зы имхо сс - это было по началу не С-соmpiler - а соmpiler of compiler's :) - ибо все остальные языке в unix-переноске отвязаны от асма и привязаны к лучшему макроассембелру - который теперь завётся the C programming language

qulinxao3 ★★
()
Последнее исправление: qulinxao3 (всего исправлений: 2)
Ответ на: комментарий от qulinxao3

ибо все остальные языке в unix-переноске отвязаны от асма и привязаны к лучшему макроассембелру - который теперь завётся the C programming language

Так я про то же. Что теперь на роль «лучшего макроассемблера» тащат LLVM.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

ну да - ибо сяшка более низкоуровневую модель целевой машины предполагает и большая та же лакуна с UB

в отличии от LLVM экосистемы - хот там типо один из синтаксивов типо линейный(ну классический в 3-4 столбика строки мнемоник с операндами)асм - по факту там нечто что «вроде» не взлетело у Intel c их i962 или i860 - там где в железе пытались обьекты и прочие теги вкрутить

насколько я заблуждаюсь LLVM и различные модели конкурентности(и|или)параллельности на уровне своего асма дозволяет хотя не достаточно если MLIR ща катят - врядли чисто бизнес есть причина выпочкования от LLVM - всёж контексты со временем меняются и инженерия эт ж не извечная матема

qulinxao3 ★★
()
Ответ на: комментарий от monk

Ну я в этом вопросе тоже несогласен с @hateyoufeel. Только цитата не моя. Да, с UB программа скомпилируется и даже будет работать. Но не всегда так как мы ожидаем.

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

Kogrom
()
Ответ на: комментарий от Kogrom

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

Наличие UB в коде является однозначным багом. Без исключений.

anonymous
()
Ответ на: комментарий от anonymous

Наличие UB в коде является однозначным багом. Без исключений.

Более того, само наличие возможности UB является признаком сырости концепции и недоработанности реализации.

Это напоминает бег по тёмной улице, можно быстро бегать, правда до первого столба (UB). Зато как быстро бежали ДО столба.

Obezyan
()
Ответ на: комментарий от Kogrom

Да, с UB программа скомпилируется и даже будет работать.

Или не скомпилируется. Ещё раз, непарные кавычки в C являются UB.

#include <stdio.h>

int main() {
  puts("PENIS" ");
  return 0;
}

Сишный компилятор вполне может вот к такому говну выдать бинарник, который что-то даже будет делать. Например, насрёт тебе в штаны. Тот факт, что нынешние компиляторы кидают ошибку, это просто особенности реализации.

hateyoufeel ★★★★★
()
Ответ на: комментарий от monk

И эта семантика унаследована LLVM.

Далеко не полностью. В любом случае, это не настолько существенно и может быть изменено. LLVM не обещает обратной совместимости.

hateyoufeel ★★★★★
()