【问题标题】:When is __m128 in an xmm register?__m128 何时在 xmm 寄存器中?
【发布时间】:2020-02-14 18:55:16
【问题描述】:

调用_mm_load_ps 会返回__m128。在英特尔内在函数指南it says

将 128 位(由 4 个打包的单精度(32 位)浮点元素组成)从内存加载到 dst。 mem_addr 必须在 16 字节边界上对齐,否则可能会生成一般保护异常。

(编者注:使用_mm_loadu_ps 进行可能未对齐的加载)


这是否意味着只要 __m128 还活着,4 个浮点包就驻留在 xmm 寄存器中?这是否意味着堆栈上的 __m128 多于可用的 xmm 寄存器会导致溢出?

【问题讨论】:

    标签: c++ compilation sse cpu-registers intrinsics


    【解决方案1】:

    这是否意味着只要 __m128 还活着,4 个浮点包就驻留在 xmm 寄存器中?

    没有。内在函数由编译器编译,向量变量将像任何其他变量一样进行寄存器分配。

    正如您在第二句话中所指出的那样 - 您可以编写具有比寄存器更多的 __m128 变量的代码 - 这会溢出到堆栈。

    内在函数 API 旨在让您假装自己在用汇编语言编写,但加载/存储内在函数实际上只是将类型/对齐信息传达给编译器。

    alignof(__m128) = 16,因此任何溢出/重新加载都可以使用需要对齐的指令来完成。重新加载甚至可以将其用作内存源操作数,而不是加载到寄存器中。)

    __m128 变量也需要在非内联函数调用中溢出,尤其是在没有调用保留 XMM 寄存器的调用约定中。 (例如 x86-64 System V)。 Windows x64 有几个保留调用的 XMM 寄存器,但有些是易失的(调用破坏的),所以函数有一些 XMM 寄存器可以使用。

    因此可以保证__m128 的数量多于可用寄存器的数量会导致溢出,而​​拥有较少的寄存器总是会避免溢出?

    编译器非常努力地按减少溢出的顺序安排指令。例如,在抽象的术语中,您可能会编写如下代码:

    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
    

    即编译器已重新排序此代码以最小化所需的物理寄存器数量。

    实际上很难预测。编译器旨在降低寄存器压力,因为溢出代价高昂,但可能故意不绝对将其降低到可能的最低水平,因为它们还需要及早获取数据以尝试隐藏内存获取的加载延迟。

    一般来说,建议您反汇编高性能代码路径,以确保编译器按照您的预期执行......

    【讨论】:

    • 感谢您的回答。因此,可以保证 __m128 多于可用寄存器会导致溢出,而​​拥有较少的 __m128 将始终避免溢出?你知道有什么资源可以更清楚地解释寄存器分配吗?
    • 查看来自英特尔的 this article。建议检查生成的程序集以确保您的算法“适合”。
    • +1 个很好的例子。是的,编译器有时会使用超过最小数量的寄存器,特别是当它不引入溢出时(即低寄存器压力)。这可以通过例如 (a+b) + (c+d) 而不是 (((a+b) + c) + d) 创建更多 ILP。这需要更多的临时工。这对 FP(更长的延迟)更重要,但 FP 数学并不是严格关联的; -ffast-math 可以让编译器假装是。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-08
    • 1970-01-01
    • 2017-10-29
    • 2023-03-31
    • 2020-05-07
    • 2019-06-10
    相关资源
    最近更新 更多