【问题标题】:Fastest way to swap alternate bytes on ARM Cortex M4 using gcc使用 gcc 在 ARM Cortex M4 上交换备用字节的最快方法
【发布时间】:2017-01-16 11:31:53
【问题描述】:

我需要在使用 ARM Cortex M4 处理器的嵌入式系统中尽快交换缓冲区中的备用字节。我使用 gcc。数据量是可变的,但最大值略高于 2K。是否转换了一些额外的字节并不重要,因为我可以使用一个过大的缓冲区。

我知道 ARM 有 REV16 指令,我可以用它来交换 32 位字中的备用字节。我不知道的是:

  1. 有没有办法在 gcc 中得到这条指令而不求助于汇编程序? __builtin_bswap16 内在函数似乎只对 16 位字进行操作。一次转换 4 个字节肯定比转换 2 个字节要快。

  2. Cortex M4 是否具有重新排序缓冲区和/或进行寄存器重命名?如果没有,当我在部分展开的循环中转换缓冲区的双字时,我需要做些什么来最大限度地减少管道停顿?

例如,这段代码是否有效,其中REV16 被适当地定义为解析 (1):

uint32_t *buf = ... ;
size_t n = ... ;  // (number of bytes to convert + 15)/16
for (size_t i = 0; i < n; ++i)
{
    uint32_t a = buf[0];
    uint32_t b = buf[1];
    uint32_t c = buf[2];
    uint32_t d = buf[3];
    REV16(a, a);
    REV16(b, b);
    REV16(c, c);
    REV16(d, d);
    buf[0] = a;
    buf[1] = b;
    buf[2] = c;
    buf[3] = d;
    buf += 4;
}

【问题讨论】:

  • inline assembler 有什么问题。它不可移植,但__builtin_bswap16 也不是。 __builtin_bswap16 仅适用于具有多种 CPU 类型的 GCC。主要针对针对 GCC 的 libc 编写者。大多数人可能会接受在移植时抛出错误的高度针对性的条件代码。放一些好的cmets。请参阅:Linux 内核中的bswapdi2.S。您的系统可能会受到内存限制而不是 CPU 限制。
  • 请参阅:godbolt,使用 'bswapdi2.s' 的掩码方法用于 arch
  • 谢谢,如果没有其他方法同样快,我可以使用内联汇编程序。我可以只使用:asm("rev16 a,a"); 将 rev16 操作应用于上面示例中的变量a 吗?
  • @dc42 以godbolt.org/g/JyZBZp 为例,您可以看到编译器内联了对Rev16 的函数调用是内联的,因此您最终得到了一条指令。

标签: gcc arm endianness


【解决方案1】:

由于您所说的原因,您不能使用 __builtin_bswap16 函数,它适用于 16 位字,因此其他半字为 0。我想这样做的原因是为了让内在函数在没有与 ARM 上的 REV16 类似的指令的处理器上工作。

功能

uint32_t swap(uint32_t in)
{
  in = __builtin_bswap32(in);
  in = (in >> 16) | (in << 16);
  return in;
}

编译为 (ARM GCC 5.4.1 -O3 -std=c++11 -march=armv7-m -mtune=cortex-m4 -mthumb)

    rev     r0, r0
    ror     r0, r0, #16
    bx      lr

您可能会要求编译器内联它,这将为每个 32 位字提供 2 条指令。我想不出一种方法让 GCC 使用 32 位操作数生成 REV16,而无需使用内联汇编声明您自己的函数。

编辑

作为跟进,并基于 artless noise 对 __builtin_bswap 函数的不可移植性的评论,the compiler recognizes

uint32_t swap(uint32_t in)
{
  in = ((in & 0xff000000) >> 24) | ((in & 0x00FF0000) >> 8) | ((in & 0x0000FF00) << 8) | ((in & 0xFF) << 24);
  in = (in >> 16) | (in << 16);
  return in;
}

并创建与上面相同的 3 指令功能,因此这是一种更便携的实现方式。不同的编译器是否会产生相同的输出...

编辑编辑

如果允许内联汇编,下面的函数

inline uint32_t Rev16(uint32_t a)
{
  asm ("rev16 %1,%0"
          : "=r" (a)
          : "r" (a));
  return a;
}

被内联,并作为一个单一的指令,可以看到here.

【讨论】:

    猜你喜欢
    • 2019-06-27
    • 2018-12-04
    • 2017-06-23
    • 2014-10-19
    • 1970-01-01
    • 1970-01-01
    • 2018-09-29
    • 2017-12-03
    • 1970-01-01
    相关资源
    最近更新 更多