gcc的奇怪/错误的程序集输出?
Posted
技术标签:
【中文标题】gcc的奇怪/错误的程序集输出?【英文标题】:Strange/bad assembly output by gcc? 【发布时间】:2018-08-24 21:19:42 【问题描述】:我有以下代码(最小示例):
#include <iostream>
#include <immintrin.h>
using namespace std;
int main()
__m128i a = _mm_set_epi32(rand(),rand(),rand(),rand());
__m128i b = _mm_set_epi32(rand(),rand(),rand(),rand());
__m128i c = _mm_add_epi32(a,b);
int d[4];
_mm_storeu_si128((__m128i*)d,c);
cout<<d[0]<<endl;
cout<<d[1]<<endl;
cout<<d[2]<<endl;
cout<<d[3]<<endl;
return 0;
当使用g++ -O3 -march=native
编译时,它会产生一些奇怪/错误/低效的程序集 (https://godbolt.org/z/TQgbim)。它存储c
一次,然后执行对齐加载和提取以执行元素访问(每次)。我可以看到为什么它需要将它存储到内存中,并且我可以看到对齐的加载和提取可能是如何高效的,但我不明白为什么它需要在提取后继续将相同的数据加载回 xmm 寄存器。此外,当d
被更改以便分配在堆上(https://godbolt.org/z/Pk7qP2)时,它甚至不再进行对齐加载,它只是将d
视为普通数组并以这种方式访问元素。有人可以解释它为什么这样做以及它可能带来什么好处吗?谢谢。
【问题讨论】:
我相信 GCC 开发人员会很高兴收到您的错误报告。或者,解释为什么你错了。为什么不联系他们? 可能发生的最坏情况是什么?你被告知你错了。没什么大不了的。最好的情况是他们同意你发现了一个错误——你赢了。为什么不试试呢? 【参考方案1】:是的,这是一个有趣的优化失误。
看起来它决定优化向量存储/标量重新加载到向量提取中,这通常很好。
但它没有考虑调用约定,它没有保留调用的向量寄存器。这段代码在 Windows x64 上会很好,例如它可以使用 xmm6。
如果您调用内联的函数,或者如果您将所有 4 个元素作为 args 传递给同一个函数,则此代码也可以。 (例如printf
)。
GCC 有多个通道,并且在程序逻辑的 GIMPLE 表示上运行的与体系结构无关的中间通道有时无法利用直到寄存器分配时间才知道的全部细节。一些优化对于 gcc 来说很难,因为它没有连接到能够看到它们。
顺便说一句,如果您关心这种效率水平,请使用'\n'
而不是endl
。您不需要在那里显式刷新cout
。
【讨论】:
是的,我写这篇文章是为了看看我写的其他代码会如何优化,但事实证明 gcc 正确地优化了它,因为它中间没有任何函数调用。以上是关于gcc的奇怪/错误的程序集输出?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 GCC 会为几乎相同的 C 代码生成如此完全不同的程序集?