組み込み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-Stest.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標準の呼び出しタイプの1つを使用していると思いますが、スタックでもう少し効率的な別の呼び出しタイプでこれらの関数にタグを付ける方法はありますか、またはスタックがもう少し賢明に使用しましたか?
再帰を使用しないように言わないでください。再帰は質問に答えていません。