【问题标题】:Convert between big-endian and little-endian on RISC-V在 RISC-V 上的大端和小端之间转换
【发布时间】:2019-02-05 13:13:49
【问题描述】:

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

我找不到字节交换指令(相当于 x86 上的 BSWAP 或 ARM 上的 REV)in the manual,也找不到任何关于大端加载和存储的信息。

【问题讨论】:

  • 您是否尝试过检查文档?这确实是一个可以通过快速阅读相关document来回答的问题。
  • 是的,多个来源。字节交换是如此常见的操作,我想我一定错过了什么,这就是我在这里问的原因。
  • 我尝试检查编译器在您请求字节交换时会执行哪些操作,但 Godbolt 的 clang risc-v 安装已损坏,并尝试将 x86 内联 asm 用于 endian.h be32toh()godbolt.org/z/6MzVWa。也许编写编译器可以识别为字节交换的纯 C 语言会起作用,但不能证明指令不存在。
  • 这可以通过带有 XBitmanip 扩展的单个指令来完成,这不是核心,甚至不是最终的扩展。无论如何,这在这个问题的范围内吗?
  • 我正在调查这个,调查代码质量......请参阅 gcc.godbolt.org/z/dwMH9S,其中显示了处理 64 位存储的几种方法的生成代码。

标签: assembly endianness riscv


【解决方案1】:

在最新的RISC-V User-Level ISA Manual(2.1 版)中没有提到字节交换指令。但是,该手册有一个占位符用于“B”位操作标准扩展。该扩展工作组的一些草稿材料是collected on GitHub。特别是,draft specification 谈到了可以执行 16、32 和 64 位字节交换的 grev 指令(广义反向):

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

扩展 B 工作组“在 2017 年 11 月因官僚原因解散”,然后才最终确定规范。

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

因此,目前似乎没有什么比做通常的换档面具或舞蹈更简单的事情了。我在 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

【讨论】:

  • 截至 2020 年初,“B”扩展名并未失效,即它移至 github.com/riscv/riscv-bitmanip - 旧存储库的 README 已于 2019 年 3 月更新,并附注:'RISC-V XBitmanip is现在是官方的 RISC-V Bitmanip 草案'
【解决方案2】:

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

【讨论】:

  • 值得注意的是,这仅在不使用 -nodefaultlibs 标志时才有效。
  • 奇怪的是,在您的示例中,GCC 没有内联 call __bswapsi2。如果您使用-O2 或更高的优化级别进行编译,它会这样做吗?
【解决方案3】:

请注意,虽然有一个指令来做这件事既漂亮又方便,但其他答案中使用的 __bswapsi2 函数将在 1.5 GHz HiFive Unleashed 上以大约 400 MB/s 的速度运行,这比gigE 接口将永远移动数据。

即使在以默认 256 MHz 运行的 HiFive1 上,它也会达到 60 MB/s,而您只有 16 KB 的 RAM 和一堆 GPIO,您不会以超过几 MHz 或可能是 10 兆赫兹。

我在 BitManipulation 工作组。完整的 GREV 指令需要相当多的硬件(接近乘法器),因此小型微控制器可能永远不会包含它。然而,我们计划使用相同的 GREVI 操作码来提供完整的字位反转和字节顺序反转,并将它们实现为不需要太多电路的更简单的特殊情况指令,希望每个人都将它们包括在内。

【讨论】:

  • 谢谢 - 从参与设计的人那里获得内幕非常有用!您是否按照某种时间表进行操作,或者规范完成后是否完成?
  • 当您想对原生字节序数据做某事时,基于“为这一微小步骤使用 100% 的 CPU 时间”的参数很容易失效。如果您在字节序处理上用尽了很大一部分处理时间预算,那么就没有多少时间用于实际工作。并且每次加载时动态处理(以避免额外副本的内存带宽)的吸引力降低。例如最近的 x86 有一个 movbe insns,可以即时加载或存储 + 转换。 (我确定知道这一点,有时你只需要复制即可。但值得为未来的读者指出
  • @Lassi 作为一个由志愿者推动的社区驱动标准,当它完成时,当我们从各个公司和其他感兴趣的团体的代表那里获得足够的支持时,它就完成了。然而,我们正在努力让它继续前进。我认为我们不会再添加任何指令(我的 GORC 是一个非常晚的添加)。一切都已在 binutils、spike、gcc(大多数)和 HDL 中实现(使用非官方操作码),并且正在进入 FPGA 内核。接下来的几个月将用于运行大量软件,以检查每条指令是否在代码大小或运行时方面发挥了重要作用。
【解决方案4】:

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

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

截至 2020-03,“B”扩展处于草案状态,因此在硬件和模拟器中的支持受到限制。

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

【讨论】:

    猜你喜欢
    • 2010-09-11
    • 1970-01-01
    • 2013-10-17
    • 2014-05-24
    相关资源
    最近更新 更多