_loaddqu_LE внутренние магазины в обратном порядке - PullRequest
0 голосов
/ 13 января 2019

_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;
}

Фактический объем производства:

0x00000000000000000000000000000000
0x62636363626363636263636362636363

Ожидаемый результат:

0x00000000000000000000000000000000
0x63636362636363626363636263636362

1 Ответ

0 голосов
/ 14 января 2019

Допустим, у вас есть 128-разрядное целое число без знака

28018020645823955151501786048551321856

В шестнадцатеричном виде это

0x15141312111009080706050403020100

На архитектурах, использующих порядок байтов с прямым порядком байтов , например 64-битный Intel / AMD (что является наиболее вероятным кандидатом, учитывая используемый тип __m128i), это число сохраняется в памяти в шестнадцатеричный как

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

Мы можем интерпретировать эти байты, например, как восемь 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 / X C / C ++, эквивалентные внутренние свойства _pshufb() и _mm_set_epi8().)

Свойство _mm_set_epi8() принимает 16 параметров, в первую очередь старший байт, и упаковывает их в 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 long s (насколько я знаю, все, что поддерживают те, что для SSE3, делают). В любом случае вы можете создать константы, используя _mm_set_epi8() встроенную функцию.

Причина, по которой они помещаются в макросы, заключается в том, что если вы встретите компилятор или архитектуру , для которых требуется объявление другого типа, чтобы получить то же эффективное значение (что и для соответствующих внутренних значений _mm_set_epi8()), вы нужен только небольшой массаж препроцессора.

Используя вышеизложенное, a = _mm_shuffle_epi8(a, swap128_128); (или a = _pshufb(a, swap128_128) для TNS / X C / C ++) полностью изменяет порядок байтов; swap128_64 только порядок байтов для обоих 64-разрядных компонентов, swap128_32 для всех четырех 32-разрядных компонентов и swap128_16 для всех восьми 16-разрядных компонентов. Существует одиннадцать других вариантов (плюс «без случайного выбора», всего 16 порядков байтов для 128-битных значений), плюс вы можете дублировать исходные байты для целевых байтов, поэтому используйте _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-разрядных чисел и четвертое - шестнадцать байтов.)

Существует много других возможных вариантов (для 128-битных значений возможны 16 уникальных порядков байтов), но, не зная точно, в чем заключается основная проблема и что именно пытается достичь OP, я не буду беспокоиться об их изучении все.

...