assembly - 如何将AVX ymm寄存器中的所有值设置为相同(均为0/1/特定值)?

标签 assembly cpu-registers avx avx2

我是汇编代码和 SSE/AVX 指令的新手。现在,我想为256位YMM寄存器中的所有位置分配一个特定的值,但我不知道最终结果是否正确。

  1. 要将 01 分配给 ymm0:
__asm__ __volatile__(
    "vpxor    %%ymm0, %%ymm0, %%ymm0\n\t" // all are 0
    or
    "VPCMPEQB    %%ymm0, %%ymm0, %%ymm0\n\t" // all are 1
: : :);

GDB 结果表明:

// all are 0
ymm0
{v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
v4_double = {0x0, 0x0, 0x0, 0x0}, 
v32_int8 = {0x0 <repeats 32 times>}, 
v16_int16 = {0x0 <repeats 16 times>}, 
v8_int32 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
v4_int64 = {0x0, 0x0, 0x0, 0x0}, 
v2_int128 = {0x0, 0x0}}

// all are 1
ymm0           
{v8_float = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, 
v4_double = {0x7fffffffffffffff, 0x7fffffffffffffff, 0x7fffffffffffffff, 0x7fffffffffffffff}, 
v32_int8 = {0xff <repeats 32 times>}, 
v16_int16 = {0xffff <repeats 16 times>}, 
v8_int32 = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, 
v4_int64 = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}, 
v2_int128 = {0xffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffff}}
  • 要将 0xA 设置为 ymm0 中的所有位置(高位和低位 128 位):
  • __asm__ __volatile__(
          "movq $0xaaaaaaaaaaaaaaaa, %%rcx\n"
          "vmovq %%rcx, %%xmm0\n" 
          "vpbroadcastq %%xmm0, %%ymm0\n": : :);
    

    GDB 结果表明:

    ymm0           
    {v8_float = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
    v4_double = {0x0, 0x0, 0x0, 0x0}, 
    v32_int8 = {0xaa <repeats 32 times>}, 
    v16_int16 = {0xaaaa <repeats 16 times>}, 
    v8_int32 = {0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa}, 
    v4_int64 = {0xaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaa}, 
    v2_int128 = {0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}}
    

    问题:

    1. GDB 结果(结构)是什么意思?例如,v8_floatv4_doublev32_int8等。
    2. 在第二种情况(0xA)中,为什么v8_floatv4_double总是0?<
    3. 如何将值(例如“a”)分配给 YMM 中的所有位置(包括高位和低位 128 位)?

    附注VPBROADCAST — Load Integer and Broadcast

    最佳答案

    首先,您的内联汇编已损坏:缺少一个 "%ymm0" clobber 来告诉编译器您编写了该寄存器。您甚至使用 asm("": : :) 扩展 asm 语法来明确告诉编译器没有破坏者。或者更好,https://gcc.gnu.org/wiki/DontUseInlineAsm - 编写一个单独的函数,或使用内部函数并查看编译器生成的 asm。


    v8_float 表示将 256 位解释为 8x float 的向量。即 Intel Intrinsics 中的 __m256

    v32_int8 是 32x int8_t 的向量,分别打印每个字节。如果您想这样查看,可以使用 p/x $ymm0.v8_int32


    (2) 整数0xa 是非常小的次正规 float 或 double 的位模式。尝试将其作为“十六进制表示”放在 https://www.h-schmidt.net/FloatConverter/IEEE754.html 上.

    (3) 您已经将 0xa 广播到 32 字节 YMM 寄存器中的所有 64 个半字节,如您从 v2_int128 = {0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 中看到的那样aa}} 输出显示两半都是 0xaa 字节。

    如果您确实想要_mm256_set1_epi8(0x0a)(将其广播到每个字节),则应该编写0x0a0a0a0a而不是0xaaaaaaaa。 (无需使用 qword 立即数;vpbroadcastd 运行速度同样快,但 mov $0x0a0a0a0a, %eax 是一条更小、更快的指令。)

    https://godbolt.org/z/z18nMT3fd显示 GCC 和 clang 编译一个返回 _mm256_set1_epi8(0x0a) 的函数(以及另一个广播函数 arg,而不是常量)的函数。 GCC11.3 进行常量传播并从 .rodata 加载 32 个字节。 GCC12.1 不明智地使用了 mov r64、imm64 和 vmovq 策略。

    Clang 使用 8 字节内存源中的 vbroadcastsd(与 vpbroadcastq 相同)。 4 字节广播负载同样高效。 (与花费额外 ALU uop 的字节或字不同:https://uops.info/https://agner.org/optimize/)

    AVX-512 引入了 vpbroadcastb/w/d/q ymm0, eax,它将 vmovd 与广播结合起来。但如果没有这个,如果数据来自整数寄存器,您通常需要 AVX2 vpbroadcastb/w/d/q ymm, xmm 。 (我在这里使用 Intel 语法,就像供应商手册一样;如果您愿意,可以像平常一样反转 AT&T 语法。)


    据我所知,没有什么好技巧可以从全 1 动态生成 0xa (0b1010)。其他一些常量(例如 0x1 或 0x8000000)可以使用 2 条指令生成,从 vpcmpeqd same,same,same 开始(全 1)。 (参见What are the best instruction sequences to generate vector constants on the fly?)

    关于assembly - 如何将AVX ymm寄存器中的所有值设置为相同(均为0/1/特定值)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72484239/

    相关文章:

    performance - 对齐 AVX-256 内存存储是否值得费心?

    performance - 为什么这个 SSE 代码在 Skylake 上没有 VZEROUPPER 的情况下会慢 6 倍?

    c++ - 如何根据另一个包含 0 或 1 个元素的 vector 有条件地否定 AVX2 int16_t vector ?

    android - 了解 ARM ASM 中的函数调用

    c - *** 检测到堆栈粉碎 *** : ./asem 终止段错误(核心转储)

    assembly - x86组件: Why Do I Need Stack Frames?

    c++ - 整数和寄存器大小之间有关系吗?

    assembly - 寄存器AL和AX溢出到哪里?

    c - GCC:禁止使用某些寄存器

    assembly - 当 eip 寄存器达到最大值时会发生什么?