我可以阻止gcc在堆栈上分配局部变量吗?

时间:2014-05-13 23:55:45

标签: gcc compiler-optimization inline-assembly

我在x86-64上使用gcc并使用“register”修饰符声明一些局部变量。我想找到一种严格阻止编译器为这些变量分配和使用堆栈空间的方法。我希望这些变量尽可能保留在寄存器中。我正在混合使用内联的C / C ++代码。

变量只是工作存储,不需要在以后永久存储和检索,但我看到我的gcc -O2代码仍然不时地将它们塞进本地堆栈空间。我知道当我不时地进行C / C ++函数调用时,它们的状态需要保留,但是我能做些什么来确定这种保存是非常不鼓励的吗?

这是我正在做的一个例子。这是事件驱动逻辑模拟器的一部分,适合那些想知道的人:

register __m128d VAL0, VAL1, diff0, diff1;
register __m128d *outputValPtr;
__m128d **cmfmLocs;
...
// all pointers are made to point to valid data
// cmfmLocs is a 0-terminated array of pointers with at least one entry

diff0 = outputValPtr[0];
diff1 = outputValPtr[1];
VAL0 = *(cmfmLocs[0]);
VAL1 = *(cmfmLocs[0]+1);

cfPin = 1;
do
{
  asm( "andpd %[src1], %[dest1]\n"
       "orpd  %[src2], %[dest2]\n" :
       [dest1] "=x" (VAL0),
       [dest2] "=x" (VAL1) :
       [src1]  "m" (*(cmfmLocs[cfPin])),
       [src2]  "m" (*(cmfmLocs[cfPin]+1)) );
  cfPin++;
} while ( cmfmLocs[cfPin] );

asm( "xorpd %[val0], %[diffBit0]\n"
     "xorpd %[val1], %[diffBit1]\n"
     "orpd  %[diffBit1], %[diffBit0]\n"
     "ptest %[diffBit0], %[diffBit0]\n"
     "jz dontSchedule\n"
     "movdqa %[val0],   (%[permStor])\n"
     "movdqa %[val1], 16(%[permStor])\n" :
     [diffBit0]  "=x" (diff0),
     [diffBit1]  "=x" (diff1),
     [memWrite1] "=m" (outputValPtr[0]),
     [memWrite2] "=m" (outputValPtr[1]) :
     [val0]      "x"  (VAL0),
     [val1]      "x"  (VAL1),
     [permStor]  "p"  (outputValPtr) );
SCHEDULE_GOTOS;
asm( "dontSchedule:\n" );

此代码使用-O2:

生成以下程序集
2348: 48 8b 4b 50           mov    0x50(%rbx),%rcx
234c: ba 01 00 00 00        mov    $0x1,%edx
2351: 48 8b 41 08           mov    0x8(%rcx),%rax
2355: 0f 1f 00              nopl   (%rax)
2358: 83 c2 01              add    $0x1,%edx
235b: 66 0f 54 00           andpd  (%rax),%xmm0
235f: 66 0f 56 48 10        orpd   0x10(%rax),%xmm1
2364: 0f b7 c2              movzwl %dx,%eax
2367: 66 0f 29 4c 24 20     movapd %xmm1,0x20(%rsp)   # Why is this necessary?
236d: 66 0f 29 44 24 30     movapd %xmm0,0x30(%rsp)   # Why is this necessary?
2373: 48 8b 04 c1           mov    (%rcx,%rax,8),%rax
2377: 48 85 c0              test   %rax,%rax
237a: 75 dc                 jne    2358 <TEST_LABEL+0x10>
237c: 66 0f 57 d0           xorpd  %xmm0,%xmm2
2380: 66 0f 57 d9           xorpd  %xmm1,%xmm3
2384: 66 0f 56 d3           orpd   %xmm3,%xmm2
2388: 66 0f 38 17 d2        ptest  %xmm2,%xmm2
238d: 0f 84 cf e7 ff ff     je     b62 <dontSchedule>
2393: 66 41 0f 7f 07        movdqa %xmm0,(%r15)     # After storing here, xmm0/1 values
2398: 66 41 0f 7f 4f 10     movdqa %xmm1,0x10(%r15) #  are not needed anymore.
... # my C scheduler routine here ...
0000000000000b62 <dontSchedule>:

2 个答案:

答案 0 :(得分:1)

我想我现在已经得到了它!

我使用内在函数,主要是因为它们易于使用且不需要离开&#34; C世界&#34;。对我来说,关键是本地化我的寄存器变量的范围。这应该是显而易见的,但我陷入了细节之中。我的实际代码现在看起来像这样:

      ...
      case SimF_AND:
      {
        register __m128d VAL0 = *(cmfmLocs[0]);
        register __m128d VAL1 = *(cmfmLocs[0]+1);
        register __m128d diff0 = outputValPtr[0];
        register __m128d diff1 = outputValPtr[1];
        cfPin = 1;
        do
        {
          VAL0 = _mm_and_pd( VAL0, *(cmfmLocs[cfPin]) );
          VAL1 =  _mm_or_pd( VAL1, *(cmfmLocs[cfPin]+1) );
          cfPin++;
        } while ( cmfmLocs[cfPin] );
        diff0 = _mm_or_pd( _mm_xor_pd( VAL0, diff0 ), _mm_xor_pd( VAL1, diff1 ) ); \
        if ( !_mm_testz_pd( diff0, diff0 ) ) \
        { \
          outputValPtr[0] = VAL0; \
          outputValPtr[1] = VAL1; \
          outputValPtr[2] = _mm_xor_pd( VAL0, VAL0 ); \
          SCHEDULE_GOTOS; \
        }
      } // register variables go out of scope here
      break;
      ...

所以现在我和编译器都很容易看到在outputValPtr更新后没有引用这些变量。这会产生不为本地人保留堆栈空间的程序集,因此它们不再生成自己的任何内存写入。

感谢所有留下回复的人。你肯定会把我引向正确的道路!

答案 1 :(得分:0)

很久以前我被告知有三类C编译器:真的很愚蠢,只关心register关键字,愚蠢关键字,注意关键字,并保留一些注册者为此;和智能的一样,它们在处理混乱值方面做得更好,而不仅仅是将值保存在固定的寄存器中。

如果您使用GCC的内联汇编,那么值所在的位置应该(几乎)透明。您可以通过使用限制强制在特定寄存器中获取参数,编译器将确保这一点得到尊重。

此外,&#34;只是工作存储&#34;没有足够的理由使用有价值的寄存器。即使在x86_64中,它也没有注册。在汇编程序中编写部分程序在程序员时间和可移植性方面花费了很大的成本,你最好确保这是相关的性能(或者代码不能首先编写代码)。