16

在尝试用内在函数和汇编回答嵌入式广播时,我试图做这样的事情:

__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) size, 64bits) 的修饰符。在向量寄存器上使用q将始终将其降低到xmm(from ymmor 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 编译器资源管理器上


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

无论如何,这个特定的问题是关于向量寄存器的操作数修饰符和约束。请关注这一点,但欢迎就其他问题发表评论和回答。(或者更好,只是评论/回答 Z Boson 关于嵌入式广播的问题。)

4

2 回答 2

9

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

       b -- 打印指定操作数的寄存器的 QImode 名称。
        如果操作数 [0] 为 reg 0,则 %b0 将打印 %al。
       w -- 同样,打印寄存器的 HImode 名称。
       k -- 同样,打印寄存器的 SImode 名称。
       q -- 同样,打印寄存器的 DImode 名称。
       x -- 同样,打印寄存器的 V4SFmode 名称。
       t -- 同样,打印寄存器的 V8SFmode 名称。
       g -- 同样,打印寄存器的 V16SFmode 名称。
       h -- 打印“高”寄存器的 QImode 名称,ah、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 regs 否则

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

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

请注意,这都是从最新的 SVN 修订版复制而来的。我不知道 GCC 的哪个版本(如果有)添加了您感兴趣的特定修饰符和约束。

于 2015-12-25T05:52:12.250 回答
3

似乎所有最新版本的 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 <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

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

于 2016-01-12T04:22:39.890 回答