在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操作数修改器 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链接


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

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

从GCC来源的文件gcc / config / i386 / i386.c :

        b  - 打印指示操作数的寄存器的QImode名称。
        如果operands [0]为reg 0,则%b0将打印%al。
        w  - 同样,打印寄存器的HImode名称。
        k  - 同样,打印寄存器的SImode名称。
        q  - 同样,打印寄存器的DImode名称。
        x  - 同样,打印寄存器的V4SFmode名称。
        t  - 同样,打印寄存器的V8SFmode名称。
        g  - 同样,打印寄存器的V16SFmode名称。
        h  - 打印QImode名称为“高”寄存器,啊,bh,ch或dh。

同样来自gcc / config / i386 / contraints.md :

     ;; 我们使用Y前缀来表示任意数量的条件寄存器集:
     ;;  z第一个SSE寄存器。
     ;;  i SSE2单元间移动到SSE寄存器使能
     ;;  j SSE2内部单元从SSE寄存器启用
     ;;  m MMX单元间移动到MMX寄存器启用
     ;;  n MMX单元间从启用MMX寄存器移动
     ;; 带有AND的零扩展被禁用时的整数寄存器
     ;;  p禁用TARGET_PARTIAL_REG_STALL时的整数寄存器
     ;; 当启用80387浮点运算时,f x87寄存器
     ;;  r SSE regs在启用前缀避免时不需要REX前缀
     ;; 并且所有SSE都注册了

该文件还定义了一个“Yk”约束,但我不知道它在asm语句中的工作效果如何:

     (define_register_constraint“Yk”“TARGET_AVX512F?MASK_EVEX_REGS:NO_REGS”
     “@internal任何可以用作谓词的掩码寄存器,即k1-k7。”)

请注意,这是从最新的SVN版本复制的。 我不知道GCC的版本是什么,如果有的话,你添加了你感兴趣的特定修饰符和约束。

似乎所有最新版本的GCC都接受’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  #include  // 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

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