ひとり勉強会

ひとり楽しく勉強会

iseq_insns_unification

命令融合。こちらも同じく現在はOFFになってるみたいです。一応ソースだけ読んでみました。

さっき見たオペランド融合は、よくある「命令+オペランド」の組を1個の専用融合命令に置き換えてしまう最適化でした。こっちの命令融合というのは、よく連続する命令、「命令+命令」の組を1個の専用融合命令に置き換えてしまう最適化です。仕組み的には3命令以上の連続「命令+命令+命令+...」も一気に融合するケースも考えて作られていましたが、実際には使われていない模様。

融合したい命令+命令の組は、ビルド時に定義ファイル"opt_insn_unif.def"から自動生成されます。(自動生成スクリプトオペランド融合のものと同じ、"tool/insns2vm.rb"。)定義ファイルの中身はこんな感じで、

putobject putobject
putobject putstring
putobject setlocal
putobject setdynamic

putstring putstring
putstring putobject
putstring setlocal
putstring setdynamic
...

たとえば最初の行はputobjectの直後にまたputobjectするパターンをまとめる、UNIFIED_putobject_putobject という専用命令を定義しています。この定義に対応して、テーブルが自動生成されます(Makefileと同じディレクトリの、optunifs.incというファイルです)。

static int UNIFIED_putobject_0[] = {  BIN(UNIFIED_putobject_putobject), 3, 
  BIN(putobject)};
static int UNIFIED_putobject_1[] = {  BIN(UNIFIED_putobject_putstring), 3, 
  BIN(putstring)};
static int UNIFIED_putobject_2[] = {  BIN(UNIFIED_putobject_setlocal), 3, 
  BIN(setlocal)};
static int UNIFIED_putobject_3[] = {  BIN(UNIFIED_putobject_setdynamic), 3, 
  BIN(setdynamic)};
...
static int *UNIFIED_putobject[] = {(int *)5, 
  UNIFIED_putobject_0,
  UNIFIED_putobject_1,
  UNIFIED_putobject_2,
  UNIFIED_putobject_3};
...
static int **unified_insns_data[] = {
  0,
  UNIFIED_getlocal,
  0,
  ...
  0,
  0,
  UNIFIED_putobject,
  UNIFIED_putstring,
  0,
  ...

unified_insns_data[1個目の命令] とテーブルをひくと、1個目の命令に対応する融合命令のリストが手に入ります。そのリストでループをまわして、次の命令と2個目の命令で一致するものがあったら置き換え、なければ何もしない、という処理で最適化が行われます。

static int
iseq_insns_unification(yarv_iseq_t *iseq, LINK_ANCHOR *anchor)
{
  list = FIRST_ELEMENT(anchor);
  while (list) {
    if (list->type == ISEQ_ELEMENT_INSN) {
      iobj = (INSN *)list;
      id = iobj->insn_id;
      if (unified_insns_data[id] != 0) {
        int **entry = unified_insns_data[id];
}

テーブルを引いて、

        for (j = 1; j < (int)entry[0]; j++) {
          int *unified = entry[j];
          LINK_ELEMENT *li = list->next;

ループを回して、

        for (k = 2; k < unified[1]; k++) {
          if (li->type != ISEQ_ELEMENT_INSN ||
              ((INSN *)li)->insn_id != unified[k]) {
             goto miss;
          }
          li = li->next;
        }

次の命令ととマッチしていたら

        /* matched */
        niobj =
         new_unified_insn(iseq, unified[0], unified[1] - 1,
           list);
        ... 続く ...

置き換え、となっています。