为什么 gcc 将 _mm256_permute2f128_ps 编译为 Vinsertf128 指令?

Why gcc compile _mm256_permute2f128_ps to Vinsertf128 instruction?

该指令是 C 程序 (gcc -O2) 的汇编输出的一部分。根据结果​​,我了解到 ymm6source operand 1,所有这些都被克隆到 ymm9,然后 xmm1 被克隆到 ymm6[127-256] 我读 Intel manual 但它使用 Intel 汇编语法而不是 At&t,我不想使用 Intel 语法。所以这里的ymm8ymm2ymm6就是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=intelobjdump -Mintel 使用英特尔助记符、操作数顺序等获得 GNU 风格的汇编。汇编程序指令仍然是 gas 风格,而不是 NASM。使用 http://gcc.godbolt.org/ 为代码获取格式良好的 asm 输出,只留下基本标签。


gcc 和 clang 都对内部函数的实际作用有一些了解,因此在内部它们将内部函数转换为一些数据移动。当需要发出代码时,他们看到所述数据移动可以用 vinsertf128 完成,所以他们发出那个。

在某些 CPU(Intel SnB 系列)上,两条指令具有相同的性能,但在 AMD Bulldozer 系列(只有 128b ALU)上,vinsertf128vperm2f128 快得多。 (来源:参见 Agner Fog 的指南,以及 标签 wiki 中的其他链接)。它们都需要 6 个字节来编码,包括立即数,因此没有代码大小差异。 vinsertf128 始终比执行相同数据移动的 vperm2f128 更好。

gcc 和 clang 没有 "literal translation of intrinsics to instructions" 模式,因为它需要额外的工作来实现。如果您确切关心编译器使用的指令,那正是内联汇编的用途。

Keep in mind that -O0 doesn't mean "no optimization"。在发出 asm 之前,它仍然需要通过几个内部表示进行转换。

查看指令分析报告中绑定5号端口的指令,发现指令为broadcastsvpermilpsbroadcasts 只能在端口 5 上执行,但是将它们替换为 128-bit 加载后接 vinsertf128 指令可以减轻端口 5 的压力,因为 vinsertf128 可以在端口 0 上执行。 from IACA user guid