Previous: , Up: Byte Compilation   [Contents][Index]


16.8 Disassembled Byte-Code

人はバイトコードを記述しません。それはバイトコンパイラーの仕事です。しかし、好奇心を満たすために、わたしたちはディスアセンブラを提供しています。ディスアセンブラは、バイトコードを人間が読めるフォームに変換します。

バイトコードインタープリターは、シンプルなスタックマシンとして実装されています。これは値を自身のスタックにpushして、計算で使用するためにそれらをpopして取り出し、おの結果を再びそのスタックにpushして戻します。バイトコード関数がリターンするときは、スタックから値をpopして取り出し、その関数の値としてリターンします。

それに加えてスタックとバイトコード関数は、値を変数とスタックの間で転送することにより、普通のLisp変数を使用したり、バインドおよびセットすることができます。

Command: disassemble object &optional buffer-or-name

このコマンドは、objectにたいするディスアセンブルされたコードを表示します。インタラクティブに使用した場合、またはbuffer-or-namenilか省略された場合は、*Disassemble*という名前のバッファーに出力します。buffer-or-nameが非nilの場合は、バッファーもしくは既存のバッファーの名前でなければなりません。その場合は、そのバッファーのポイント位置に出力され、ポイントは出力の前に残りされます。

引数objectには関数名、ラムダ式(Lambda Expressionsを参照)、またはバイトコードオブジェクト(Byte-Code Objectsを参照)を指定できます。ラムダ式の場合、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   varref   integer      ; integerの値を取得して
                          ;   それをスタック上にpushする。
1   constant 1            ; スタック上に1をpushする。
2   eqlsign               ; 2つの値をスタックからpopして取り出し、
                          ;   それらを比較して結果をスタック上にpushする。
3   goto-if-nil 1         ; スタックのトップをpopしてテストする。
                          ;   nilなら1へ、それ以外はcontinue。
6   constant 1            ; スタックのトップに1をpushする。
7   return                ; スタックのトップの要素をリターンする。
8:1 varref   integer      ; integerの値をスタック上にpushする。
9   constant factorial    ; factorialをスタック上にpushする。
10  varref   integer      ; integerの値をスタック上にpushする。
11  sub1                  ; integerをpopして値をデクリメントする。
                          ;   スタック上に新しい値をpushする。
12  call     1            ; スタックの最初(トップ)の要素を引数として
                          ;   関数factorialを呼び出す。
                          ;   リターン値をスタック上にpushする。
13 mult                   ; スタックのトップ2要素をpopして取り出し乗じ
                          ;   結果をスタック上にpushする。
14 return                 ; スタックのトップ要素をリターンする。

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
                                  ;   スタック上のトップにpushする。
1   call     0            ; 引数なしでcurrent-time-stringを呼び出し
                          ;   結果をスタック上にpushする。
2   varbind  t1           ; スタックをpopしてt1にpopされた値をバインドする。
3:1 varref   n            ; 環境からnの値を取得して
                          ;   その値をスタック上にpushする。
4   sub1                  ; スタックのトップから1を減ずる。
5   dup                   ; スタックのトップを複製する。
                          ;   たとえばスタックのトップをコピーしてスタック上にpushする。
6   varset   n            ; スタックのトップをpopして
                          ;   nをその値にバインドする。

;; (要はシーケンスdup varsetはpopせずに
;;  スタックのトップをnの値にコピーする。)

7   constant 0            ; スタック上に0をpushする。
8   gtr                   ; スタックのトップ2値をpopして取り出し
                          ;   nが0より大かテストし
                          ;   結果をスタック上にpushする。
9   goto-if-not-nil 1     ; n > 0なら1へ
                          ;   (これはwhile-loopを継続する)
                          ;   それ以外はcontinue。
12  varref   t1           ; t1の値をスタック上にpushする。
13  constant current-time-string  ; current-time-string
                                  ;   スタックのトップにpushする。
14  call     0            ; 再度current-time-stringを呼び出す。
15  unbind   1            ; ローカル環境のt1をアンバインドする。
16  list2                 ; スタックのトップ2要素をpopして取り出し
                          ;   それらのリストを作りスタック上にpushする。
17  return                ; スタックのトップの値をリターンする。

Previous: , Up: Byte Compilation   [Contents][Index]