__m128 何时在 xmm 寄存器中?
When is __m128 in an xmm register?
正在调用 _mm_load_ps
returns 和 __m128
。在英特尔内部函数指南中 it says:
Load 128-bits (composed of 4 packed single-precision (32-bit) floating-point elements) from memory into dst. mem_addr must be aligned on a 16-byte boundary or a general-protection exception may be generated.
(编者注:使用 _mm_loadu_ps
用于可能未对齐的加载)
这是否意味着只要 __m128 还存在,4 个浮点数包就会驻留在 xmm 寄存器中?这是否意味着堆栈上的 __m128 多于可用的 xmm 寄存器会导致溢出?
Does this mean that the 4 float pack resides in the xmm registers as long as the __m128 is alive?
没有。内部函数由编译器编译,向量变量将像任何其他变量一样受到寄存器分配的影响。
正如您在第二句话中指出的那样 - 您可以编写包含比寄存器更多的 __m128
变量的代码 - 这会溢出到堆栈。
内在函数 API 旨在让您假装您正在用汇编语言编写,但 load/store 内在函数实际上只是将类型/对齐信息传达给编译器。
(alignof(__m128) = 16
,所以任何 spill/reload 都可以用需要对齐的指令来完成。重载甚至可以将它用作内存源操作数,而不是加载到寄存器中。)
__m128
变量也需要在非内联函数调用中溢出,尤其是在没有调用保留 XMM 寄存器的调用约定中。 (例如 x86-64 系统 V)。 Windows x64 有几个调用保留的 XMM 寄存器,但有些是易变的(调用破坏的)所以函数有几个 XMM 寄存器可以使用。
So it is guaranteed that having more __m128
than there are registers available causes spilling, and that having less will always avoid spilling?
编译器非常努力地按照减少溢出的顺序安排指令。例如,在抽象方面,您可能会编写如下代码:
int A = *<foo>;
int B = *<foo+1>;
int C = *<foo+2>;
int D = A + B + C;
您可能认为这需要 4 个寄存器,因为您创建并分配了 4 个变量,但很可能您最终得到的东西在机器级别看起来更像这样:
int A = *<foo>;
int B = *<foo+1>;
int D = A + B
int A = *<foo+2>;
int D = D + A
即编译器已重新排序此代码以尽量减少所需的物理寄存器的数量。
实际上很难预测。编译器旨在减少寄存器压力,因为溢出的代价很高,但可能不会故意将其绝对降低到尽可能低的水平,因为它们还需要尽早获取数据以尝试隐藏内存获取的加载延迟。
一般来说,建议您反汇编高性能代码路径,以确保编译器执行您期望的操作...。
正在调用 _mm_load_ps
returns 和 __m128
。在英特尔内部函数指南中 it says:
Load 128-bits (composed of 4 packed single-precision (32-bit) floating-point elements) from memory into dst. mem_addr must be aligned on a 16-byte boundary or a general-protection exception may be generated.
(编者注:使用 _mm_loadu_ps
用于可能未对齐的加载)
这是否意味着只要 __m128 还存在,4 个浮点数包就会驻留在 xmm 寄存器中?这是否意味着堆栈上的 __m128 多于可用的 xmm 寄存器会导致溢出?
Does this mean that the 4 float pack resides in the xmm registers as long as the __m128 is alive?
没有。内部函数由编译器编译,向量变量将像任何其他变量一样受到寄存器分配的影响。
正如您在第二句话中指出的那样 - 您可以编写包含比寄存器更多的 __m128
变量的代码 - 这会溢出到堆栈。
内在函数 API 旨在让您假装您正在用汇编语言编写,但 load/store 内在函数实际上只是将类型/对齐信息传达给编译器。
(alignof(__m128) = 16
,所以任何 spill/reload 都可以用需要对齐的指令来完成。重载甚至可以将它用作内存源操作数,而不是加载到寄存器中。)
__m128
变量也需要在非内联函数调用中溢出,尤其是在没有调用保留 XMM 寄存器的调用约定中。 (例如 x86-64 系统 V)。 Windows x64 有几个调用保留的 XMM 寄存器,但有些是易变的(调用破坏的)所以函数有几个 XMM 寄存器可以使用。
So it is guaranteed that having more
__m128
than there are registers available causes spilling, and that having less will always avoid spilling?
编译器非常努力地按照减少溢出的顺序安排指令。例如,在抽象方面,您可能会编写如下代码:
int A = *<foo>;
int B = *<foo+1>;
int C = *<foo+2>;
int D = A + B + C;
您可能认为这需要 4 个寄存器,因为您创建并分配了 4 个变量,但很可能您最终得到的东西在机器级别看起来更像这样:
int A = *<foo>;
int B = *<foo+1>;
int D = A + B
int A = *<foo+2>;
int D = D + A
即编译器已重新排序此代码以尽量减少所需的物理寄存器的数量。
实际上很难预测。编译器旨在减少寄存器压力,因为溢出的代价很高,但可能不会故意将其绝对降低到尽可能低的水平,因为它们还需要尽早获取数据以尝试隐藏内存获取的加载延迟。
一般来说,建议您反汇编高性能代码路径,以确保编译器执行您期望的操作...。