在GNU C inline asm中,单个操作数的xmm / ymm / zmm的修饰符是什么?

时间:2015-12-25 03:35:23

标签: c gcc assembly sse avx512

在尝试回答Embedded broadcasts with intrinsics and assembly时,我试图做这样的事情:

__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 Operand Modifiers doc仅指定最多q的修饰符(DI(DoubleInt)大小,64位)。在向量寄存器上使用q将始终将其降至xmm(来自ymmzmm)。

问题:

在矢量寄存器的大小之间有哪些修饰符可以改变?

此外,是否有任何特定大小的约束用于输入或输出操作数?除通用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_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;
}

godbolt link

另外,我认为我试图解决的问题的整个方法将是一个死胡同,因为Multi-Alternative constraints不允许你为不同的约束模式赋予不同的asm。我希望xr约束最终从寄存器中发出vbroadcastss,而m约束最终会发出vmulps (mem_src){1to16}, %zmm_src2, %zmm_dst(折叠的广播 - 加载)。使用内联asm执行此操作的目的是gcc还不知道如何将set1()内存操作数折叠到广播加载中(但是clang确实如此)。

无论如何,这个具体问题是关于向量寄存器的操作数修饰符和约束。请关注这一点,但欢迎在另一个问题上提出意见和答案。 (或者更好,只是对Z Boson关于嵌入式广播的问题发表评论/回答。)

2 个答案:

答案 0 :(得分:8)

来自海湾合作委员会来源的档案gcc/config/i386/i386.c

       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的版本,如果有的话,你感兴趣的特定修饰符和约束被添加。

答案 1 :(得分:2)

似乎所有最近版本的海湾合作委员会都会同时接受' q'和' x'作为修改器来打印YMM寄存器的XMM版本。

英特尔的icc希望接受“q'”而不是' x' (至少通过版本13.0.1)。

[编辑:嗯,它在下面这个小例子中有效,但在一个真实的测试案例中,我遇到了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

(很抱歉,这不是对你的问题的完整答案,但它似乎是有用的信息分享,而且评论太长了)