プログラミング
C言語における末尾呼び出し最適化は比較的新しい
Tail-call optimization in C is relatively recent (lwn.net)
要約
C言語における末尾呼び出し最適化(TCO)は、長らく標準的な呼び出し規約ではサポートされていませんでしたが、2001年のGCCでの実装試行を経て、最近のGCCやClangコンパイラで特定の形式の末尾呼び出しがサポートされるようになりました。この進歩は、Gforthやインタプリタ開発など、コード効率が重要な分野での応用が期待されています。
全文翻訳
C言語における末尾呼び出し最適化は比較的新しいです。
実際、C言語には末尾呼び出し最適化が永遠にあったわけではありません。
C言語の呼び出し規約では、呼び出し先(callee)が呼び出し元(caller)がスタックに積んだものを削除しないというのが一般的でした。
呼び出し元は `int f();` のような宣言を見ることができましたが、実際の呼び出しでは `n>0` 個の引数があり、実際の関数は `m≤n` 個のパラメータを持つことがありました。
呼び出し先が引数を削除した場合、これは常にうまくいくとは限りませんでした。
そのため、呼び出し元は呼び出しとそれに続くreturnの間に引数を削除する必要があり、呼び出しを非末尾呼び出しに変えていました。
私が1994年に当時のCコンパイラを調べたとき、記事に示されているような使い方の末尾呼び出し最適化は行われていませんでした。
2001年にMark Probstは、別の呼び出し規約でGCCに末尾呼び出し最適化を実装しました。
彼はそのセクション6.4で、当時のGCCにおける末尾呼び出し最適化の制限を挙げており、その中には「間接呼び出しを処理できない」(インタプリタのディスパッチで末尾呼び出しに使われるもの)というものがありました。
それ以来、私はこの問題を見ていませんでした(GCCの `goto *` は十分でした(まあ、ほとんどですが))。そして、GCCの末尾呼び出しサポートに関して何かが変わったと仮定する理由はあまりありませんでした(ただし、あるリリースノートでsibcallsに言及されており、それをチェックすべきだと考えていたのを覚えています)。
いずれにせよ、昨年、XuとKjolstadによる「Copy-and-Patch Compilation」という論文を読み、彼らは末尾呼び出し最適化を使用しています。
いずれにしても、その論文を読んだ後、gccとclangが記事に示されているような末尾呼び出しを最適化できるかどうかテストしました。
そして、それは機能しました。
XuとKjolstadは、彼らが100,000個のコードスニペットを使用していると報告していますが、私たちはGforthではVM命令、スタックキャッシュのバリエーション、静的スーパーインストラクションなどのために2000個未満に制限しています。
100,000個を使用できるようになれば、`goto *` ベースのシステムでは使用するにはコードスニペットが多すぎる技術を使用できるようになります。
まだGforthにそれを組み込むところまで至っていませんが、Pythonコミュニティが最初に達成したことに祝意を表します。
コメントを投稿する
末尾呼び出し最適化はC言語では比較的新しいです。
投稿日: 2025年8月23日 19:43 UTC (土)
投稿者: lafp (購読者、#89554) [リンク]
参考までに、最近、末尾呼び出しを命令のディスパッチに使用するForthのバリアントの(トイ)インタプリタを実装しました。
主な利点は、すべての組み込み関数が命令自体のように動作することでした。単に「組み込み関数を呼び出す」命令ではなく、いくつかのスーパーインストラクションもサポートしており、それらは他のものとそれほど違いがないため、かなりの助けとなりました。
パフォーマンスはかなり良好ですが、必要なレベルに達するにはオプティマイザでもう少し作業が必要です(最終的には、これを小型コンピュータで実行し、インタプリタをブラウザ経由で公開し、人々がLEDマトリックスを制御するために使用するコードを書けるようにしたいと考えています。このプロジェクトをより小規模にしたようなものです: https://www.noisebridge.net/wiki/Flaschen_Taschen)。
コードはここにあります (https://github.com/lpereira/lwan/blob/master/src/samples/...)。
これはForth Haiku言語のバリアントであり、ShaderToyがGLSLに対して行うのと同様に、小さなForthコードの断片でアートを作成できます。