首页 文章

'vpbroadcastd'的操作数类型不匹配

提问于
浏览
3

我试图找到Xeon Phi平台的KNC广播指令 . 但我找不到任何指示 . 相反,我试图在程序集中使用此AVX _mm512_set1_epi32内在函数 . 我有两个问题:首先是有任何KNC广播指令吗?其次,当我编译下面的代码时,我得到了'vpbroadcastd'错误的操作数类型不匹配 .

int op = 2;
__asm__("vmovdqa32 %0,%%zmm0\n\t"
            "mov %1, %%eax\n\t"
            "vpbroadcastd %%eax, %%zmm1\n\t"
            "vpsravd %%zmm1,%%zmm0,%%zmm1\n\t"
            "vmovdqa32 %%zmm1,%0;"
            : "=m" (tt[0]): "m" (op));

哪个tt使用下面的代码定义,我使用k1om-mpss-linux-gcc编译器来编译这段代码

int * tt = (int *) aligned_malloc(16 * sizeof(int),64);

2 回答

  • 3

    我看了AVX2如何用内在函数做到这一点,并注意到广播从内存读取就像使用KNC一样 . 从AVX2内在函数看组件,我写了内联汇编,它做了同样的事情 .

    #include <stdio.h>
    #include <x86intrin.h>
    void foo(int *A, int n) {
        __m256i a16 = _mm256_loadu_si256((__m256i*)A);
        __m256i t = _mm256_set1_epi32(n);
        __m256i s16 = _mm256_srav_epi32(a16,t);
        _mm256_storeu_si256((__m256i*)A, s16);
    }
    
    void foo2(int *A, int n) {
        __asm__("vmovdqu      (%0),%%ymm0\n"
                "vpbroadcastd (%1), %%ymm1\n"
                "vpsravd      %%ymm1, %%ymm0, %%ymm0\n"
                "vmovdqu      %%ymm0, (%0)"
                :
                : "r" (A), "r" (&n)
                : "memory"
            );
    }
    
    int main(void) {
        int x[8];
        for(int i=0; i<8; i++) x[i] = 1<<i;
        for(int i=0; i<8; i++) printf("%8d ", x[i]); puts("");
        foo2(x,2);
        for(int i=0; i<8; i++) printf("%8d ", x[i]); puts("");
    }
    

    这是我对KNC的猜测(使用对齐的加载):

    void foo2_KNC(int *A, int n) {
        __asm__("vmovdqa32      (%0),%%zmm0\n"
                "vpbroadcastd   (%1), %%zmm1\n"
                "vpsravd        %%zmm1, %%zmm0, %%zmm0\n"
                "vmovdqa32      %%zmm0, (%0)"
                :
                : "r" (A), "r" (&n)
                : "memory"
            );
    }
    

    使用KNC和AVX512似乎有一种更有效的方法 .

    Intel says关于AVX12的"2.5.3 Broadcast"部分:

    EVEX编码提供了一个位域,用于编码某些加载操作指令的数据广播

    然后举例说明

    vmulps zmm1, zmm2, [rax] {1to16}
    

    哪里

    {1to16}原语从内存加载一个float32(单精度)元素,复制16次以形成16个32位浮点元素的向量,将16个float32元素与第一个源操作数中的对应元素相乘向量,并将16个结果中的每一个放入目标操作数 .

    我之前从未使用过他的语法,但你可以试试

    void foo2_KNC(int *A, int n) {
    __asm__("vmovdqa32      (%0),%%zmm0\n\t"
            "vpsravd        (%1)%{1to16}, %%zmm0, %%zmm0\n\t"
            "vmovdqa32      %%zmm0, (%0)\t"
            :
            : "r" (A), "r" (&n)
            : "memory", "%zmm0"
        );
    

    }

    这会产生

    vmovdqa32      (%rax),%zmm0
    vpsravd        (%rdx){1to16}, %zmm0, %zmm0
    vmovdqa32      %zmm0, (%rax)
    

    他说,Agner Fog偶然在_2877026中有一个 Headers 为"8.4 Assembly syntax for AVX-512 and Knights Corner instructions"的部分

    这两个指令集非常相似,但具有不同的可选指令属性 . 来自这两个指令集的指令在前缀中只有一个位,即使是相同的指令也是如此 .

    根据他的文档,NASM支持AVX-512和KNC语法,因此您可以在NASM中尝试这种语法 .

  • 2

    这个答案的早期版本是错误的 . 根据An Intels PDF of the KNC insn from Sep 2012, which I hope is current/up-to-date,512b vpsrad 仅适用于即时计数 . 当你在GP寄存器(而不是内存)中计数时,它看起来相当不方便 .

    似乎变量计数移位( vpsravd )是在KNC上进行非立即计数移位的唯一方法,即使每个元素的计数相同 . 由于它可以使用广播负载进行移位计数,因此这不是一个大问题 . KNC似乎也有一个来自寄存器源的"swizzle" shuffle或广播( zmm1 {aaaa} ),但我不确定该广播的宽度是多少 .

    这不能在普通的编译器上编译:{1to16}被忽略,并且你得到一个错误,“对于`vpsravd',这种类型的操作数需要广播” . IDK,如果这只是一个语法问题,使用intel-syntax而不是AT&T .

    // compile with -masm=intel
    // todo: something clever to use vpsrad when the shift count is a compile-time constant
    void shift_KNC(int *A, int n) {
    
      __asm__ volatile(
        // ".intel_syntax noprefix\n"
        "vmovdqa32      zmm0, %0\n\t"
        "vpsravd        zmm0, zmm0, %1 {1to16}\n\t"
        "vmovdqa32      %0,  zmm0\n\t"
        : "+m" (*(__m512i*)A)
        : "m" (n) /* force it to memory */
        : "%zmm0"
      );
    }
    

    仍然使用完整的“内存”代码,因为我们只告诉编译器使用第一个整数作为输入/输出内存操作数,而不是下一个16 .

    If you can keep the zmm value in memory, instead of storing/reloading between tiny fragments of inline asm, that will perform much better.


    根据Xeon Phi Knights Corner intrinsics with GCC,gcc不支持KNC的内在函数 .


    我认为我的PDF版本适用于AVX512(KNL / Skylake-E) . IDK关于KNC;它可能没有这个 . (特别是:英特尔®架构指令集扩展编程参考,自2014年10月起 . )

    有一个GP-register源形式 VPBROADCASTD ,只需要AVX512F . VPBROADCASTD zmm1 {k1}{z}, r32 . 内在的是

    __m512i _mm512_maskz_set1_epi32( __mmask16 k, int a);
    

    没有面具没有列出,但也许只能试试 _mm512_set1_epi32(int) .

    顺便说一句,你的内联汇编compiles ok with a normal compiler on godbolt . ("binary"复选框使它实际组装然后反汇编,所以我确定说明已被接受 . )

    如果您仍然使用内联asm而不是内在函数,请确保整理代码:如果您要求编译器将 op 放入内存,请使用广播加载,而不是 mov 到GP寄存器中从那里播放 . 更好的是,为 vpsravdVPSRAVD zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst 使用广播加载内存操作数 . 然后你根本不需要VPBROADCAST指令 . (我假设编译器会使用内在函数执行此操作 . )

相关问题