Так и тянет меня задать в заголовке статьи вопрос, что по здешним правилам не допускается. А ответ опять очевиден: регистр SPL вообще не нужен.

Я уже давно выступал с критикой системы команд AMD64, сейчас более известной как x86-64. Причем, задача специально анализировать появившиеся и исчезнувшие команды не стояла. Просто при переносе средств программирования с Win32 на Win64 возникал ряд проблем, вызывавших один и тот же вопрос: «почему же раньше все работало, а теперь нет?». Это касается некоторых выброшенных разработчиками архитектуры AMD64 команд, которые пришлось эмулировать, и, особенно, аппаратной поддержки контроля целочисленного переполнения с помощью инструкции INTO, которая вдруг стала недоступной.

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

Но все-таки проблемы как-то разрешились, и пришло время не только бороться с недостатками системы команд AMD64, но и воспользоваться ее достоинствами. А основных достоинств, по сравнению с IA-32, напомню, два: восьмибайтная адресация, снимающая предел в 4 Гбайт, и увеличенное число регистров общего назначения в два раза.

В случае регистров размером в 2, 4 или 8 байт действительно все логично и естественно. Можно даже сказать, что число регистров увеличилось более чем в два раза, поскольку указатель стека и не используется в вычислениях как остальные. Поэтому в IA-32 у программиста реально было 7 регистров общего назначения, а в AMD64 их стало 15, т.е. RAX, RBX, RCX, RDX, RBP, RSI, RDI и R8-R15.

Но вот в случае регистров размером в байт не все так логично. Немного отвлекаясь, замечу, что объектам размером в байт вообще «не везет» в системах программирования. По сравнению с объектами больших размеров они выглядят какими-то ущербными.

Помнится, в «Турбо»-Паскале был всего один тип переменной размером в байт – это CHAR, т.е. символ. Но это еще ладно. А вот в стандарте Си CHAR – это вообще почему-то не символ, а целое число.

Мне это потому кажется странным, что в языке PL/1, который я использую, объекты размером в байт – это самые обычные объекты. Только маленькие. Есть целое знаковое число с атрибутами BINARY FIXED(7), есть строка бит BIT(8), есть строка символов CHAR(1). Да и логические данные с атрибутами BIT(1) при вычислениях все равно обычно занимают весь байтный регистр, поскольку так намного удобнее.

Я это все веду к тому, что регистры с размером в байт очень часто нужны и используются в самых разных задачах. Поэтому увеличение числа таких регистров можно только приветствовать. Тем более что разработчики системы команд AMD64 предложили неожиданный бонус: регистров стало не 16, как можно было предположить, а целых 20.

Так как к восьми имевшимся AL, AH, BL, BH, CL, CH, DL, DH добавлены R8L-R15L, а также еще SPL, BPL, SIL и DIL.

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

Первая ложка дегтя – появление регистра SPL, т.е. младшего байта указателя стека. Вопрос для чего он нужен, я и вынес в заголовок. Единственная, хоть какая-то полезная операция, которую я смог для него придумать – это проверка указателя стека на кратность 8 или 16. В самом деле, если проверять весь указатель стека RSP на кратность 8 потребуется команда в 7 байт:

48F7C407000000        test rsp,7

А если использовать регистр SPL, то только 4:

40F6C407              test spl,7

Но это сущая мелочь. И, кстати, если использовать не RSP, а двухбайтный SP, команда станет лишь на байт длиннее:

66F7C40700            test sp,7

И для выравнивания стека на 16, которое часто требуется для обращения к Win API, наличие регистра SPL вообще не дает преимуществ, сравните:

4080E4F0            AND SPL,0F0H
6683E4F0            AND SP,0FFF0H

Безо всякого SPL можно выполнить такое же действие и командой такой же длины. Т.е. этот регистр бесполезен и просто занимает номер (№4) в системе кодов команд. Так что, реально можно использовать уже не 20 регистров, а 19.

Но хуже другое. Дополнительные «бонусные» регистры SPL, BPL, SIL и DIL используют те же коды, что AH, BH, CH, DH (т.е. 4, 5, 6, 7). Это приводит к тому, что «старые» регистры нельзя использовать вместе с «бонусными» в одной команде. Например, нельзя написать команду:

408AE6              mov ah,sil

Потому, что процессор просто воспримет ее как:

408AE6              mov spl,sil

Поскольку коды регистров AH и SPL одинаковые – 4.

А для задачи распределения регистров при компиляции очень важно, чтобы все регистры были «совместимы», т.е. чтобы их легко можно было присваивать друг другу. Потому что часто байтовый регистр нужно сохранить, а затем восстановить. Эффективнее всего запомнить его в другом байтовом регистре. Но, например, если нужно сохранить регистр AH, а свободны в этот момент R8L-R15L, нельзя это сделать командой:

mov r8l,ah

а потом восстановить командой:

mov ah,r8l

Вот и получается, что надо или вообще не использовать AH, BH, CH, DH и тогда байтовых регистров остается 15 (а если бы не делали «бонусные» регистры их реально было бы 16), или как-то эмулировать отсутствующие команды. Например, несуществующую команду:

mov ah,sil

Можно эмулировать тремя командами:

86E0                xchg ah,al
408AC6              mov al,sil
86E0                xchg ah,al

Но все это начинает убивать преимущества дополнительных байтовых регистров.

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

Обратите внимание, что новые по сравнению с IA-32 команды реализованы с помощью REX-префиксов. По сути это просто часть кода команды, вынесенная в отдельный байт. И вот в этом REX-байте только 4 бита несут информацию. Три – это старшие биты номеров регистров-операндов команды (что и дает формальное удвоение их числа), а четвертый – это признак «W», определяющий работу данной команды с 8 или 4 байтами.

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

408AF4              mov sil,spl ; бит «W» сброшен
488AF4              mov sil,spl ; бит «W» установлен, но игнорируется

Этот неиспользуемый бит «W» и надо было бы тоже задействовать для указания типов байтовых регистров. Например, если бит сброшен, то считать, что по-прежнему используются «старые» восемь регистров AL, AH, BL, BH, CL, CH, DL, DH. Тогда бы и получилось 16 (а не 15 + ненужный SPL) нормальных полностью независимых и «совместимых» регистров, которые любым образом можно было бы использовать в одной команде.

Если же бит «W» установлен, то считать, что это дополнительные регистры. Хотя я бы ввел не BPL, SIL, DIL и уж тем более не бессмысленный SPL, а вторые байты регистров R8-R15, т.е. ввел бы дополнительные регистры R8H-R15H, которые и имели бы в этом случае коды 0-7.

Тогда бы получилось не 20, а даже 24 байтовых регистра, и можно было бы хранить по две независимые переменные (размером в байт) во всех регистрах RAX, RBX, RCX, RDX, R8-R15.

Правда, все равно «старые» и «бонусные» регистры в одной команде было бы использовать нельзя. Одного признака «W», увы, для возможности таких команд мало.

Т.е. можно было бы писать команды типа:

and r12l,r13h

Но нельзя писать:

and ah,r13h

Поскольку нет способа указать, какой из двух операндов «старый», а какой – «бонусный» из AMD64.

Заключение

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

Но, возможно, при этом рассматривались как раз системы программирования, где байтные объекты являются «пасынками» и поэтому просто не попалось программ «нормально» работающих с регистрами AH, BH, CH, DH, хотя это совершенно обычные регистры при манипулировании объектами размером в байт. Эти регистры также позволяют удобно хранить по две независимые переменные в одном физическом регистре и не тратить команды на распаковку.

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

Конечно, я обойдусь в своем компиляторе и без 24 однобайтных регистров, тем более что возможности по сравнению с IA-32 все равно значительно расширились. Однако, как показано выше, в случае однобайтных регистров можно было бы сравнительно легко повысить эффективность исполнения и увеличить ресурсы (число таких регистров) без существенных изменений системы команд и внутреннего устройства процессора.

Комментарии (46)


  1. Hisoka
    26.08.2021 04:35
    +2

    Главный вопрос - все эти варианты, что занимают меньше места, действительно выполняются с той же скоростью что и их "жирный" аналог? Не исключено же, что в "железе" такое имеет более длинный путь обработки и будет "занимать больше места" во внутренностях проца. Т.е. будет появляться дополнительный предварительный этап дополнения до полного регистра, к примеру, а далее идут внутренние &|.


    1. marsianin
      26.08.2021 04:56
      +3

      В том-то и дело, что для железа выгоднее, когда код вообще не использует 8- и 16-битную арифметику


    1. khim
      26.08.2021 05:56
      +2

      Главный вопрос - все эти варианты, что занимают меньше места, действительно выполняются с той же скоростью что и их "жирный" аналог?

      Всё зависит от процессора. И на большинстве современных процессоров эти короткие версии исполняются со скоростью от медленно до… катастрофически медленно.

      Ни о какой эффективности речь не идёт в принципе.

      Можно было бы предположить, что речь идёт о какой-нибудь странной встраиваемой системе на базе какого-нибудь 386EX с дикими ограничениями по памяти… но тогда причём тут x86-64?

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

      Всех этих команд уже много десятилетий нет в железе. Они эмулируются в микрокоде и делается это строго для возможности запускать дикий легаси. Отсюда вся неэффективность.


      1. Hisoka
        26.08.2021 05:58

        Про это я и написал :)


  1. vicsoftware
    26.08.2021 04:39
    +2

    В Turbo Pascal вроде был ещё один байтовый тип. Так и назывался - BYTE. Или я путаю?


    1. amphasis
      26.08.2021 09:16

      Был. А еще вроде enum занимал один байт.


      1. khim
        26.08.2021 12:19

        Enum и сейчас, в C++, может один байт занимать. А в Rust в один байт может быть запакован и enum и Option<enum>.

        Да вообще много чего бывает, но если эффективность программы мерить количеством байт кода, не обращая внимание на то, сколько времени оные байты исполняются“… можно много интересного наизобретать.


  1. marsianin
    26.08.2021 04:46
    +4

    На современных процессорах x86 использование 8- и 16-битных регистров бьёт по производительности. В частности из-за того, что называется source merge. Собственно, все регистры общего назначения 64-битные. 32-битные инструкции обнуляют старшую часть регистра, а 8- и 16-битные её сохраняют --- очевидно это достигается исполнением дополнительных микроинструкций.

    В общем-то, эти регистры сохранены для совместимости со старым софтом, и лучше бы их совсем не использовать. Собственно, разработчики мэйнстрим-компиляторов и не используют этих регистров, а всюду, где возможно, расширяют 8- и 16-битную арифметику до 32 бит.


    1. Hisoka
      26.08.2021 05:01

      Вот, да. Конечно приятнее, когда код более плотный, но не такой ценой.


      1. marsianin
        26.08.2021 05:08
        +1

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


  1. teology
    26.08.2021 05:05

    Кто знает, может с точки зрения архитектуры процессора и закладки туда шпионских примочек, SPL и прочие вещи очень даже логичны... ?


    1. khim
      26.08.2021 05:56

      Нет, всё банальнее и проще. Они логичны с точки зрения устройства декодера.


  1. khim
    26.08.2021 05:09
    +4

    Статья оставляет какое-то дико странное впечатление.

    С одной стороны — автор утверждает, что у него есть какой-то там компилятор.

    С другой — наблюдаем явный наив и какой-то лютый бред. Так, эта, компилятор — он таки есть или его таки нет?

    Ибо, ну… О ужас, INTO отобрали! А ведь было так удобно: приписал INTO и инструкция, занимавшая четверть такта начала занимать в двадцать раз больше времени! Зашибись!

    И это если мы говорим о каком-нибудь Skylake или Haswell'е, где они 5-6 тактов занимает, а не о AMD'шном Bulldozer'е, где она 24 такта занимает (а получаемое замедление от использования, соотвественно, программу почти в сто раз замедляет!). А началось это, прости господи, с классического Pentium, где она уже требовала 4 такта (больше, чем на 80486), притом что простая арифметика уже могла исполняться за две инструкции за такт. Правда AMD, как раз, долгое время достаточно эффективно её реализовывала, но так как это никому не было нужно, то… см. что они в “тяжёлой технике” с ней сделали.

    Но на Intel платформе INTO нельзя было использовать, к моменту её отмены, уже лет 10 как и дико странно, что вы о ней вообще вспомнили.

    Рассуждения про однобайтовые регистры — тоже вызывает недоумение. Ещё раз: компилятор у вас есть или его нет? Ибо почти все виденные мною компиляторы считают, что на классической IA32 платформе однобайтовых регистров не 8, но 4: AL/CL/DL/BL. И всё. AH/CH/DH/BH как бы и не существуют вовсе. Только если руками ассемблерную вставку оформляете — тогда они могут быть использованы.

    Причём это было настолько твёрдо установленным фактом уже четверть века назад, что Pentium Pro под это был заточен (на чём и погорел: оказалось, что компиляторы компиляторами, а вот ручками написанный на ассемблере код Windows 95 устроен иначе).

    Соотвественно с точки зрения компилятора на x86-64 всё просто и логично: от 4 регистров перешли к 15. Увеличив их количество в три с лишним раза. Ну а что AH/CH/DH/DH ещё существуют и их иногда можно поиспользовать — так это, опять-таки, для переноса ручками написанного кода.

    Разговоры про хранение двух переменных в одном физическом регистре тоже “доставляют”… ещё раз: оно у вас, извините, есть? Бенчмарки можно какие-нибудь посмотреть, сравнить с LLVM или GCC? Или это, опать, сферические рассуждения в вакууме?


    1. marsianin
      26.08.2021 05:17

      Так, эта, компилятор — он таки есть или его таки нет?

      Компилятор, вероятно, есть. Но Optimization Reference Manual от Intel и AMD читать, похоже, умеют не все (-:


      1. x86128
        26.08.2021 05:25

        Вот, да, достаточно взглянуть на какой-нибудь godbolt чтобы посмотреть как современные компиляторы оптимизируют код. Иногда программа которая содержит в 5 раз больше машинных команд работает в 2-3 раза быстрее такой вот компактной.

        Да и в целом не ясно зачем может понадобиться работать с 8-ми вычислениями и ловить переполнения, когда есть широкие регистры?


      1. khim
        26.08.2021 05:38
        +2

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

        Вместо того, чтобы оную муху взять — и эти ноги пересчитать.

        Читаешь вот это вот:

        Например, взять те же DAA и DAS. Они однобайтные, не имеют параметров и (редкий случай!) не имеют исключительных ситуаций. Т.е. обрабатываются в процессоре самым простым образом. Зачем же было отключать несложную и эффективную аппаратную поддержку целого класса объектов и задач? Неужели только ради убирания нескольких примитивных инструкций?

        И возникает ощущение, что столнулся с каким-то пришельцем из прошлого века, этаким Фи́лип Джей Фрайем. Несложная и эффективная аппаратная поддержка? А ничего, что уже в Pentium 4 больше 20 лет назад DAA и DAS начали требовать по сотне тактов процессора? Prescott, правда, ускорился, всего-навсего 29 тактов требуется…

        А эффективность кода оценивается, барабанная дробь… в байтах.

        Мы эта, вообще в каком веке живём?

        Если вас эффективность не волнует (а если вы всё ешё используете DAA/DAS и INTO, то она вас точно не волнует), то почему, вдруг, начали волновать размеры?

        Что вы пытаетесь создать, для кого и причём тут x86-64?


        1. Dukarav Автор
          26.08.2021 16:44

          Откуда Вы взяли такие такты? Со времен Пентиума DAA - 3 такта, DAS - 3 такта, INTO - срабатывание 56 тактов, пропуск - 4 такта. Откуда взялись сотни тактов? Современные процессоры могут добавить задержку от 0.33 до 1.5 тактов. И, да, меня волнует эффективность моих программ, поэтому и использую аппаратную поддержку точных вычислений. И волнует надежность, поэтому и нужен аппаратный контроль переполнения.


          1. khim
            26.08.2021 17:24
            +1

            Откуда Вы взяли такие такты?

            А откуда их вообще все люди, занимающиеся компиляторами, берут? Ну, кроме тех, кто непосредственно работает в AMD или Intel? Из всем известных таблиц, очевидно.

            Вообще дико видеть человека, рассуждающего о написании компилятора и не знаюшего об Агнере.

            Со времен Пентиума DAA - 3 такта, DAS - 3 такта

            Угу. Вот начиная с Пентиума на них и начали “забивать”. 80486 — 2 такта, Pentium — 3 такта (и параллельно ничего другого исполняться не может, то есть уже тогда, уже на том самом Pentium'е INTO в 3-6 раз хуже, чем JO, который только V pipe занимает на один такт, давая запустить на U pipe какое-нибудь сложение), дальше — ещё хуже.

            Чего вы хотите от “инструкций, которые более не используются”? Это из той же оперы, что и LOOP какой-нибудь, который на том же Pentium уже тоже 5-6 тактов занимают (и который, кстати, в x80-86 режиме остался и даже, на AMD, не сильно тормозит, но если ваш код используется ещё и на Intel'овских процах, то, разумеется, LOOP вы использовать не будете)

            Откуда взялись сотни тактов?

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

            Впрочем не спрорю: на большинстве процессоров вы, скорее, получите десятикратное замедление, чем стократное, 50-100 тактов это только на отдельных “особо выдающихся” архитектурах.

            И волнует надежность, поэтому и нужен аппаратный контроль переполнения.

            Дык вы ж, вроде как, компилятор пишите? Причём тут вообще надёжность? У вас JO уже ненадёжным стал?

            Вы в курсе того, что делают другие на этом поприще? Про Intel MPX знаете? А про то, что его использование оказалось бессмысленным (и его выпилили и из GCC и из ядра Linux)?

            Как я уже написал в другом месте: такое ощущение что читаешь записки Фрая, свалившегося в криогенную камеру и провалявшегося в ней лет 20-30.

            И не знающего ни того, как работают современные компиляторы, ни того, как работают современные процессоры.

            Но при этом поддерживающего x86-64.

            Странно это всё выглядит.


            1. JPEGEC
              28.08.2021 01:57

              Странно это всё выглядит.

              Это выглядит как последствия самообразования. Когда человек имеющий образование отличное от профильного в области доходит до всего своим умом. В этой среде очень часто возникают разнообразные ереси.

              И это даже в чем-то хорошо - иногда встяхивает общее "потому что это так" болотце.


              1. khim
                28.08.2021 02:20

                И это даже в чем-то хорошо - иногда встяхивает общее "потому что это так" болотце.

                Когда вы в поседний раз такое видели?

                Во всех областях развитие, действительно, проходит “сургучно-верёвочный” период.

                Когда талантливые одиночки, без какого-образования могут изобрести нечто революционное.

                Как громоотвод, который был изобретён человеком, которого мы знаем, как политика.

                В разработке компиляторов такое время тоже было. Но кончилось. Где-то четверть века назад.

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

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

                P.S. Вообще я глянул на сайт и мне стало грустно и обидно за Российскую космонавтику. У неё ведь нет будущего с такими самоучками! Потому что они ведь сделали всё возможное и невозможное для того, чтобы все их разработки сгинули вместе с ними, чтобы их невозможно было использовать никому и никак после того, как они уйдут на пенсию. Ну ладно, не совсем невозможно, если археологов привлечь, то может что-то ещё можно и спасти. Но ведь этого никто не делает, так что шансов мало. И ладно бы если бы они, таким путём, заработали себе на домик на Гаваях. Так нет же — этого ведь тоже нет! Вот кем надо быть, чтобы сознательно гробить дело своей жизни? И даже не понимать, что этим всё кончится?


      1. Hisoka
        26.08.2021 06:00

        Может это транслятор в 1С? Тогда это многое бы объяснило D:


        1. marsianin
          26.08.2021 06:05

          Ну, судя по сайту автора, это компилятор PL/I. Только там нет исходников, а у меня нет винды, поэтому я не могу запустить бинарную сборку.


          1. khim
            26.08.2021 06:30

            А даже если запустите? Тут же главный вопрос не в том даже “существует оно или нет”, а скорее “а зачем оно такое кому-то нужно”.

            То есть, с одной стороны, можно предположить, что это жуткое легаси, которое боятся перевести на что-то более вменяемое. Ибо как работает та каша, что у нас имеется мы не знаем, а использовать-то её как-то нужно. Ладно. Бывает.

            Но если эффективность никого не волнует (волновала бы — давно бы адаптировали что-то более эффективное, чем слегка допиленный компилятор четвертьвековой давности), то почему, вдруг, стал волновать режим x86-64 ?


            1. marsianin
              26.08.2021 06:36

              Ну, мне иногда интересно потыкать палочкой в разные компиляторы и посмотреть, какой код они генерируют. Не то, чтобы в данном случае был какой-то практический смысл. В конце концов, если бы PL/I был кому-то нужен, компиляторы для него писали бы разработчики процессоров (-:


              1. khim
                26.08.2021 12:22

                На все языки, которые "кому-то нужны" раработчиков процессоров не хватит.

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


  1. anonymous
    00.00.0000 00:00


  1. NeoCode
    26.08.2021 09:07
    +1

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

    Сама система регистров — очевидный пример такого нагромождения. Есть базовые регистры x86, регистры x64, регистры FPU, регистры MMX, регистры SSE и т.д. При этом некоторые регистры совмещены, некоторые — отдельные. Регистры FPU зачем-то организованы в виде стека, что выбивается из общей системы прямой индексации.

    В системе команд нет встроенных команд MIN, MAX и CLAMP (это все делается конечно через проверки и условные переходы, но операции определения минимума и максимума достаточно частые, почему бы их не сделать командами?)

    Нет команды RBIT (разворота битов в слове). Хотя места для того чтобы ее воткнуть имеются — например в унарной группе 0xF6 есть свободная позиция (TEST, <свободно>, NOT, NEG, MUL, IMUL, DIV, IDIV). Кто-то говорит что такая команда не нужна, т.к. редко используется. Но в ARM она есть, и кроме того это по смыслу фундаментальная операция.

    Арифметические операции с насыщением хоть и существуют, но не для базовых регистров, а в MMX и SSE. Хотя наверное можно было бы сделать это префиксами для основных команд и регистров.

    В FPU почему-то операции сравнения числа на равенство и неравенство с NaN всегда дают false, хотя какой в этом смысл? В результате код сравнения для float усложняется. При этом минимум и максимум с NaN дает второй аргумент (не NaN).

    Система SIMD сделана на отдельных командах, хотя тоже напрашивается мысль — почему бы не сделать ее префиксами для стандартного набора команд? Есть же префиксы повторения операций (REPE, REPNE). Но и они работают не унифицированно, а только с ограниченным набором команд. Но конечно все это потребовало бы глубокого редизайна системы команд и регистров.

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


    1. alashkov83
      26.08.2021 09:31

      И к возможной перекомпиляции туевой кучи софта, если еще есть исходники. С другой стороны: историю про шатл и древнеримские лошадиные жопы напоминает.


      1. khim
        26.08.2021 12:57

        Напоминает и будет напоминать, пока какая-нибудь внешняя по отношению к IT-индустрии сила ситуацию не изменит.

        Например если Россия или Китай, на уровне законодательства, заставят народ отказаться от Windows и пересесть на RISC-V или там E2k — ситуация может и измениться.

        Если же рынок будет предоставлен сам себе, то никаких шансов нет.

        Посмотрите на Apple, который уже 4 раза меняет процессорную архитектуру. Каждый раз это начинается с того, что выпуск железок на старой архитектуре принудительно прекращается: 68060 ни в одной модели не использовался, старшие модели PowerPC (перед переходом на x86) тоже были проигнорированы, как и Ryzen.

        Ну и кто ещё контролирует какую-либо платформу так плотно, что может себе подобное позволить и не обанкротится, когда ниша будет занята конкурентами?


    1. khim
      26.08.2021 12:52

      Регистры FPU зачем-то организованы в виде стека, что выбивается из общей системы прямой индексации.

      Почти все странности в таких случаях объясняются “историческими причинами”. Изначально для FPU было выделено всего-навсего 8 опкодов и формат инструкции большего не предполагал: 3 бита в основном опкоде, ещё 3 в ModRM - и… крутись как хочешь.

      Программы в те годы писали на ассемблере и стековая организация позволила совместить компактность кода и эффективность.

      Что касается MIN и MAX, то они есть, но лучше бы их не было. Ибо MIN(1.0, NaN) или MAX(1.0, NaN) — это NaN, а вот MIN(NaN, 1.0) и MAX(NaN, 1.0) — это 1.0.

      Ну так эффективнее было реализовывать, а что такой хоккей нафиг никому не нужен — так это мелочи.

      Кто-то говорит что такая команда не нужна, т.к. редко используется. Но в ARM она есть, и кроме того это по смыслу фундаментальная операция.

      Правильно говорят. Вот именно ARM и показывает почему она не нужна. Команду RBIT я встречал в живом виде только в двух случаях:

      • Для эмуляции RBIT (в интерпретаторах) — просто “шоб було”

      • В ARM - чтобы эмулировать CTZ (которого там нету).

      Сама по себе она просто никому особо не нужна (хотя будет очень инетресно узнать что-нибудь о существовании алгоритма, где она применима), а CTZ нужен куда как чаще, потому логично что в x86 имеется LZCNT и TZCNT, но не RBIT.

      В FPU почему-то операции сравнения числа на равенство и неравенство с NaN всегда дают false, хотя какой в этом смысл?

      Смысл в возможности использовать языки созданные до повяления понятия NaN. Заметьте, что в уже упомянутом вами ARM NaN ведёт себя так же.

      При этом минимум и максимум с NaN дает второй аргумент (не NaN).

      То есть вы-таки в курсе о том, что они существуют?

      Система SIMD сделана на отдельных командах, хотя тоже напрашивается мысль — почему бы не сделать ее префиксами для стандартного набора команд?

      А в каком процессоре сделано так, как вы предлагаете, извините?

      Но в какой-то момент нужно уже остановиться и полностью пересмотреть систему команд, провести полный рефакторинг всего — как минимум для повышения ясности самой архитектуры.

      Отличное предложение и уже реализованное. Один раз, второй, третий, четвёртый, пятый… это то, что до пользователей дошло только! А сколько миллиардов было выкинуто на разработки, которые просто были выкинуты в корзину? Не сосчитать.

      Кто знает, возможно это привело бы и к высвобождению транзисторов на кристалле, и к устранению каких-то уязвимостей, и к упрощению компиляторов

      Главное, к чему бы это привело — к гневозможности запускать существующие бинарники и последующим убыткам.

      Автор статьи — неординарная личность, зачем-то пилит поделку, которая ещё в 80е была создана под 64 бита.

      Этим действительно люди почти не занимаются — но вот запускать имеющиеся у них бинарники они таки хотят. И если у них не получается — сильно огорчаются. А для разработчика процессора это кончается убытками.


  1. anonymous
    00.00.0000 00:00


    1. marsianin
      27.08.2021 18:41

      Зачем вообще нужны эти R8-R15? Это что, RISC-процессор?

      Когда доступно много регистров, компилятору становится проще жить. Не надо без конца спиливать переменные в память. Плюс, с добавлением дополнительных регистров пришло новое соглашение о передаче параметров в функции: первые 6 параметров передаются на регистрах.


      1. Dukarav Автор
        27.08.2021 18:49

        первые четыре - через RCX, RDX, R8 и R9


        1. marsianin
          27.08.2021 19:55

          Первые шесть: RSI, RDI, RDX, RCX, R8 и R9. Подробнее здесь: https://uclibc.org/docs/psABI-x86_64.pdf


          1. Dukarav Автор
            27.08.2021 20:24

            я имею ввиду Win API - там четыре


            1. marsianin
              27.08.2021 21:09

              Всё-таки ABI, а не API


  1. anonymous
    00.00.0000 00:00


    1. Dukarav Автор
      27.08.2021 18:48

      Байтовые операции не нужны.

      Сообщите свои соображения разработчикам AMD64. Очевидно, они их не учли и в результате имеется двадцать лишних байтовых регистров.


      1. marsianin
        27.08.2021 20:03
        +1

        Они и не нужны. На самом деле компиляторы практически не используют их.


        1. Dukarav Автор
          27.08.2021 20:33

          А вот сейчас стало обидно за компиляторы, которым надо реализовывать логические переменные true-false


          1. marsianin
            27.08.2021 21:09

            По стандарту C++, например, во всех операциях переменные целочисленных типов, меньшьх int, должны быть преобразованы к int. Так сложилось, что в x86-64 тип int 32-битный. Поэтому вся целочисленная арифметика как минимум 32-битная.

            GCC и LLVM так же генерируют 32-битные операции для bool-переменных: https://godbolt.org/z/1vqnjWezs


            1. Dukarav Автор
              27.08.2021 21:31
              -2

              Правильно. Зачем нужны легковые автомобили, когда можно прекрасно ездить на самосвале. У него и кабина просторнее и обзор лучше.


              1. khim
                28.08.2021 02:41

                Причём тут самосвал? Работа с регистрами размером с байт банально медленнее. Потому что у них идиотская семантика: прочитать регистр, изменить один или два байта, записать значение обратно.

                Формально время на исполнение то же, но при этом могут возникать паразитная зависимость между данными, которая не позволяет исполнять инструкции спекулятивно.

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


  1. anonymous
    00.00.0000 00:00


    1. khim
      28.08.2021 02:35

      Уже Pentium Pro прекрасно параллелил такие штуки:

      Не умел. Не надо сказок.

      EAX в строке 1 и EAX в строке 4 мапятся на разные регистры регистрового файла, и блоки 1-3 и 4-6 исполняются в порядке (1, 4), (2, 5), (3, 6) - это утрированно, на самом деле операции ещё бьются на микроопы, и уже те переупорядочиваются при исполнении.

      Вот только в вашем случае ничего переупорядочить на Pentium Pro нельзя ибо у блока процессора, этим занимающегося, нет информации о том, являются [EDI]и [ESI+8]одним и тем же местом в памяти или нет.

      В последних процессорах появились кой-какие попытки иногда в этом разобраться и запустить чтение до записи.. Записи же в память поменять нельзя вообще никогда. MOV [EDI+8], EAXне может отработать до MOV [EDI], EAXни при каких условиях.

      Вот для того и нужно много регистров: чем меньше мы ображаемся к памяти и, главное, чем реже мы оттуда читаем — тем лучше.

      Исследования показали, что наилучшее количнество регистров — 32, если их больше, то становятся слишком длинными команды, если меньше — то приходится часто обращаться в память.

      Но это если мы ISA делаем с нуля. Так как AMD пришлось прикручивать x86-64 к уже существующей ISA, то оказалось выгоднее 16 регистров сделать.


      1. marsianin
        28.08.2021 06:41

        Более того, архитектура x86 явно запрещает переупорядочивать операции чтения. То есть нельзя поменять местами два чтения или две записи. Можно только чтение и запись.