Каковы модификаторы переопределения размера для xmm / ymm / zmm во встроенном asm GNU C для одного операнда?

Пытаясь ответить на встроенные трансляции с встроенными функциями и сборкой, я пытался сделать что-то вроде этого:

__m512 mul_bcast(__m512 a, float b) {
    asm(
        "vbroadcastss  %k[scalar], %q[scalar]\n\t"  // want  vbcast..  %xmm0, %zmm0
        "vmulps        %q[scalar], %[vec], %[vec]\n\t"
        : [vec] "+x" (a), [scalar] "+&x" (b)
        : :
    );
    return a;
}

В документе GNU C x86 Operand Modifiers указаны модификаторы только до q (размер DI (DoubleInt), 64 бита). Использование q в векторном регистре всегда будет понижать его до xmmymm или zmm). например скалярные регистры:

 long scratch = 0;  // not useful instructions, just syntax demo
 asm(
     "movw         symbol(%q[inttmp]), %w[inttmp]\n\t"  // movw symbol(%rax), %ax
     "movsbl        %h[inttmp], %k[inttmp]\n\t"     // movsx %ah, %eax
   :  [inttmp] "+r" (scratch)
   :: "memory"  // we read some index in symbol[]
 );

Вопрос:

Какие модификаторы можно изменять между размерами векторного регистра?

Кроме того, существуют ли какие-либо ограничения конкретного размера для использования с входными или выходными операндами? Нечто другое, кроме общего x, которое может оказаться xmm, ymm или zmm в зависимости от типа выражения, которое вы поместили в круглые скобки.

Не по теме:
clang, похоже, имеет некоторые Yi / Yt ограничения (не модификаторы), но я тоже не могу найти документацию по этому поводу. clang даже не скомпилирует это, даже если векторные инструкции закомментированы, потому что ему не нравится +x как ограничение для вектора __m512.


Предпосылки / мотивация

Я могу получить желаемый результат, передав скаляр в качестве входного операнда, который должен находиться в том же регистре, что и более широкий выходной операнд, но это более неуклюже. (Самым большим недостатком этого варианта использования является то, что AFAIK ограничение сопоставления может ссылаться только по номеру операнда, а не по [symbolic_name], поэтому оно подвержено поломке при добавлении / удалении ограничений вывода.)

// does what I want, by using a paired output and input constraint
__m512 mul_bcast(__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;
}

На Godbolt compiler explorer


Кроме того, я думаю, что весь этот подход к проблеме, которую я пытался решить, будет тупиком, потому что Множественные альтернативные ограничения не позволяет вам указывать разные asm для разных шаблонов ограничений. Я надеялся, что ограничения x и r приведут к выдаче vbroadcastss из регистра, в то время как ограничения m будут выдавать vmulps (mem_src){1to16}, %zmm_src2, %zmm_dst (сложенная широковещательная загрузка). Цель выполнения этого с помощью встроенного asm заключается в том, что gcc еще не знает, как свернуть set1() операнды памяти в широковещательные загрузки (но clang знает).

В любом случае, этот конкретный вопрос касается модификаторов операндов и ограничений для векторных регистров. Пожалуйста, сосредоточьтесь на этом, но комментарии и отступления в ответах приветствуются по другому вопросу. (Или лучше просто прокомментируйте / ответьте на вопрос Z Boson о встроенных трансляциях.)


person Peter Cordes    schedule 25.12.2015    source источник
comment
Глядя на ix86_print_operand в i386.c, я бы попробовал% g.   -  person David Wohlferd    schedule 25.12.2015
comment
Кроме того, вам не нужно использовать номера операндов при сопоставлении входов и выходов: asm("" : [me] "=a" (a) : "[me]"(7));.   -  person David Wohlferd    schedule 25.12.2015
comment
@DavidWohlferd: Спасибо! Я действительно рад узнать о синтаксисе "[me]". Это было главным возражением против метода ограничения выходного соответствия.   -  person Peter Cordes    schedule 25.12.2015
comment
Когда Энгер сказал, что синтаксис встроенной сборки GCC сложен и труден для изучения, он не шутил. Я чувствовал, что более или менее получил NASM через несколько дней и могу выяснить что-нибудь еще из документации, но сборка GCC inliene в некоторых случаях все еще сбивает с толку. На самом деле я не особо возражаю против синтаксиса AT&T, но расширенный синтаксис GCC сложен.   -  person Z boson    schedule 25.12.2015
comment
@Zboson Официальная документация лучше, чем раньше. Раньше половина этого была скрыта во внутренней документации GCC. Сложность заключается в том, что вам нужно описать каждый эффект и побочные эффекты, которые имеет ваш asm-оператор, и можно легко что-то упустить.   -  person Ross Ridge    schedule 26.12.2015
comment
@firo: эта ссылка уже находится в вопросе, и все еще не задокументирован модификатор %g0 для получения ZMM-имени входа или выхода "x"(__m256)   -  person Peter Cordes    schedule 21.09.2018


Ответы (2)


Из файла gcc / config / i386 / i386.c источников GCC:

       b -- print the QImode name of the register for the indicated operand.
        %b0 would print %al if operands[0] is reg 0.
       w --  likewise, print the HImode name of the register.
       k --  likewise, print the SImode name of the register.
       q --  likewise, print the DImode name of the register.
       x --  likewise, print the V4SFmode name of the register.
       t --  likewise, print the V8SFmode name of the register.
       g --  likewise, print the V16SFmode name of the register.
       h -- print the QImode name for a "high" register, either ah, bh, ch or dh.

Аналогично из gcc / config / i386 / contraints.md:

    ;; We use the Y prefix to denote any number of conditional register sets:
    ;;  z   First SSE register.
    ;;  i   SSE2 inter-unit moves to SSE register enabled
    ;;  j   SSE2 inter-unit moves from SSE register enabled
    ;;  m   MMX inter-unit moves to MMX register enabled
    ;;  n   MMX inter-unit moves from MMX register enabled
    ;;  a   Integer register when zero extensions with AND are disabled
    ;;  p   Integer register when TARGET_PARTIAL_REG_STALL is disabled
    ;;  f   x87 register when 80387 floating point arithmetic is enabled
    ;;  r   SSE regs not requiring REX prefix when prefixes avoidance is enabled
    ;;  and all SSE regs otherwise

Этот файл также определяет ограничение «Yk», но я не знаю, насколько хорошо оно будет работать в выражении asm:

    (define_register_constraint "Yk" "TARGET_AVX512F ? MASK_EVEX_REGS : NO_REGS"
    "@internal Any mask register that can be used as predicate, i.e. k1-k7.")

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

person Ross Ridge    schedule 25.12.2015
comment
Отлично работает с gcc 5.3 на godbolt. За исключением ложного создания кадра стека и избыточного push / pop %r10. Похож на gcc.gnu.org/bugzilla/show_bug.cgi?id=69041 (о котором я сообщил вчера), но это влияет не только на -m32, но на цель размером 64b. - person Peter Cordes; 25.12.2015

Похоже, что все последние версии 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

(Извините, что это неполный ответ на ваш вопрос, но мне показалось, что это полезная информация, и она была слишком длинной для комментария)

person Nathan Kurz    schedule 12.01.2016
comment
(Попал сюда случайно откуда-то еще) Этот код на самом деле тонко ошибочен - gcc в основном берет то, что вы зашли, и что-то распечатывает: vmovd% xmm0,% eax Однако у вас есть модификатор вывода в регистре xmm, а не целочисленный регистр. Если вы поменяете их местами, вы получите правильный вывод rax для 64-битной версии. Вам также нужно ограничение y для случая регистра ymm. - person echristo; 18.04.2018
comment
Я давно не думал об этом, но не думаю, что ваше исправление правильное. Не является ошибкой то, что модификатор q находится в регистре XMM: цель состоит в том, чтобы найти синтаксис, который изменит переданный регистр YMM и выходную сборку для соответствующего XMM в ICC, Clang и GCC. И VMOVD требует 32-битного целочисленного регистра (в отличие от VMOVQ): felixcloutier.com/x86 /MOVD:MOVQ.html. Но, возможно, я не правильно слежу за тобой. Не могли бы вы дать ссылку на тест на Godbolt, показывающий, что именно вы предлагаете? - person Nathan Kurz; 19.04.2018
comment
Итак, вы определенно правы в том, что я ошибался, честно говоря, во многих отношениях. Я должен был быть более осторожным. Похоже, вам может понадобиться модификатор 'x': godbolt.org/g/mxRBVd, который будет относиться к операнду как к типу V4SF и распечатывать правильные данные - по крайней мере, в gcc. В настоящее время он не работает в clang (отправьте сообщение об ошибке, и я постараюсь добраться до него), а также не работает в самой последней версии, которая есть в проводнике компилятора :( В противном случае вы можете попробовать соответствующий встроенный? Во всяком случае , извините за путаницу и надеюсь, что это немного поможет. - person echristo; 21.05.2018