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

How to set all the values in AVX ymm register to be the same (all are 0/1/specific value)?

我是汇编代码和 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}}
  1. 0xA设置为ymm0中的所有位置(高128位和低128位):
__asm__ __volatile__(
      "movq [=12=]xaaaaaaaaaaaaaaaa, %%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位和低128位)?

P.S VPBROADCAST — Load Integer and Broadcast

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


v8_float表示将256位解释为8xfloat的Vector。即英特尔内部函数中的__m256

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


(2) 整数 0xa 是非常小的次正规浮点数或双精度数的 bit-pattern。尝试将其作为 https://www.h-schmidt.net/FloatConverter/IEEE754.html.

上的“十六进制表示”

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

如果你真的想要 _mm256_set1_epi8(0x0a)(将其广播到每个字节),你应该写 0x0a0a0a0a 而不是 0xaaaaaaaa。 (不需要使用 qword 立即数;vpbroadcastd 运行速度一样快,但 mov [=26=]x0a0a0a0a, %eax 是一个更小更快的指令。)

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

策略

Clang 使用来自 8 字节内存源的 vbroadcastsd(与 vpbroadcastq 相同)。 4 字节 broadcast-loads 同样有效。 (不像 byte 或 word 需要额外的 ALU uop:https://uops.info/ and https://agner.org/optimize/

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


AFAIK,没有什么好技巧可以从 all-ones 动态生成 0xa (0b1010)。其他一些常量,如 0x1 或 0x8000000 可以用 2 条指令生成,从 vpcmpeqd same,same,same 开始,表示 all-ones。 (参见