Лучший способ загрузки векторов из памяти. (Лязг)

Я пишу тестовую программу, чтобы привыкнуть к языковым расширениям Clang для векторов стиля OpenCL. Я могу заставить код работать, но у меня возникают проблемы с одним аспектом. Я не могу понять, как заставить clang просто загружать вектор из скалярного массива.

На данный момент мне нужно сделать что-то вроде:

byte16 va = (byte16){ argv[1][start], argv[1][start + 1], argv[1][start + 2], 
                      argv[1][start + 3], argv[1][start + 4], argv[1][start + 5], 
                      argv[1][start + 6], argv[1][start + 7], argv[1][start + 8],
                      argv[1][start + 9], argv[1][start + 10], argv[1][start + 11],
                      argv[1][start + 12], argv[1][start + 13], argv[1][start + 14],
                      argv[1][start + 15]};

В идеале мне бы хотелось что-то вроде этого:

byte16 va = *(byte16 *)(&(argv[1][start]));

Что я могу легко сделать, используя правильные возможности для ARM или x86. Но этот код приводит к сбою программы, хотя она компилируется.

Ответ 1

Одна из причин возникновения сбоя на x86 связана с проблемами выравнивания. Я не звоню в своей системе, чтобы воспроизвести проблему, но я могу продемонстрировать ее на примере GCC.

Если вы сделаете что-то вроде:

/* Define a vector type of 16 characters.  */
typedef char __attribute__ ((vector_size (16))) byte16;

/* Global pointer.  */
char *  foo;

byte16 test ()
{
  return *(byte16 *)&foo[1];
}

Теперь, если вы скомпилируете его на вектор-совместимом x86 с помощью:

$  gcc -O3 -march=native -mtune=native   a.c

Вы получите следующую сборку для тестирования:

test:
    movq foo(%rip), %rax
    vmovdqa 1(%rax), %xmm0
    ret

Обратите внимание, что перемещение выравнивается, что, конечно, неправильно. Теперь, если вы встроили эту функцию в основную, и у вас будет что-то вроде:

int main ()
{
  foo = __builtin_malloc (22);
  byte16 x = *(byte16 *)&foo[1];
  return x[0];
}

У вас все будет хорошо, и вы получите не выровненную инструкцию. Это своего рода ошибка, которая не имеет очень хорошего исправления в компиляторе, поскольку для этого потребуются межпроцедурные оптимизации с добавлением новых структур данных и т.д.

Происхождение проблемы состоит в том, что компилятор предполагает, что векторные типы выровнены, поэтому, когда вы разыскиваете массив выровненных векторных типов, вы можете использовать выровненный ход. В качестве обходного пути для проблемы в GCC можно определить неглавный векторный тип, например:

typedef char __attribute__ ((vector_size (16),aligned (1))) unaligned_byte16;

И используйте его для разыменования неизменной памяти.

Я не уверен, что вы столкнулись именно с этой проблемой в своей настройке, но это то, что я бы рекомендовал проверить, проверив вывод сборки из вашего компилятора.