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;
}