Random thoughts on variable reordering
おっと、ヤマケンさんが gcc 依存での解決に踏み切ったらしい。これを見てこういうことを思いついた。
関数 call ではなく従来どおり object を保護してみる。Stack の延びる方向は compile option で決めれるし、cross compile で問題になるようなら動的に判定するのも簡単。そこで stack 方向は既知のものとして、protect_stack を複数回呼び出せば一番古いアドレスが stack の底として採用されるように protect_stack() を変更。
ScmObj *stack_bottom; void gc_protect_stack (ScmObj *loc) { if (LOWER_IN_STACK (loc, stack_bottom)) stack_bottom = loc; }
こうした上で、client はこうやって使う。
uim_lisp foo () { uim_lisp bar; uim_lisp baz; gc_protect_stack (&bar); gc_protect_stack (&baz); /* 好きなように Scheme code を評価する */ gc_unprotect_stack (&bar); gc_unprotect_stack (&baz); return bar; }
問題は protect と unprotect をどれだけ自動化できるか。一個一個書いていったのでは local 変数を増やしたときに protect し忘れるに決まってる。関数 call 保護のデメリットは「書くのが面倒」で済むけどこっちはバグにつながる。しかもかなり表面化しにくい。uim に限っていえば、unprotect はサボってもあんまり変わらないのでこうすることもできる。宣言のすぐ後にやるだけだから間違いも関数 call 保護と同じ程度まで多分減る。
uim_lisp foo () { uim_lisp bar; uim_lisp baz; GC_PROTECT_OBJ2 (bar, baz); ... return bar; }
GC_PROTECT_OBJ2 () は protect_stack (min (&bar, &baz)) と展開される。min 部分を inline 関数として書けば、定数扱いしてもらえると思う (未確認)。
しかしものすごく使い勝手が悪い。C99 なら可変引数にできなくもないけど、引数全部に & をつけて回る羽目に。型チェックでひっかかるからバグにはつながらないけどね。
何か参考になれば。ならないかも。いいや、何でも。
ところで関数 call の uninline は volatile pointer を使えば C89 の範囲で保証できることに今気づいた。
{ volatile UIM_SCM_GC_PROTECTED_FUNC_T(fp) = func; (*__func)args; }