ЙажаСценарий / Говнокод #27414 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
function main() {    
    const x = 21;
    let s = "foo";
    const r = `a${x * 2}X${s}X${s}Z`;
    print(r);
}

Продолжаем будни говно-писания говно-компилятора на LLVM. серия - а ваш говно-компилятор может так...

и результат работы
>>
C:\temp\MLIR_to_exe>out.exe
a42XfooXfooZ

Запостил: ASD_77 ASD_77, (Updated )

Комментарии (66) RSS

  • и дайте знать если кому интересно глянуть на дамп асма
    Ответить
      • Мне не нужен, я в нём не разбираюсь, я анскильный.
        Ответить
          • Я так думаю можно было просто высрать constexpr строку "a42XfooXfooZ", которой в высере я не нашёл. Поэтому анскилл )))
            Ответить
        • Ты хоть мне объясни чем настоящий асм отличается от llvm

          Тем, что в нём нету регистра 33?
          Ответить
          • Ну да, это такой псевдокот, очень далёкий от реальных процессоров. Даже сраная jvm ближе к железу.
            Ответить
            • Общеизвестно, что в процессоре куда больше регистров, чем в его ISA (Томасуло и его станции резервирования вроде бы на этом основаны, а за одно и мельтдаун со спектром)

              Если я сделаю процессор, в котором можно "создать" 64 регистра, то я смогу запустить на нём llvm?
              Ответить
              • В LLVM байткоте вроде SSA, т.е. тебе нужно в теории бесконечое количество регистров.
                Ответить
                • По идее их можно как-то эмулировать в памяти, но наверное это уже будет не просто на настоящем железе

                  А что, их вообще может быть сколько угодно много? Фронтэнд может сгенерить код, в котором будет 65536 регистров?
                  Ответить
                  • > Фронтэнд может сгенерить код, в котором будет 65536 регистров

                    Ну да, это просто форма записи такая. Все переменные, промежуточные результаты и т.п. попадают в "регистры". Если функция сложная, там запросто нагенерится тыща "регистров".

                    Ну и да, посколько это single assignment, то там одна переменная может в десяток-другой раскрыться, чтобы для оптимизатора поток данных нагляднее был.
                    Ответить
                • Ну можно выделить под них место на стеке и закешировать верхние штук 30-60 в чём-то более быстром.
                  Ответить
                  • Но если фронт не будет про них знать, что он похерит нахер весь перформанс же?

                    А вот как это в gcc сделано? Там же тоже есть фронт и бек, но там не генерится промежуточный код? Или генерится? А тогда в чем разница?

                    Ябло вроде хвасталось, что перейдя на llvm они смогли применять какие-то фиксы (типа ARC или чего-то такого) уже вот прямо к llvm коду
                    Ответить
                    • А не надо фронту про это знать. Аллокация реальных регистров и слотов на стеке -- это уже прям одна из финальных фаз конпеляции, когда все общие оптимизации пройдены.

                      В gcc тоже промежуточный код был, как без него. Вроде даже джва. Но он там пиздец запутанный и фазы не так чётко разделены как в llvm, емнип.
                      Ответить
                      • Тем более, что и куда аллоцировать по определению может знать только бэкенд.
                        Ответить
                      • Я не очень понял

                        вот clang же высирает код в llvm, а далее llvm генерит по нему код под x86, верно?

                        как же сlang может чего-то не знать?
                        Ответить
                        • Когда фронт clang'а высирает LLVM IR, он ещё не знает под какой процессор это будет конпелироваться в итоге. Ну, какие-то вещи типа длины интов он знает, конечно. Но в целом он об этом не задумывается.

                          А соглашения о вызовах, аллокация конкретных регистров, выбор конкретных инструкций -- это уже задача бэка.
                          Ответить
                          • В каком месте тут ошибка в рассуждениях:

                            * в "промежуточном коде" LLVM есть понятие "регистр"
                            * в этом коде регистров может быть неограниченное количество
                            * clang генерирует это код, используя понятие "регистр"
                            * clang может создать промежуточный код, в котором используется 234234 регистра
                            * если доступ к первым пяти будет дешевым, а к остальным дорогим (бо в реальной архитектуре они эмулируются), то clang сгенерит неоптимизированный код

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

                            Типа clang насрал 100500 регистров, а бек их причесал?
                            Ответить
                            • Ну суть в том, что в IR нет переменных. Есть только вот эти "регистры", которые ещё и присваиваются по одному разу, емнип.

                              Какие из них горячие, а какие нет фронт не знает. Да его это и не должно ебать. Выделять регистры и слоты на стеке -- задача бэка и только бэка. Ибо только он знает сколько там регистров на таргете и видит более-менее финальную форму кода после кучи перестановок и оптимизаций.
                              Ответить
                            • > в "промежуточном коде" LLVM есть понятие "регистр"

                              LLVM IR регистры — абстрактные, и к хардварным отношения не имеют.

                              > в этом коде регистров может быть неограниченное количество
                              > clang может создать промежуточный код, в котором используется 234234 регистра

                              Да, потому что SSA, и LLVM "регистры" иммутабельные. Если фронтенд заанроллит цикл с 234234 итерациями, то под i понадобится 234234 регистров.

                              > clang генерирует это код, используя понятие "регистр"

                              ...из LLVM IR.

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

                              Бекенд по определению не делает код для гипотетической машины, он делает код для конкретной хардварной рахитектуры.
                              Ответить
                              • понятно

                                я просто думал, реально ли сделать такое

                                фронт --> LLVM код --> реальный проц

                                но уже понял, что без бека это нереально
                                Ответить
                                • Да реально в принципе... Просто с пирфомансом можешь попрощаться.
                                  Ответить
                                  • ну да, если только я не выдумаю такой процессор, который умеет забесплатно создавать регистры налету
                                    Ответить
                                • LLVM это и есть по сути "универсальный" бэкенд. (В кавычках, т.к. GHC-шники об него обломались)
                                  Ответить
                                  • вроде пишут, что GHC умеет в llvm, но не очень перформансно..
                                    Ответить
                                  • > GHC-шники об него обломались

                                    А почему? Их ленивые thunk'и плохо вписались в LLVM IR?
                                    Ответить
                                    • Не вникал. По слухам, там с TCO были проблемы. Плюс у GHC свой особый call convention.
                                      Ответить
                                      • > GHC-шники об него обломались

                                        Интересненько.
                                        А у них до сих пор в качестве «промежуточного» кода урезанная Сишка?
                                        Ответить
                                        • Я не шибко слежу за Haskell в последнее время (у меня есть более тяжёлые вещества), но вроде в GHC ничего радикально не поменялось.
                                          Ответить
                                  • > LLVM это и есть по сути "универсальный" бэкенд.

                                    Этот "универсальный" бэкенд настолько же универсален, как и какое-нибудь внутреннее говнопредставление из GCC. Нихуя такого прорывного там нет с т.з. фундаментальных вещей, просто еще одна питушня для промежуточного представления питушни с целью трансформации-оптимизации, коих было дохуя, есть дохуя, и будет еще дохуя. Не очень-то ясно, с хуев каких на него так все надрачивают.
                                    Ответить
                                    • > Не очень-то ясно, с хуев каких на него так все надрачивают.

                                      Очевидно же. Питузу в нём проще разобраться, чем в хитросплетениях гцц.
                                      И любой питуз может клепать на коленке свои анскильные rustы и кричать на каждом углу «йаже как Сишка».
                                      Ответить
                          • > Когда фронт clang'а высирает LLVM IR, он ещё не знает под какой процессор это будет конпелироваться в итоге.

                            Нет. Он знает про то, что такой-то процессор имеет такой-то размер указателя и такие-то требования по выравниванию, а это влияет много на что, например на чтение-загрузку структур с указателями
                            Вот пример https://godbolt.org/z/ednTbTfMM - align 4 для 32 бит и и align 8 для 64 бит

                            А если clang будет генерить код под процессор, где например char 16-битный, там указатели на char (и указатели на void) будут не i8* а i16*
                            Ответить
                          • Еще тот же LLVM имеет какие-то спецификаторы для соглашения вызова https://llvm.org/docs/LangRef.html#calling-conventions - и не всякие такие спецификаторы поддерживаются на всех архитектурах, так что если фронт генерирует такие спецификаторы для соглашения вызова, он "знает" что генерирует под какую-то архитектуру.

                            А еще https://llvm.org/docs/LangRef.html#x86-amx-type есть вот такая x86-специфичная хуйня, которой очевидно нет нигде, кроме x86.

                            Так что говорить что фронт ничего не знает про процессор - некорректно. Он вообще-то дохуя чего знает
                            Ответить
                            • > X86_amx Type
                              > X86_mmx Type

                              Какое говно )))
                              Они добавили эту крайне специфичную питушню к таким базовым, универсальным вореционным типам как i8, i16, i32 и плавающим fp32, fp64, fp128.
                              Ответить
                              • > Они добавили эту крайне специфичную питушню к таким базовым, универсальным вореционным типам как i8, i16, i32 и плавающим fp32, fp64, fp128.

                                Так-то этот LLVM может теоретически изначально оперировать generic-хуитой типа i8 i16 и проч, и на какой-то из говностадий перефигачить это во всякое процессорозависимое говно типа X86_mmx. Это еще называется "lowering"
                                https://llvm.org/docs/WritingAnLLVMBackend.html#basic-steps

                                > Describe the selection and conversion of the LLVM IR from a Directed Acyclic Graph (DAG) representation of instructions to native target-specific instructions. Use TableGen to generate code that matches patterns and selects instructions based on additional information in a target-specific version of TargetInstrInfo.td. Write code for XXXISelDAGToDAG.cpp, where XXX identifies the specific target, to perform pattern matching and DAG-to-DAG instruction selection. Also write code in XXXISelLowering.cpp to replace or remove operations and data types that are not supported natively in a SelectionDAG.
                                Ответить
                        • Ну и вот например ты можешь написать такой код:
                          return (x << 15) | (x >> 15);
                          Фронт это высрет как есть. А бэк подумает: "ага, у моего проца есть инструкция rol, заюзаю ка я её вместо этого говна!"
                          Ответить
                          • Эт я понимаю

                            Я думал, что если я создам такую машину, которая запускает LLVM код напрямую, и в ней будет 4 регистра (а остальные эмулируются), то код будет не оптимальным

                            Но теперь я понял, что мне всё равно нужен будет для нее бек, и превратить идеальный LLVM код в реальный это будет задача уже бека
                            Ответить
                            • > то код будет не оптимальным

                              Ну да, если ты надумаешь исполнять LLVM код "вживую", он будет неоптимальным. Ну потому что эта промежуточная форма весьма далека от железа.

                              А если у тебя будет бэк, то ты уже не LLVM код исполняешь, лол.
                              Ответить
                              • > Ну да, если ты надумаешь исполнять LLVM код "вживую", он будет неоптимальным.

                                Чтобы "вживую" его исполнять, нужно иметь соответствующий процессор, а такого процессора нет, да и сделать его похоже что нереально, ну вот как ты будешь регистры хуй знает какого размера в процессоре реализовывать? LLVM разве что интерпретировать можно вживую
                                Ответить
                                • С размером то всё просто -- можно сделать 64 битные регистры, которые покроют большинство программ. Если кто-то выебнулся и поюзал больше -- эмулировать в микрокоде.

                                  Сложнее с количеством. Видимо придется штук 30 держать регистрами, а остальное своповать в память.
                                  Ответить
                                  • > Если кто-то выебнулся и поюзал больше -- эмулировать в микрокоде.

                                    Сомнительная идея. Не всякий регистр ты сможешь микрокодом эмулировать.

                                    Проэмулируй мне регистр размером в килобайт в микрокоде
                                    Ответить
                                    • доступ к памяти есть? в чем проблнма?
                                      Ответить
                                      • К какой памяти? Кешу процессора? А если я захотел такой жирный регистр собрать, что кеша процессора не хватает? А где (по какому адресу) эту память аллоцировать в основной RAM? А как эту память потом освобождать, и кто это будет делать?
                                        Ответить
                                        • не понимаю почему CPU не может писать в основной RAM

                                          пусть ОСили прошивка загрузит ему адрес куска памятив регистр, как таблы страниц в CR3 (или куда их там грузят)

                                          вот как освобождать -- это интересно


                                          сделайте в процессоре GC
                                          Ответить
                                          • > сделайте в процессоре GC

                                            Уже сделали: https://en.wikipedia.org/wiki/Symbolics#The_3600_series

                                            > Hardware support was provided for virtual memory, which was common for machines in its class, and for garbage collection, which was unique.

                                            Но эти аппаратные лисп-машины закономерно обосрались. Железо это не говноязык типа "C++", что туда всякое говно можно набрасывать спокойно, и ничего особо плохого не будет
                                            Ответить
                                            • Когда прочитал про GC в процессоре, стало казатьcя, что речь о лисп машине

                                              так и случилось
                                              Ответить
                                        • А может еще надо чтоб в swap можно было регистр засовывать? Процессор и железо с такими свойствами конечно же возможно сделать, только это будет какой-то сраной анскильной аппаратной виртуальной машиной, которая крайне неэффективно будет работать и непонятно нахуя будет кому-то нужна
                                          Ответить
                                        • > где

                                          На стеке же. А в реальный регистр положить её адрес и длину. Освободится вместе с фреймом.
                                          Ответить
                                          • > На стеке же.

                                            А если стека окажется недостаточно для такого жирного регистра? Ну и к тому же регистр можно возвратить из функции, а стек нельзя, как эта проблема будет решаться?
                                            Ответить
                                            • > такого жирного регистра

                                              Мне вот интересно, что реальные бекенды с этим делают. Просто шлют нахуй или эмулируют через длинную арифметику?
                                              Ответить
                                              • > Мне вот интересно, что реальные бекенды с этим делают. Просто шлют нахуй или эмулируют через длинную арифметику?

                                                Думаю что middle-end говнопредставление перед переходом в back-end должно все такое говно вычистить. Т.е. хуйня с жирными LLVM регистрами должна в этом LLVM там как-то заоптимизироваться и потом в итоге переписаться в регистры с допустимым размером.
                                                Ответить
                                • > интерпретировать

                                  А где эта грань? Любой проц интерпретирует какой-то байткод.

                                  Реальная конпеляция разве что в верилоге да вхдл, там не доебёшься.
                                  Ответить
                                    • > ничего не интерпретировали

                                      Да как это? Загружали команды по одной и исполняли их. Что это, если не интерпретация?
                                      Ответить
                                    • >Интелы до P6 ничего не интерпретировали, вроде

                                      В x86 микрокод был всегда.
                                      Пруфом тому количество тактов на команду.

                                      Причём всё было наоборот, они только где-то с 486 научились некоторые инстры исполнять напрямую.
                                      Ответить
                          • > return (x << 15) | (x >> 15);

                            У тебя тут "rol" а какая-то хуйня.

                            Если ты сдвигаешь 32-битного питуза, надо return (x << 15) | (x >> (32-15) );
                            Ответить

Добавить комментарий

Семь раз отмерь — один отрежь, guest!

    А не использовать ли нам bbcode?


    8