6

我有一个用于嵌入式 ARM 处理器的递归下降解析器(在 C + GCC 中,用于 ARM Cortex M3)。

在运行它时,我注意到它使用了大量的堆栈空间(甚至超出您的预期),并且在仔细检查后我发现这种情况正在发生:

extern int bar(int *p);

int foo() {
 int z = foo(); // it's an example!

 int n[100];  // stack usage
 return z+bar(n); // calling bar(n) stops n from being optimised out
}

运行arm-none-eabi-gcc -fomit-frame-pointer -S test.c 的结果

foo:
    str lr, [sp, #-4]!  ; Push link register
    sub sp, sp, #412    ; Reserve space on stack, even if we don't need it now!
    bl  foo             ; Recurse
    str r0, [sp, #404]  ; Store result
    ...

因此,在函数开始时,它将整个堆栈帧压入堆栈。然而,经过几次迭代后,它的堆栈上有很多尚未使用的东西。

理想情况下,我希望 GCC 生成:

foo:
    str lr, [sp, #-4]!  ; Push link register
    ; Don't reserve space, because we don't need it
    bl  foo             ; Recurse
    sub sp, sp, #412    ; Reserve space now
    str r0, [sp, #404]  ; Store result
    ...

(这可能不正确,但我希望你明白)

使用下面的代码可以实现一些类似的事情,但它真的很讨厌(如果 GCC 内联 fooworker,它会再次中断!)。肯定有更好的办法?

int fooworker(int z) {
 int n[100];  // stack usage
 return z+bar(n); // calling bar(n) stops n from being optimised out
}


int foo() {
 return fooworker(foo());
}

那么有没有办法告诉 GCC 只在基本块的开始处扩大堆栈,或者是否有一个“障碍”语句导致在该点添加额外的推送/弹出操作?我猜 GCC 正在使用一种 ARM 标准调用类型 - 但是有没有办法用另一种调用类型来标记这些函数,这种调用类型对堆栈来说更有效,或者有没有办法重写函数使得堆栈是使用更明智一点?

请不要告诉我不要使用递归,它没有回答问题。

4

2 回答 2

3
int *n = alloca(sizeof(*n) * 100);

这很难看,我个人将函数分成两部分,但似乎在我的 gcc 上的 amd64 上的所有优化级别上都有效。

于 2012-10-31T12:58:12.117 回答
0

这都容易被优化,但你也可以尝试引入一个新的范围:

extern int bar(int *p);

int foo() {
  int z = foo();

  {
    int n[100];
    return z+bar(n);
  }
}

新作用域的引入意味着不n应该在之前foo()被调用。同样,优化可能会破坏所有这一切,就像您自己的解决方案或接受的解决方案一样。

于 2014-07-22T14:15:17.327 回答