assembly 首先移动,然后用零解压缩,不改变高位零,为什么?为什么?

ogsagwnx  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(104)

我是x86的新手,没有经验,所以这段代码看起来有点过时。这样做有什么目的吗?
说明如下:
rcx+000003F8 = 32位浮点数
xmm0 = 0(所有128位)

movss xmm4,[rcx+000003F8]
unpcklps xmm4,xmm0

“unpcklps xmm4,xmm0”不会过时吗,因为它不会改变xmm4中的任何东西?

lsmepo6l

lsmepo6l1#

这可能是来自MSVC 19.20或更早版本的braindead代码生成器,它不知道_mm_setr_ps(x, 0,0,0)_mm_set_ss(x)相同。

#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
}

GCC和Clang以及MSVC 19.21和更高版本都编译为预期的movss xmm0, DWORD PTR [rcx](或x86-64 System V调用约定的[rdi])。
但是,正如我们在Godbolt上看到的,MSVC 19.20.27525和更早的版本使这个braindead asm,在另一个寄存器中生成高元素并将它们混在一起。

foo     PROC                                          ; COMDAT
        movss   xmm0, DWORD PTR [rcx]
        ret     0
foo     ENDP

bar     PROC                                          ; COMDAT
        movss   xmm0, DWORD PTR [rcx]
        xorps   xmm1, xmm1
        unpcklps xmm0, xmm1
        xorps   xmm2, xmm2
        unpcklps xmm0, xmm2
        ret     0
bar     ENDP

它确实知道它可以用xorps生成零,而不是加载一个常数,但甚至没有注意到它可以重复使用同一个零向量两次。它确实设法“只”使用两条unpcklps指令,而不是我们在_mm_setr_ps(a,b,c,d)只使用SSE 2(而不是SSE4.1 insertps)的一般情况下需要的三条。
即使是其他编译器的最老版本on Godbolt,GCC 4.1和Clang 3.0,也将_mm_set_ps优化为仅加载。MSVC 19.14来自Visual Studio 2017;我认为MSVC的内部函数代码生成现在基本上是好的,尽管它不会对内部函数进行太多优化,但最近它变得更糟了。
有可能某些执行路径跳转到此块,而没有运行将XMM 0置零的代码。
也许你可以在unpcklps上设置一个条件断点,它只会在xmm0为非零时触发。如果它在您运行程序时从不出错,并且您没有看到任何分支进入包含它的基本块,那么它可能只是来自MSVC的愚蠢代码生成。

相关问题