Next: , Previous: Byte-Code Objects, Up: Byte Compilation


15.7 バイトコードの逆アセンブル

人間はバイトコードを書きません。 それはバイトコンパイラの仕事です。 しかし、好奇心を満たすために逆アセンブラを用意してあります。 逆アセンブラはバイトコンパイルしたコードを人が読める形式に変換します。

バイトコードインタープリタは、単純なスタックマシンとして実装してあります。 値を自前のスタックに積み、計算に使うためにスタックから取り出し、 計算結果そのものはスタックにまた積みます。 バイトコード関数から戻るときには、スタックから値を取り出して 関数値としてその値を返します。

スタックに加えて、変数とスタックのあいだで値を転送することで、 バイトコード関数は、普通のLisp変数を使ったり、 束縛したり、値を設定できます。

— コマンド: disassemble object &optional stream

この関数はobjectの逆アセンブルしたコードを出力する。 streamを指定すると、そこへ出力する。 さもなければ、逆アセンブルしたコードはストリームstandard-outputへ 出力する。 引数objectは関数名かラムダ式である。

特別な例外として、この関数を対話的に使うと、 ‘*Disassemble*’という名前のバッファへ出力する。

disassemble関数の使用例を2つ示します。 バイトコードとLispソースとの対応を取れるように 特別なコメントを追加してありますが、 これらはdisassembleの出力には現れません。 これらの例は、最適化してないバイトコードです。 現在、バイトコードは、普通、最適化しますが、 目的は果たせるので、例を書き換えてありません。

     (defun factorial (integer)
       "Compute factorial of an integer."
       (if (= 1 integer) 1
         (* integer (factorial (1- integer)))))
           factorial
     
     (factorial 4)
           24
     
     (disassemble 'factorial)
          -| byte-code for factorial:
      doc: Compute factorial of an integer.
      args: (integer)
     
     
     0   constant 1              ; スタックに1を積む
     
     
     
     
     
     1   varref   integer        ; 環境からintegerの値を取得し、
                                 ; スタックに積む
     
     
     
     
     2   eqlsign                 ; スタックの先頭から2つの値を
                                 ; 取りさって比較し、
                                 ; 結果をスタックに積む
     
     
     
     
     3   goto-if-nil 10          ; スタックの先頭から値を取りさり
                                 ; 検査する。nilならば10へ飛び、
                                 ; さもなければつぎへ進む
     
     
     6   constant 1              ; スタックに1を積む
     
     
     
     7   goto     17             ; 17へ飛ぶ(この場合、関数は1を返す)
     
     
     10  constant *              ; スタックにシンボル*を積む
     
     
     11  varref   integer        ; スタックにintegerの値を積む
     
     
     12  constant factorial      ; スタックにfactorialを積む
     
     
     13  varref   integer        ; スタックにintegerの値を積む
     
     
     
     14  sub1                    ; スタックからintegerを取りさり、
                                 ; 減した新たな値をスタックに積む
     
     
     
                                 ; スタックの現在の内容はつぎのとおり
                                 ; − integerを減らした値
                                 ; − factorial
     
                                 ; − integerの値
                                 ; − *
     
     
     
     
     
     15  call     1              ; スタックの最初(先頭)要素を使って
                                 ; 関数factorialを呼び出す
                                 ; 戻り値をスタックに積む
     
     
     
     
     
                                 ; スタックの現在の内容はつぎのとおり
                                 ; − factorial
                                 ;      再帰呼び出しの結果
                                 ; − integerの値
                                 ; − *
     
     
     
     
     
     
     
     16  call     2              ; スタックの最初の要素の2つ
                                 ; (先頭の2つ)を引数として
                                 ; 関数*を呼び出し
                                 ; 結果をスタックに積む
     
     
     
     17  return                  ; スタックの先頭要素を返す
           nil

関数silly-loopは、少々複雑です。

     (defun silly-loop (n)
       "Return time before and after N iterations of a loop."
       (let ((t1 (current-time-string)))
         (while (> (setq n (1- n))
                   0))
         (list t1 (current-time-string))))
           silly-loop
     
     (disassemble 'silly-loop)
          -| byte-code for silly-loop:
      doc: Return time before and after N iterations of a loop.
      args: (n)
     
     
     
     
     0   constant current-time-string  ; current-time-string
                                       ; スタックの先頭に積む
     
     
     
     
     1   call     0              ; 引数なしでcurrent-time-string
                                 ; 呼び出し、結果をスタックに積む
     
     
     
     2   varbind  t1             ; スタックから値を取りさり、
                                 ; t1に束縛する
     
     
     
     
     3   varref   n              ; 環境からnの値を取得し、
                                 ; 値をスタックに積む
     
     
     4   sub1                    ; スタックの先頭から1を引く
     
     
     
     
     
     5   dup                     ; スタックの先頭の値を複製する
                                 ; つまり、スタックの先頭の値を
                                 ; コピーして、それをスタックに積む
     
     
     
     6   varset   n              ; スタックの先頭から値を取りさり、
                                 ; 値をnに束縛する
     
     
     
     
     
                                 ; つまり、dup varset
                                 ; スタックの先頭の値を取りさらずに
                                 ; nにコピーする
     
     
     7   constant 0              ; スタックに0を積む
     
     
     
     
     8   gtr                     ; スタックから2つの値を取りさり、
                                 ; nが0より大きいか調べ、
                                 ; 結果をスタックに積む
     
     
     
     
     
     9   goto-if-nil-else-pop 17 ; n <= 0ならば17へ飛ぶ
                                 ; (whileループから抜ける)
                                 ; さもなければ、スタックの先頭から
                                 ; 値を取りさり、つぎへ進む
     
     
     
     12  constant nil            ; スタックにnilを積む
                                 ; (これはループの本体)
     
     
     
     
     
     13  discard                 ; ループの本体の結果を捨てる
                                 ; (whileループは副作用のために
                                 ; つねに評価される)
     
     
     
     14  goto     3              ; whileループの先頭へ飛ぶ
     
     
     
     
     
     17  discard                 ; スタックの先頭の値を取りさって、
                                 ; whileループの結果を捨てる。
                                 ; これは、9での飛び越しのために
                                 ; 取りさっていない値nil
     
     
     18  varref   t1             ; t1の値をスタックに積む
     
     
     
     
     19  constant current-time-string  ; current-time-string
                                       ; スタックに積む
     
     
     20  call     0              ; ふたたびcurrent-time-string
                                 ; 呼び出す
     
     
     
     
     21  list2                   ; スタックの先頭から2つの値を取りさり
                                 ; それらのリストを作り、
                                 ; リストをスタックに積む
     
     
     22  unbind   1              ; ローカルの環境のt1の束縛を解く
     
     
     23  return                  ; スタックの先頭の値を返す
     
           nil