【发布时间】:2012-10-20 18:17:35
【问题描述】:
我有一个用于嵌入式 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 标准调用类型 - 但是有没有办法用另一种调用类型来标记这些函数,这种调用类型对堆栈来说更有效,或者有没有办法重写函数使得堆栈是用得更理智一点?
请不要告诉我不要使用递归,它没有回答问题。
【问题讨论】:
-
我现在尝试了这个功能,它只是保存了 r7 和 lr,但我使用的是不同的工具链,我停止使用 codeourcy 有太多问题
-
对不起,我稍微改变了问题(我做了一个更好的代码示例)-您使用了最新的代码吗?经过一些测试,我认为大多数 GCC 版本都会做同样的事情。
-
Aniket:我更改了示例,但尾递归不是问题末尾代码中正在执行的操作吗?理想情况下,GCC 将能够在函数中拥有两个不同的堆栈帧
-
虽然这显然是次优代码,并且延迟在堆栈上分配空间很好,但我想知道对于非平凡的示例是否检测到它不是非常困难?