assembly 在RISC-V上的大端和小端之间转换

scyqe7ek  于 2022-11-13  发布在  其他
关注(0)|答案(4)|浏览(296)

在RISC-V中,在汇编语言级别处理big-endian值最简单的方法是什么?也就是说,如何将big-endian值从内存加载到寄存器中,以本机端序(小端序)处理寄存器值,然后以big-endian将其存储回内存中。16、32和64位值用于许多网络协议和文件格式。
我找不到一个字节交换指令(相当于x86上的BSWAP或ARM上的REV)in the manual,也找不到任何关于big-endian加载和存储的东西。

eyh26e7m

eyh26e7m1#

在最新的RISC-V User-Level ISA Manual中没有提到字节交换指令(2.1版)。但是,手册中有一个占位符 *“B”位操作标准扩展 *。该扩展工作组的一些草稿材料是collected on GitHub。特别是,draft specification讨论了grev指令(广义反向),可以进行16、32和64位字节交换:
该指令提供了一个硬件指令,它可以实现所有的字节顺序交换、逐位反转、短顺序交换、字顺序交换(RV 64)、半字节顺序交换、字节中的逐位反转等,所有这些都来自一个硬件指令。它接受一个寄存器值和一个立即数,该立即数通过控制递归树中发生反转的层来控制哪个函数发生。
扩展B工作组在最终确定规范之前“因官僚主义原因于2017年11月解散”。

2020年,工作组再次活跃起来,在链接的GitHub存储库上发布他们的工作成果。

因此,目前似乎没有什么比执行通常的shift-mask-or dance更简单的了。我在GCC或clang riscv端口中找不到任何汇编语言的bswap内部函数。作为一个例子,下面是riscv64-linux-gnu-gcc编译器8.1.0-12版本发出的bswapsi2函数(该函数对32位值进行字节交换)的反汇编:

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
vwoqyblh

vwoqyblh2#

RISC-V伊萨没有明确的字节交换指令。你最好的选择是使用一个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
42fyovps

42fyovps3#

与x86不同,RISC-V没有movbe(可以在一个指令中加载和字节交换)。
因此,在RISC-V上,你像往常一样加载/存储,在加载/存储之后/之前,你必须用额外的指令交换字节。
RISC-V "B" (Bitmanip) extension(版本0.92)包含通用的位反转指令(grevgrevi)和几条可用于字节交换的伪指令:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

(表格基于第18页表2.5,RISC-V Bitmanip Extension V0.92
截至2020年3月,“B”扩展处于草案状态,因此在硬件和模拟器中的支持有限。
如果没有“B”扩展,则必须使用多个基本指令来实现字节交换。例如,请参阅第16页的"B" specification,或者查看__builtin_bswap16__builtin_bswap32__builtin_bswap64gcc/clang内部函数的disassembled code

qzwqbdag

qzwqbdag4#

请注意,虽然有一条指令来执行此操作非常方便,但其他答案中使用的__bswapsi2函数在1.5 GHz HiFive Unleashed上的运行速度约为400 MB/s,这比gigE接口移动数据的速度要快得多。
即使在默认256 MHz下运行的HiFive 1上,它也将以60 MB/s的速度运行,而您只有16 KB的RAM和一堆GPIO,您不会在超过几MHz或几十MHz的频率下摆动。
我是BitManipulation工作组的成员。完整的GREV指令需要相当多的硬件(接近乘法器的东西),所以小型微控制器可能永远不会包含它。然而,我们计划使用相同的GREVI操作码,给予完整的字位反转和字节顺序反转,并将它们作为更简单的特殊情况指令来实现,不需要太多的电路,希望每个人都能包含它们。

相关问题