最適化の流れ
だいぶ間が空いてしまったので、まずは流れのおさらいから。
- yarvcore_eval_parsed @ yarvcore.c
- th_compile_from_node @ yarvcore.c
- yarv_iseq_new_with_opt @ iseq.c
- iseq_compile @ compile.c
- iseq_compile_each @ compile.c
- iseq_setup @ compile.c
- iseq_compile @ compile.c
- yarv_iseq_new_with_opt @ iseq.c
- th_compile_from_node @ yarvcore.c
第3回〜第9回まで読んだのがiseq_compile_each、Rubyの構文木をYARVの命令のリストに変換する関数でした。その次に来るのが、iseq_setup。この関数の前半は、生成された命令リストの最適化を行っています。
static int iseq_setup(yarv_iseq_t *iseq, LINK_ANCHOR *anchor) { iseq_optimize(iseq, anchor); if (iseq->compile_data->option->instructions_unification) { iseq_insns_unification(iseq, anchor); } if (iseq->compile_data->option->stack_caching) { set_sequence_stackcaching(iseq, anchor); } ...続く...
コメントやチェックコードなどは勝手にわたしが除いて転載しております。大筋はこんな感じということで。さて、最適化ステップは3段階に分かれているようです。最初のiseq_optimizeはさらに内部で3つに分かれています。
static int iseq_optimize(yarv_iseq_t *iseq, LINK_ANCHOR *anchor) { LINK_ELEMENT *list; const int do_peephole = iseq->compile_data->option->peephole_optimization; const int do_si = iseq->compile_data->option->specialized_instruction; const int do_ou = iseq->compile_data->option->operands_unification; list = FIRST_ELEMENT(anchor); while (list) { if (list->type == ISEQ_ELEMENT_INSN) { if (do_peephole) { iseq_peephole_optimize(iseq, list); } if (do_si) { iseq_specialized_instruction(iseq, (INSN *)list); } if (do_ou) { insn_operands_unification((INSN *)list); } } list = list->next; } return COMPILE_OK; }
命令1個1個をwhileループで回って、各命令に対して最適化をほどこしています。
まとめると、この段階で行われる最適化は5つ。
最適化 | 関数名 | |
---|---|---|
覗き穴最適化 | iseq_peephole_optimize | |
特化命令 | iseq_specialized_instruction | |
オペランド融合 | insn_operands_unification | |
命令融合 | iseq_insns_unification | |
スタックキャッシング | set_sequence_stackcaching |
それぞれ順番に見ていきます。