続 Gauche の $if の最適化 - Scheme VM を書く

昨日の説明は分かりづらかったので擬似バイトコードで説明しよう。

(if (if test1 then1 else1) then0 else0)

のコードは

test1 branch then1 jmp[A] else1 label[A] branch then0 jmp[B] else label[B]

コンパイルされます。
branch は直前の評価結果で分岐する命令です。


これの特殊型として then1 が it のもの

(if (if test1 it else1) then0 else0)

は同じようにコンパイルされます。

test1 branch it jmp[A] else1 label[A] branch then0 jmp[B] else0 label[B]

よーく眺めていると branch の直後に it がある場合、test1 の評価結果である it は必ず #t であることが分かります。
つまりこう書けます。

test1 branch #t jmp[A] else1 label[A] branch then0 jmp[B] else0 label[B]


するとさらに jmp[A] 後の branch の結果も明らかに then0 に飛びます。

test1 branch then0 jmp[B] else1 branch then0 jmp[B] else0 label[B]

と短くできます。 then0 が2箇所でてくるのでどちらかはラベルにして jmp することを考えると

test1 branch jmp[A] else1 branch [A] then0 jmp[B] else0 [B]

のようになります。
つまり test1 が #t のときには it branch の分だけ実行命令数が減ります。


同様に

(if (if test1 then1 it) then0 else0)
=>
test1 branch then1 jmp[A] it label[A] branch then0 jmp[B] else0 label[B]
=>
test1 branch then1 jmp[A] #f label[A] branch then0 jmp[B] else0 label[B]
=>
test1 branch then1 jmp[A] jmp[C] label[A] branch then0 jmp[B] label[C] else0 label[B]

となります。