在godbolt.org上使用gcc 7.2我可以看到以下code在汇编程序中得到了最佳的翻译。我看到1个加载,1个加法和1个商店。
#include <immintrin.h>
__attribute__((alwaysinline)) double foo(double x, double y)
{
return x+y;
}
void usefoo(double x, double *y, double *z)
{
*z = foo(x, *y);
}
这导致:
usefoo(double, double*, double*):
addsd xmm0, QWORD PTR [rdi]
movsd QWORD PTR [rsi], xmm0
ret
但是,如果我尝试使用下面的code使用内在函数和模板来实现相同的功能,我可以看到添加了一些开销。特别是,教学的重点是什么:movq xmm0, xmm0
?
#include <immintrin.h>
__attribute__((alwaysinline)) double foo(double x, double y)
{
return _mm_cvtsd_f64(_mm_add_sd(__m128d{x}, __m128d{y}));
}
void usefoo(double x, double *y, double *z)
{
*z = foo(x, *y);
}
这导致:
usefoo(double, double*, double*):
movq xmm1, QWORD PTR [rdi]
movq xmm0, xmm0
addsd xmm0, xmm1
movlpd QWORD PTR [rsi], xmm0
ret
如何使用标量内在函数实现与编译器生成的代码相同的代码?
如果你想知道为什么我想这样做,考虑用+
替换<=
:如果我写x<y
,编译器将结果转换为bool,而内在函数将它保持为双位掩码。因此,对于我的用例,编写x<y
不是一种选择。然而,使用+
很简单,足以说明这个问题。
正如你在列表初始化movq
所要求的那样,“无关的”__m128d
正在清除__m128d{x}
中的第二个元素。
当源操作数是XMM寄存器时,移动低四字;当目标操作数是XMM寄存器时,四字存储到寄存器的低四字,高四字被清除为全0。
请记住,当提供的初始化程序数少于成员数时,所有剩余成员都会进行值初始化(为零)。
我希望更高级别的优化能够看到第二个元素从未使用过,并删除无关的指令。另一方面,即使未使用,也不允许在添加操作期间捕获第二个值,并且明确地清除它可能是确保它不是最安全的方法。