ひとり勉強会

ひとり楽しく勉強会

NODE_OPTBLOCK

YARVの最適化の一つとして、「ブロックのインライン化」があります。例えば

3.times { ブロックの中身 }

をブロックを作ったりyieldで呼んだりすると、スタック操作が無駄に発生してしまって効率がよくありません。あと、ブロックを使うとredoやnextが例外になってしまうのも避けたいところです。というわけで、こういう簡単なループがブロックで書かれているときは、while文に変換されます。

e = 0
while e < 3 do
 lredo:
    ブロックの中身
 lnext:
  e = e + 1
end

「実はされません。とても微妙なバグがあるため、この機能はオフになっています。」だそうです。むむむー残念。
この最適化変換でwhileの中に移動されたブロックを表すノードが、OPTBLOCKです。

case NODE_OPTBLOCK:{
  /* for optimize */
  LABEL *redo_label = NEW_LABEL(0);
  LABEL *next_label = NEW_LABEL(0);

  iseq->compile_data->start_label = next_label;
  iseq->compile_data->redo_label = redo_label;

  ADD_LABEL(ret, redo_label);
  COMPILE_(ret, "optblock body", node->nd_head, 1 /* pop */ );
  ADD_LABEL(ret, next_label);
  ADD_INSN(ret, 0, opt_checkenv);
  break;
}

コンパイルのしかたは、redoとnext用のラベルではさみながら、ブロックの本体をその場にコンパイルするだけでした。