我有一个嵌入式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只在基本块的开头放大堆栈,或者是否有一个'barrier'语句导致在该点添加额外的push / pop操作?我猜GCC正在使用ARM标准调用类型之一 - 但有没有办法用另一种调用类型来标记这些函数,这种调用类型对堆栈更有效,或者有没有办法重写函数,使得堆栈是使用得更明智一点?
请不要告诉我不要使用递归,它没有回答这个问题。
答案 0 :(得分:3)
int *n = alloca(sizeof(*n) * 100);
这很丑陋,我个人将这个功能分成两部分,但似乎在我的gcc amd64上适用于所有优化级别。
答案 1 :(得分:0)
这很容易进行优化,但您也可以尝试引入新范围:
extern int bar(int *p);
int foo() {
int z = foo();
{
int n[100];
return z+bar(n);
}
}
新范围的引入意味着n
在foo()
被调用之前不应该存在。同样,优化可以打破所有这些,就像您自己的解决方案或接受的解决方案一样。