【问题标题】:AVX-512 - How to gather data from memory using assembly instruction?AVX-512 - 如何使用汇编指令从内存中收集数据?
【发布时间】:2021-07-24 16:26:37
【问题描述】:

我正在尝试使用汇编指令从内存中收集 64 位整数。下面你可以看到我是如何从C 调用assembly 代码的。请注意,汇编代码使用NASM 语法。

nasm_gather.asm文件

bits 64

        section .text
global nasm_gather:function

extern base_addr
extern vindex

nasm_gather:
        ; prolog
        push            rbp
        push            rbx
        push            r12
        push            r13

        mov             r12         ,[rel base_addr]                ; r12 point to base_addr
        mov             r13         ,[rel vindex]                   ; r13 points to vindex
        vmovdqu32       zmm1        ,[r13]                          ; zmm1 = [2, 5, 1, 3, 0, 4, 7, 6]
        vpxorq          zmm2        ,zmm2               ,zmm2       ; zmm2 = [0, 0, 0, 0, 0, 0, 0, 0]
        vpgatherqq      zmm2        ,[r12 + zmm1*8]                 ; ----> Illegal instruction at address = ...
        ...
        ; epilog
        pop             r13
        pop             r12
        pop             rbx
        pop             rbp
        ret

main.cpp文件

#include <iostream>
#include <immintrin.h>

using namespace std;

extern "C" int nasm_gather();

const int N=32;
int64_t* base_addr /*__attribute__ ((aligned (64)))*/  = (int64_t *) malloc(sizeof(int64_t) * N);
int64_t* vindex =  (int64_t *) malloc(sizeof(int64_t) * 8);


int main() {
    /* initialize indices */
    vindex[0]=2; vindex[1]=5; vindex[2]=1; vindex[3]=3;
    vindex[4]=0; vindex[5]=4; vindex[6]=7; vindex[7]=6;
    // ...
    int64_t result = nasm_gather();
    ...

    return 0;
}

(vpgatherqq zmm, vm64z汇编指令对应C中的_mm512_i64gather_epi64内部函数)

正如程序到达这一点:

vpgatherqq      zmm2        ,[r12 + zmm1*8]

我收到非法指令错误:

地址非法指令 = 4011f0: 62 d2 fd 48 91 14 cc 62 f1 7e 48 6f c2 e8 10
如果您认为您的应用程序应该尝试 执行这条非法指令(以及其他可能存在的指令), 然后使用这个旋钮:-emit-illegal-insts 0,这个错误信息将 避免。

有什么问题?

【问题讨论】:

  • mov r12, [rel base_addr] 将使用来自base_addr[0] 的值加载r12,而我认为您只希望它包含base_addr 的地址。所以我认为你想要lea r12, [rel base_addr]。对于以下行也是如此。虽然我仍然希望出现分段错误而不是非法指令。
  • @NateEldredge 我收到相同的错误消息Illegal instruction at address = 4011dc: 62 d2 fd 48 91 14 cc 62 f1 7e 48 6f c2 e8 07
  • 但是说真的,如果你只想使用 C 中的 vpgatherqq,请使用 immintrin.h 中的 _mm512_mask_i64gather_epi64software.intel.com/sites/landingpage/IntrinsicsGuide/…。并且不要使用全局变量来传递参数!我希望您实际上并不打算将其用于性能。
  • 您是否告诉您的模拟器启用 AVX-512?是哪个模拟器?另请注意,您的评论是错误的; kxnorW 将产生 65535。或者正如大多数人所写的那样,-1 或 0xFFFF
  • @him 您没有回答彼得关于您使用的是什么模拟器(或您传递给它的参数)的问题。在您的情况下,这将是 minimal reproducible example 的一部分(以及实际编译的代码,即摆脱 ...

标签: c++ assembly nasm simd avx512


【解决方案1】:

聚会需要一个面具(这样他们就可以在被打断或某个元素出现故障时记录进度)。 NASM 通常不会让你在没有警告的情况下汇编非法指令。这是一个 NASM 错误,它无法帮助您发现此错误。

此外,您使用全局变量而不是函数 args 的整个方法对可维护性和性能都不利。如果您已经愿意告诉 GCC 它可以在您的源代码中发出 AVX-512 指令 (-march=skylake-avx512) 和 #include &lt;immintrin.h&gt;,请像普通人一样使用内在函数。例如_mm512_mask_i64gather_epi64。调用任何函数而不是内联收集指令将花费收集成本的很大一部分,而且如果以这种方式编写的是一个笨拙的低效函数,则更多。如果您的索引尚未在 SIMD 向量中,则收集是非常有问题的,并且使用存储在全局变量中的指针作为索引肯定无济于事,而不是为收集函数传递指针 arg 以加载向量来自某个地方的索引。


以下代码在 SDE 8.33.0、NASM 2.15.05 中运行良好。您声称添加 {k1} 并不能为您解决问题。要么你的 SDE 版本坏了,要么你做错了什么。或者您忘记从更新的源代码重建可执行文件。

default rel
global _start
_start:
    lea     rax, [rel buf]      ; dummy base = static array.  In a function, use RDI (first int/pointer arg)
    vpxor   xmm1, xmm1,xmm1     ; ZMM1 = dummy index = all zeros, efficiently done with a VEX-coded AVX instruction

    kxnorb      k1, k0,k0           ; mask = -1
    vpxor       xmm0, xmm0,xmm0     ; optional: dependency-breaking before merge-masking.  GCC will do this for the intrinsic.
    vpgatherqq  zmm0{k1}, [rax + zmm1*8]
        
    mov eax, 231
    syscall             ; exit_group(RDI)

section .bss
buf: resd 1024

如果我删除{k1},我可以重现该 SDE 错误消息,使其像您原来的问题一样被揭开。如果您尝试使用 {k1}{z},则 NASM 2.15.05 错误 - Gathers 仅支持合并屏蔽(同样,它可以在被 #PF 或可能中断的部分执行中断后恢复)。但是使用正确的源代码,它可以在静态可执行文件中正常构建和运行。主机 CPU 是 i7-6700k Skylake 客户端(它不支持 AVX-512,因此由 SDE 决定)。

$ nasm -felf64 avx512-gather.asm
$ ld -o avx512-gather avx512-gather.o
$ /opt/sde-external-8.33.0-2019-02-07-lin/sde64 -- ./avx512-gather
$ echo $?
0

(当然,sde64 -icl 也有效。)

将相同的机器代码链接到可从 C++ 调用的函数会以相同的方式运行,但同样,当您可以使用内部函数时,这将毫无意义(并使用 objdump -drwC -Mintel a.out 反汇编以查看 GCC 如何使用该指令。)

【讨论】:

  • 谢谢!我会检查一下。
  • 你为什么将zmm0归零?单独加载到zmm0 是否依赖于寄存器的其余部分或其他内容?
  • @Noah:聚集仅适用于 merge 掩码,所以是的,错误的 dep。虽然既然你提到它,价值并不重要,只是破坏,所以我应该修复评论。写这个答案时我脑子里放了个屁,我认为价值对正确性很重要,就像它是对它进行 ORing 之类的。但当然不是。 GCC 也使用异或归零,但那是因为它喜欢 dep-break。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-16
  • 2018-12-22
  • 1970-01-01
  • 1970-01-01
  • 2017-05-08
相关资源
最近更新 更多