如何将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寄存器的所有位置都赋一个特定的值,不知道最后的结果对不对
- 将0或1赋值给
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位和低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}}
问题:
- GDB结果(结构)是什么意思?例如,v8_float、v4_double、v32_int8、等等
- 第二种情况(0xA),为什么v8_float和v4_double总是0?
- 如何将值(例如'a')赋值到YMM中的所有位置(包括高128位和低128位)?
首先,你的内联 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。 (参见 )
我是汇编代码和 SSE/AVX 指令的新手。现在想给256位YMM寄存器的所有位置都赋一个特定的值,不知道最后的结果对不对
- 将0或1赋值给
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位和低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}}
问题:
- GDB结果(结构)是什么意思?例如,v8_float、v4_double、v32_int8、等等
- 第二种情况(0xA),为什么v8_float和v4_double总是0?
- 如何将值(例如'a')赋值到YMM中的所有位置(包括高128位和低128位)?
首先,你的内联 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。 (参见