#include <immintrin.h>
__m128 foo(float *xptr){
return _mm_set_ss(*xptr); // load and zero-extend a float into a vector
}
__m128 bar(float *xptr){
return _mm_setr_ps(*xptr, 0,0,0); // same, but the compiler has to notice
// that the explicit zeros can be produced for free by MOVSS
}
1条答案
按热度按时间lsmepo6l1#
这可能是来自MSVC 19.20或更早版本的braindead代码生成器,它不知道
_mm_setr_ps(x, 0,0,0)
与_mm_set_ss(x)
相同。GCC和Clang以及MSVC 19.21和更高版本都编译为预期的
movss xmm0, DWORD PTR [rcx]
(或x86-64 System V调用约定的[rdi]
)。但是,正如我们在Godbolt上看到的,MSVC 19.20.27525和更早的版本使这个braindead asm,在另一个寄存器中生成高元素并将它们混在一起。
它确实知道它可以用
xorps
生成零,而不是加载一个常数,但甚至没有注意到它可以重复使用同一个零向量两次。它确实设法“只”使用两条unpcklps
指令,而不是我们在_mm_setr_ps(a,b,c,d)
只使用SSE 2(而不是SSE4.1insertps
)的一般情况下需要的三条。即使是其他编译器的最老版本on Godbolt,GCC 4.1和Clang 3.0,也将
_mm_set_ps
优化为仅加载。MSVC 19.14来自Visual Studio 2017;我认为MSVC的内部函数代码生成现在基本上是好的,尽管它不会对内部函数进行太多优化,但最近它变得更糟了。有可能某些执行路径跳转到此块,而没有运行将XMM 0置零的代码。
也许你可以在
unpcklps
上设置一个条件断点,它只会在xmm0
为非零时触发。如果它在您运行程序时从不出错,并且您没有看到任何分支进入包含它的基本块,那么它可能只是来自MSVC的愚蠢代码生成。