Преобразование между прямым порядком и прямым порядком байтов на RISC-V

Каков самый простой способ работы с прямым порядком байтов в RISC-V на уровне языка ассемблера? То есть, как загрузить значение с прямым порядком байтов из памяти в регистр, работать со значением регистра с прямым порядком байтов (с прямым порядком байтов), а затем сохранить его обратно в память с прямым порядком байтов. 16-, 32- и 64-битные значения используются во многих сетевых протоколах и форматах файлов.

Мне не удалось найти инструкцию по замене байтов (эквивалент BSWAP на x86 или REV на ARM) в руководстве, а также ничего о загрузке и хранении с прямым порядком байтов.


person Lassi    schedule 30.08.2018    source источник
comment
Вы пробовали проверить документацию? Это действительно вопрос, на который можно ответить, быстро прочитав соответствующий документ.   -  person fuz    schedule 30.08.2018
comment
Да, несколько источников. Обмен байтами - настолько распространенная операция, что я подумал, что, должно быть, что-то пропустил, поэтому и спрашиваю здесь.   -  person Lassi    schedule 30.08.2018
comment
Если этого нет в спецификации, его, вероятно, нет.   -  person fuz    schedule 30.08.2018
comment
Я попытался проверить, что делают компиляторы, когда вы запрашиваете обмен байтами, но установка Godbolt clang risc-v не работает и пытается использовать встроенный asm x86 для endian.h be32toh(). godbolt.org/z/6MzVWa. Возможно, написание чистого C, который компиляторы могут распознать как перестановку байтов, сработает, но не докажет отсутствие инструкции.   -  person Peter Cordes    schedule 30.08.2018
comment
Спасибо за усилия, Питер. Я переработал вопрос в вопрос с практическими рекомендациями вместо того, чтобы спрашивать конкретно об инструкции, сделанной для этой цели. Надеюсь, что так лучше.   -  person Lassi    schedule 30.08.2018
comment
@Lassi Достаточно честно. Голос против отклонен.   -  person fuz    schedule 30.08.2018
comment
Это можно сделать с помощью одной инструкции с расширением XBitmanip, это не ядро ​​и даже не завершенное расширение. Так или иначе, это входит в рамки этого вопроса?   -  person harold    schedule 30.08.2018
comment
@harold Да, я тоже это заметил. Я думаю, что это входит в рамки и стоит иметь в виду. Поскольку расширение имеет дело с такими простыми задачами, как манипуляции с битами, оно могло бы получить чрезвычайно широкое распространение после завершения, что сделало бы его жизнеспособным решением для большинства целей.   -  person Lassi    schedule 30.08.2018
comment
Я изучал это, исследовал качество кода ... См. Gcc.godbolt.org/z/dwMH9S, где показан сгенерированный код для нескольких подходов, имеющих дело с 64-битными хранилищами.   -  person jorgbrown    schedule 14.12.2018


Ответы (4)


В последней версии Руководство ISA уровня пользователя RISC-V (версия 2.1). Однако в руководстве есть заполнитель для стандартного расширения «B» для обработки битов. Некоторые черновые материалы рабочей группы этого расширения собраны на GitHub. В частности, в проекте спецификации говорится о grev инструкция (обобщенная обратная), которая может делать 16-, 32- и 64-битные перестановки байтов:

Эта инструкция предоставляет одну аппаратную инструкцию, которая может реализовать все операции по замене порядка байтов, побитовое обращение, замену короткого порядка, замену порядка слов (RV64), замену порядка полубайтов, побитовое обращение в байтах и ​​т. Д., Все из единая аппаратная инструкция. Он принимает значение одного регистра и непосредственное значение, которое контролирует, какая функция выполняется, посредством управления уровнями в рекурсивном дереве, на которых происходят развороты.

Рабочая группа по расширению B была «распущена по бюрократическим причинам в ноябре 2017 года», прежде чем они смогли завершить спецификацию.

В 2020 году рабочая группа снова активна и публикует свои работы в связанном репозитории GitHub.

В результате, в настоящее время, кажется, нет ничего проще, чем делать обычный сдвиг с маской или танцевать. Я не смог найти ни одного встроенного языка ассемблера bswap в GCC или портов clang riscv. В качестве примера приведем разборку функции bswapsi2 (которая меняет местами 32-битное значение), выданной компилятором riscv64-linux-gnu-gcc версии 8.1.0-12:

000000000000068a <__bswapsi2>:
 68a:   0185169b                slliw   a3,a0,0x18
 68e:   0185579b                srliw   a5,a0,0x18
 692:   8fd5                    or      a5,a5,a3
 694:   66c1                    lui     a3,0x10
 696:   4085571b                sraiw   a4,a0,0x8
 69a:   f0068693                addi    a3,a3,-256 # ff00 <__global_pointer$+0xd6a8>
 69e:   8f75                    and     a4,a4,a3
 6a0:   8fd9                    or      a5,a5,a4
 6a2:   0085151b                slliw   a0,a0,0x8
 6a6:   00ff0737                lui     a4,0xff0
 6aa:   8d79                    and     a0,a0,a4
 6ac:   8d5d                    or      a0,a0,a5
 6ae:   2501                    sext.w  a0,a0
 6b0:   8082                    ret
person Lassi    schedule 30.08.2018
comment
По состоянию на начало 2020 года расширение B не умерло, т.е. оно перемещено в github.com/riscv/riscv- bitmanip - README старого репозитория был обновлен в марте 2019 года с пометкой: «RISC-V XBitmanip теперь является официальным черновиком RISC-V Bitmanip» - person maxschlepzig; 11.02.2020

RISC-V ISA не имеет явных инструкций по замене байтов. Лучше всего использовать встроенный C для выполнения этого расчета, который в мире GCC будет примерно таким: __builtin_bswap32(). Это дает компилятору максимум информации, чтобы он мог принимать правильные решения. С текущим набором определенных ISA вы почти наверняка в конечном итоге вызовете процедуру, но если расширение B когда-либо будет определено, вы прозрачно получите лучше сгенерированный код. Полный набор определенных встроенных команд доступен в Интернете: https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html.

Если вы застряли, делая это в сборке, лучше всего вызвать существующую процедуру обмена байтами. Каноническим для 32-битного свопа является __bswapsi2, который является частью libgcc - вы, вероятно, все равно используете его, так что он будет. Это то, что в настоящее время делает компилятор, поэтому все, что вы теряете, - это игнорирование вызова функции, когда доступна лучшая реализация.

В качестве конкретного примера, вот мой пример функции C

unsigned swapb(unsigned in) { return __builtin_bswap32(in); }

и сгенерированная сборка

swapb:
    addi    sp,sp,-16
    sd  ra,8(sp)
    call    __bswapsi2
    ld  ra,8(sp)
    sext.w  a0,a0
    addi    sp,sp,16
    jr  ra
person Palmer Dabbelt    schedule 04.10.2018
comment
Стоит отметить, что это будет работать, только если не используется флаг -nodefaultlibs. - person seds; 20.01.2020
comment
Как ни странно, GCC не встраивает call __bswapsi2 в ваш пример. Это происходит, если вы компилируете с -O2 или более высоким уровнем оптимизации? - person Lassi; 12.02.2020

Обратите внимание, что хотя приятно и удобно иметь инструкцию для этого, функция __bswapsi2, используемая в других ответах, будет работать со скоростью около 400 МБ / с на HiFive Unleashed 1,5 ГГц, что намного быстрее, чем интерфейс gigE. когда-либо собирался перемещать данные.

Даже на HiFive1, работающем на частоте 256 МГц по умолчанию, он будет делать 60 МБ / с, а у вас есть только 16 КБ ОЗУ и несколько GPIO, которые вы не собираетесь раскачивать на частотах более нескольких МГц или, может быть, 10 секунд. МГц.

Я в рабочей группе BitManipulation. Полная инструкция GREV требует изрядного количества оборудования (что-то близкое к множителю), поэтому небольшие микроконтроллеры могут никогда не включать его. Однако мы планируем использовать те же коды операций GREVI, которые обеспечивают полное изменение битов слова и порядка байтов, и реализовать их как более простые инструкции для особых случаев, которые не требуют много схем, и, надеюсь, каждый будет их включать.

person Bruce Hoult    schedule 30.08.2019
comment
Спасибо - очень полезно узнать подробности от кого-то, кто участвовал в разработке! Вы действуете по какому-то графику или спецификация выполняется, когда она завершена? - person Lassi; 30.08.2019
comment
Аргументы, основанные на использовании 100% процессорного времени для этого крошечного шага, легко ломаются, когда вы хотите сделать что-то с данными с прямым порядком байтов. Если вы израсходуете значительную часть своего бюджета времени на обработку с порядком байтов, это не оставит столько времени для реальной работы. И делает обработку на лету каждый раз при загрузке (чтобы избежать пропускной способности памяти из-за дополнительной копии) менее привлекательной. например последняя версия x86 имеет movbe insns, который загружает или сохраняет + конвертирует на лету. (Я уверен, вы это знаете, и иногда вам не нужно ничего делать, кроме копирования. Но стоит указать будущим читателям - person Peter Cordes; 30.08.2019
comment
@Lassi как стандарт добровольцев, реализуемый сообществом, это делается, когда это делается, и когда мы получаем достаточную поддержку от представителей различных компаний и других заинтересованных групп. Однако мы стараемся, чтобы он продолжал развиваться. Я не думаю, что мы будем добавлять какие-либо инструкции (мой GORC был добавлен очень поздно). Все было реализовано (с неофициальными кодами операций) в binutils, spike, gcc (большинство) и в HDL и находит свое отражение в ядрах FPGA. Следующие несколько месяцев будут использованы для запуска большого количества программного обеспечения, чтобы убедиться, что каждая инструкция имеет свой вес в размере кода или времени выполнения. - person Bruce Hoult; 31.08.2019

В отличие от x86, в RISC-V нет чего-то вроде movbe (который может загружать и менять байты в одной инструкции).

Таким образом, на RISC-V вы загружаете / сохраняете как обычно, а после / перед загрузкой / сохранением вы должны поменять местами байты с дополнительными инструкциями.

Расширение RISC-V "B" (Bitmanip) (версия 0.92) содержит обобщенные инструкции битового обратного преобразования. (grev, grevi) и несколько псевдо-инструкций, которые можно использовать для обмена байтами:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
RISC-V    ARM      X86      Comment
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
rev       RBIT     ☐        bit reverse
rev8.h    REV16    ☐        byte-reverse half-word (lower 16 bit)
rev8.w    REV32    ☐        byte-reverse word (lower 32 bit)
rev8      REV      BSWAP    byte-reverse whole register
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

(Таблица основана на Таблице 2.5, RISC-V Bitmanip Extension V0.92, стр. 18)

По состоянию на 2020-03 год расширение «B» имеет статус черновика, поэтому поддержка аппаратного обеспечения и эмуляторов ограничена.

Без расширения «B» вы должны реализовать перестановку байтов с помощью нескольких базовых инструкций. См., Например, страницу 16 в спецификации «B» или просмотрите дизассемблированный код __builtin_bswap16 , __builtin_bswap32 и __builtin_bswap64 gcc / clang intrinsics.

person maxschlepzig    schedule 14.03.2020