使用GCC + ARM减少递归期间的堆栈使用

我有一个用于嵌入式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只在基本块的开头放大堆栈,或者是否有一个“屏障”语句导致在该点添加额外的push / pop操作? 我猜GCC正在使用ARM标准调用类型之一 – 但有没有办法用另一种调用类型标记这些函数,这种调用类型对堆栈更有效,或者有没有办法重写函数,使得堆栈是使用得更明智一点?

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

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

这很丑陋,我个人将function分成两部分,但似乎在amd64上的所有优化级别上使用我的gcc。

这很容易进行优化,但您也可以尝试引入新的范围:

 extern int bar(int *p); int foo() { int z = foo(); { int n[100]; return z+bar(n); } } 

引入新范围意味着在调用foo()之前n不应该存在。 同样,优化可能会破坏所有这些,例如您自己的解决方案或已接受的解决方案。