为什么 gcc 将 _mm256_permute2f128_ps 编译为 Vinsertf128 指令?
Why gcc compile _mm256_permute2f128_ps to Vinsertf128 instruction?
该指令是 C 程序 (gcc -O2) 的汇编输出的一部分。根据结果,我了解到 ymm6
是 source operand 1
,所有这些都被克隆到 ymm9
,然后 xmm1
被克隆到 ymm6[127-256]
我读 Intel manual 但它使用 Intel 汇编语法而不是 At&t,我不想使用 Intel 语法。所以这里的ymm8
、ymm2
和ymm6
就是SRC1
。这是真的?
vshufps , %ymm0, %ymm8, %ymm6
vshufps , %ymm4, %ymm2, %ymm1
Vinsertf128 , %xmm1, %ymm6, %ymm9
主要问题是为什么gcc
改变了指令
row0 = _mm256_permute2f128_ps(__tt0, __tt4, 0x20);
至
Vinsertf128 , %xmm1, %ymm6, %ymm9
和
row4 = _mm256_permute2f128_ps(__tt0, __tt4, 0x31);
至
Vperm2f128 , %ymm1, %ymm6, %ymm1
我怎么能忽略这个优化呢?我尝试了 -O0
但没有用。
So ymm8, ymm2 and ymm6 here is SRC1. is this true?
是的,在两种语法的 3 操作数指令中,中间操作数始终是 src1。
- AT&T:
op %src2, %src1, %dest
- 英特尔:
op dest, src1, src2
I don't want to use Intel syntax
艰难。我所知道的关于每条指令确切功能的唯一真正好的文档是 Intel insn ref 手册。我曾经认为 AT&T 语法更好,因为 $ 和 % 装饰器消除了歧义。我确实喜欢那样,但现在更喜欢 Intel 语法。每个规则都非常简单,您可以轻松地在脑海中转换,或者 "think" 在您正在阅读 ATM 的任何一个中。
除非您实际上正在编写 GNU C 内联汇编,否则您可以仅使用 gcc -masm=intel
和 objdump -Mintel
使用英特尔助记符、操作数顺序等获得 GNU 风格的汇编。汇编程序指令仍然是 gas
风格,而不是 NASM。使用 http://gcc.godbolt.org/ 为代码获取格式良好的 asm 输出,只留下基本标签。
gcc 和 clang 都对内部函数的实际作用有一些了解,因此在内部它们将内部函数转换为一些数据移动。当需要发出代码时,他们看到所述数据移动可以用 vinsertf128
完成,所以他们发出那个。
在某些 CPU(Intel SnB 系列)上,两条指令具有相同的性能,但在 AMD Bulldozer 系列(只有 128b ALU)上,vinsertf128
比 vperm2f128
快得多。 (来源:参见 Agner Fog 的指南,以及 x86 标签 wiki 中的其他链接)。它们都需要 6 个字节来编码,包括立即数,因此没有代码大小差异。 vinsertf128
始终比执行相同数据移动的 vperm2f128
更好。
gcc 和 clang 没有 "literal translation of intrinsics to instructions" 模式,因为它需要额外的工作来实现。如果您确切关心编译器使用的指令,那正是内联汇编的用途。
Keep in mind that -O0
doesn't mean "no optimization"。在发出 asm 之前,它仍然需要通过几个内部表示进行转换。
查看指令分析报告中绑定5号端口的指令,发现指令为broadcasts
和vpermilps
。 broadcasts
只能在端口 5 上执行,但是将它们替换为 128-bit
加载后接 vinsertf128
指令可以减轻端口 5 的压力,因为 vinsertf128
可以在端口 0 上执行。 from IACA user guid
该指令是 C 程序 (gcc -O2) 的汇编输出的一部分。根据结果,我了解到 ymm6
是 source operand 1
,所有这些都被克隆到 ymm9
,然后 xmm1
被克隆到 ymm6[127-256]
我读 Intel manual 但它使用 Intel 汇编语法而不是 At&t,我不想使用 Intel 语法。所以这里的ymm8
、ymm2
和ymm6
就是SRC1
。这是真的?
vshufps , %ymm0, %ymm8, %ymm6
vshufps , %ymm4, %ymm2, %ymm1
Vinsertf128 , %xmm1, %ymm6, %ymm9
主要问题是为什么gcc
改变了指令
row0 = _mm256_permute2f128_ps(__tt0, __tt4, 0x20);
至
Vinsertf128 , %xmm1, %ymm6, %ymm9
和
row4 = _mm256_permute2f128_ps(__tt0, __tt4, 0x31);
至
Vperm2f128 , %ymm1, %ymm6, %ymm1
我怎么能忽略这个优化呢?我尝试了 -O0
但没有用。
So ymm8, ymm2 and ymm6 here is SRC1. is this true?
是的,在两种语法的 3 操作数指令中,中间操作数始终是 src1。
- AT&T:
op %src2, %src1, %dest
- 英特尔:
op dest, src1, src2
I don't want to use Intel syntax
艰难。我所知道的关于每条指令确切功能的唯一真正好的文档是 Intel insn ref 手册。我曾经认为 AT&T 语法更好,因为 $ 和 % 装饰器消除了歧义。我确实喜欢那样,但现在更喜欢 Intel 语法。每个规则都非常简单,您可以轻松地在脑海中转换,或者 "think" 在您正在阅读 ATM 的任何一个中。
除非您实际上正在编写 GNU C 内联汇编,否则您可以仅使用 gcc -masm=intel
和 objdump -Mintel
使用英特尔助记符、操作数顺序等获得 GNU 风格的汇编。汇编程序指令仍然是 gas
风格,而不是 NASM。使用 http://gcc.godbolt.org/ 为代码获取格式良好的 asm 输出,只留下基本标签。
gcc 和 clang 都对内部函数的实际作用有一些了解,因此在内部它们将内部函数转换为一些数据移动。当需要发出代码时,他们看到所述数据移动可以用 vinsertf128
完成,所以他们发出那个。
在某些 CPU(Intel SnB 系列)上,两条指令具有相同的性能,但在 AMD Bulldozer 系列(只有 128b ALU)上,vinsertf128
比 vperm2f128
快得多。 (来源:参见 Agner Fog 的指南,以及 x86 标签 wiki 中的其他链接)。它们都需要 6 个字节来编码,包括立即数,因此没有代码大小差异。 vinsertf128
始终比执行相同数据移动的 vperm2f128
更好。
gcc 和 clang 没有 "literal translation of intrinsics to instructions" 模式,因为它需要额外的工作来实现。如果您确切关心编译器使用的指令,那正是内联汇编的用途。
Keep in mind that -O0
doesn't mean "no optimization"。在发出 asm 之前,它仍然需要通过几个内部表示进行转换。
查看指令分析报告中绑定5号端口的指令,发现指令为broadcasts
和vpermilps
。 broadcasts
只能在端口 5 上执行,但是将它们替换为 128-bit
加载后接 vinsertf128
指令可以减轻端口 5 的压力,因为 vinsertf128
可以在端口 0 上执行。 from IACA user guid