Как написать переносимый GNU C встроенный вектор, который не зависит от встроенного x86 set1?
typedef uint16_t v8su __attribute__((vector_size(16)));
v8su set1_u16_x86(uint16_t scalar) {
return (v8su)_mm_set1_epi16(scalar); // cast needed for gcc
}
Конечно, должен быть лучший способ, чем
v8su set1_u16(uint16_t s) {
return (v8su){s,s,s,s, s,s,s,s};
}
Я не хочу писать версию AVX2 для трансляции одного байта!
Даже ответ gcc-only или clang-only для этой части будет интересен, для случаев, когда вы хотите назначить переменную вместо использования только в качестве операнда для двоичного оператора (который хорошо работает с gcc, см. ниже).
Если я хочу использовать широковещательный скаляр как один операнд двоичного оператора, это работает с gcc (как описано в руководстве), но не с clang:
v8su vecdiv10(v8su v) { return v / 10; } // doesn't compile with clang
С clang, если я нацелен только на x86 и просто использую собственный синтаксис векторных чтобы заставить компилятор генерировать модульные мультипликативные инверсные константы и инструкции для меня, я могу написать:
v8su vecdiv_set1(v8su v) {
return v / (v8su)_mm_set1_epi16(10); // gcc needs the cast
}
Но тогда мне нужно изменить внутреннее, если я увеличиваю вектор (до _mm256_set1_epi16
) вместо того, чтобы преобразовать весь код в AVX2, изменив его на vector_size(32)
в одном месте (для чисто-вертикального SIMD, который не нужно перетасовать). Он также побеждает часть целей нативных векторов, так как это не будет компилироваться для ARM или любой цели, отличной от x86.
Требуется уродливый бросок, потому что gcc, в отличие от clang, не считает v8us {aka __vector(8) short unsigned int}
совместимым с __m128i {aka __vector(2) long long int}
.
Кстати, все это компилируется в хороший asm с gcc и clang (видеть его на Godbolt). Это просто вопрос о том, как писать элегантно, с читаемым синтаксисом, который не повторяет скалярные N раз. например v / 10
достаточно компактен, что нет необходимости даже помещать его в свою собственную функцию.
Компиляция эффективно с ICC является бонусом, но не обязательным. Нативные векторы GNU C явно являются запоздалой мыслью для ICC, и даже простые вещи, подобные этому, не компилируются эффективно. set1_u16
компилируется в 8 скалярных хранилищ и векторную нагрузку вместо MOVD/VPBROADCASTW (при включенном -xHOST
, поскольку он не распознает -march=haswell
, но Godbolt работает на сервере с поддержкой AVX2). Чистое выполнение результатов _mm_
intrinsics в порядке, но деление вызывает функцию SVML!