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

Las*_*ssi 3 assembly endianness riscv

在汇编语言级别处理 RISC-V 中大端值的最简单方法是什么?也就是说,如何将内存中的 big-endian 值加载到寄存器中,以 native-endian(little-endian)的形式处理寄存器值,然后以 big-endian 的形式将其存储回内存。16、32 和 64 位值用于许多网络协议和文件格式。

我找不到一个字节交换指令(相当于BSWAP对ARM x86或REV)的手册中,也没有任何关于大端加载和存储。

Las*_*ssi 6

在最新的RISC-V 用户级 ISA 手册(2.1 版)中没有提到字节交换指令。但是,该手册有一个占位符,用于表示位操作的“B”标准扩展。该扩展工作组的一些草稿材料收集在 GitHub 上。特别是,草案规范谈到了grev可以进行 16、32 和 64 位字节交换的指令(广义反向):

该指令提供了一条硬件指令,可以实现字节中的所有字节顺序交换、按位反转、短顺序交换、字序交换(RV64)、半字节顺序交换、按位反转等,全部来自单个硬件指令。通过控制递归树中发生反转的级别,它接受单个寄存器值和一个立即数来控制哪个函数发生。

扩展 B 工作组在最终确定规范之前“因官僚主义原因于 2017 年 11 月解散”。

2020 年,工作组再次活跃,在链接的 GitHub 存储库中发布他们的工作。

因此,目前似乎没有什么比做通常的换挡面具或跳舞更简单的了。我在 GCC 或 clang riscv 端口中找不到任何内在的汇编语言 bswap。例如,bswapsi2以下是riscv64-linux-gnu-gcc编译器版本 8.1.0-12发出的函数(字节交换 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
Run Code Online (Sandbox Code Playgroud)

  • 截至 2020 年初,“B”扩展尚未消亡,即它已移至 https://github.com/riscv/riscv-bitmanip - 旧存储库的自述文件已于 2019 年 3 月更新,并注明:“RISC-V” XBitmanip 现在是官方 RISC-V Bitmanip 草案' (3认同)

小智 5

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); }
Run Code Online (Sandbox Code Playgroud)

和生成的程序集

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
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是,这仅在不使用“-nodefaultlibs”标志时才有效。 (2认同)

max*_*zig 5

与 x86 不同,RISC-V 没有类似的东西movbe(可以在一条指令中加载和字节交换)。

\n

因此,在 RISC-V 上,您可以照常加载/存储,并且在加载/存储之后/之前,您必须使用额外的指令交换字节。

\n

RISC -V“B”(Bitmanip)扩展(版本 0.92)包含通用位反转指令(grevgrevi)和几个可用于字节交换的伪指令:

\n
\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\nRISC-V    ARM      X86      Comment\n\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\xe2\x80\x95\nrev       RBIT     \xe2\x98\x90        bit reverse\nrev8.h    REV16    \xe2\x98\x90        byte-reverse half-word (lower 16 bit)\nrev8.w    REV32    \xe2\x98\x90        byte-reverse word (lower 32 bit)\nrev8      REV      BSWAP    byte-reverse whole register\n\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\xe2\x94\x81\n
Run Code Online (Sandbox Code Playgroud)\n

(表基于表 2.5,RISC-V Bitmanip Extension V0.92,第 18 页)

\n

截至 2020 年 3 月,“B”扩展处于草案状态,因此硬件和模拟器的支持有限。

\n

如果没有“B”扩展,您必须使用多个基本指令来实现字节交换。例如,请参阅“B”规范中的第 16 页,或者查看,和gcc /clang 内在函数的反汇编代码__builtin_bswap16__builtin_bswap32__builtin_bswap64

\n