1. C++ / Говнокод #27456

    0

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    // https://llvm.org/doxygen/AArch64AddressingModes_8h_source.html#l00051
    
     /// getShiftName - Get the string encoding for the shift type.
     static inline const char *getShiftExtendName(AArch64_AM::ShiftExtendType ST) {
       switch (ST) {
       default: llvm_unreachable("unhandled shift type!");
       case AArch64_AM::LSL: return "lsl";
       case AArch64_AM::LSR: return "lsr";
       case AArch64_AM::ASR: return "asr";
       case AArch64_AM::ROR: return "ror";
       case AArch64_AM::MSL: return "msl";
       case AArch64_AM::UXTB: return "uxtb";
       case AArch64_AM::UXTH: return "uxth";
       case AArch64_AM::UXTW: return "uxtw";
       case AArch64_AM::UXTX: return "uxtx";
       case AArch64_AM::SXTB: return "sxtb";
       case AArch64_AM::SXTH: return "sxth";
       case AArch64_AM::SXTW: return "sxtw";
       case AArch64_AM::SXTX: return "sxtx";
       }
       return nullptr;
     }

    Хорош ли тот язык, в котором такую херню надо писать?

    Именно поэтому я за гомоиконы.

    Запостил: j123123, 06 Июня 2021

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

    • И да, часто слышу возгласы, что вот в GCC код такой запутанный и непонятный, черт ногу сломит, то ли дело LLVM. Так вот хренушки. Я смотрю код этого LLVM и я б не сказал, что это сильно проше макроговнокода из GCC
      Ответить
    • https://llvm.org/doxygen/X86TargetTransformInfo_8cpp_source.html
      Так, что тут у нас?
      /// About Cost Model numbers used below it's necessary to say the following:
      /// the numbers correspond to some "generic" X86 CPU instead of usage of
      /// concrete CPU model. Usually the numbers correspond to CPU where the feature
      /// apeared at the first time. For example, if we do Subtarget.hasSSE42() in
      /// the lookups below the cost is based on Nehalem as that was the first CPU
      /// to support that feature level and thus has most likely the worst case cost.

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

      И конечно же хуй знает откуда взятые значения этой самой "стоимости":
      static const CostTblEntry SLMCostTable[] = {
          { ISD::MUL,   MVT::v4i32, 11 }, // pmulld
          { ISD::MUL,   MVT::v8i16, 2  }, // pmullw
          { ISD::MUL,   MVT::v16i8, 14 }, // extend/pmullw/trunc sequence.
          { ISD::FMUL,  MVT::f64,   2  }, // mulsd
          { ISD::FMUL,  MVT::v2f64, 4  }, // mulpd
          { ISD::FMUL,  MVT::v4f32, 2  }, // mulps
          { ISD::FDIV,  MVT::f32,   17 }, // divss
          { ISD::FDIV,  MVT::v4f32, 39 }, // divps
          { ISD::FDIV,  MVT::f64,   32 }, // divsd
          { ISD::FDIV,  MVT::v2f64, 69 }, // divpd
          { ISD::FADD,  MVT::v2f64, 2  }, // addpd
          { ISD::FSUB,  MVT::v2f64, 2  }, // subpd
      ...

      которые вряд ли кто пересматривает. Схуя б не сделать некий конфигурационный файл с этими стоимостями, и чтобы кто-то мог легко эти говностоимости в конфиге поменять и пересобрать, померять изменения пирфоманса откомпилированных программ? Хуй там, иди в исходнике ковыряйся, ручками переписывай!
      Ответить
      • ой, кажется у нас портабельная C++ Struct Notation через 3... 2...
        Ответить
      • > Схуя б не сделать некий конфигурационный файл

        Эти яблоумники не осилили отличную от захардкоженной директорию с компилятором, поэтому если вдруг кто-то захочет просто взять и попробовать «LLVM», то придётся либо ебаться и править исходники, либо ебаться и крутить-вертеть директории.
        Ответить
      • > которые вряд ли кто пересматривает.
        Зачем? Зачем?
        ЕМНИП так для каждого поколения штеуда и амуд они пишут свои таблицы и веса.
        Причём, по-моему, их комитит производитель cpu, т.к. он больше всего заинтересован в пирформансе.

        То что передаётся потом в -mtune
        Ответить
        • > ЕМНИП так для каждого поколения штеуда и амуд они пишут свои таблицы и веса.

          Покажи мне в исходниках эти таблицы для каждого поколения штеуда и амуд.
          Ответить
          • см. llvm/lib/Target/X86/*

            $ less llvm/lib/Target/X86/X86ScheduleZnver2.td
            
            defm : Zn2WriteResPair<WriteBSF, [Zn2ALU], 3>;
            defm : Zn2WriteResPair<WriteBSR, [Zn2ALU], 4>;
            defm : Zn2WriteResPair<WriteLZCNT,          [Zn2ALU], 1>;
            defm : Zn2WriteResPair<WriteTZCNT,          [Zn2ALU], 2>;
            defm : Zn2WriteResPair<WritePOPCNT,         [Zn2ALU], 1>;
            
            // m32.
            def Zn2WriteMul32Ld : SchedWriteRes<[Zn2AGU, Zn2ALU1, Zn2Multiplier]> {
              let Latency = 7;
            }
            def : SchedAlias<WriteIMul32Ld, Zn2WriteMul32Ld>;
            def : SchedAlias<WriteIMul32ImmLd, Zn2WriteMul32Ld>;
            def : SchedAlias<WriteIMul32RegLd, Zn2WriteMul32Ld>;
            
            // r64.
            def Zn2WriteMul64 : SchedWriteRes<[Zn2ALU1, Zn2Multiplier]> {
              let Latency = 4;
              let NumMicroOps = 2;
            }

            Файл огромен, весь приводить не буду.

            В gcc тоже самое здесь: gcc/config/i386/x86-tune-costs.h
            Ответить
            • Это не те веса.

              Смотри исходник по адресу https://llvm.org/doxygen/X86TargetTransformInfo_8cpp_source.html

              Обрати внимание на комментарий:
              /// TODO: Develop and implement  the target dependent cost model and
              /// specialize cost numbers for different Cost Model Targets such as throughput,
              /// code size, latency and uop count.


              Хуйня в https://llvm.org/doxygen/X86TargetTransformInfo_8cpp_source.html описывает "стоимости" LLVM-опкодов для generic x86 процессора.

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

              Компиляторы это такая очень заебаная многоуровневая хуита, и вот на одном говнопредставлении там процессор считается как "Generic X86" и никакого учета говноархитектуры там нет. А в более низком абстрактном говнопредставлении - да, там учитывается
              Ответить
              • А в более низком абстрактном
                говнопредставлении - да, там учитывается, говно там гниет и отваливается. А где-нибудь на десятом, десятом уровня - там нет вообще никакой информации. Есть только правила управления, которые нужно запомнить, чтобы управлять гусеничным креслом. При этом можно воспользоваться интуитивно заложенной командой, которая включит этот процессор. Но откуда берется эта интуиция? Похоже, что от спама. Мы много раз слышали на тренингах фразу о том, что нет разницы между Yahoo! и Goodyear. Заебись! Почему? Потому что эти люди поднимают спам-пошлину.
                Ответить
      • def AtomWrite01_9 : SchedWriteRes<[AtomPort01]> {
          let Latency = 9;
          let ResourceCycles = [9];
        }
        def : InstRW<[AtomWrite01_9], (instrs POPA16, POPA32,
                                              PUSHF16, PUSHF32, PUSHF64,
                                              SHLD64mrCL, SHRD64mrCL,
                                              SHLD64mri8, SHRD64mri8,
                                              SHLD64rri8, SHRD64rri8,
                                              CMPXCHG8rr)>;
        def : InstRW<[AtomWrite01_9], (instregex "(U)?COM_FI", "TST_F",
                                                 "(U)?COMIS(D|S)rr",
                                                 "CVT(T)?SS2SI64rr(_Int)?")>;

        Кстати для атома они регулярками загрепали чтобы не копипастить.
        Ответить
    • Не совсем понял что такое «Гомоиконы», но я за автоген.
      Ответить
      • Гомоиконы -- это лисп. Когда у тебя исходный код и данные представлены одинаково и ты можешь обрабатывать код как данные и исполнять данные как код...
        Ответить
        • показать все, что скрытоГомоикона:
          .                     `.                                   .o-.
          .                    .`           ..```````````..`         . .  .
          .                  `.          ..`               ``.`     .   `.  `
          .                 ..        `.`                     `.`        `.
          .                `.        ..                         `.        `.
          .                -        .`                `..``````..`-        .`
          .               .`       ..                .`          `--        -
          .   Procedure,  -        -                -`             /`       -   Expression,
          .               -        -      Eval     .`    Apply     `-       .`
          .   Arguments   -        :               -                -       .`  Environment
          .               -        /`             .`               -`       -
          .               .`       .:.`         `.`               `-        -
          .                -        ..`.```````.``               `-        .`
          .                `.        .. ```````                 `.        `.
          .                 `.   .     ..                     `.`        `.
          .               `. `.  .       ...               `..`         `.
          .                  . . .          `..``.`.`.``...`          `.`
          .                    `.                                    `.
          Ответить
        • лучше всего исполнять стек как код
          Ответить
          • А ты второй версии или третьей? И ня чём тебя треняровали?
            Ответить
          • Fortщ?
            Ответить
          • >лучше всего исполнять стек как код
            программисты на сишечке так часто делали раньше, и не всегда по собственной воле
            Ответить
            • по моей воле, разумеется
              Ответить
            • Крестовики так и делают, когда используют лямбды.
              Ответить
              • Почему? Лямбды в C++ — это просто сахар няд обычным объектом с перегруженным методом operator(). Никаких особых техник исполнения там нят.
                Ответить
                • Он про gcc'шное расширение для няшной спутал, видимо.
                  Ответить
              • Лямбда "выглядит" как данные, но по сути это обычный код.
                Обычно все (кроме скриптушков) про них так и думают: код.

                Имхо: чтобы исполнять данные как код, нужно ебаца с защитой страниц, и вероятно еще обманывать спекулятивное выполнение, и префетчер, и вряд-ли бы такую важную фичу запилили бы таким грязным неоптимальным хаком
                Ответить
                • Технически лямбда -- это как раз данные. Захваченный контекст + указатель на заранее приготовленный конпелятором код.

                  Если бы она была кодом, то её пришлось бы генерить в rwx страничке. Ну собственно как gcc делает для "лямбд" в няшной, потому что там надо совместимость с обычной функцией.
                  Ответить
                  • А я думал это код, в который вшит указатель на данные
                    Сама лямбда -- код, а её контекст может и данные

                    Ну так знаете обычная процедура тоже код, а её стек -- данные
                    Ответить
                    • Как ты вошьешь данные в код без rwx?

                      Указатель на код в структурку с данными гораздо проще засунуть )))
                      Ответить
                      • ды напрямую, не?

                        MOV RAX, 12345

                        где 12345 это адрес какого-то говна с контекстом (забудем пока про PIC)
                        Ответить
                        • Дык у тебя 100500 инстансов лямбды может быть...

                          Под каждую генерить такой thunk с mov'ом? Ну gcc'шное расширение так и делает, потому что выхода нет.
                          Ответить
                          • всё, дошло

                            Код один, данные разные.

                            Логично один раз сгенерить код, а потом кортежи
                            (данные1, указатель-на-код) , (данныеN, указатель-на-код) положить в секцию данных

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

                                Блин, захваты контекста такая неоднозначная тема, конечно: в большинстве мейнстрима оно происходит неявно (вот как раз хорошо, что в крестах явно)

                                Обратился к переменной, и сам не заметил.

                                А потом блуждаешь среди ссылок когда память потекла
                                Ответить
                                • Да, должна быть гарантия.

                                  В крестах кстати тоже есть автозахват. Пишешь [&] или [=] и конпелятор сам разберётся что в контекст затащить.
                                  Ответить
                                  • да, но ты можешь явно попросить этово НЕ делать: []


                                    Очень часто мне кложить ничего не нужно, мне нужно просто функцию чтобы сортирнуть чото или памнуть

                                    Мало того, что с пустыми скобочками это чище, так теперь вот я узнал, что это еще и дешевле)
                                    Ответить
                                • > гарантирует

                                  Короче почитала спеку, там немного хитрее.

                                  Лямбда это всегда класс, но в captureless случае у нее есть оператор каста в сишную функцию. Поэтому она так интуитивно и работает.
                                  Ответить
                                  • И типа ты ождиаешь функцию, компилятор видит, что туда передают объект, который неявно кастится в функцию, и кастит его?

                                    выходит, я могу и свой класс так сделать, не () у него реализовывать, а каст в функцию?
                                    Ответить
                    • Ну вот как Полина выше пишет -- представь, что это обычный класс с оператором скобки.

                      Т.е. можно даже указатель на код туда не ложить если полиморфизм не нужен.
                      Ответить
        • >Гомоиконы -- это лисп
          или XLST
          или TCL

          Много есть могучих языков
          Ответить
          • Или «Nim», но я не уверен, что там такое есть. Зато на «Nim» написали клон «Vim»!
            Ответить
          • XSLT -- да, наверное. Данные и код выглядят одинаково хуёво...

            А TCL подошёл из-за того, что и код и данные -- просто строки, которые он даже не парсит до запуска команды? Ну хотя там же квадратные скобочки есть, у которых нет аналога в виде данных... Про TCL я сомневаюсь, в общем.
            Ответить
            • >хуёво
              ой,ну ладно! а где у вас еще прямо в языке есть средства для навигации по дереву типа XQuery и XPath?:)

              Вы просто ненавидите XML!

              >а больше он ничего и не умеет
              ну да, там вроде еще массивы есть, но можно и без них.
              Ответить
              • > массивы

                А их вроде нет в синтаксисе, это просто команда?

                Ну вот [ ] как данные трактовать не получится, походу (они выполнятся). Только самому закавычить в { } и парсить как строку. Так что не чистая гомоиконщина получается.
                Ответить
                • >А их вроде нет в синтаксисе, это просто команда?
                  скобочки же круглые?

                  >Ну вот [ ] как данные трактовать не получится,
                  ну да, это как $() или ` `

                  Тем не менее, везде TCL туда причисляют
                  Ответить
      • сварщик что ли?
        Ответить
      • Машинный код гомоиконен

        Любой код это просто данные
        Почти любые данные это код (ну ладно, иногда это инвалид опкод)

        Если ты в реальном режиме, то вообще всяческие разделения на код и данные для тебя чисто абстрактные

        -----

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

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

            Хотя это к интерпретаторам относится уже.
            Ответить
        • > Главный враг гомокион это гарвардская архитектура

          Емнип, была упоротая либа для эзернета для AVR, которая генерила код для отправки пакета на флешку, а потом исполняла его. При гарвардской архитектуре, да. По-другому этот проц просто не успевал.

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

              Ну такое... По идее любой указатель на стеке -- это потенциальный эксплойт при переполнении буфера. Не только адрес возврата.

              Да и с другими данными, возможно, что-то могут сделать. Хоть это и будет сложнее.

              З.Ы. Лучше какую-то аппаратную поддержку рейнджей прикрутить с двойными регистрами, чтобы буфера вообще не переполнялись. Интел пытался, кстати. Но получилась хуйня и её закопали.
              Ответить
              • > З.Ы. Лучше какую-то аппаратную поддержку рейнджей прикрутить с двойными регистрами, чтобы буфера вообще не переполнялись. Интел пытался, кстати. Но получилась хуйня и её закопали.

                Ну так такую хуйню в Эльбрусах как раз реализовали, можешь посмотреть например тут
                https://cyberleninka.ru/article/n/zaschischennoe-ispolnenie-programm-na-baze-apparatnoy-i-sistemnoy-podderzhki-arhitektury-elbrus/pdf


                > Разница между дескрипторами и обычными указателями заключается в том, что помимо адреса объекта в дескрипторе сохраняется некоторая дополнительная информация, существенная для защиты. Состав этой дополнительной информации зависит от типа объекта, на который ссылается дескриптор

                > Дескриптор массива кроме адреса начала массива содержит размер массива, и смещение относительно начала массива, соответствующее текущему значению указателя (рис. 1).
                Ответить
                • >Разница между дескрипторами и обычными указателями заключается в том, что помимо адреса объекта в дескрипторе сохраняется некоторая дополнительная информация, существенная для защиты. Состав этой дополнительной информации зависит от типа объекта, на который ссылается дескриптор



                  прямо как у виндовых объектов))

                  но было бы круто, если бы это было хардварно, и да: с размерами массива
                  Ответить
                  • Да, это можно сделать хардварно, и в Эльбрусах так сделали. Но лично я считаю эту идею хуйней т.к. даже хардварно это какие-то дополнительные расходы (дополнительная логика в процессоре, расход электричества и времени на эти говнопроверки).

                    А, ну еще есть https://en.wikipedia.org/wiki/Intel_MPX только вот софтварная проверка границ не оказывается более медленной:

                    > Even though Intel MPX is a specially designed hardware-assisted approach with its own added set of hardware registers, it is not faster than any of the software-based approaches. New Intel MPX instructions can cause up to 4× slowdown in the worst case, although compiler optimizations amortize it and lead to runtime overheads of ~50% on average.
                    Ответить
              • Вот еще https://habr.com/ru/post/214377/

                > После провала 432-ой машины весь мир бросил заниматься аппаратной поддержкой языков высокого уровня. Об этом забыли в районе 84-го года и до сих пор снова не вспомнили. Все считают, что это красивая идея, однако не практичная. Считают, несмотря на то, что у нас была полная реализация этой идеи, причём реализация эффективная.

                > Но к чему в итоге пришли языки высокого уровня? Все нынешние языки высокого уровня на две группы можно поделить: в одну входят языки типа Java, в другую – типа C. Java – это строгий контроль типов, но очень неэффективный. И динамики там нет. Никто даже не думает на Java операционную систему писать. По моим нормам, это не универсальный язык высокого уровня. Другая группа – С-подобные языки. Общее мнение таково, что это не языки высокого уровня. Скорее, вариант ассемблера.

                > Получается, что никто в мире, кроме наших ребят-эльбрусовцев не знает действительных преимуществ языков высокого уровня. И это просто ужасно!

                > Теперь, что же нужно для того, чтобы получить эти преимущества? Очень немного. Это легко сделать.

                > Прежде всего, в языках и аппаратуре вводится два типа дескрипторов – пространственные и временнЫе. Пространственный дескриптор – это, фактически, указатель на данные. Он может, например, содержать размер массива и адрес начала массива. Типичные операции с таким дескриптором – индексирование (доступ к конкретному элементу массива) и взятие подмассива. Всего существует очень небольшое количество операций над указателями на данные.

                > И важным моментом здесь является то, что базовый адрес для операций чтения и записи памяти может браться только с таких указателей. С их помощью аппаратура осуществляет контроль выхода за границу массива. О том, что ещё дают пространственные дескрипторы, я скажу позже, когда буду говорить о «контекстной защите».
                Ответить
                • ну блин, люди даже с хардварным переключением контекста обосрались, что уж о другом говорить

                  >аппаратной поддержкой языков высокого уровня. О
                  место для шутки про поддержку плавпитуха из JS в арме
                  Ответить
              • гдето в XMM есть поддержка clamped integers
                Ответить

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