为什么在实践中向右移动在 Neon 和 SSE 中向左移动(反之亦然)?

Why does shift right in practice shifts left (and viceversa) in Neon and SSE?

(注意,在 Neon 中我使用 来避免处理 16 位数据类型之间的转换)

为什么 "shift left" 在实践中的内在函数 "shift right"?

// Values contained in a
// 141 138 145 147 144 140 147 153 154 147 149 146 155 152 147 152
b = vshlq_n_u32(a,8);
// Values contained in b
// 0 141 138 145 0 144 140 147 0 154 147 149 0 155 152 147
b = vshrq_n_u32(a,8);
// Values contained in b
// 138 145 147 0 140 147 153 0 147 149 146 0 152 147 152 0

我记得在使用 _mm_slli_si128 时发现了相同的情况(虽然不同,转换后的结果如下所示:

// b = _mm_slli_si128(a,1);
// 0 141 138 145 147 144 140 147 153 154 147 149 146 155 152 147

是字节顺序的问题吗? 它会因平台而异吗?

这些内在函数的结果似乎取决于系统字节序,因此如果我们将代码移植到大端系统,我已经准备好举起旗帜

#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
    #pragma GCC error "Intrinsics used with little endian systems in mind. Start by reviewing all shifts operators."
#endif

checking endianness at compile time

你说 "is this because of endianess" 但这更像是一种类型滥用。您正在假设机器跨 byte/word 边界的位排序以及您的非字节指令对操作施加本地字节序(您正在使用 _u32 指令,该指令期望值是无符号的 32 位值,不是 8 位值的数组)。

如您所说,您要求它通过/要求/它以 32 位为单位移位值来移位一系列 unsigned char 值。

不幸的是,如果您希望能够对它们进行架构转换,则需要将它们按架构顺序排列。

否则你可能想寻找 blit 或移动指令,但你不能在不支付体系结构成本的情况下人为地将机器类型强制转换为机器寄存器。 Endianness 将只是您头疼的问题之一(对齐、填充等)

--- 后期编辑 ---

从根本上说,您混淆了字节移位和位移位,我们认为最高有效位是 "left"

bit number
87654321

hex
8421
00008421

00000001  = 0x01 (small, less significant)
10000000  = 0x80 (large, more significant)

但是您要移动的值是 32 位字,在小端机器上,这意味着每个后续地址都会增加值的更高有效字节,对于 32 位字:

bit numbers
                1111111111111111
87654321fedcba0987654321fedcba09

表示32位值0x0001

                1111111111111111
87654321fedcba0987654321fedcba09

00000001000000000000000000000000

将其左移 2 个位置

00000001000000000000000000000000
     v<
00000100000000000000000000000000

要将它向左移动另外 8 个位置,我们必须将它扭曲到下一个地址:

00000100000000000000000000000000
      >>>>>>>v
00000000000001000000000000000000

如果您以字节为单位考虑,这看起来像是一个右移。但是我们告诉这个 little-endian CPU 我们正在处理一个 uint32,所以这意味着:

                1111111111111111
87654321fedcba0987654321fedcba09
 word01  word02  word03  word04   
00000001000000000000000000000000 = 0x0001
00000100000000000000000000000000 = 0x0004
00000000000001000000000000000000 = 0x0400

问题是这与您期望的 8 位值本地数组的顺序不同,但您告诉 CPU 值是 _u32,所以它使用它的本机字节顺序进行操作.