私は非侵襲的な保守的な GC を C で書いていますが、そのスタック スキャン フェーズの正確性について懸念があります。
具体的には、コンパイラの最適化が有効になっていない場合、すべてのローカル変数 (オブジェクトを指す) が「予測どおりに」スタックに割り当てられるため、正常に動作します。では-O3
、GC はいくつかの有効な参照を見逃しています。これは、コンパイラが一部の変数と関数引数の受け渡しに (GC がスキャンするスタックの代わりに) レジスタを使用することを選択し、GC が (まだ) それを処理するようにプログラムされています。(これがまだ起こるべきではなく、私が問題の原因を誤解していると思われる場合は、お知らせください。)
いくつかの基本的な要件 ( GC オブジェクトのGC_malloc
代わりにa を使用する必要があるmalloc
こと、非 GC ヒープから GC ヒープを指さないようにすること、そしてもちろん、明示的に を呼び出さないことfree
) を除けば、GC はクライアントからこれ以上の要件を持つべきではありません。コードまたはコンパイラ。したがって、クライアント コードから特別なパターンを追加する必要はありません。同様に、この GC を使用するプログラムを特別なコンパイラ フラグ (スタックの最適化を抑制する) で強制的にコンパイルすることは、せいぜい最後の手段です。これらの 2 つが邪魔にならないので、これが私の質問への積み上げです。
私は、GC が-O3
(スタックの最適化を使用して) ケースをシームレスに処理できるようにする方法を見つけようとしています。これが私の一連の考えです(より正確には仮定):
- GCC (または有効なコンパイラ) がどこまで最適化を行っても、プログラムの正確性が損なわれることはありません。
- [1] が成り立つ場合、(少なくとも C の実際の実装では)到達可能な (任意の呼び出しレベルでローカルとして割り当てられた)変数がアクセス可能なままである場合、それはスタック上または現在のレジスタセットですが、実際には他のどこにもありません。
- [2] が成り立つ場合、GC サイクルの開始時にすべてのレジスタをスタックにダンプするだけで、その後のスタック スキャンで以前に見逃していた参照も検出されることが保証されます。
- さらに、[1] と [2] が成り立つ場合、コンパイラーが変数の上書きを許可する場合 (ほとんどの場合、レジスターベースの変数の場合)、それは何らかのレベルのコード分析が実行され、それが証明されたことを意味します。その変数は、その関数の他の場所では使用されていません。したがって、その意味で、スタックまたはレジスタ ダンプに変数が表示されない場合、(理論的には) まだスコープ内にあるにもかかわらず、これは (私にとって) 警告の原因にはなりません。死んだ参照を取り除くことで、GC を助けただけです。
質問 #1: 4 つの仮定はすべて正しいですか?
質問 #2:レジスタ ダンプを強制する最も移植性の高い方法は何ですか? 単純なsetjmp
呼び出しでこの効果が得られると主張する情報源をいくつか見つけました。これは正しいです?