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

    +6

    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
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    #include "stdafx.h"
    #include "hackery.h"
    #include <cstdint>
    #include <windows.h>
    
    
    /*XXX*/
    unsigned char udiv128Data[] =
    {
      0x48, 0x89, 0xD0, // mov rax,rdx
      0x48, 0x89, 0xCA, // mov rdx,rcx
      0x49, 0xF7, 0xF0, // div r8
      0x49, 0x89, 0x11, // mov [r9],rdx
      0xC3              // ret
    };
    
    /*XXX*/
    unsigned char sdiv128Data[] =
    {
      0x48, 0x89, 0xD0, // mov rax,rdx
      0x48, 0x89, 0xCA, // mov rdx,rcx
      0x49, 0xF7, 0xF8, // idiv r8
      0x49, 0x89, 0x11, // mov [r9],rdx
      0xC3              // ret
    };
    
    unsigned __int64(__fastcall *udiv128)(unsigned __int64 numhi, unsigned __int64 numlo, unsigned __int64 den, unsigned __int64* rem);
    
    __int64(__fastcall *sdiv128)(__int64 numhi, __int64 numlo, __int64 den, __int64* rem);
    
    namespace {
    struct Q {
      Q() {
        /*XXX*/
        udiv128 = reinterpret_cast<unsigned __int64(__fastcall *)(unsigned __int64, unsigned __int64, unsigned __int64, unsigned __int64*)>(&udiv128Data[0]);
        /*XXX*/
        sdiv128 = reinterpret_cast<__int64(__fastcall *)(__int64, __int64, __int64, __int64*)>(&sdiv128Data[0]);
        /*XXX*/
        DWORD dummy;
        /*XXX*/
        VirtualProtect(udiv128Data, sizeof(udiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
        /*XXX*/
        VirtualProtect(sdiv128Data, sizeof(sdiv128Data), PAGE_EXECUTE_READWRITE, &dummy);
      }
    } q;
    }

    Вот такая вот загогулина получилась по причине того что в 64-х битной Вижуал Студии нет встроенного асма, нет интринсика для простой асмовой команды, но мне вот надо именно полное деление 128-битного числа на 64-х битное именно с остатком и мне совершенно фиолетово на то что Майкрософт думает на тему вредности команды DIV и заменимости деления умножением на обратное. Да, и мне влом усложнять структуру проекта и линковать какие-то символы с внешнего асма.

    Запостил: MinorThreat, 12 Ноября 2015

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

    • Вспоминается старый добрый Турбо Паскаль с его оператором inline.

      А ещё вот это (не говно, просто старьё): http://govnokod.ru/3694
      Ответить
      • Помнится на Спектруме был такой Тетрис. Мы очень удивились что он написан на Бейсике а не на асме. Но мы никак не могли понять 80% логики работы программы и почему в комментариях такие встречаются странные кракозяблы. Оказывается на Спектруме можно было вставлять машинный код прямо в бейсиковские комментарии и делать туда Call, поскольку листинги на бейсике грузились на фиксированный адрес.
        Ответить
    • > заменимости деления умножением на обратное

      Кстати, а как обратить целое число?
      Ответить
      • Есть трюк связанный с поиском такого волшебного числа magic(x) при умножении на которое в младшем 64-х битном слове окажется результат деления на x. Весь мусор от этой магии уезжает в старшее 64-х битное слово и отбрасывается. Естественно, таких образом можно заменить деление на константу. Мне же нужно делить на переменную. Ну и в устаревших допотопных никому не нужных 32-х битных системах речь идет о 32-х битовых словах, соответственно.
        Ответить
    • создать отдельный объектник для асма и слинковаться с ним - не, не слышали
      кстати, антивирус в теории не может это дело запалить?
      Ответить
      • В данный момент пишу в стол. Если возникнет желание отправить этот шедевр в опенсорц, придется наверное портировать на гцц и разобраться с nasm-ом. Хотя наверное в гыцаце и интринсик подходящий есть.
        Ответить
        • В gcc есть тип __int128
          Ответить
          • Мне нужно еще остаток сохранять. div это умеет делать. С и C++ - нет. Только интринсик. ЧСХ, интринсик для i64*i64->i128 у них есть. Интринсика для div у них нет из-за идеологических соображений.
            Ответить
        • > разобраться с nasm-ом
          Ну вот с насмом разбираться, имхо, намного быстрее, чем байткоды руками хуярить...
          section .text
          
          bits 64
          
          global udiv128
          udiv128:
              mov rax, rdx
              mov rdx, rcx
              div r8
              mov [r9],rdx
              ret
          Всё! Ну разве что ещё прототипчик сишный описать и в систему сборки этот асм добавить.

          И да, насм отлично линкуется и с вижуалкиным кодом и с гццшным (см. опцию -f). Так что можно ничего не портировать.
          Ответить
          • Жалко, что нет транзитивности: из того, что nasm линкуется с gcc И nasm линкуется с MSVC, не следует, что gcc линкуется с MSVC.

            Нельзя просто так взять и слинковать вижуалкин код с гэцэцэшным, потому что у формата COFF более 9000 модификаций.
            Ответить
            • Там ещё соглашения о вызовах разные. Слава богу, что в 64битке их осталось всего 2.
              Ответить
              • Уже появился третий - __vectorcall который использует SIMD регистры.

                https://msdn.microsoft.com/ru-ru/library/dn375768.aspx
                Ответить
            • >из того, что nasm линкуется с gcc И nasm линкуется с MSVC, не следует, что gcc линкуется с MSVC.
              Конвертер есть http://www.agner.org/optimize/#objconv
              Ответить
    • И где тут говнокод? Говнокод это сам вижуал и M$. Но за неимением другого приходится вертеться, аффтар, зачОтЪ!
      Ответить
    • Изящно, изящно! Однако вызов функции всю производительность испортит.
      Ответить
      • А за изменение атрибутов страницы на лету можно ещё и схлопотать ворнинг от антивируса... Может быть... Я не проверял...
        Ответить
        • Страницы. Т.е. ещё куча переменных, лежащих рядом, стали исполняемыми... Oh, exploitable...
          Ответить
          • The region of affected pages includes all pages containing one or more bytes in the range from the lpAddress parameter to (lpAddress+dwSize). This means that a 2-byte range straddling a page boundary causes the protection attributes of both pages to be changed.

            Луц частями не продаётся. Один заряд — десять чатлов.
            Ответить
          • Я ещё подумал о том, что можно добить до четырёх килобайт инструкциями типа INT 3, чтобы безопасность не пострадала. Но на 64-битной машине страница может быть гигантской...
            Ответить
            • > может быть гигантской
              Да вроде операционки не юзают большие страницы кроме редких случаев (когда мапают физическую память целиком и когда явно попросили большую страницу).

              Ну а в общем случае - да. Гигабайтные странички рулят. Жаль, что мало где поддерживаются.
              Ответить
      • Тут x64 __fastcall же. Параметры через регистры.
        Ответить
        • call/return в любом случае разбивает конвейер. Инлайн работал бы чуть быстрее.
          Ответить
          • По моему DIV это настолько тормозная инструкция что call/ret на фоне ее меркнет. Пдф-ы от интел курил очень давно и по моему там было больше десятка тактов. Возможно - больше тридцати.
            Ответить
          • Емнип, у свежих процов есть буфер под предсказание одного (или даже нескольких?) адресов возврата. Так что не разбивает оно конвейер.
            Ответить
    • Не понял, а в Студии разве нету встроенного типа double_native_int?
      Если в компиляторе нет встроенных типов на всё, что поддерживает процессор, то это говно.

      Ой, кажется я обосрал все компиляторы, потому что типы для векторных команд ни одна сволочь вшить не захотела.
      Ответить
      • Есть тип __m128i, но работать с ним можно только интринсиками для SSE2. Еще он должен быть выровнен в памяти по адресу кратному 16 байтам.
        Ответить
    • Можно _emit использовать https://msdn.microsoft.com/en-us/library/1b80826t.aspx

      Вот кстати с хабра http://habrahabr.ru/company/itseez/blog/210832/ :
      >Подход с отдельной компиляцией ассемблера нас совсем не устраивал, потому как требовались значительные модификации в билдовой инфраструктуре. Важно ещё то, что компилятор отказывается встраивать ассемблерные функции и проводить какие-либо оптимизации их вызова. Решить проблему удалось только применением ключевого слова __emit. Оно позволяет вставить в любом блоке C или C++ кода произвольный кусок машинного кода. Зная соглашения о вызовах и опкоды инструкций, можно составить любой недостающий интринсик самостоятельно.
      Ответить
      • Вместе со встроенным асмом из 64-битной вижуалки убрали еще и emit.
        Ответить
        • https://msdn.microsoft.com/en-us/library/50bewfwa.aspx вот так еще можно попробовать. Делать через VirtualProtect это уберкостылизм
          Ответить
          • Короче, собрал я статическую .lib при помощи консольного MASM-a, добавил зависимость, поменял эти указатели на extern "C" функции и выкинул говнокод. Этот пост сподвиг меня наконец привести этот кусок в порядок. Спасибо всем участникам.
            Ответить
        • __declspec(allocate("segname")) declarator
          https://msdn.microsoft.com/en-us/library/5bkb2w6t.aspx
          Ответить
        • А почему убрали асм, кстати?
          Ответить
          • Да поди лень было интегрировать его с оптимизатором.
            Ответить
            • ну вообще я согласен с тем что асм наверное правильнее писать в .asm файле, собирать либу и линковать потом статически, все таки это другой язык
              Ответить
              • Ну у инлайн асма всё-таки были преимущества в high performance коде... А теперь придётся эти куски писать полностью на асме. Вместе с циклами и т.п.
                Ответить
                • Скажи мне как художник художнику, часто ли ты фиксишь боттлнеки в своих программах переписывая за компилятором на асме что-то вручную?
                  Ответить
                  • Ну, если честно, то один раз фиксил, и то от лени и для project euler :)

                    Всё оставшееся время asm юзал только ради всякой системщины, которая из C невозможна...
                    Ответить
    • --Мои вкусы весьма специфичны
      --Поведай мне о них
      --Я иногда люблю писать на машинных кодах под не самую простую архитектуру, где даже размер опкода не фиксирован
      --Грязный ублюдок

      Вот за что я обожаю крестовиков, так это за умение насрать на абстракцию. В одной руке у них там абстрактные фабрики и визиторы, а другой они вхуяривают машинный код и ставят странице флажок на запуск.
      Треш же!
      Ответить
      • Ну дык это фича... Можно писать dll'ку, которая грузится из MBR и дёргает функции bios для чтения диска, но при этом использовать в ней shared_ptr и прочие няшности... Попробуй это сделать на чём-то другом...

        > умение насрать на абстракцию
        Дык главное хорошенько инкапсулировать все эти хаки. Так что это не умение насрать на абстракцию, а как раз таки наоборот - запилить годную абстракцию на основе низкоуровневого пиздеца.
        Ответить
        • Бор, ну как .dllка будет грузится из MBR когда у нее там заголовков-то 100500 (Portable Executable жы), да еще и для виндового загрузчика?
          А В MBR 512 байт минус таблица разделов, так что и того меньше.
          Ответить
          • Дллку линкуешь с фиксированной базой и выравниванием секций меньше страницы (у неё тогда образ на диске и образ в памяти будут одинаковые). PE'шный entry point ищется в десяток строк асма. Переход в защищённый режим - секция на асме внутри dll'ки. Вот и все дела.
            Ответить
            • А чтобы перейти в защищенку, нужно же всякие говнотаблички заполнить: GDT, вот это все.

              Наврядли все это удобно делать из одного блока) Обычно же в MBR тупой код который просто грузит с диска (через BIOSовый, простигосподи, LBA интерфейс, прямо через прерывание) остальные несколько блоков и запускает загрузчик оттуда.
              Ответить
              • GDT для flat режима - банальнейшая статика на 24 байта (три дескриптора - null, code, data). А без IDT вполне можно прожить до main'а. И я же написал - в защищённый режим переходит не MBR, а асмовая секция внутри dll'ки (да, entry point тогда получается 16-битный).

                От MBR требуется только прочитать сектора с диска, найти entry и передать управление. Это в него влезет с запасом. В общем-то туда и flat GDT с переходом влезли бы...
                Ответить
                • >>От MBR требуется только прочитать сектора с диска, найти entry и передать управление.

                  Так ведь все операцыонки на PC (ну кроме UEFI) так именно и делают.

                  Ну в общем я генеральную мыслю понял: можно пользоваться писёй без оси. Я правда совершенно не представляю зачем такое может понадобиться.
                  Ответить
              • Кстати, к слову, UEFI firmware (по крайней мере на моей материнке) переходит в защищённый режим где-то на десятой команде.
                Ответить
                • ты дизассмеблировал фирмварю своей материнки?
                  Ответить
                  • Да, грешен я. Там прикольная структура - PE'шный бинарник, прижатый к концу флешки. А в последних его 16 байтах (как раз там, куда передаётся управление при старте проца) - jmp на переход в защищённый режим.

                    Так что народ и без памяти (контроллер памяти включится чуть позже, в следующем модуле) юзает PE'шки... А ты про dll'ку из MBR не веришь...
                    Ответить
                    • Наверняка ведь хотел чтобы после POSTа вместо логотипа материнки появлялось твое фото.

                      Больше в фирмварю лазить не за чем.
                      Ответить
                      • Не, хотел core boot на свою материнку портануть...

                        Эх, времени и сил не хватает, чтобы эмулятор SPI флешки на STM32 допаять/дописать... А без него совсем не айс.
                        Ответить
                        • а я.. а у меня... а я помню времена когда стирать микросхему можно было светом, а заливать биос через программатор) Мне конечно в голову не приходило там ничего писать, хотя биос в те времена был всяко проще уефи
                          Ответить
                          • > через программатор
                            Да через программатор и сейчас можно. Даже проще, наверное. Потому что SPI флешки восьминогие и более-менее стандартные.

                            Но толком не поотлаживаешься... Чипсет же ещё не настроен, емнип, в этом состоянии даже до pci карточек не добраться...
                            Ответить
                            • PCI мост требует включения и настройки конечно же, и наверное это делает биос.

                              Правда он делает это очень рано, потому что во время POSTа он пишет в IO 80h, и там его слышит POST карта на PCI.

                              А вот как ДО этого дебажиться я даже не знаю. JTAG какой-нить?
                              Ответить
                              • > как ДО этого дебажиться
                                Ну вот я и думал дебажиться через эмулятор SPI флешки. Прочитали определённый байт - единичка, прочитали другой - нолик... Читать флешку там всё-таки можно :3

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

                        В core boot, емнип, ради этой инициализации сделали подпиленную версию gcc, которая компилит код, который может работать без памяти, на одних регистрах и r/o флешке.
                        Ответить
                        • ))было когда-то ключевое слово "register":)

                          По идее всё что товрица в материнке это личное дело вендора. Для программиста существует UEFi/BIOS API и ACPI. Так что мне вообще странно что core boot существует. И много материнок он умеет?

                          Так а как включение контроллера происходит физически? Надо в IO порт писать или по определенному адресу?
                          Ответить
                          • > Надо в IO порт писать или по определенному адресу?
                            Надо много писать и читать в порты/из них. Там же ещё сначала надо SPD у планок памяти выпытать... А вот memory mapped эти порты или нет - я х.з. В общем-то большой разницы нет.

                            > Так что мне вообще странно что core boot существует
                            Ну гугл на хромбуках юзает... Кстати, хромбуковские процы/чипсеты - считай единственные из свежих интеловских, где корбут работает. На амд дела получше обстоят.

                            > Для программиста существует UEFi/BIOS API и ACPI.
                            А для прикладника - так вообще только юзермод :)
                            Ответить
                            • >>Там же ещё сначала надо SPD у планок памяти выпытать

                              И контроллер на частоту настроить и на всякие латенси, да. Причем пользователь же может свои значения указать в CMOS.

                              Страшно подумать сколько всего происходит в BIOS

                              >> А для прикладника - так вообще только юзермод :)

                              да, пошел я плагин к jQuery писатьЖ)
                              Ответить
                              • > пользователь же может свои значения указать
                                Имхо, можно какие-нибудь безопасные значения поставить, скорость же не важна на этом этапе. А потом уже подкрутить на заданные юзером, когда окружение дружелюбнее станет.
                                Ответить
                                • а разве можно на лету поменять частоту памяти и задержки?

                                  ну а самые дружелюбные это, конечно, spd. Правда я видел говнопамять которая не работала на своих заявленных, пришлось cas латенси снижать, но это бракованый экземпляр
                                  Ответить
                                  • > на лету поменять частоту памяти и задержки
                                    Вай нот? Главное память в это время не юзать ;) Ну и не факт, что данные в ней выживут.
                                    Ответить
                                    • >> Ну и не факт, что данные в ней выживут.

                                      Вот, да. Пока контроллер будет её менять у нее заряд утечет. Её же рефрешить нужно, болезную. Этим же контроллер занимается? Или там прямо на DRAMе чип?

                                      Хотя когда ты BIOS и единственная программа на компе то можно конечно и такое провернуть.
                                      Ответить
                                      • > когда ты BIOS
                                        Ну. Код никуда не денется, он на флешке. А данные можно и в кеше проца подержать, если не жадничать.

                                        > Или там прямо на DRAMе чип
                                        Емнип, и так и так. Во сне сама рефрешится. А в рабочее время контроллер должен кидать команду.
                                        Ответить
                                        • Ну да, когда S3 то контроллер спит по идее, только память питается.

                                          Во время работы она видимо сама не рефрешится чтобы не конфликтовать с синхронными запросами контроллера, она же SDRAM лет 18 как.

                                          Так вот и получается что если контроллер будет менять частоту то велик шанс продалбывания содержимого. Круто писать код зная что у тебя вся память или r/o или регистры:)
                                          Ответить
                                          • > велик шанс продалбывания содержимого
                                            Кстати, можно же загнать память в self refresh, как перед сном, и спокойно настраивать контроллер...
                                            Ответить
      • Майкрософт тоже бывают грешны. Конверсия из void(*)(struct RECT*) -> void(*)(CRect&) у них это обычное дело. Размеры и оффсеты полей данных в классе же совпадают с CRect? Совпадают. Ссылка на бинарном уровне это тот же указатель. Значит сигнатуры идентичны, йопта. И пох что с точки зрения стандарта жизнь объекта начинается после завершения работы конструктора. Привести HWND к указателю на функцию по причине того что сверху предусмотрительно сорван стек и HWND это уже на самом деле не HWND, а указатель на функцию-обработчик оконных сообщений тоже обычное дело.
        Ответить
      • то, что у тебя в коробке инструментов рядом с отверткой и пасатижами лежит скальпель, не значит, что тебе надо делать монстра франкенштейна
        Ответить
    • Кстати, нашел в C++11 няшную функцию std::lldiv. Часть кейсов покрывает, но не все к сожалению.
      Ответить
    • Предтерминальные стадии байтоёбства - интринсикоёбство и его более тяжёлая форма - инлайн-ассемблероёбство. Подсадка начинается с убеждённости поциента в необходимости ручками использовать SIMD -инструкции.

      Зачем это все?
      Ответить
      • скорость и чсв. скорее чсв
        Ответить
      • Это не продакшен, а для души. Могу себе позволить.
        Ответить
        • У тебя душа байтослесаря.
          Ответить
        • Нет у тебя души, байтоложец

          А вообще всегда отвечай на такие вопросы - потому что могу!
          Ответить
      • чтобы выебываться

        типа все тут ламеры, а он один понимает как работает проц
        Ответить
    • Какой багор )))
      Ответить
    • mov rax,rdx можно заменить на push rdx; pop rax. В опкодах это будет "RX".
      mov rdx,rcx можно заменить на push rcx; pop rdx. В опкодах это будет "QZ".
      Для PUSH/POP REX-префиксы не требуются, поэтому удалось сэкономить два байта.

      Вот div и ret уже в ASCII-кодах не представить. IMUL вот в ASCII-кодах есть. И mov с косвенной адресацией придётся заменять на арифметику (SUB, XOR, AND).
      Ответить
      • Записал инструкцию mov [r9], rdx в ASCII-кодах. Правда, пришлось использовать символ перевода строки. Получилось "PAQRH%UUUUH%\"\"\"\"PZjUfZRTZL+\nZZI!AUI1QUAYX".
        \n — это символ с кодом 0x0A, \" — это экранированная кавычка.

        push rax                                                 ; 50           = P
        push r9                                                  ; 4151         = AQ
        
        push rdx                 ; <- сохраняем rdx              ; 52           = R
        and  rax, 0x55555555                                     ; 482555555555 = H%UUUU
        and  rax, 0x22222222     ; обнуляем rax                  ; 482522222222 = H%""""
        push rax                                                 ; 50           = P
        pop  rdx                 ; обнуляем rdx                  ; 5A           = Z
        push byte 0x55                                           ; 6A55         = jU
        pop  dx                  ; в rdx теперь 0х55             ; 665A         = fZ
        push rdx                 ; в стек                        ; 52           = R
        push rsp                                                 ; 54           = T
        pop  rdx                 ; rdx теперь указывает на стек  ; 5A           = Z
        sub  r9, qword [rdx]     ; вычитаем 0x55 из r9           ; 4C2B0A       = L+ и перевод строки
        pop  rdx                 ; забираем 0x55 из стека        ; 5A           = Z
        pop  rdx                 ; -> восстанавливаем rdx        ; 5A           = Z
        and  qword[r9+0x55], rax ; обнуляем выходной параметр    ; 49214155     = I!AU
        xor  qword[r9+0x55], rdx ; записываем в результат        ; 49315155     = I1QU
        
        pop  r9                                                  ; 4159         = AY
        pop  rax                                                 ; 58           = X


        Открываю специальную олимпиаду по сокращению этого кода. Необходимое условие — машинные коды должны быть печатаемыми ASCII-символами (в качестве исключения разрешаю перевод строки).

        Нидлесс, есть идеи?
        Ответить
        • Зачем? Зачем?

          Ты же уже показал, что div и ret не выразить.
          Ответить
          • Для чистого безумия, разумеется!

            Конкретно в этой программе и вправду не нужно.
            Ответить
          • В RWX секции можно было бы попробовать, а так хз. Можно попробовать поискать на помойке в исполняемой памяти нужные инструкции, может среди мусора шо есть.

            Кстати, код петуха насрал мне в переменную мусором.
            Ответить
            • Какой багор )))
              Ответить
            • > среди мусора

              Сейчас везде ASLR :(
              Ответить
              • Какой багор )))
                Ответить
              • Можно посканить всю оперативку на предмет наличия RX регионов с нужными батами. В венде можно «VirtualQuery()» подрочить, в лине — няверное, /proc/self/maps.
                Ответить
                • А ну да, мы же в своём процессе код пишем.
                  Ответить
              • показать все, что скрытоvanished
                Ответить
                • Не очень быстро-то да, но так у нас тут и не соревнование Царей. А ret-то гарантированно найдётся — он в любой процедуре есть.

                  UPD: ну ладно, ладно, не в любой, в [[noreturn]] нет.
                  Ответить
                  • > в noreturn нет

                    Там ud2 какой-нибудь? Или просто UB и следующая функция начинается?
                    Ответить
                    • Проверил. «Gcc» не генерит ничего: https://gcc.godbolt.org/z/UuM_V2, даже если не указать [[noreturn]]. Если указать и попробовать возвратить — ругнётся ворнингом и сгенерирует пустую функцию.
                      Ответить
                      • > пустую функцию

                        Ну да, UB'ов в коде не бывает, значит ты ее не вызываешь.

                        Какая UBтимизация )))
                        Ответить
                      • В C17 (C11?) просто noreturn.

                        Я эту фичу юзал, когда интерпретатор ШК писал, хотя можно было бы и без неё, конеплятор не настолько тупой, чтобы за goto return вставлять.
                        Ответить
                  • noreturn — это же вроде такой хинт для оптимизатора, чтобы не ждать, что управление вернётся из функции. Т. е. он скорее для вызывающего кода, чтобы компилятор знал, что всё, что ниже места вызова, может быть unreachable. А на код самой функции этот атрибут может никак не повлиять.
                    Ответить

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