“vpbroadcastd”的操作数类型不匹配

operand type mismatch for `vpbroadcastd'

我试图找到一个用于 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);

这个答案的早期版本是错误的。根据 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" 从注册源 (zmm1 {aaaa}) 随机播放或广播,但我不确定该广播的宽度是多少。

这不会在普通编译器上编译:{1to16} 被忽略,您会得到一个 "broadcast is needed for operand of such type for `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"
  );
}

仍在使用完整的 "memory" 破坏,因为我们只告诉编译器使用第一个整数作为 input/output 内存操作数,而不是下一个 16。

如果您可以将 zmm 值保留在内存中,而不是 storing/reloading 在内联 asm 的微小片段之间,那将执行 很多


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


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

VPBROADCASTD的GP-register源码形式,只需要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" 复选框实际上是 assemble,然后是 disassemble,所以我确定指令已被接受。)

如果您仍然使用内联汇编而不是内部函数,请确保整理您的代码:如果您要要求编译器将 op 放入内存,请使用广播加载,而不是 mov 到 GP 寄存器并从那里广播。更好的是,为 vpsravd 使用广播加载内存操作数:VPSRAVD zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst。那么你根本不需要 VPBROADCAST 指令。 (我假设编译器会使用内在函数来做到这一点。)

我查看了 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 关于“2.5.3 广播”部分中的 AVX12:

EVEX encoding provides a bit-field to encode data broadcast for some load-op instructions

然后给出例子

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

其中

The {1to16} primitive loads one float32 (single precision) elem ent from memory, replicates it 16 times to form a vector of 16 32-bit floating-point elements, multiplies the 16 float32 elements with the corresponding elements in the first source operand vector, and put each of the 16 results into the destination operand.

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

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 在 the documentation for objconv 中有一个标题为“AVX-512 和 Knights Corner 指令的 8.4 汇编语法”的部分,他说

these two instruction sets are very similar, but have different optional instruction attributes. Instructions from these two instruction sets differ by a single bit in the prefix, even for otherwise identical instructions.

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

中尝试此语法