Intel x86_64 程序集,如何从 xmm 寄存器到 int 加倍?

Intel x86_64 assembly, how to floor double from xmm register to int?

我该怎么做?如果结果将在 e*x 寄存器中,那将是最好的。

地板双打使用CVTTPD2DQ—Convert with Truncation Packed Double-Precision FP Values to Packed Dword Integers

CVTTPD2DQ 在 XMM 寄存器中执行 floor/trunctate 两个 double 到两个 int

对应的Intel C/C++ Compiler Intrinsic

____m128i _mm_cvttpd_epi32(__m128d a)

要像EAX一样把结果放到GPR(General Purpose Register)中,可以使用下面的指令:

CVTTPD2DQ xmm0, xmm1 ; XMM1 is the source of doubles
movd eax, xmm0       ; extracts int(0) from XMM0 to EAX
pextrd ecx, xmm0, 1  ; extracts int(1) from XMM0 to ECX

您问了几个微不足道的问题,只需查看 C 编译器的工作原理即可回答这些问题。从那里,您可以查看它使用的说明,并决定您要实际使用哪些。 (libm 中大约有无数种不同的舍入函数,因此首先选择正确的函数并不总是那么容易)。

使用 -O3 -ffast-math 获取内联的最简单的 libm 函数(因为它不必潜在地在 NaN 上设置 errno,或类似的废话)。

无论如何,编译器输入输出,来自gcc 5.3 on the Godbolt Compiler Explorer:

#include <math.h>

int floor_double(double x) { return (int) floor(x); }
    roundsd xmm0, xmm0, 9
    cvttsd2si       eax, xmm0
    ret

int truncate_double(double x) { return (int)x; }
    cvttsd2si      eax, xmm0
    ret

查看从 PDF 生成的 tag wiki for links to Intel's instruction set reference manual pdf. There's also an unofficial HTML version

cvttsd2si truncates towards zero, like trunc(), instead of rounding towards -Infinity like the floor() function.

这就是为什么 floor() 在该指令集可用时编译为 SSE4.1 roundsd 的原因。 (否则它必须 fiddle 使用舍入模式,正如您通过删除或更改 godbolt 上的 -march 选项所看到的那样)。


还有压缩版本的转换指令,例如 CVTTPD2DQ` 一次执行 2 个。 (或 4 个带 AVX)。