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

时间:2011-05-11 07:29:07

标签: gcc 64-bit 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.);
    }
}

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

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

2 个答案:

答案 0 :(得分:13)

两点:

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

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

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

答案 1 :(得分:5)

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