如何强制gcc使用所有SSE(或AVX)寄存器?

Nor*_* P. 9 64-bit gcc sse register-allocation avx

我正在尝试为Windows x64目标编写一些计算密集型代码,使用SSE或新的AVX指令,在GCC 4.5.2和4.6.1,MinGW64(TDM GCC构建和一些自定义构建)中进行编译.我的编译器选项是-O3 -mavx.(-m64暗示)

简而言之,我想对4个打包浮点数的3D矢量进行一些冗长的计算.这需要4x3 = 12 xmm或ymm寄存器用于存储,2或3个寄存器用于临时结果.这应该恕我直言,适合64位目标可用的16个SSE(或AVX)寄存器.但是,GCC使用寄存器溢出产生非常不理想的代码,仅使用寄存器xmm0-xmm10并将数据从堆栈中移入和移入堆栈.我的问题是:

有没有办法说服GCC使用所有寄存器xmm0-xmm15

要修改想法,请考虑以下SSE代码(仅供参考):

void example(vect<__m128> q1, vect<__m128> q2, vect<__m128>& a1, vect<__m128>& a2) {
    for (int i=0; i < 10; i++) {
        vect<__m128> v = q2 - q1;
        a1 += v;
//      a2 -= v;

        q2 *= _mm_set1_ps(2.);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里vect<__m128>只是一个struct 3 __m128,有自然的加法和乘以标量.当行a2 -= v被注释掉时,即我们只需要3x3寄存器进行存储,因为我们忽略了a2,所产生的代码确实很简单,没有移动,所有内容都在寄存器中执行xmm0-xmm10.当我删除注释时a2 -= v,代码非常糟糕,寄存器和堆栈之间有很多混乱.即使编译器可以只使用寄存器xmm11-xmm13或其他东西.

我实际上还没有看到GCC xmm11-xmm15在我的所有代码中的任何地方使用任何寄存器.我究竟做错了什么?我知道它们是被调用者保存的寄存器,但这种开销完全可以通过简化循环代码来证明.

jal*_*alf 13

两点:

  • 首先,你做了很多假设.寄存器溢出在x86 CPU上相当便宜(由于快速的L1缓存和寄存器阴影和其他技巧),而64位的寄存器访问成本更高(就更大的指令而言),所以它可能只是GCC的版本和你想要的一样快或者快.
  • 其次,与任何编译器一样,GCC可以进行最佳的寄存器分配.没有"请做更好的注册分配"选项,因为如果有,它总是被启用.编译器并没有试图惹恼你.(正如我记得的那样,寄存器分配是NP完全问题,因此编译器永远无法生成完美的解决方案.它能做的最好就是近似)

所以,如果你想要更好的寄存器分配,你基本上有两个选择:

  • 写一个更好的寄存器分配器,并将其修补到GCC,或
  • 绕过GCC并在汇编中重写函数,这样就可以精确控制何时使用哪些寄存器.

  • @thechao那不是那项研究的结果.您可以将任何程序转换为SSA,并在线性时间内在SSA上进行最佳寄存器分配,但这并不意味着您为原始程序进行了最佳寄存器分配.SSA引入了更多变量以简化图结构.使用这种更简单的结构,您可以最佳地分配寄存器,但它只适用于具有更多变量的更简单结构; 最终结果不是原始问题的最佳分配.阅读您链接的演示文稿; 他们就这么说! (7认同)
  • 实际上,还有一个我应该提到的选项:摆弄你的代码,使GCC及其寄存器分配器更容易理解.声明新变量而不是重用旧变量可能有所帮助,同时最小化每个变量的范围和生命周期,并且通常来回试验可能会帮助您诱导GCC生成不同的代码 (2认同)

小智 5

实际上,你看到的不是溢出,它是gcc在内存中的a1和a2上运行,因为它无法知道它们是否有别名.如果将最后两个参数声明为vect<__m128>& __restrict__GCC,则将注册分配a1和a2.