为啥 gcc 将 _mm256_permute2f128_ps 编译为 Vinsertf128 指令?
Posted
技术标签:
【中文标题】为啥 gcc 将 _mm256_permute2f128_ps 编译为 Vinsertf128 指令?【英文标题】:Why gcc compile _mm256_permute2f128_ps to Vinsertf128 instruction?为什么 gcc 将 _mm256_permute2f128_ps 编译为 Vinsertf128 指令? 【发布时间】:2016-03-31 01:19:49 【问题描述】:该指令是 C 程序 (gcc -O2) 的汇编输出的一部分。根据结果,我知道ymm6
是source operand 1
,所有这些都被克隆到ymm9
,然后xmm1
被克隆到ymm6[127-256]
我读到Intel manual,但它使用英特尔汇编语法而不是At&t而且我不想使用英特尔语法。所以ymm8
、ymm2
和ymm6
这里是SRC1
。这是真的?
vshufps $68, %ymm0, %ymm8, %ymm6
vshufps $68, %ymm4, %ymm2, %ymm1
Vinsertf128 $1, %xmm1, %ymm6, %ymm9
主要问题是为什么gcc
改变了指令
row0 = _mm256_permute2f128_ps(__tt0, __tt4, 0x20);
到
Vinsertf128 $1, %xmm1, %ymm6, %ymm9
和
row4 = _mm256_permute2f128_ps(__tt0, __tt4, 0x31);
到
Vperm2f128 $49, %ymm1, %ymm6, %ymm1
我怎么能忽略这个优化?我试过-O0
,但没有用。
【问题讨论】:
【参考方案1】:所以这里的 ymm8、ymm2 和 ymm6 是 SRC1。这是真的吗?
是的,在这两种语法中,中间操作数始终是 3 操作数指令中的 src1。
美国电话电报公司:op %src2, %src1, %dest
英特尔:op dest, src1, src2
我不想使用 Intel 语法
艰难。我所知道的关于每条指令的确切作用的唯一真正好的文档是英特尔 insn 参考手册。我曾经认为 AT&T 语法更好,因为 $ 和 % 装饰器消除了歧义。我确实喜欢这样,但现在更喜欢英特尔语法。每个规则都非常简单,您可以轻松地在思想上转换或“思考”您正在阅读 ATM 的任何一个。
除非您实际上是在编写 GNU C 内联 asm,否则您可以使用 gcc -masm=intel
和 objdump -Mintel
使用英特尔助记符、操作数顺序等来获得 GNU 风格的 asm。汇编器指令仍然是 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 没有“内在函数到指令的字面翻译”模式,因为它需要额外的工作来实现。如果您确切关心编译器使用哪些指令,那就是内联 asm 的用途。
Keep in mind that -O0
doesn't mean "no optimization"。在发出 asm 之前,它仍然需要通过几个内部表示进行转换。
【讨论】:
【参考方案2】:检查指令分析报告中绑定到端口 5 的指令显示指令为broadcasts
和vpermilps
。 broadcasts
只能在端口 5 上执行,但是将它们替换为 128-bit
加载后跟 vinsertf128
指令可以减少端口 5 的压力,因为 vinsertf128
可以在端口 0 上执行。来自IACA user guid
【讨论】:
以上是关于为啥 gcc 将 _mm256_permute2f128_ps 编译为 Vinsertf128 指令?的主要内容,如果未能解决你的问题,请参考以下文章
在 GCC 10.3.0 中找不到 _mm256_rem_epu64 内在函数
gcc 标头错误:“_mm256_set_m128d”未在此范围内声明
如何通过英特尔 OpenCL SVML 使用 _mm256_log_ps?