Ноль / знак-расширение не нужны, зачем тогда инструкции для каждого типа размера? - PullRequest
0 голосов
/ 04 октября 2018

Для x86 и x64 компиляторы генерируют одинаковое расширение ноль / знак MOVSX и MOVZX.Само расширение не является бесплатным, но позволяет процессорам ускорять магические операции вне очереди.

Но в RISC-V:

Следовательно, преобразование между неподписанным и подписанным 32-битные целые числа не допускаются, равно как и преобразование 32-разрядного целого числа со знаком в 64-разрядное целое число со знаком.

Требуется несколько новых инструкций (ADD [I] W / SUBW / SxxW)для сложения и сдвигов, чтобы обеспечить разумную производительность для 32-битных значений.

(C) RISC-V Spec

Но в то же время, новый современный RISC-V 64-битовые процессоры содержат инструкции для 32-битных целых чисел со знаком.Зачем?Чтобы увеличить производительность?Где тогда 8 и 16 бит?Я уже ничего не понимаю.

Ответы [ 3 ]

0 голосов
/ 04 октября 2018

Это один из тех случаев, когда ABI начинает кровоточить в ISA.Вы найдете несколько таких в RISC-V.В результате того, что у нас был довольно значительный программный стек, портированный к моменту стандартизации ISA, мы получили возможность точно настроить ISA для соответствия реальному коду.Поскольку явная цель базовых ISA RISC-V состояла в том, чтобы сохранить много места для кодирования, доступного для будущего расширения.

В этом случае проектное решение ABI состоит в том, чтобы ответить на вопрос "Существует ли каноническое представлениетипы, которые при хранении в регистрах не нуждаются в каждом битовом шаблоне, предоставленном этими регистрами, чтобы представлять каждое значение, представляемое типом? "В случае RISC-V мы решили назначить каноническое представление для всех типов.Здесь есть петля обратной связи с некоторыми проектными решениями ISA, и я думаю, что лучший способ сделать это - проработать пример того, что ISA развивалось бы совместно с ABI, где мы не требовали канонического представления.

В качестве упражнения для размышления давайте предположим, что RISC-V ABI не предписывал каноническое представление старших бит int при сохранении в регистре X в RV64I.В результате получается, что существующее семейство инструкций W не будет особенно полезным: вы можете использовать addiw t0, t0, 0 в качестве расширения знака, чтобы компилятор мог полагаться на то, что находится в старших битах, но это добавляет дополнительную инструкцию кмного общих шаблонов, таких как сравнение + ветвь.Правильное проектное решение ISA, которое нужно принять, будет иметь другой набор W-инструкций, что-то вроде «сравните на младших 32 битах и ​​ветви».Если вы запустите числа, вы получите примерно такое же количество дополнительных инструкций (ветвление и настройка в отличие от сложения, подстановки и сдвига).Проблема в том, что инструкции ветвления намного дороже с точки зрения пространства кодирования, потому что они имеют намного более длинные смещенияПоскольку пространство кодирования считается важным ресурсом в RISC-V, когда нет явного преимущества в производительности, мы склонны выбирать проектное решение, которое экономит больше места для кодирования.В этом случае нет никакого значимого различия в производительности, если ABI соответствует ISA.

Здесь необходимо принять решение о разработке второго порядка: будет ли каноническое представление расширяться до нуля или расширяться до нуля?Здесь есть компромисс: расширение знака приводит к более быстрому программному обеспечению (при том же объеме используемого пространства кодирования), но более сложному аппаратному обеспечению.В частности, общий фрагмент C

 long func_pos();
 long func_neg();

 long neg_or_pos(int a) {
     if (a > 0) return func_pos();
     return func_neg();
 }

очень эффективно компилируется, когда используется расширение знака

neg_or_pos:
    bgtz    a0,.L4
    tail    func_neg
.L4:
    tail    func_pos

, но медленнее, когда используется нулевое расширение (опять же, предполагая, что мы не хотимвзорвать много места для кодирования при сравнении размера слова + инструкции ветвления)

neg_or_pos:
    addiw   a0, a0, 0
    bgtz    a0,.L4
    tail    func_neg
.L4:
    tail    func_pos

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

0 голосов
/ 31 июля 2019

Чтобы расширить комментарий принятого ответа, что «8- и 16-битная арифметика редка»: некоторые из наиболее распространенных компьютерных языков разработаны так, чтобы не нуждаться в этом, потому что в популярных ISA прошлого этого не было

C указывает, что любой операнд, более узкий, чем int, «повышается» до int при выполнении любой арифметики с ним.В RISC-V int имеет ширину 32 бита.Существуют инструкции LB / LBU и LH / LHU для выбора между расширением нуля unsigned short и расширением знака signed char при загрузке их из памяти.

C-Семейные языки не нуждаются в поддержке 8-битной или 16-битной математики.Для общих случаев, таких как some_unsigned_short += 1, может быть несколько полезно иметь некоторый гипотетический ADDIH, который автоматически усекает результат.Однако это всего лишь одна дополнительная инструкция (битовая маска 0xFFFF).Такие выражения, как some_signed_short -= 1, даже не нужно делать слишком много, чтобы быть «правильными» или, по крайней мере, для того, чтобы их компиляторы технически соответствовали языковому стандарту, потому что переполнение со знаком или недополнение является неопределенным поведением в C, поэтому компилятор может простоигнорируйте возможность или делайте что хотите.

0 голосов
/ 04 октября 2018

Полная цитата мне кажется ясной:

Соглашение компилятора и вызовов поддерживает инвариант, согласно которому все 32-разрядные значения хранятся в расширенном знаковом формате в 64-разрядных регистрах.Даже 32-разрядные целые числа без знака расширяют бит 31 на биты с 63 по 32.

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

Для добавления и сдвига требуется несколько новых инструкций (ADD [I] W / SUBW / SxxW), чтобы обеспечить приемлемую производительность для 32-битных значений.

В нем говорится, что 32-битные значения хранятся в 64-битных регистрах, причем их MSb (самый значимый бит) повторяется через биты 32-63.
Это делается для как со знаком, так и без знака целых чисел.

Это позволяет выполнить несколько оптимизаций, как указано в цитате:

  • Беззнаковое <-> преобразование со знаком бесплатно.
    Сравните это с обычным алгоритмом, в котором требуется ноль илизнак расширяет нижнее 32-битное значение, чтобы превратить его в 64-битное значение с другой «сигнатурой» (игнорирование переполнения).
  • знаковый 32-битный <-> 64-битный знак - свободный.
    Это экономит знак расширения.
  • Ветви и инструкции установки все еще работают.
    Это потому, что повторение MSb не меняет результат сравнения.
  • Логические 64-битные операции сохраняют это свойство
    Этоэто легко увидеть после нескольких примеров.

Однако дополнение (чтобы назвать одно) не сохраняет этот инвариант: 0x000000007fffffff + 0x0000000000000001 = 0x0000000080000000, что нарушает предположение.

Поскольку а) работа с 32-битными значениями происходит очень часто и б) исправление результата потребует дополнительной работы (я могу подумать об использовании пары slli / srai), новый формат инструкций имеет
Эти инструкции работают с 64-битными регистрами, но используют только их более низкое 32-битное значение и расширяют 32-битный результат.
Это легко сделать аппаратно, поэтому стоит иметь этот новый классобучения.

Как отмечается в комментариях, 8- и 16-битная арифметика встречается редко, поэтому на поиск места для нее не было потрачено никаких технических усилий (как с точки зрения требуемых вентилей, так и используемого пространства кода операции).

...