Сколько инструкций процессора использует компилятор?

    Месяц назад я попытался сосчитать, сколько разных инструкций поддерживается современными процессорами, и насчитал 945 в Ice Lake. Комментаторы затронули интересный вопрос: какая часть всего этого разнообразия реально используется компиляторами? Например, некто Pepijn de Vos в 2016 подсчитал, сколько разных инструкций задействовано в бинарниках у него в /usr/bin, и насчитал 411 — т.е. примерно треть всех инструкций x86_64, существовавших на тот момент, не использовались ни в одной из стандартных программ в его ОС. Другая любопытная его находка — что код для x86_64 на треть состоит из инструкций mov. (В общем-то известно, что одних инструкций mov достаточно, чтобы написать любую программу.)

    Я решил развить исследование de Vos, взяв в качестве «эталонного кода» компилятор LLVM/Clang. У него сразу несколько преимуществ перед содержимым /usr/bin неназванной версии неназванной ОС:

    1. С ним удобно работать: это один огромный бинарник, по размеру сопоставимый со всем содержимым /usr/bin среднестатистического линукса;
    2. Он позволяет сравнить разные ISA: на releases.llvm.org/download.html доступны официальные бинарники для x86, ARM, SPARC, MIPS и PowerPC;
    3. Он позволяет отследить исторические тренды: официальные бинарники доступны для всех релизов начиная с 2003;
    4. Наконец, в исследовании компиляторов логично использовать компилятор и в качестве подопытного объекта :-)

    Начну со статистики по мартовскому релизу LLVM 10.0:
    ISA Размер бинарника Размер секции .text Общее число инструкций Число разных инструкций
    AArch64   97 МБ 74 МБ 13,814,975 195
    ARMv7A 101 МБ 80 МБ 15,621,010 308
    i386 106 МБ 88 МБ 20,138,657 122
    PowerPC64LE 108 МБ 89 МБ 17,208,502 288
    SPARCv9 129 МБ 105 МБ 19,993,362 122
    x86_64 107 МБ 87 МБ 15,281,299 203
    В прошлом топике комментаторы упомянули, что самый компактный код у них получается для SPARC. Здесь же видим, что бинарник для AArch64 оказывается на треть меньше что по размеру, что по общему числу инструкций.

    А вот распределение по числу инструкций:


    Неожиданно, что код для SPARC на 11% состоит из nop-ов, заполняющих branch delay slots. Для i386 среди самых частых инструкций видим и int3, заполняющую промежутки между функциями, и nop, используемую для выравнивания циклов на строки кэша. Наблюдение de Vos о том, что код на треть состоит из mov, подтверждается на обоих вариантах x86; но даже и на load-store-архитектурах mov оказывается если не самой частой инструкцией, то второй после load.

    А как набор используемых инструкций менялся со временем?

    Единственная ISA, для которой в каждом релизе есть официальный бинарник — это i386:


    Серая линия, отложенная на правой оси — это число разных инструкций, использованных в компиляторе. Как мы видим, некоторое время назад компилятор компилировался гораздо разнообразнее. int3 стала использоваться для заполнения промежутков только с 2018; до этого использовались такие же nop, как и для выравнивания внутри функций. Здесь же видно, что выравнивание внутри функций стало использоваться с 2013; до этого nop-ов было гораздо меньше. Ещё интересно, что до 2016 mov-ы составляли почти половину компилятора.

    Самые первые версии LLVM, до появления clang, выпускались ещё и с бинарниками для SPARC. Потом поддержка SPARC утратила актуальность, и вновь она появилась лишь через 14 лет — с на порядок увеличившимся числом nop-ов:


    Исторически следующая ISA, для которой выпускались бинарники LLVM — это PowerPC: сначала для Mac OS X и затем, после десятилетнего перерыва, для RHEL. Как видно из графика, переход после этого перерыва к 64-битному варианту ISA сопровождался заменой большинства lwz на ld, и вдобавок удвоением разнообразия инструкций:


    В бинарниках для x86_64 и ARM частота использования разных инструкций почти не изменялась:




    При подсчёте инструкций ARM я отсекал суффиксы условий — вместе с ними получалось около тысячи разных инструкций, но даже и без них ARM сильно опережает другие ISA по разнообразию генерируемых инструкций. Таким образом, слой b на этом графике включает и все условные переходы тоже. Для остальных ISA, где условными могут быть только переходы и немногие другие инструкции, суффиксы условий при подсчёте не отсекались.

    Наконец, самая недавняя ISA, для которой публикуются официальные бинарники — это AArch64. Здесь интересно то, что orr с прошлого года почти перестала использоваться:


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

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 223

      +2
      В конце концов ничего удивительного. Компиляторы тоже пишут люди — зачем им влезать в дебри и разбираться с экзотическими командами, когда двух десятков хватает для всего.
        +14
        Удивительным может быть то, что с десяток лет назад экзотические инструкции генерировались, а сейчас уже не генерируются.
          +7

          Если вникнуть — есть острый недостаток в SSA формах и их проекциях (array SSA, mem SSA) для решения задач Data-Computational Locality Projection. Для этого приходится часть логики работы с mSSA/aSSA/tSSA выносить на фронт LLVM'a, и плодить mir/mlir'ы, как это было с тем же Rust'ом, и как теперь будет с СLang'ом ...


          Есть проблемы с типизацией в самих языках программирования — уже лет 10 изучаю этот вопрос, и пришёл в выводу что языки надо дизайнить что бы не создавали сложностей трансляции, правда по синтаксису получается лиспо-образное веселье с привкусом питонщины и эрланга.

            +2
            Не могли вы рассказать подробнее о проблемах типизации в ЯП?
              +13

              Если очень поверхностно, и если не вникать в логику (Constrainted Bunch/Separation logic и прочие расширения логики Хоара если точно), то получается что интерпретацию программ в автоматах Тьюринга надо рассматривать как "многоленточный" автомат — с командами переменной длинны и данными переменной длинны, когда длинна данных зависит от результата выполнения предыдущих команд, а длинна команд от позиций соседних лент.


              Так в языке


              1. Не должно быть макросов и прочей кодогенерации, т.к. вся информация о зависимых типах должна быть доступна на этапе компиляции. Вся рефлексия должна быть выполнима на этапе компиляции. Generic'и в топку...
              2. Не должно быть линковки в привычном её понимании — JIT/AOT должен уметь пересобрать с PGO любой отпрофилированный код и выбрать наилучшую реализацию по существующему профилю.
              3. Язык должен быть гомоиконным и строго типизированным (типизированные Lisp'ы 98го года и Shift, например), желательно что бы легко приводился к каноническим минимальным mSSA/aSSA формам без дополнительных трансформаций и свёртки.
              4. Во время компиляции должны быть доступны диапазоны всех принимаемых переменной значений, например http статус от 200 до 600 и т.п., что бы компилятор мог выбрать конкретную размерность типа в зависимости от архитектуры и использовать соответствующий набор команд. Наличие диапазонов очень упрощает обработку ошибок и освобождение/планировку соответствующих ресурсов (сокеты, файлы и прочее).
              5. Желательно использовать зависимые типы для формальной верификации и доказывать прувером отсутствие побочки на всех диапазонах принимаемых значений.
                +1
                Я так понимаю вы собираетесь в перспективе написать ЯП? Если это так, то было бы здорово услышать все размышления на эту тему
                  +3

                  Скорее сам ЯП уже давно написан и решаются сугубо политические и бумажные вопросы.

                  +1
                  Желательно использовать зависимые типы для формальной верификации и доказывать прувером отсутствие побочки на всех диапазонах принимаемых значений.

                  Интересно. Как вы это формализуете? Условно, какие query вы генерируете к пруверу?


                  Так-то обычно этим заморачиваются сами программисты, когда пишут сигнатуры функций, в которых нет условного IO, и отсутствие сайд-эффектов следует из well-typedness.


                  И я бы посмотрел на полноценные завтипы (а не как в ATS) для императивного языка (иначе к чему бы вспоминать про сепарационную логику).

                    +1
                    Как вы это формализуете?

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


                    query вы генерируете к пруверу?

                    Пруверы на решётках в общем… точка в решётке — диапазон значений строгий/не строгий или функция диапазона. Прувер должен доказывать что решётки не перескутся между собой и исследовать соответствующие зависимые функции диапазонов. Потом поверх этого есть ещё пару проходов полиэдральных трансформаций для реализации свёрток типов.


                    и отсутствие сайд-эффектов следует из well-typedness

                    Сама система типов реализует referential transparency т.к. есть transactional-SSA проекция с mem-SSA. Само блокировки/ожидания короч может расставить, выбрать где лучше скопировать, а где лучше указатель… и как потом Сompare-and-Swap делать etc. Много разных задач решается.


                    И я бы посмотрел на полноценные завтипы (а не как в ATS) для императивного языка

                    О них и речь… и это довольно комплексная проблема.

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

                      Ну это вполне формализуется в рамках обычного CoC, но как отсюда получается отсутствие эффектов (если мы эффектами одно и то же называем, конечно), мне сходу неочевидно.


                      О них и речь… и это довольно комплексная проблема.

                      Да, весьма интересно. Ждём статью (хоть здесь, хоть на условном arxiv).

                        0
                        но как отсюда получается отсутствие эффектов

                        Все эффекты лифтятся аргументами функции при вызовах… да и сам вызов функции рассматривается как "возникновение события" с timeout'ом и cancelation'ом — т.е. можно "отменить" каскадно при возникновении ошибок когда не совпадает range в runtime'e… под "побочкой" подразумевается гарантированная невозможность возникновения "непредвиденных" эффектов i.e. lifelock'ов / deadlock'ов / race condition'ов и т.д. и верификация именно во время компиляции. Можно представить как rust где не нужны Boxed Types, RefCells / Rc, Mutex'ы и ARC вообщe. Так как это решается системой типов.


                        Наличие диапазонов принимаемых значений также позволяет значительно упростить модель памяти и сделать её абсолютно детерминированной на этапе компиляции.


                        p.s. у меня есть HDL таргеты...

              +5
              Некоторые инструкции существовали для удобства программистов на ассемблере (типа rep cmpsw), потребность в них падает.
              С другой стороны, «обычные», часто используемые инструкции оптимизируются производителями процессоров так, что можно оставаться на обычных и отказаться от экзотических без потери скорости (типа leave/enter для пролога/эпилога функций)
                +3
                Тольво вы путаете «можно» и «нужно». Гляньте в табличку. Если LEAVE просто тормозит (3 тика на Ryzen для того, что она делает — это много), то ENTER сегодня — это «гроб с музыкой»: 16 тактов, за это время можно штук 20-30 обычных MOV исполнить!

                Исселование, подобное тому, что делает автор разработчики процессоров тоже делают… и в результате на современных процессорах разные экзотические инструкции не то, что бессмысленно использовать — их вредно использовать! Даже руками на ассмеблере! Скорость будет никакая!
                +3
                может модуль предсказания ветвления не дружит с экзотикой, вот больше их и не используют?
                  +3
                  Там не всё так просто. Поскольку компиляторы «экзотику» не используют, то на неё «подзабили»: инструкции есть, но поддерживаются «на отвяжись».

                  Одно время «забили» даже на MOVS и разработчики упражнялись в подборе оптимального набора SSE инструкций. А в Ivy Bridge «случилось чудо»: внезапно старая добрая MOVSB была оптимизирована и стала работать быстрее любых SSE (для больших объёмов: 256 байт и больше). Интересно — умеет это использовать LLVM или нет…
                    0
                    странно. По Вашей ссылке p11-55^
                    For copy length that are smaller than a few hundred bytes, REP MOVSB approach is slower than using 128-bit SIMD technique described in Section 11.16.3.1
                      +1
                      А моё замечание в скобочках вы проигнорировали?
                        0
                        наверно, я просто неправильно понял «few hundred bytes»- мне казалось, что «несколько сотен»- это больше, чем три сотни (256), а для больших объемов- вроде и так понятно, что раз оно не ложится в один запрос к памяти- то как ни оптимизируй эту инструкцию, скорость ее все равно упрется в ПСП, которой все равно, как оно там в процессоре реализовано, хоть через SSE, хоть через IA-32.
                        На числодробилке я не вижу особой разницы между movdqa, movapd, movupd и movsd, когда надо больше, чем «few hundred bytes» прожевать: все выдают ~12GB/s.
                      0
                      а я помню, что movsb быстро работала уже в sandy bridge
                        0
                        Она там работала гораздо быстрее, чем у AMD, но медленнее, чем SSE-мувы.
                          0
                          не в курсе, как сейчас с этим у рязани?
                            –1
                            Нет, не в курсе. Но это не так важно: процессоры, которые такое умеют должны выставлять специальный CPUID бит.

                            Так что бенчмарки при запуске программы устраивать не нужно. Либо Ryzen так умеет и выставляет бит, либо не умеет и тогда нужно использовать SSE.
                              0
                              что умеет? movsb была еще в 8086.
                                +2
                                Есть такой флаг в CPUID:
                                Enhanced REP MOVSB/STOSB
                                  –1
                                  «Умеет в MOVSB, который быстрее SSE».
                                  +1
                                  Либо Ryzen так умеет и выставляет бит, либо не умеет и тогда нужно использовать SSE.

                                  … либо это описано в ERRATA

                        +1

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

                          +2
                          И пополнить багтрекер разработчиков компилятора, так как чето меня терзают сомнения что все эти оптимизации под конкретные процы хорошо реализованы.
                            0

                            Обычно так и собирается под конкретный проц при деплое на железные сервера или в облако, т.к. проц уже заранее известен. Это для десктопных приложений, или проприетарных бинарников с закрытым кодом генерится общий код, а-ля Generic x86-64 с SSE2.

                              –1
                              Обычно так и собирается под конкретный проц при деплое на железные сервера или в облако, т.к. проц уже заранее известен.
                              В облаке проц далеко не всегда известен. Именно поэтому godbolt.org не рекомендует использовать -march=native, например.
                                0
                                А как обвязка зависимостями сторонними зависимостями на этот подход влияет? Одно дело скомпилить один пакет, а другое дело скомпилить все древо заисимостей, что вообще не всегда возможно, а когда возможно то может занять пару/тройку десятков часов чистого времени.
                                  +1

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

                                    0

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

                              0
                              Было бы интересно сравнить подходы с:

                              «A Superscalar Out-of-Order x86 Soft Processor for FPGA» Henry Wong, Ph.D. Thesis, University of Toronto, 2017

                              We have shown in this thesis the design of an out-of-order soft processor
                              that achieves double the singlethreaded (wall-clock) performance of
                              a performance-tuned Nios II/f (2.2x on SPECint2000) at a cost of 6.5 times the area of
                              the same processor. This area is about 1.5% of the largest Altera (Stratix 10) FPGA.

                              We presented a methodology for simulating and verifying the microarchitecture of
                              our processor, which we used to design a microarchitecture that is sufficiently complete and correct
                              to boot most unmodified 32-bit x86 operating systems. We showed that the FPGA substrate differences
                              from custom CMOS do affect processor microarchitecture design choices, such as our use of a physical
                              register file organization, low-associativity caches and TLBs, and a relatively large TLB. The
                              resulting microarchitecture did not require major microarchitectural compromises to fit an FPGA
                              substrate, and remains a fairly conventional design. As a result, the per-clock performance of our
                              microarchitecture compares favourably to commercial x86 processors of similar design. Our two-issue
                              design has slightly higher perclock performance than the three-issue out-of-order Pentium Pro (1995)
                              and slightly less than the newer two-issue out-of-order Atom Silvermont (2013).

                              +13

                              Как раз затем что разработчики компиляторов должны использовать все что только можно чтоб ускорить полученный код.

                                +5
                                Тогда придется перекомпилировать код под каждый процессор. Вы не забывайте, что в процессорах тоже бывают ошибки.
                                  –3
                                  Потому и стали так популярны виртуальные среды исполнения, т.к. на лету могут давать более оптимальный код для конкретного современного процессора, по сравнению с заранее компилированным универсальным вариантом.
                                    +3
                                    Могут… но не дают. C++ всё ещё быстрее — даже без использования экзотики.

                                    Другое дело, что когда выбором заведуют маркетологи реальные цифры никого не волнуют, главное — красивые лозунги.
                                      +1
                                      Интеловский компилятор может делать бинарь, который загрузившись, смотрит, какие расширения поддерживаются процессором, и загружает наиболее оптимальный вариант.
                                        +3
                                        Ага. А перед этим он смотрит, чтобы процессор был от Intel, не AMD, да.
                                          0
                                          Ну да, есть такое, расширенные инструкции у AMD он не использует.
                                            0

                                            Даже те, которые полностью совместимы с Intel?

                                              0
                                              +- да
                                              типичный пример — LinPack: если на AMD-ом проце запустить стандартную версию, собранную Intel-ским компилятором, то результаты в разы ниже аналогичных Intel-процессоров и версии, собранной не Intel-компилятором.
                                                0
                                                Никакие. Проверка на производителя процессора идёт в самом начале.

                                                И её можно вырезать из бинарника, кстати — есть умельцы. Тогла скорость работы на AMD'шных процах резко возрастает.
                                                  0
                                                  Да. Игнорируется поддержка SSE2, SSE3.
                                                  0

                                                  Он иногда не использует и обычные SSE, если проц не от интел.

                                            0

                                            В этом нет ничего страшного. Говорю как гентушник дома и компиляющий вычислительный код с -march=native на работе.

                                            +5

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

                                              +2
                                              Возможно, интеловский или иной коммерческий компилятор использует больше инструкций.
                                          –4
                                          Любопытно. Следующий вопрос — насколько полно скомпилированные программы используют имеющиеся ресурсы — кэш процессора, ОЗУ, ядра (не говоря уже о GPU).
                                            +3
                                            Любой хром стремится полностью занять ОЗУ, это факт.
                                            +10

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

                                              +1
                                              Смотря что понимать под «реально могут использовать»: интринсики считаются? А стандартные библиотеки?
                                                0

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

                                                  +2
                                                  Тогда покрыто >90% инструкций однозначно: когда в процессорах появляются новые инструкции в Clang/GCC/MSVC/ICC симметрично же появляются новые интринзики для их вызова.

                                                  А вот встречаются ли они хоть где-то, кроме тестовых программ — вопрос интересный.
                                                  0
                                                  Наверное, правильным ответом будет, что не считаются. Интересно, скорее, сколько инструкций процессора использует компилятор при компиляции чистого C. Если компилятору напрямую сказать используй эту инструкцию, то очевидно, что он использует её.

                                                  Правда, это очень сложная задача. И либо нужно разбирать исходники компилятора, чтобы понять какие инструкции он будет вставлять, либо под каждую инструкцию писать такой C код, при котором данную инструкцию стоило бы использовать.
                                                  +8
                                                  скорее не «не могут», а «не хотят». Это может быть что-то древнее как какашки мамонта. Которое никто давно не использует, потому что есть способы решить это быстрее и производители процессоров обработку этих команд не улучшают — так как видят, что они никому не нужны. Глупо делать новый процессор на 50% быстрее в командах, которые никому не нужны и пользователи не заметят разницы. Замкнутый круг.
                                                    +6

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

                                                      +1
                                                      у меня нет идей, кстати, как такое исследование провести. Только если не взять какой-нибудь очень большой репо, а не просто /usr/bin И то не факт, что этого будет достаточно. Как минимум, надо ещё кажду популярную операционку проверить — что там у них внутри.
                                                        0

                                                        Например, смотреть изменения по поколениями процессоров. Грубо говоря, MMX команды появились в 95-м (от балды), в бинарниках они появились в 2000 (ещё больше от балды) и одновременно пропали FPU команды. Потом пропали MMX, зато появились… Вывод: FPU и MMX не хотят уже

                                                          0

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

                                                            +1
                                                            исследовать такой код тяжело, проще спросить разработчиков (если они конечно согласятся что-то прояснить).
                                                      +1
                                                      У меня такое чувство, что хотя процессоры имеют сотни инструкций, компиляторы больше половины из них никогда не генерируют просто потому, что не умеют.
                                                      Чувство, очевидно, неверное, потому что, к примеру, VIA Padlock ни одним процессором не поддерживается и даже в ассемблере поддержи нету… но разработчиков OpenSSL это, конечно, не остановило: byte 0xf3,0x0f,0xa7,0xc8 можно написать всегда.
                                                        +1

                                                        Иногда просто не могут.


                                                        Например, в SSE4.2 есть pcmpestrm и pcmpistrm, которые позволяют, среди прочего, подсчитывать количество вхождений символа в строке. pcmpestrm требует явного задания длины строки, pcmpistrm сама ищет терминирующий ноль, при этом pcmpistrm умудряется быть почти в два раза быстрее на доступных мне процах (сандик и хазвелл), но при этом она ломается, если во входной строке есть нулевые байты.


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


                                                        for (auto ch : string1)
                                                          assert(ch);
                                                        for (auto ch : string2)
                                                          assert(ch);

                                                        то он поймет, что я имею в виду.

                                                          0
                                                          А как вообще компилятор может понять, какие входные данные будут поданы Вами на вход программы? никак.
                                                            0
                                                            Ну компиляторы предоставляют всякие атрибуты и built in для подсказок. Ну, а, вообще, это скорее проблема языка, так как часто мы знаем какие значения может принимать байт или группа байт, но ни C, ни C++ возможностей указать это не предоставляют.
                                                              0
                                                              На уровне языка такая возможность есть:
                                                              void foo(int bar) {
                                                                  if (bar < 0 || bar > 42) *(int*)0;
                                                                  ...
                                                              
                                                              --означает «мамой клянусь, bar будет от 0 до 42».

                                                              Что с такими указаниями делает компилятор — это другой вопрос.
                                                              В ряде случаев clang умеет ими пользоваться: godbolt.org/z/AbQk-p
                                                                +3
                                                                Ну это хак, а не фича языка. Да и хотелось бы, чтобы в C++ была возможность типам диапазоны значений указывать.

                                                                Вот есть у нас Utf-8 нуль-термированная строка. Первый тип — октет. Октет принимает такие-то значения. Второй тип — «символ». Это динамическая структура от 1 до 4 октет. Первый октет вот такой-то и по нему можно узнать длинну этой структуры, второй октет может принимать вот такие-то значение и т.д. Третий тип — сама строка. Есть один символ, означающий конец строки, он встречается всего один раз и всегда в конце. При чём он состоит из одного октета, который полностью заполнен нулями. Если я неправильные значения запишу в данные типы, то согласен на UB.
                                                                  +1

                                                                  Ну в завтипах можете это выразить:


                                                                  data Utf8Char = 
                                                                     OneByte (a : Byte ** a `LTE` 0x7F)
                                                                     TwoByte ((a, b: Byte) ** (a `GE` 0x7f && a `LTE` 0xC0 && b `LTE` 0x7F)) 
                                                                     ThreeByte ((a, b, c: Byte) ** (a `GE` 0xC0 && a `LTE` 0xE0 && b `LTE` 0x7F && c `LTE` 0x7F)) 
                                                                     FourByte ((a, b, c, d: Byte) ** (a `GE` 0xE0 && b `LTE` 0x7F && c `LTE` 0x7F && d `LTE` 0x7F)) 

                                                                  Правда, не готов сказать, насколько это будет удобно.


                                                                  Про то какие октеты где встречаются тоже можно выразить отдельно.

                                                                0
                                                                ааа, ну так std:string- это же, емнип, по стандарту и есть nullterminated string, так что это-то Ваш компилятор как раз знает :-).
                                                                  +2
                                                                  Неправда: std::string — это произвольный массив байт, и он может содержать любые символы, в т.ч. '\0'
                                                                    –2
                                                                    формально- да. но стд считает, что работать Вы с этим «произвольным массивом» будете именно как с нуль-строкой.

                                                                    cout << my_string;

                                                                    выводит только до первого нуля.

                                                                    // string::begin/end
                                                                    #include #include int main ()
                                                                    {
                                                                    std::string str ("Test string\0 2222");
                                                                    for ( std::string::iterator it=str.begin(); it!=str.end(); ++it)
                                                                    std::cout << *it;
                                                                    std::cout << '\n';

                                                                    return 0;
                                                                    }


                                                                    бегает до первого нуля.

                                                                    typedef basic_string string;
                                                                    template < class charT,
                                                                    class traits = char_traits, // basic_string::traits_type
                                                                    class Alloc = allocator // basic_string::allocator_type
                                                                    > class basic_string;
                                                                    template struct char_traits;
                                                                    template <> struct char_traits;

                                                                    а у последнего есть мембер
                                                                    length //Get length of null-terminated string ( public static member function )

                                                                    Я понимаю, что технически можно в std::string сложить все, что угодно, но задумана-то она была именно для хранения строк с нулем в конце. А вопрос ведь был о том, знает компилятор- что строка null-terminated, или нет, чтобы использовать оптимизированную инструкцию pcmpistrm из SSE4.2? Так вот std::string ему прямо об этом и говорит. Если нет- то есть и другие контейнеры.
                                                                      +3
                                                                      Это потому, что вы используете конструктор из const char*.

                                                                      std::cout << std::string("Test string\0 2222", 18); напечатает всю строку вместе с нулём, и итерироваться она будет вместе с нулём, и length() вернёт 18.
                                                                        0
                                                                        Мне что-то вспомнилась дискуссия про нуль-терминированные строки и паскалевские строки, начинающиеся с 8 бит длины строки, в результате которой родился мерзкий гибрид, у которого в начале октет длины. а в конце \0. Вроде этот способ является основным в Обероне.
                                                                          +1
                                                                          А что в этом плохого? std::string так и хранит — длину отдельно, а в конце терминирующий nul, чтобы и c_str(), и length() работали за O(1).
                                                                            0
                                                                            Отдельно — это нормально, мне не нравится, что в гибриде в первом элементе массива вместо char uint8_t. Ну и тут или длина, или терминатор, всё-таки c_str и std::string разные вещи, не совсем понимаю стремление плюсов к обатной совместимости с чистым С. В 98м это ещё смысл имело.
                                                                              0
                                                                              И сейчас, к сожалению, имеет. Ибо у C++ тупо нет некоторых вещей, которые реализуются через C API — либо C++версии банально менее популярны, либо их вообще нету…

                                                                              Причём, внезапно, очень много всего, связанного со строками: GetText или LibXML2 какие-нибудь…
                                                                                0
                                                                                Как в 98м имела смысл совместимость с чистым Си, так в 2020 имеет смысл совместимость с С++98.
                                                                                Это как в басне про шатл и лошадиную задницу.
                                                                            –4
                                                                            :-) именно! если явно указать длину строки, то компилятор знает, что лучше при сравнении использовать pcmpestrm, а если не указать- то компилятор знает, что лучше использовать pcmpistrm. Потому что стд предполагает, что string- null-terminated, хотя и позволяет Вам использовать ее и по другому. Вопрос ведь изначально был о том, откуда компилятору знать, будет в строке нулевой символ, или нет? и на это я и обратил внимание- что компилятор не знает, какие именно данные Вы загоните на вход, но вот что он знает- так это какие данные Вы ожидаете на входе, так как эту информацию можно получить из типов данных. std::string предполагает нуль-терминатед, но не гарантирует.
                                                                            И еще- это не я использую конструктор, это он у меня в std::string такой, его за меня сделали. :-)
                                                                              +3
                                                                              > Потому что стд предполагает, что string- null-terminated, хотя и позволяет Вам использовать ее и по другому.

                                                                              Читаю стандарт (ну, last draft, как обычно)…

                                                                              The class template basic_string describes objects that can store a sequence consisting of a varying number of arbitrary char-like objects with the first element of the sequence at position zero. Such a sequence is also called a “string” if the type of the char-like objects that it holds is clear from context.


                                                                              «Arbitrary» не предполагает запрет на элемент с кодом 0.

                                                                              И конструкторы вида

                                                                              basic_string(const charT* s, size_type n, const Allocator& a = Allocator());

                                                                              никак не ограничивают сделать хоть все символы NULами.

                                                                              Все операции точно так же могут складывать/искать/итд. с NUL внутри.

                                                                              Всё, что есть для совместимости — это что s.c_str()[s.size()] должно быть CharT(NUL), и возможность получать в аргументах const CharT* без длины (для перехода с C-style).

                                                                              > если явно указать длину строки, то компилятор знает, что лучше при сравнении использовать pcmpestrm, а если не указать- то компилятор знает, что лучше использовать pcmpistrm.

                                                                              Это вполне возможно, но на std::string не имеет смысла — там длина хранится всегда.
                                                                                +1
                                                                                если явно указать длину строки, то компилятор знает, что лучше при сравнении использовать pcmpestrm, а если не указать- то компилятор знает, что лучше использовать pcmpistrm.

                                                                                pcmpistrm на моих машинах всегда эффективнее, чем pcmpestrm. Соответственно, использовать его можно, когда внутри строки нет нулей.


                                                                                Вы ожидаете на входе, так как эту информацию можно получить из типов данных. std::string предполагает нуль-терминатед, но не гарантирует.

                                                                                Начиная с кажется C++11 гарантирует.


                                                                                Но проблема не в нуль-терминированности, а в наличии нулей посередине строки, опять же.

                                                                +5

                                                                Было бы интересно сравнить clang и icc. По идее, icc как раз должен использовать все, что можно, чтобы ускорить код, т.к. по идее построен с учетом знаний о внутренней архитектуре интеловских процессоров.

                                                                  +8

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

                                                                  +2
                                                                  Надо ещё смотреть профиль использования приложения. Если взять на userland-приложение, а драйвер уровня ядра или само ядро, статистика немного поедет.
                                                                    +3
                                                                    а теперь отрезать неиспользованное и получить RISC посмотреть сколько площади освободиться
                                                                      +1

                                                                      Думаю что мало, меньше 10% — большая часть редких инструкций просто микрокодом в RISC транслируется же.

                                                                        0
                                                                        Ну так x86-64 имеют RISC ядро. То что много инструкций поддерживаются процессором, не значит, что каждая инструкция распаяна, много делается программно. Просто сейчас процессоры скорее некий сервер, которому мы говорим некие абстрактные комманды, а он уже делает что хочет. Вон, в Intel ME встроили. По сути другой процессор, который ещё ОС Minix исполняет. Сейчас процессоры — системы на чипе.
                                                                        0
                                                                        Заметил (давно), что i387 инструкции больше не используются. Это учтено?

                                                                        Хотя их и было немного, но могли попасть в дебаг версии.

                                                                        Ну и было бы неплохо добавить в статистику число тиков на инструкцию как ИТОГО… Архитектуры то разные.
                                                                          +1
                                                                          Ну например, fstp в clang-10 встречается 1693 раза, fld — 1235 раз.
                                                                          Я бы это не назвал «больше не используются».
                                                                            +1
                                                                            А в 64-бит версиях?

                                                                            Я встречал мнения, что 32бит покинуты (abandoned), т.е не развиваются.
                                                                              0
                                                                              Вы правы: в версиях для x86_64 инструкций i387 с самого начала было очень мало, а после версии clang-3.5 (2014) их осталось меньше десятка на весь бинарник.
                                                                          0
                                                                          1. Большое количество MOVs и LOADs — это несовершенство аллокатора регистров и генерация кода для Phi-функций SSA представления программы.
                                                                          2. Компилятор не использует все возможные инструкции, потому что чтобы их применить нужно обнаружить соотвествующие операции в исходной программе. Внутреннее представление (IR) программы в компиляторе (пример llvm.org/docs/LangRef.html) в основном состоит из простых операций, которые чаще всего один в один отображаются в target ISA. Добавление всевозможных сложный операций в IR усложняет написание платформенно-независимых оптимизаций. А в кодо-генераторе полно других важных проблем, которые нужно решать.
                                                                            +1
                                                                            1. Верно, но это сложно как-либо оценить статистикой, т к даже с идеальным регистр аллокатором мувы все равно будут
                                                                            2. В LLVM есть пяток проходов, где он пытается по набору операторов распознать всякие интринсики, например https://godbolt.org/z/yRJubW
                                                                              0
                                                                              вы, мне кажется, слегка перегибаете с «состоит из простых операций». начать с того, что в LIR'е операции различают типы, а в во всех мэйнстримных архитектурах — это разные мнемоники.
                                                                                +1
                                                                                Простые с точки зрения разработчика компилятора. А именно, каждая инструкция LIR делает только одно определенное действие, не имеет неявных зависимостей и обладает минимумом побочных эффектов. Инструкция явно предоставляет всю информацию о себе, что позволяет писать оптимизации на основе matching, пример InstrCombine pass. Наличие типов как раз упрощает их.
                                                                                Если бы в IR были бы такие сложные x86 инструкции как 'LOOP' или 'REP CMPS', то всем оптимизациям приходилось бы каждый раз иметь в виду что инструкция делает больше чем одно действие. Это все затрудняет написание generic оптимизаций. Кстати интринсики в IR — это как раз и есть возможность использовать сложные инструкции. Только вот оптимизации не любят интринсики.
                                                                                По поводу различных мнемоник, как раз простота IR позволяет автоматизировать процесс мэпинга инструкций IR в машинные инструкции. В LLVM за это отвечает tablegen, который берет описание ISA и генерирует таблицу конечного автомата.
                                                                                  0
                                                                                  это всё прекрасно, что вы рассказываете, но никаким one to one mapping тут и не пахнет. и к слову «каждая инструкция LIR делает только одно определенное действие» это такое себе, вот вам описание инструкции load, к примеру:

                                                                                  result = load [volatile] , * [, align ][, !nontemporal !][, !invariant.load !][, !invariant.group !][, !nonnull !][, !dereferenceable !<deref_bytes_node>][, !dereferenceable_or_null !<deref_bytes_node>][, !align !<align_node>]
                                                                                    +1
                                                                                    А что в вашем понимании 1-to-1? Вы говорите:
                                                                                    но никаким one to one mapping тут и не пахнет.

                                                                                    Привидите пример того, сколько инструкций LIR отображается не один в один.
                                                                                    Все что с! — это метаданные, хинты для оптимизаций и кодогенерации. Они могут быть проигнорированы, либо вообще отброшены. Оптимизация не должна использовать метаданные для передачи информации влияющие на корректность операции.
                                                                                    Если отбросить метаданные то определение:
                                                                                    result = load [volatile], * [, align ]
                                                                                    И семантика простая:
                                                                                    «The location of memory pointed to is loaded. If the value being loaded is of scalar type then the number of bytes read does not exceed the minimum number of bytes needed to hold all bits of the type. For example, loading an i24 reads at most three bytes. When loading a value of a type like i20 with a size that is not an integral number of bytes, the result is undefined if the value was not originally written using a store of the same type.»
                                                                                      0
                                                                                      Привидите пример того, сколько инструкций LIR отображается не один в один.

                                                                                      так вот я же вам привел load. вы там, кстати, выкинули nontemporal, а это вполне могут быть разные инструкции. то есть, то, что LIR load при кодогенерации на АРМе может распадаться на ldr,ldp,ld1,ld2,ld3,ld4 вас не убеждает? вы по-прежнему считаете, что это one to one mapping?
                                                                                        +1
                                                                                        В теории может, но на практике я этого в LLVM не видел.

                                                                                        что LIR load при кодогенерации на АРМе может распадаться на ldr,ldp,ld1,ld2,ld3,ld4 вас не убеждает


                                                                                        Такие вещи могут делать в LLVM backend'е, где оперируют MIR (https://llvm.org/docs/MIRLangRef.html).
                                                                                        Вначале MIR стараются получить как можно близко похожим на LIR. И он неоптимален. Затем этот MIR прогоняют через кучу оптимизаций, где могут делать свёртки/разбивки инструкций (strength reduction/peephole optimizations). Затем MIR трансформируют в MachineCode, который также прогоняют через оптимизации. И эти оптимизации пишутся под конкретный target ISA, где уже оперируют в терминах инструкций ISA.
                                                                                        Цепочка преобразований: LIR-MIR(здесь очень похожи на LIR)->MachineCode(здесь уже все дальше от IR)->Assembly
                                                                                        LLVM позволяет быстро создать кодогенератор с помощью TD файлов, где описывается mappping MIR в Target ISA. Так как этот кодогенератор сгенерированный, то он просто мепит одни инструкции на другие без особого анализа и обработки. Поэтому можно утверждать что исходный IR отображается практически один в один в target ISA.
                                                                                        Если в вашу ISA так просто IR не отобразить, то тогда нужно будет писать такое отображение ручками, где каждая инструкция MIR как-то сложно преобразуется.
                                                                                        LLVM разрабатывался таким образом, чтобы IR максимально легко было отображать на target ISA.
                                                                                        Я помню как мы добавляли ARMv8.x расширения к LLVM. На первом этапе — это просто создание td файлов описаний. Затем мы реализовывали специфичные оптимизации, и то если в этом есть необходимость.
                                                                              +2
                                                                              Как-то странно смотрится большее количество инструкций в x86 против ARM в контексте CISC vs RISC. Либо функционально бинарники не эквивалентны?
                                                                                +1
                                                                                CISC по определению имеет больше инструкций чем RISC (reduced instruction set computer)
                                                                                  +3
                                                                                  Ээээ… Больше разных инструкций в ISA => больше функционала на одну инструкцию => меньше количество инструкций в коде. Нет?
                                                                                    0
                                                                                    Даже с моим минимальным опытом, я бы сказал, что весьма неоднозначно.
                                                                                      +2

                                                                                      Только если код пишет человек. Когда код пишет компилятор, гораздо важнее становятся такие вещи, как распределение регистров. Например, для инструкций семейства MOVS на x86 можно использовать только регистры ESI и EDI, что приводит к куче лишних MOV и сводит на нет все преимущества.

                                                                                        0
                                                                                        1. Есть переименование регистров.
                                                                                        2. Есть shadow registers.
                                                                                        3. AMD64 много чего добавила.

                                                                                        Ну и вообще там всё сложно может быть.
                                                                                        Руками asm код править можно, но не всегда целесообразно.
                                                                                          0
                                                                                          Но здесь мы говорим о плотности кода. О том, что несмотря на наличие высокоуровневых команд типа movs, для их запуска аргументы нужно разместить в определённых регистрах, а это лишние инструкции. И никакое переименование тут не поможет, потому что оно работает не на этом уровне.
                                                                                            0
                                                                                            Есть переименование регистров внутри процессора самим процессором.
                                                                                              +2
                                                                                              Ну так mov нам в любом случае писать надо. То, что процессор, вместо физического переписывания данных в регистрах, меняет регистровый файл — его дело. Количество инструкций от этого не меняется.
                                                                                        0
                                                                                        Intel ушла от этого начиная с i486.
                                                                                        Оказалось выгоднее делать RISC ядро + переводчик команд.
                                                                                          0
                                                                                          Всё-таки с Pentium Pro, а не с i486. И i486 и даже Pentium исполняют x86 инструкции «напрямую».
                                                                                            0
                                                                                            С Википедии:
                                                                                            Intel 80486 (также известный как i486, Intel 486 или просто 486-й) — 32-битный скалярный x86-совместимый микропроцессор четвёртого поколения, построенный на гибридном CISC-RISC-ядре и выпущенный фирмой Intel 10 апреля 1989 года.
                                                                                              0
                                                                                              В английской этого нет. Кто и что понаписал в русской — я не в курсе. μopsы — это P6.
                                                                                                0
                                                                                                Как вам такой источник? books.google.com/books?id=nceYr-Zzca8C&pg=PA10&dq=risc+core
                                                                                                  +3
                                                                                                  То что вы откопали — это рекламная статья, причём не от Intel, а от кого-то, кто что-то где-то, по слухам, узнал — причём не о 80486, а о будущем P5.

                                                                                                  С учётом того, что P5 и P6 разрабатывались одновременно… подозреваю что там «всё смешалось в доме Облонских».

                                                                                                  Называть это ну хоть сколько-нибудь надёжной информацией я бы не стал…

                                                                                                  P.S. Собственно логика-то простая: разбивать что-то типа inc byte ptr [eax] на три отдельных операции имеет смысл только тогда, когда вы можете выполнить эти инструкции не по очереди, а в каком-то другом порядке. Этого ни 80486й, ни оригинальный Pentium (который P5) не умеют. Спекулятивное исполнение появилось в P6. Который был изначально назван Pentium Pro, а потом, на его основе, сделали Pentium II. И который, несмотря на близость названия, к Pentium и Pentium MMX не имеет никакого отношения.
                                                                                                    0
                                                                                                    Я про то, что это не википедисты выдумали: такое действительно писали.
                                                                                                    На сайте Падуанского университета тоже висит такое в материалах по курсу Advanced Computer Architectures.

                                                                                                    RISC-ядро в 486/P5 чем-то похоже на чайник Рассела: как доказать, что его нет? Особенно при наличии публикаций о том, что оно якобы есть.
                                                                                                      –1
                                                                                                      Особенно при наличии публикаций о том, что оно якобы есть.
                                                                                                      Записать рекламный мусор в «неавторитетные источники» и потребовать ссылки на технический мануал?

                                                                                                      В Wikipedia есть механизм, нужно только, чтобы кто-то желал им воспользоваться.

                                                                                                      Ну астрологов же из астрономических статей как-то изгоняют?

                                                                                                      Иногда такие замечания пытаются «отшить» объясняя, что никаких других мануалов у нас и нету… в случае iAPX 432 это, может быть, даже и оправдано, но когда есть подробные исследован ия микроархитектуры. Тот же Agner Pentium и Pentium Pro подробно исследовал…
                                                                                                        +1
                                                                                                        downloads.gamedev.net/pdf/gpbb/gpbb12.pdf
                                                                                                        Enter the 486 No chip that is a direct, fully compatible descendant of the 8088,286, and 386 could ever be called a RISC chip, but the 486 certainly contains RISC elements, and it’s those elements that are most responsible for making 486 optimization unique. Simple, common instructions are executed in a single cycle by a RISC-like core processor, but other instructions are executed pretty much as they were on the 386, where every instruction takes at least 2 cycles. For example, MOVAL, [Testchar] takes only 1 cycle on the 486, assuming both instruction and data are in the cache-3 cycles faster than the 386”but STOSB takes 5 cycles, 1 cycle slower than on the 386. The floating-point execution unit inside the 486 is also much faster than the 38’7 math coprocessor, largely because, being in the same silicon as the CPU (the 486 has a math coprocessor built in), it is more tightly coupled. The results are sometimes startling: FMUL (floating point multiply) is usually faster on the 486 than IMUL (integer multiply) !

                                                                                                        Декодера CISC -> RISC похоже что нет, но работа подобна RISC.
                                                                                                          0
                                                                                                          но работа подобна RISC.

                                                                                                          Камень подобен сердцу человеческому и в нём заключен кристалл сияющий!(с)
                                                                                                            –1
                                                                                                            Декодера CISC -> RISC похоже что нет, но работа подобна RISC.
                                                                                                            Ну маркетологи и не такое придумают.

                                                                                                            В каком оно месте «подобна RISC»? Да — и CISC и RISC слегка размытые понятия, но… вот примерно так:
                                                                                                            A RISC computer has a small set of simple and general instructions, rather than a large set of complex and specialized ones. The main distinguishing feature of RISC is that the instruction set is optimized for a highly regular instruction pipeline flow. Another common RISC trait is their load/store architecture, in which memory is accessed through specific instructions rather than as a part of most instructions.
                                                                                                            Первые два критерия явно не в кассу: 80386 от 80486 архитектурно отличается на 4 инструкции (BSWAP, CHPXCHG, WBINVD и XADD), всё остально — такое же.

                                                                                                            Load/Store тоже нету (это в P6 завезли). Так с какой стороны это RISC? Только со стороны отдела продаж… ну так они и трактор самолётом назовут и глазом не моргнут…

                                                                                                            Кстати в русской Wikipedia прямо написано:
                                                                                                            В итоге RISC-архитектуры стали называть также архитектурами load/store.
                                                                                                            Да, это «в итоге» и можно при желании, написать, что 80486 называли RISC-процессором… но ни о каком «RISC ядро + переводчике команд» речь не идёт ни в 80486, ни в P5.
                                                                                                              +1
                                                                                                              В те годы RISC была «стильно-модно-молодёжной» технологией, поэтому производители прикручивали эти красивые буковки ко своему товару. Считалось, что RISC — это «простые инструкции за 1 такт», чему i486 соответствует отчасти.
                                                                                          +1
                                                                                          Нет такого определения. CISC характеризуется сложностью самих инструкций, а не набора.

                                                                                          Например, в таком CISC, как PDP-11, одна и та же MOV выполняет:
                                                                                          — загрузку константы в регистр: MOV #123, R1
                                                                                          — загрузку константы по абсолютному адресу в память: MOV #123, $#456
                                                                                          — загрузку константы по относительному адресу в память: MOV #123, 456(R1)
                                                                                          — копирование регистров: MOV R1, R2
                                                                                          — копирование из памяти в память: MOV $#246, 776(R2)
                                                                                          — копирование из памяти на стек: MOV 776(R3), -(SP)
                                                                                          и много других вариантов, вплоть до совершенно безумных типа сохранить значение из регистра в коде данной команды(!): MOV R4, (PC)+ (потом его можно оттуда извлечь, уже зная точный адрес)

                                                                                          То же самое с пачкой других команд, для которых допустимо самое широкое из доступных разнообразие адресаций (BIS, BIC, ADD, SUB, CMP...)

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

                                                                                          Сравните с ARM, RISC-V — части операций просто нет (из памяти в память, константа в память), остальные делаются разными командами: загрузка константы в регистр это одно, читать по адресу регистр+смещение это другое, то же самое с предекрементом (как для PUSH) это третье, это всё разные команды (хотя часть различия синтаксически записана как разница в записи операнда в памяти).
                                                                                          Команд — больше. Каждая сама по себе — проста и выполняется с минимумом вложенной многошаговой логики, в идеале укладывается только в один шаблон «прочитал — операция — записал». Превращения входного потока команд в микрооперации просты и в идеале вообще 1:1 (реальность портит, но не радикально). Система команд формата PDP-11 тут требовала бы радикальной трансляции. Даже x86, у которого максимум один операнд в памяти (строковые не в счёт), требует тут трансляции.
                                                                                            0
                                                                                            x86, у которого максимум один операнд в памяти (строковые не в счёт)

                                                                                            В прошлом топике напомнили ещё и про push [addr]
                                                                                            :-P
                                                                                              0
                                                                                              Ну отдельные подобные исключения, да, есть. Спасибо за подсказку.
                                                                                              0
                                                                                              Насчёт PDP-11 — а разве это не просто следствие ортогональности системы команд? Я не большой знаток архитектур, но в таком RISC, как MSP430, система команд так же ортогональна и тот же mov делает так же кучу вещей.
                                                                                                0
                                                                                                Это не «просто» следствие ортогональности системы команд, это следствие того, что ортогональность системы команд это один из принципов CISC. В идеале — любая команда с любыми операндами со сколь угодно сложными адресациями, и PDP-11 здесь не предел — вершиной CISC были VAX и M68000. За что, похоже, и пострадали — Intelʼу хватило ресурсов перевести x86 на внутренний RISC, Мотороле — нет, и в 1994 была последняя модель новой разработки (embedded не в счёт); DEC похерил VAX в пользу Alpha (но они перегнули палку в противоположную сторону).

                                                                                                RISC, наоборот, принципиально уходит от ортогональности, нагружая автора кода (и человека, и компилятор) тем, что он не может произвольно сочетать всё со всем, и вообще сильно меньше может: сложные многоэтапные адресации — в топку, операции память-память — к ногтю, адресации больше чем с двумя регистрами — с корнем, и так далее. Зато исполняется полученное легче и дешевле (не нужно трансляции, проще блоки синхронизации внутри конвейера...)
                                                                                                  0
                                                                                                  Ага, спасибо, теперь понял))
                                                                                                    0
                                                                                                    А куда тогда относится какой-нибудь 6502, где система команд, с одной стороны включает в себя разные конструкции типа «возьми адрес ячейки, там адрес другой ячейки, а там уже данные», а с другой стороны — набор команд столь ограничен и крив, что при написании программ возникает в основном вопрос «а как на этом чуде вообще хоть что-то написать»?
                                                                                                      0
                                                                                                      Так много чего можно отнести к совершенно разным классам одновременно.

                                                                                                      Вот S/360: с одной стороны, команды типа сложить/умножить бинарное или плавучее — не имеют вариантов с получателем в памяти; они могут читать из памяти, но не писать. Значит, когда можно «A 3,4(12)», но нельзя «A 4(12),3», это больше RISC, чем x86, в котором может быть и «add ebx, [r12+4]», и «add [r12+4], ebx».
                                                                                                      Но с другой стороны, в нём есть какие-нибудь AP и EDMK, которые занимаются итерированием десятичной записи в памяти. Эта часть — в RISC такое не вводят, а команда целиком для оптимизации работы программисту — значит, CISC.

                                                                                                      6502 многие относят к RISC, за счёт простоты большинства команд и того, что ради экономии тактов там даже сделали некоторые выломы из традиционной логики (например, стек там поставтодекрементный/преавтоинкрементный, в отличие от почти всех остальных). Но я бы его отнёс к кастрированным инвалидам, из-за урезанности которых вообще различие начинает терять смысл, но за счёт вариантов типа «LDA ($36,X)» — его проектировали как урезанный CISC, а не RISC…
                                                                                                      Ну и регистров для RISC откровенно мало (нулевая страница — это не регистры, как бы ни хотелось обратного его поклонникам...) в общем, типичный продукт мышления 70-х. Если бы за 6800 не просили в 3 раза больше, про 6502 никто бы и не знал.

                                                                                                      > при написании программ возникает в основном вопрос «а как на этом чуде вообще хоть что-то написать»?

                                                                                                      Ну я в школе на нём целый Форт наваял… но были вдохновение и новизна, да. Сейчас буду только плеваться на такие идеи.
                                                                                                    0
                                                                                                    Кстати, посмотрел я на доки по MSP-430… RISC тут с заметной натяжкой, если не сказать жёстче.

                                                                                                    An example: Let’s say you want to clear a word in memory at the address dst. To do this, a MOVE instruction could be used:

                                                                                                    MOVE #0, dst

                                                                                                    This instruction would have 3 words: the first contains the opcode and addressing mode specifiers. The second word keeps the constant zero, and the third word contains the address of the memory location.

                                                                                                    Alternatively, the instruction

                                                                                                    MOVE R3, dst

                                                                                                    performs the same task, but we need only 2 words to encode it.


                                                                                                    Ну это откровенно стиль CISC, как PDP-11. В RISC, во-первых, разделили бы загрузку константы в регистр и запись из регистра в память. Во-вторых, старались бы сделать все команды одной длины, а если константе требуется полная ширина — грузили бы её по частям или из памяти рядом с кодом. ARM, MIPS, SPARC, PPC, RISC-V — у всех тут одни и те же проблемы и сходные решения. В ARM/64 вообще полную 64-битную константу надо грузить в 4 команды (каждая вписывает по 16 бит), 32 бита большинство вписывает в 2 этапа (старшая или младшая вперёд — уже особенности местного стиля).
                                                                                                    В-третьих, не было бы такого, что только режим адресации меняет, будет ли читаться константа, смещение к регистру, и т.п., или просто из регистра; да, по сравнению с PDP-11 самые переусложнённые методы вроде косвенного преавтодекрементного — срезали, но само различие — осталось. Даже в ARM чтение из регистра — одно, а из памяти — другое.

                                                                                                    Так что, мало команд — да, не перезапутано — так себе, RISC — ой нет ;(
                                                                                                      0
                                                                                                      В ARM/64 вообще полную 64-битную константу надо грузить в 4 команды (каждая вписывает по 16 бит)
                                                                                                      От компилятора зависит. Clang в 4 делает, MSVC — в одну.

                                                                                                      Но да, это уже «читерство», конечно.
                                                                                                        +1
                                                                                                        Интересно, какой из этих способов быстрее, или же на разных чипах по-разному.
                                                                                                          0
                                                                                                          От чипа не сильно должно зависеть, а вот от кэширования памяти — напрямую. Потратить 16 байт кодового потока там, где он напрямую и читается для выполнения, или 8 байт где-то рядом (насколько рядом — зависит от объёма функции и стиля линковки, в общем, вполне может быть соседняя кэш-строка или даже пара килобайт вбок).
                                                                                                          Ну и хранение константы где-то рядом приводит к тому, что в кодовых секциях появляются данные — как раз для анализа данной статьи может быть жуткой диверсией :)
                                                                                                        0
                                                                                                        Так по поводу MOV #0, dst и MOV R3, dst — в MSP430 есть 2 хитрых регистра, генераторы констант — R2 (который ещё и Status Register) и R3.
                                                                                                        Если правильно понял — MOV #0, dst как раз заменится на MOV R3, dst.
                                                                                                  +2
                                                                                                  А что странного? ARMv8 ISA более выразительная и обычно требуется меньше инструкций для аналогичного кода. Часто код ещё и меньше занимает.
                                                                                                  Если не верите, проверьте сами на gcc.godbolt.org
                                                                                                    0
                                                                                                    Да у них есть различия в функционале.
                                                                                                      0
                                                                                                      А конкретнее?
                                                                                                        +1
                                                                                                        В исходниках LLVM eсть немного "#if defined(__i386__) || defined(__x86_64__)".
                                                                                                        Но основное различие в том, поддержку каких targets включили при построении Clang/LLVM. Если все бинарники с llvm.org построены с одними и теми же настройками, то отличия должны быть минимальны.
                                                                                                    0

                                                                                                    Что-то мне подсказывает, что все мультимедиа расширения, начиная с MMX в коде компилятора встретить сложно, ибо они не про то вообще. Кстати, было бы интересно проверить эту гипотезу

                                                                                                      +2
                                                                                                      Для x64 наоборот — в основном SSE.
                                                                                                        +2
                                                                                                        Не подтверждается: pxor в clang-10 встречается 5933 раза, pcmpeqb — 5193 раза, и т.д.
                                                                                                          0
                                                                                                          А выше чем SSE? А, к примеру, AVX'ы, которых по числу инструкций как бы не больше, чем всего остального вместе взятого?
                                                                                                            0
                                                                                                            SSE2 используется, AVX нет.

                                                                                                            (Нет, инструкций AVX пока ещё не больше, чем всего остального вместе взятого, а примерно 30% от общего числа. Об этом был мой предыдущий пост.)
                                                                                                              +1
                                                                                                              вы считали AVX только по мнемоникам, я так понимаю? но в 512-м есть, во-первых, маскирование с двумя режимами. есть отсутствие маскирование (вырожденная маска), есть broadcasting bit, который тоже меняет семантику. посмотрите сюда:

                                                                                                              software.intel.com/sites/landingpage/IntrinsicsGuide/#techs=AVX_512

                                                                                                              это разбивка 512-го на семантически разные — с точки зрения интела — куски. предположим, мы поделим это на три (с учетом разной ширины регистров), всё равно это больше тысячи функционально разных операций.
                                                                                                            0
                                                                                                            подождите, а не подтверждается что именно? для pxor и pcmpeqb есть оба варианта. вы смотрели на их аргументы? я сомневаюсь, что современные компиляторы генерят код для mmx — медленно и регаллоку лишняя головная боль.
                                                                                                              0
                                                                                                              Не подтверждается именно то, на что я отвечал: «что все мультимедиа расширения, начиная с MMX в коде компилятора встретить сложно»
                                                                                                                0
                                                                                                                так мой вопрос именно в этом: инструкция может быть больше, чем в одном варианте и если там xmm регистры, то это уже не MMX, хоть и название то же самое.
                                                                                                            0
                                                                                                            Вопрос в том, что встречать расширениями мультимедиа :)

                                                                                                            В 64-битке SSE — основной механизм для плавающей точки. Соответственно операции с ней (в самом компиляторе их таки есть) — исполняются на SSE.

                                                                                                            SSE активно используется для заливки памяти нулями — pxor + movaps/movdqu/etc. составляют вообще основную часть всех операций. Сюда же копирование памяти.

                                                                                                            Это основное, что видно по простому «objdump -d | grep xmm».

                                                                                                            Можно сказать, что они используются в компиляторе не по назначению, но если они есть в процессоре, то почему бы и не применить? ;)

                                                                                                            А так —
                                                                                                            $ objdump -d libclang-cpp.so.10 | grep xmm | wc -l
                                                                                                            188295
                                                                                                            $ objdump -d libclang-cpp.so.10 | wc -l
                                                                                                            7884211


                                                                                                            2.4% всех команд это немало.
                                                                                                              0
                                                                                                              objdump -d libclang-cpp.so.10

                                                                                                              У вас здесь та же неточность, что и у Pepijn de Vos: вы дизассемблируете не только .text, но и неисполнимые данные.
                                                                                                              На одном только исполнимом коде он бы 411 разных мнемоник не набрал :-)
                                                                                                                +1
                                                                                                                Я уверен, что нет. objdump -d по умолчанию разбирает только те секции, которые помечены как исполнимые.
                                                                                                                Для компилированного x86 нетипично складывать данные рядом с кодом, поэтому тут ложных срабатываний не должно быть. Я их видел в некоторых библиотеках типа libcrypto, где много ассемблера с ручными фокусами, но не в clang. И ещё я просмотрел результат грепа глазами (по тысяче строк в начале, середине и конце) — если бы там был дизассемблинг данных, начались бы массы команд очень странного содержания и примерно равномерное распределение по регистрам, а этого не было.
                                                                                                                  0
                                                                                                                  Для компилированного x86 нетипично складывать данные рядом с кодом, поэтому тут ложных срабатываний не должно быть.
                                                                                                                  Некоторые версии некоторых компиляторов пихают таблицы для switch и работу с вариадиками в код.

                                                                                                                  Но в типичной программе этого добра действительно немного. Так что процент кода вы посчитали верно, а вот разных мнемоник — действительно могло из-за этого насчитаться…
                                                                                                                    +1
                                                                                                                    > в типичной программе этого добра действительно немного

                                                                                                                    Естественно. Поэтому надо чем-то вначале проверить, откуда можно вычитывать данные, а откуда нет — и самые редкие отфильтровать уже вручную.

                                                                                                                    Я прошёлся после вчерашнего вопроса tyomitch'а по /usr/bin рабочей машины простой проверкой — у кого в выхлопе такого objdump -d будут rcl или rcr? — один таки нашёлся: zoiper. Не знаю, зачем его так собрали, но там таки попадают данные в декодирование, вот характерный пример:

                                                                                                                    dbeefe: 9b fwait
                                                                                                                    dbeeff: c1 d2 4a rcl $0x4a,%edx
                                                                                                                    dbef02: f1 icebp
                                                                                                                    dbef03: 9e sahf
                                                                                                                    dbef04: c1 69 9b e4 shrl $0xe4,-0x65(%rcx)
                                                                                                                    dbef08: e3 25 jrcxz dbef2f
                                                                                                                    dbef0a: 4f 38 86 47 be ef b5 rex.WRXB cmp %r8b,-0x4a1041b9(%r14)
                                                                                                                    dbef11: d5 (bad)
                                                                                                                    dbef12: 8c 8b c6 9d c1 0f mov %cs,0xfc19dc6(%rbx)
                                                                                                                    dbef18: 65 9c gs pushfq
                                                                                                                    dbef1a: ac lods %ds:(%rsi),%al


                                                                                                                    Если бы Pepijn de Vos отбраковал такое перед основным анализом, то цифры явно бы уменьшились.

                                                                                                                    > Некоторые версии некоторых компиляторов пихают таблицы для switch и работу с вариадиками в код.

                                                                                                                    Хм, этого не видел. Но они тоже должны отловиться теми же методами.
                                                                                                                      –1
                                                                                                                      Хм, этого не видел. Но они тоже должны отловиться теми же методами.
                                                                                                                      С этим разработчикам NaCl пришлось бороться. Потому что, внезапно, эти таблицы не хотели валидироваться.

                                                                                                                      Но я не знаю — отправили они правки в upstream или нет.
                                                                                                            0
                                                                                                            Хм. даже в спектруме было около 1200 инструкций, насколько я помню. Их стало меньше?
                                                                                                            Я думал наоборот должно быть гораздо больше
                                                                                                              +1
                                                                                                              Это пермутации всех регистров.
                                                                                                              Самих инструкций гораздо меньше.
                                                                                                              Вот в ARMv1 лишь 45 различных инструкций и 23 мнемоники.
                                                                                                              en.wikichip.org/wiki/arm/armv1
                                                                                                                0
                                                                                                                Но ведь каждая такая инструкция, имеет отдельный машинный код и следовательно выстроенную для нее логику в железе камне процессора?
                                                                                                                  0
                                                                                                                  Машинный код сформирован по простым правилам — есть поля кода операции, регистров и непосредственных данных.
                                                                                                                  ADD A,r
                                                                                                                  10000 rrr
                                                                                                                  |     ^ номер регистра источника
                                                                                                                  ^ код операции ADD a,r
                                                                                                                  000 b
                                                                                                                  001 c
                                                                                                                  010 d
                                                                                                                  011 e
                                                                                                                  100 h
                                                                                                                  101 l
                                                                                                                  110 (hl)
                                                                                                                  111 a
                                                                                                                  
                                                                                                                  LD R,r
                                                                                                                  01 RRR rrr
                                                                                                                  |   |  ^ номер регистра источника (см. выше)
                                                                                                                  |   ^ номер регистра приёмника 
                                                                                                                  ^ код операции LD r,r
                                                                                                                  
                                                                                                                  01 110 110 является невозможной операцией ld (hl), (hl), поэтому вместо неё другая.
                                                                                                                  Причём бесполезные LD A,A LD B,B имеются.
                                                                                                                  
                                                                                                                  или операция BIT n,r
                                                                                                                  
                                                                                                                  префикс CB 
                                                                                                                  01 nnn rrr
                                                                                                                  |   |  ^ номер регистра (см. выше)
                                                                                                                  |   ^ номер бита
                                                                                                                  ^ код операции BIT
                                                                                                                  


                                                                                                                  Т.о. мы имеем 135 различных кодов (8+63+64), но по факту это лишь три инструкции.
                                                                                                                  clrhome.org/table
                                                                                                                    0
                                                                                                                    Например
                                                                                                                    LD DE, xxxx — 11xxxxh — 00010001 0000000000000000
                                                                                                                    LD A, xxxxh — 3axxxxh — 00011101 00000000000000000
                                                                                                                    LD A, xxh — 3exxh — 00011111 000000000

                                                                                                                    Как данная схема обрабатывается процессором на уровне архитектуры кристалла?
                                                                                                                    Тут присутствует паттерн для инструкции LD, а затем просто маркер для работы с разными регистрами, либо все таки для всех трех инструкций есть отдельный электрический путь?
                                                                                                                    p.s. да, тут третья команда выбивается работой с верхней частью регистра, но дана для примера, что IMHO LD могут быть разными инструкциями?
                                                                                                                      –1
                                                                                                                      Если вы про Z80, то в первоисточнике первая инструкция называется LXI, вторая LDA, а третья MVI… (если не напутал).

                                                                                                                      А с другой стороны CMP и SUB в большинстве процессоров — это «почти одно и то же», первая инструкция просто в регистр значение не записывает… Есть даже процессоры, где её просто нету, вместо неё регистр с неизменно-нулевым значением…

                                                                                                                      PCMPEQB/PCMPEQW/PCMPEQD/PCMPEQQ/PCMPGTB/PCMPGTW/PCMPGTD/PCMPGTQ — это восемь разных инструкций x86 (причём они ещё и в разных процессорах появились: SSE2, SSE4.1, SSE4.2!), а в ARM — это всего-навсего одна инструкция VCMP…
                                                                                                                        +1
                                                                                                                        Ваша ссылка ведет просто на статью в википедии, где про инструкции ни слова…

                                                                                                                        Вычитал вот тут: en.wikipedia.org/wiki/Instruction_set_architecture, что вроде как я был прав, считая инструкциями все варианты, включая перебор регистров.

                                                                                                                        То есть инструкция — это собственно полная команда, которая включает в себя opcode и возможно допольнительные байты с data

                                                                                                                        Для инструкций типа LD, если параметром или одним из них является регистр, то для каждой комбинации LD <регистр> будет разный opcode.
                                                                                                                        А если параметром будет значение или адрес — это уже просто data, которая идет после opcode.

                                                                                                                        Итого:
                                                                                                                        LD A, 0000h — одна инструкция.
                                                                                                                        LD B, 0000h — другая инструкция.
                                                                                                                        LD B, 0001h — таже вторая инструкция с другими данными.

                                                                                                                        Таким образом количество инструкций которое поддерживает процессор нужно считать с перечислением всех вариантов с разными регистрами.
                                                                                                                          –1
                                                                                                                          Таким образом количество инструкций которое поддерживает процессор нужно считать с перечислением всех вариантов с разными регистрами.
                                                                                                                          Я не знаю кому и зачем это нужно. Знаю только что для подобного маразма даже маркетолого не додумались. Когда MMX описывается как 57 новых инструкций — то это точно не по вашей методике делается. А иначе они бы в одной им MOVD насчитали бы сотни инструкций — там для задания регистров есть аж два байта: ModR/M и SIB.

                                                                                                                          А после появляения x86-64 их количество бы, примерно, учетверилось бы.

                                                                                                                          Для инструкций типа LD, если параметром или одним из них является регистр, то для каждой комбинации LD <регистр> будет разный opcode.
                                                                                                                          А если будет одинаковый? У VFMADDPD 4й регистр в байте immediate задаётся, а у CMPPD там же задаётся условие.

                                                                                                                            0
                                                                                                                            Суть в том, что если разный opcode, то в кристалле процессора должна быть реализована логика для каждой отдельно взятой инструкции.
                                                                                                                            Вполне возможно, что в z80 так и было, но сейчас и регистров больше и наборов регистров больше и микрокод существует, поэтому ситуация вполне могла измениться.
                                                                                                                              +1
                                                                                                                              Суть в том, что если разный opcode, то в кристалле процессора должна быть реализована логика для каждой отдельно взятой инструкции.
                                                                                                                              Никогда так не было. Ни в одном известном мне кристалле.

                                                                                                                              Вполне возможно, что в z80 так и было, но сейчас и регистров больше и наборов регистров больше и микрокод существует, поэтому ситуация вполне могла измениться.
                                                                                                                              Уж в 8080/z80 — так не было на 200%. Если вы посмотрите на карту опкодов 8080, то обнаружите, что ровно четверть её (64 инструкции из возможных 256 кодов) занимает ровно одна инструкция mov. Зачем же реализовавывать шесть десятков раз почти одно м то же? Да и вообще: как вы это себе представляете? 256 опкодов, 6000 транзисторов, 24 транзистора на опкод… что вы в 24 транзистора упихаете? А учтите, что в эти 6000 транзисторов нужно ещё уложить и регистры и модуль общения с памятью и кучу всего ещё…

                                                                                                                              Конечно же всё было совсем не так: увидев, что старшие биты опкода 01 — всё «уезжало» в реализацию одной инструкции MOV и все 63 инструкции обрабатывались по одному шаблону. 63 потому что, что «MOV (HL), (HL)» (которая должна была, исходя из логики декодирования, переслать адрес из ячейки памяти в неё же) вызывала у процессора «несварение» и он «замораживался». Ей просто дали название HLT и так и оставили…

                                                                                                                              А у 6502 было ещё круче: все опкоды пропускались через «таблицу декодирования», где было пара десятков строк (точное число не помню). И они были подобраны так, что на каждый документированный опкод реагировали 3-4 «строки» — и что-то делали.

                                                                                                                              Вместе — получилась не вполне бессмысленная система команд (хотя и очень-очень странная).

                                                                                                                              Самое смешное, что незадокументированные опкоды тоже активизировали какие-то строки и тоже что-то делали… ну что им захотелось — то и делали.

                                                                                                                              Энтузиасты, разумеется, всё происследовали и составили список

                                                                                                                              Так что нет — никогда и нигде, ни в одном процессоре, реально созданном людьми, ваше странное правило «если разный opcode, то в кристалле процессора должна быть реализована логика для каждой отдельно взятой инструкции» не соблюдалась.

                                                                                                                              P.S. Странно только что вы об этих самых-самых азах не знаете. В том смысле, что если вам это всё неинтересно… то почему вы вообще об этом пишите? А если интересно — то информации ж вагон (в том числе на Хабре), зачем же выдумывать?
                                                                                                                                0
                                                                                                                                все опкоды пропускались через «таблицу декодирования», где было пара десятков строк (точное число не помню)

                                                                                                                                По вашей же ссылке написано, что 130 — т.е. на порядок больше, чем «пара десятков».
                                                                                                                                  –1
                                                                                                                                  130 — это количество ячеек в этой таблице, а не количество строк.

                                                                                                                                  А где-то видел статью, где всё это подробно разбиралось.
                                                                                                                                    0
                                                                                                                                    Нет же, 130 строк по 21 биту в строке.
                                                                                                                                    Подробный разбор тут: www.pagetable.com/?p=39
                                                                                                                                      +1
                                                                                                                                      Ага. Теперь всё понял. Кто-то считает, что это таблица 130x21, кто-то что 21x130.

                                                                                                                                      Так как это всё — не из чтения документации, а из исследования чипа под микроскопом, то сложно сказать кто прав… достаточно чип повернуть на 90 градусов — и строки станут столбцами, а столбцы строками.

                                                                                                                                      На фотографии по вашей ссылке, где эта «таблица» подкрашена зелёненьким, строк ну явно сильно меньше, чем столбцов кстати.
                                                                                                                                  0
                                                                                                                                  Это понятно, что группы инструкций типа LD не имели индивидуальной обработки, и в архитектуре для них были общие транзисторы до определенного момента. Это логично и правильно.

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

                                                                                                                                  По вашей же ссылке, где указывается, что добавлено 57 новых инструкций, есть конкретный документ, где они описаны:
                                                                                                                                  www.intel.com/content/dam/www/public/us/en/documents/research/1997-vol01-iss-3-intel-technology-journal.pdf

                                                                                                                                  Overall, 57 new MMX instructions were added to theIntel Architecture instruction set

                                                                                                                                  и ниже таблица, подпись к которой гласит, что это ВСЕ mmx Инструкции. И похоже, что 57 как раз означает разные варианты, потому что в таблице едва десяток, с указанием на разные размерности наберется десятка два. И из этого уже нужно насчитать 57.

                                                                                                                                  Заголовок спойлера

                                                                                                                                    +1
                                                                                                                                    Во-первых, с указанием размерностей уже получится 35 штук — это не так сложно подсчитать, как кажется. Во-вторых, если смотреть по полным спискам в мануале, например, кроме PADD{B,W,D} будет PADDQ — и это не более поздний набор фич, это всё тот же MMX. Так что журнальная публикация наверняка просто не всё перечислила. Откуда 57 — не знаю. Текущий мануал говорит про 47 (том 1 глава 9.4). Может, 57 — просто опечатка.

                                                                                                                                    А вот если бы там была зависимость от регистра, то каждую надо было бы умножать на 64 (mm0..mm7, 2-3 регистра), а если учесть варианты адресации памяти — то и на пару тысяч. Я вот уверен, что этот подход там не применялся :)
                                                                                                                                      +1
                                                                                                                                      Во-первых, с указанием размерностей уже получится 35 штук — это не так сложно подсчитать, как кажется.
                                                                                                                                      Вы второй столбец забыли. PADDB и PADDSB это разные инструкции.

                                                                                                                                      Во-вторых, если смотреть по полным спискам в мануале, например, кроме PADD{B,W,D} будет PADDQ — и это не более поздний набор фич, это всё тот же MMX.
                                                                                                                                      А зато вот с насыщением там только два размера: PADDSB и PADDSW.

                                                                                                                                      Текущий мануал говорит про 47 (том 1 глава 9.4).
                                                                                                                                      Он говорит про 47, они там даже приведены… и как раз PADDQ там и нету.

                                                                                                                                      Похоже кто-то решил, что 57 — это опечатка и лишние инструкции «выкинул за ненадобностью». А вот если вернуть «забытые» PADDQ и PSUBQ и засчитать каждый из 8 сдвигов как два (там две версии у каждого из них: одна сдвигает на значение в MMX регистре или памяти, другая на значение, заданное в инструкции), то как раз 57 и получится.
                                                                                                                                        0
                                                                                                                                        Текущий мануал же явно отличается от того, когда MMX ввели впервые. Я больше обсуждаю то, что касалось z80

                                                                                                                                        www.z80.info/z80arki.htm
                                                                                                                                        The Z80 CPU instructions length can be from one to four bytes long. To increase the Z80 CPU speed most instructions are only one byte long. 252 instructions are one byte, the rest are 2, 3 or 4 bytes long.

                                                                                                                                        Что означает, что посчитали как раз все инструкции, и 3 специальных, которые начинают 2-3-4 байтные инструкции.

                                                                                                                                        В общем я не думаю, что имеет смысл спорить на эту тему. Думаю никто не заморачивался стандартизацией как правильно подсчитывать кол-во инструкций в процессоре, поэтому значение может отличаться в разных документациях.
                                                                                                                            0
                                                                                                                            Там табличный декодер. Опкод переводится в последовательность управляющих сигналов. В процессе выполнения этой последовательности считываются непосредственные данные xxxx.

                                                                                                                            LD могут быть разными инструкциями?

                                                                                                                            Разумеется тут все 3 инструкции разные, с одинаковыми мнемониками, для удобства.

                                                                                                                            Но когда вы говорили про 1200 инструкций, вы считали
                                                                                                                            LD a,n LD b,n LD c,n LD e,n LD d,n и т.д. как разные инструкции, а она одна.
                                                                                                                              0
                                                                                                                              Я почему-то всегда считал, что
                                                                                                                              LD — это команда
                                                                                                                              «LD a,n» это инструкция, и поэтому у нее есть конкретный машинный код напрямую обрабатывается уже архитектурой CPU

                                                                                                                              Я был неправ?

                                                                                                                              P.S. Понятно что у современного компа есть уже несколько чипов еще на материнке, добавились микрокоды внутри процессора, и так далее. но в Z80 было проще.
                                                                                                                                0

                                                                                                                                Я вот со времён КР580/Z80 считал, что LD — это мнемоника, LD , — это команда, а LD a, 0 — конкретная инструкция

                                                                                                                                  0
                                                                                                                                  А разве у КР580 не 8080 ассмеблер? Там такого бардака ещё не было: одна мнемоника = одна инструкция = один шаблон.

                                                                                                                                  А вот уже в Z80/8086 начался «разброд и шатание». Причём вот на самых-самых простейших инструкциях.

                                                                                                                                  Вот такое вот:
                                                                                                                                  a0 34 12 mov al, byte ptr[0x1234]
                                                                                                                                  8a 06 34 12 mov al, byte ptr[0x1234]

                                                                                                                                  Это вот две инструкции или одна? Заметим что на 8086 первая — в полтора раза быстрее второй. А вот уже начиная с 80286 — без разницы.

                                                                                                                                    0

                                                                                                                                    Он самый. Но времена-то одни. Дома на 580, в школе на Z80. И как-то после привыкания (580 раньше на пару лет дома появился) даже нравиться стало. Но вот в институте на 8086 уже как-то ассемблер перестал радовать. То ли система команд, то ли "640 кб хватит всем" и нет смысла байты экономить, то ли стало нравится решать прикладные задачи.

                                                                                                                                      –1
                                                                                                                                      Я думаю времени перестало хватать. Если просто задуматься: Б3-34 вышел в 1983м году, а Клуб Электронных Игр его исследовал Еггогологию до 1988го. Пять лет.

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

                                                                                                                                      Просто «соблазнов» стало больше, уже на ассемблер и машинные коды стало не хватать терпения…
                                                                                                                                        +1
                                                                                                                                        > Вы сегодня можете себе представить, чтобы с одной платформой сегодня возились столько лет и копали так глубоко?

                                                                                                                                        Ну я тоже копал еггоги :)
                                                                                                                                        А что ещё делать, когда это единственная толком доступная электронная игрушка, и интересно, как она работает?
                                                                                                                                        Было бы что-то поприличнее и поконструктивнее (хотя бы с постоянной памятью! МК-52 был дорог и редок) — занимались бы чем-то менее специфическим.

                                                                                                                                        > Причём не в одиночку, а тысячими, совместно?

                                                                                                                                        А сколько человек, по-вашему, занимаются тонкостями работы процессоров x86? Явно ещё больше, причём не только из чисто поржать :)

                                                                                                                                        > уже на ассемблер и машинные коды стало не хватать терпения…

                                                                                                                                        Вот вполне хватает.
                                                                                                                                          –1
                                                                                                                                          А сколько человек, по-вашему, занимаются тонкостями работы процессоров x86? Явно ещё больше, причём не только из чисто поржать :)
                                                                                                                                          Вот только этих процессоров — десятки. Какая нибудь ALTINST со своим «подземельем» (причём более глубоким, чем у МК-52) — только на C3 есть.

                                                                                                                                          Вот вполне хватает.
                                                                                                                                          Да ладно? Даже никто не составил карту незадокументированных инструкций, не вызывающих #UD, по моделям! Это, извините, дюже халтурная Еггогология.

                                                                                                                                          Хватает, максимум, инструкцию почитать…
                                                                                                                                            +1
                                                                                                                                            Справедливости ради, инструкция там хорошо за 4к страниц.
                                                                                                                                              0
                                                                                                                                              У ARM — 8 тысяч с лишним. Причём из последних версий много чего выкинули, раньше больше было.

                                                                                                                                              Но это потому что, Aarch32 и Aarch64 имеют между собой мало общего и описываются, фактически, отдельно.

                                                                                                                                              Я потому, кстати, и сказал «почитать», а не «прочитать». Не знаю — читал ли все эти тома хоть кто-нибудь «от корки до корки».
                                                                                                                                          0

                                                                                                                                          Возможно ещё другой вариант сработал: стал программированием зарабатывать, сдельно, по результату, а не на окладе, и стала больше интересовать скорость достижения результата чуть выше чем приемлемая для заказчика. Когда вопрос для себя стоит сдать задачу сейчас и получить деньги сразу, или сдать через неделю и получить те же деньги, максимум денёк-другой (точнее процентов 5 от общих затрат времени) отдашь на невидимые (или незначимые типа потребляемой памяти) заказчику улучшения.