1. JavaScript / Говнокод #27414

    +1

    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, 10 Мая 2021

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

    • и дайте знать если кому интересно глянуть на дамп асма
      Ответить
    • Какой скилл )))
      Ответить
      • я понял что народу нужен дамп :) https://pastebin.com/eZxkjgqd
        Ответить
        • Мне не нужен, я в нём не разбираюсь, я анскильный.
          Ответить
          • нет уж. ты полистай и посмотри :)
            Ответить
            • Я так думаю можно было просто высрать constexpr строку "a42XfooXfooZ", которой в высере я не нашёл. Поэтому анскилл )))
              Ответить
        • Это не настоящий асм, это какая-то LLVM-питушня.
          Ответить
          • Ты хоть мне объясни чем настоящий асм отличается от 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 ничего не интерпретировали, вроде
                                      Ответить
                                      • > ничего не интерпретировали

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

                                          Причём микрокодом. И со стадиями конвейера (начиная с 486).
                                          Ответить
                                      • >Интелы до P6 ничего не интерпретировали, вроде

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

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

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

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

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