呼叫_mm_load_ps
将返回__m128
。在英特尔内部函数指南it says中:
从内存中将128位(由4个压缩的单精度(32位)浮点元素组成)加载到dst中。 mem_addr必须在16字节的边界上对齐,否则可能会生成常规保护异常。
(编者注:使用_mm_loadu_ps
处理可能未对齐的负载)
这是否意味着只要__m128仍然有效,第4个浮点程序包就驻留在xmm寄存器中?那么这是否意味着堆栈上的__m128数量多于可用的xmm寄存器会导致溢出?
答案 0 :(得分:5)
这是否意味着只要__m128仍然有效,第4个浮点程序包就驻留在xmm寄存器中?
不。内在函数由编译器编译,向量变量将像其他变量一样进行寄存器分配。
您在第二句话中指出-您可以使用比寄存器多的__m128
变量来编写代码-这会溢出到堆栈中。
intrinsic API旨在让您假装您正在用汇编语言编写,但实际上加载/存储内在函数只是将类型/对齐信息传递给编译器。
({alignof(__m128) = 16
,因此任何溢出/重装都可以使用需要对齐的指令来完成。重装甚至可以将其用作内存源操作数,而不是加载到寄存器中。)
__m128
变量也需要在非内联函数调用中溢出,尤其是在没有保留调用的XMM寄存器的调用约定中。 (例如x86-64系统V)。 Windows x64有几个保留呼叫的XMM寄存器,但是有些是易失性的(调用优先),因此函数中有几个XMM寄存器可以使用。
因此,可以保证
__m128
数多于可用寄存器数会导致溢出,而确保少int A = *<foo>; int B = *<foo+1>; int C = *<foo+2>; int D = A + B + C;
总会避免溢出吗?
编译器非常努力地按顺序安排指令,以减少溢出。例如,用抽象的术语来说,您可能会编写如下代码:
int A = *<foo>;
int B = *<foo+1>;
int D = A + B
int A = *<foo+2>;
int D = D + A
您可能会认为这需要4个寄存器,因为您创建并分配了4个变量,但是很有可能最终在计算机级别上看起来像这样:
{{1}}
即编译器已对该代码重新排序以最大程度地减少所需的物理寄存器数量。
实际上很难预测。编译器旨在降低寄存器压力,因为溢出很昂贵,但有意将其绝对地降低到可能的最低水平是因为它们还需要尽早获取数据以尝试隐藏内存获取的加载延迟。
通常,建议您反汇编高性能代码路径,以确保编译器完成了您期望的工作。