В GNU C inline asm, каковы модификаторы для xmm/ymm/zmm для одного операнда?
Пытаясь ответить на встроенные трансляции с помощью встроенных функций и ассемблера, я пытался сделать что-то вроде этого:
__m512 mul_broad(__m512 a, float b) {
int scratch = 0;
asm(
"vbroadcastss %k[scalar], %q[scalar]\n\t" // want vbr.. %xmm0, %zmm0
"vmulps %q[scalar], %[vec], %[vec]\n\t"
// how it's done for integer registers
"movw symbol(%q[inttmp]), %w[inttmp]\n\t" // movw symbol(%rax), %ax
"movsbl %h[inttmp], %k[inttmp]\n\t" // movsx %ah, %eax
: [vec] "+x" (a), [scalar] "+x" (b), [inttmp] "=r" (scratch)
:
:
);
return a;
}
В документе " Модификаторы операндов GNU C x86" указываются только модификаторы q
(Размер DI (DoubleInt), 64 бита). С помощью q
в векторном регистре всегда приведут к xmm
(от ymm
или же zmm
).
Вопрос:
Какие модификаторы нужно менять между размерами векторного регистра?
Кроме того, существуют ли какие-либо ограничения определенного размера для использования с операндами ввода или вывода? Что-то кроме общего x
в конечном итоге это может быть xmm, ymm или zmm в зависимости от типа выражения, которое вы заключили в скобки.
Не по теме:
у лязга, кажется, есть некоторые Yi
/ Yt
ограничения (не модификаторы), но я также не могу найти документы по этому вопросу. clang даже не скомпилирует это, даже с закомментированными векторными инструкциями, потому что это не нравится +x
в качестве ограничения для __m512
вектор.
Фон / мотивация
Я могу получить желаемый результат, передав скаляр в качестве входного операнда, ограниченный тем же регистром, что и более широкий выходной операнд, но он более громоздкий. (Самым большим недостатком этого варианта использования является то, что AFAIK должен использовать номер операнда, а не [symbolic_name]
, поэтому он подвержен поломке при добавлении / удалении выходных ограничений.)
// does what I want, by using a paired output and input constraint
__m512 mul_broad(__m512 a, float b) {
__m512 tmpvec;
asm(
"vbroadcastss %[scalar], %[tmpvec]\n\t"
"vmulps %[tmpvec], %[vec], %[vec]\n\t"
: [vec] "+x" (a), [tmpvec] "=x" (tmpvec)
: [scalar] "1" (b)
:
);
return a;
}
Кроме того, я думаю, что весь этот подход к проблеме, которую я пытался решить, будет тупиком, потому что ограничения с несколькими альтернативами не позволяют вам давать различный ассм для разных шаблонов ограничений. Я надеялся получить x
а также r
ограничения в конечном итоге испускают vbroadcastss
из реестра, пока m
ограничения в конечном итоге излучают vmulps (mem_src){1to16}, %zmm_src2, %zmm_dst
(сложенная широковещательная нагрузка). Цель сделать это с помощью встроенного asm состоит в том, что gcc еще не знает, как сложить set1()
операнды памяти в широковещательные нагрузки (но clang делает).
В любом случае, этот конкретный вопрос касается модификаторов операндов и ограничений для векторных регистров. Пожалуйста, сосредоточьтесь на этом, но комментарии и комментарии в ответах приветствуются по другому вопросу. (Или лучше, просто прокомментируйте / ответьте на вопрос Z Boson о встроенных трансляциях.)
2 ответа
Из файла gcc / config / i386 / i386.c источников GCC:
b - вывести имя QImode регистра для указанного операнда. %b0 напечатает%al, если операнд [0] равен reg 0. w - аналогично, напечатайте имя HImode регистра. k - аналогичным образом выведите имя режима SI регистра. q - аналогичным образом выведите имя регистра DImode. x - аналогично, напечатайте имя регистра V4SFmode. t - аналогично, напечатайте имя регистра V8SFmode. g - аналогично, напечатайте имя регистра V16SFmode. h - вывести имя QImode для "старшего" регистра, ах, bh, ch или dh.
Аналогично из gcc / config / i386 / contraints.md:
;; Мы используем префикс Y для обозначения любого количества наборов условных регистров:;; z Первый регистр SSE.;; i SSE2 inter-unit переходит в регистр SSE включен;; j SSE2 inter-unit перемещается из регистра SSE включен;; m MMX inter-unit перемещается в регистр MMX включен;; n MMX-перемещение между единицами из регистра MMX включено;; Целочисленный регистр, когда нулевые расширения с AND отключены;; p Целочисленный регистр, когда TARGET_PARTIAL_REG_STALL отключен;; f x87 регистр, когда включена арифметика с плавающей запятой 80387;; r Регуляторы SSE не требуют префикса REX, когда разрешено избегание префиксов;; и все рег SSE в противном случае
Этот файл также определяет ограничение "Yk", но я не знаю, насколько хорошо это будет работать в операторе asm:
(define_register_constraint "Yk" "TARGET_AVX512F? MASK_EVEX_REGS: NO_REGS" "@internal Любой регистр маски, который можно использовать в качестве предиката, т.е. k1-k7.")
Обратите внимание, что все это скопировано из последней версии SVN. Я не знаю, какой выпуск GCC, если таковые имеются, были добавлены конкретные модификаторы и ограничения, которые вас интересуют.
Кажется, что все последние версии GCC будут принимать и q, и x в качестве модификаторов для печати XMM-версии регистра YMM.
ICC от Intel, похоже, принимает "q", но не "x" (по крайней мере, до версии 13.0.1).
[Edit: Ну, это сработало в этом небольшом примере ниже, но в реальном тестовом случае у меня проблемы с icc 14.0.3, принимающим 'q', но пишущим 'ymm'.]
[Редактировать: тестируя с более свежими версиями icc, я обнаружил, что ни icc 15, ни icc 16 не работают ни с 'q', ни с 'x'.]
Но Clang 3.6 и ранее не принимают ни синтаксис. И по крайней мере на Godbolt, Clang 3.7 падает с обоими!
// inline assembly modifiers to convert ymm to xmm
#include <x86intrin.h>
#include <stdint.h>
// gcc also accepts "%q1" as "%x1"
// icc accepts "%q1" but not "%x1"
// clang-3.6 accepts neither
// clang-3.7 crashes with both!
#define ASM_MOVD(vec, reg) \
__asm volatile("vmovd %q1, %0" : \
"=r" (reg) : \
"x" (vec) \
);
uint32_t movd_ymm(__m256i ymm) {
uint32_t low;
ASM_MOVD(ymm, low);
return low;
}
uint32_t movd_xmm(__m128i xmm) {
uint32_t low;
ASM_MOVD(xmm, low);
return low;
}
Ссылка для проверки на Godbolt: http://goo.gl/bOkjNu
(Извините, что это не полный ответ на ваш вопрос, но он показался вам полезной информацией и был слишком длинным для комментария)