В 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

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

Другие вопросы по тегам