首页 > 解决方案 > _loaddqu_LE 内部存储以相反的顺序

问题描述

_loaddqu_LE内在存储以相反的顺序。请建议一种解决方法或使用数组在使用之前先重新排列字节_loaddqu_LE

#include <stdio.h>

int main() {
uint32_t src[16];
__m128i a; /* 128 bit */

src[0] = 0x00000000;
src[1] = 0x00000000;
src[2] = 0x00000000;
src[3] = 0x00000000;
src[4] = 0x63636362;
src[5] = 0x63636362;
src[6] = 0x63636362;
src[7] = 0x63636362;
src[8] = 0xc998989b;
src[9] = 0xaafbfbf9;
src[10] =0xc998989b; 
src[11] =0xaafbfbf9;
src[12] =0x50349790;
src[13] =0xfacf6c69;
src[14] =0x3357f4f2;
src[15] =0x99ac0f0b;

/* load 32 bits */
a = _loaddqu_LE((const char _ptr64 *) & (((__m128i *)src)[0]));
printf("0x%016llx%016llx\n", a.v0, a.v1);
a = _loaddqu_LE((const char _ptr64 *) & (((__m128i *)src)[1]));
printf("0x%016llx%016llx\n", a.v0, a.v1);

return 0;
}

实际输出:

0x000000000000000000000000000000000
0x62636363626363636263636362636363

预期输出:

0x000000000000000000000000000000000
0x63636362636363626363636263636362

标签: cx86bytebit

解决方案


假设您有一个 128 位无符号整数

28018020645823955151501786048551321856

在十六进制中,它是

0x15141312111009080706050403020100

在使用little-endian 字节顺序的架构上,如 64 位 Intel/AMD(考虑到__m128i所使用的类型,这是最可能的候选),该数字以十六进制存储在内存中

0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15

例如,我们可以将这些字节重新解释为 8 个 16 位无符号整数,

0x0100 0x0302 0x0504 0x0706 0x0908 0x1110 0x1312 0x1514

或四个 32 位无符号整数,

0x03020100 0x07060504 0x11100908 0x15141312

或两个 64 位无符号整数,

0x0706050403020100 0x1514131211100908

OP 希望将 128 位无符号整数输入拆分为两个 64 位无符号整数。Intel/AMD 内部函数为此提供了_mm_shuffle_epi8()和内部函数。_mm_set_epi8()(如果 OP 使用 TNS/XC/C++,等效的内在函数是_pshufb()and _mm_set_epi8()。)

内在函数接受 16 个参数,首先_mm_set_epi8()是最高有效字节,并将它们打包成一个 128 位整数。_mm_shuffle_epi8()/内部函数将_pshufb()两个 128 位整数作为参数,并返回一个 128 位整数,由第一个参数中的字节构造而成,由第二个参数中的字节指示。


以下是一些有用的字节顺序常量:

/* SWAP128_128 = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); */
#define  SWAP128_128  { 579005069656919567LL, 283686952306183LL }

/* SWAP128_64 = _mm_set_epi8(8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7); */
#define  SWAP128_64  { 283686952306183LL, 579005069656919567LL };

/* SWAP128_32 = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); */
#define  SWAP128_32  { 289644378169868803LL, 868365760874482187LL }; 

/* SWAP128_16 = _mm_set_epi8(14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); */
#define  SWAP128_16  { 434320308619640833LL, 1013041691324254217LL };

const __m128i  swap128_128 = SWAP128_128;
const __m128i  swap128_64  = SWAP128_64;
const __m128i  swap128_32  = SWAP128_32;
const __m128i  swap128_16  = SWAP128_16;

请注意,常量声明假定 C 编译器将__m128i类型实现为就好像它是两个long longs (据我所知,所有支持 SSE3 的都是如此)。_mm_set_epi8()在任何情况下,您都可以使用内在函数构造常量。

将它们作为宏的原因是,如果您遇到需要不同类型的声明以获得相同有效值的编译器或体系结构_mm_set_epi8()(作为各自的内在收益率),您只需要一点预处理器按摩。

使用上述a = _mm_shuffle_epi8(a, swap128_128);(或a = _pshufb(a, swap128_128)用于 TNS/XC/C++)反转整个字节顺序;swap128_64只是两个 64 位组件、swap128_32所有四个 32 位组件和swap128_16所有八个 16 位组件的字节顺序。还有其他十一种变体(加上“无随机播放”,对于 128 位值总共有 16 个字节顺序),此外,您可以将源字节复制到目标字节,因此请使用_mm_set_epi8()找到您需要的字节。

鉴于上述数据,

const uint8_t  data[16] = {
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15
};
__m128i vector = _mm_lddqu_si128((const __m128i *)data);
__m128i v128 = _mm_shuffle_epi8(vector, swap128_128);
__m128i v64 = _mm_shuffle_epi8(vector, swap128_64);
__m128i v32 = _mm_shuffle_epi8(vector, swap128_32);
__m128i v16 = _mm_shuffle_epi8(vector, swap128_16);

将产生:

vector = 0x0706050403020100 0x1514131211100908
       = 0x03020100 0x07060504 0x11100908 0x15141312
       = 0x0100 0x0302 0x0504 0x0706 0x0908 0x1110 0x1312 0x1514
       = 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x10 0x11 0x12 0x13 0x14 0x15

v128 = 0x0809101112131415 0x0001020304050607
     = 0x12131415 0x08091011 0x04050607 0x00010203
     = 0x1415 0x1213 0x1011 0x0809 0x0607 0x0405 0x0203 0x0001
     = 0x15 0x14 0x13 0x12 0x11 0x10 0x09 0x08 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00

v64 = 0x0001020304050607 0x0809101112131415
    = 0x04050607 0x00010203 0x12131415 0x08091011
    = 0x0607 0x0405 0x0203 0x0001 0x1415 0x1213 0x1011 0x0809
    = 0x07 0x06 0x05 0x04 0x03 0x02 0x01 0x00 0x15 0x14 0x13 0x12 0x11 0x10 0x09 0x08

v32 = 0x0405060700010203 0x1213141508091011
    = 0x00010203 0x04050607 0x08091011 0x12131415
    = 0x0203 0x0001 0x0607 0x0405 0x1011 0x0809 0x1415 0x1213
    = 0x03 0x02 0x01 0x00 0x07 0x06 0x05 0x04 0x11 0x10 0x09 0x08 0x15 0x14 0x13 0x12

v16 = 0x0607040502030001 0x1415121310110809
    = 0x02030001 0x06070405 0x10110809 0x14151213
    = 0x0001 0x0203 0x0405 0x0607 0x0809 0x1011 0x1213 0x1415
    = 0x01 0x00 0x03 0x02 0x05 0x04 0x07 0x06 0x09 0x08 0x11 0x10 0x13 0x12 0x15 0x14

取决于您希望如何解释每个__m128i. (第一个是两个 64 位整数,第二个是四个 32 位整数,第三个是八个 16 位整数,第四个是 16 个字节。)

还有许多其他可能的变化(对于 128 位值,16 个唯一的字节顺序是可能的),但是在不确切知道根本问题是什么以及 OP 试图实现什么的情况下,我不会费心去探索它们。


推荐阅读