@c -*-texinfo-*-
@c This is part of the GNU Emacs Lisp Reference Manual.
@c Copyright (C) 1990, 1991, 1992, 1993, 1994 Free Software Foundation, Inc. 
@c See the file elisp-jp.texi for copying conditions.
@c 複製の条件は elisp-jp.texi に書いてあります。
@setfilename ../info/compile-jp
@clear jp-draft
@node Byte Compilation, Debugging, Loading, Top
@chapter バイトコンパイル
@cindex byte-code
@cindex compilation
@cindex バイトコード[はいとこーと]
@cindex コンパイル[こんはいる]

  GNU Emacs Lispには@dfn{コンパイラ(compiler)}があります。
これは、Lispで書いた関数を
@dfn{バイトコード(byte-code)}と呼ばれる、より効率的に実行できる特殊な表現に
変換します。
コンパイラはLisp関数定義をバイトコードで置き換えます。
バイトコードになった関数が呼び出されると、その関数定義は
@dfn{バイトコード・インタプリタ(byte-code interpreter)}によって評価されます。

バイトコンパイルされたコードはマシンのハードウェアによって
直接実行されるのではなく、
バイトコード・インタプリタによって評価されるため
(本当のコンパイルされたコードはハードウェアによって
直接実行されるのですが)、
バイトコードは再コンパイルなしに、あるマシンから別のマシンに移して
動作させることが可能です。
しかし、バイトコードは本当のコンパイルされたコードほどは
速くありません。

一般的にどのバージョンのEmacsにおいても、
それよりも少し前のバージョンのEmacsで
作ったバイトコンパイル・コードであれば実行することができます。
ただし、その逆は成り立ちません。
特に Emacs バージョン 19.29 でコンパイルされたプログラムは
それ以前のバージョンでは動作しません。
@iftex
@xref{Docs and Compilation}。
@end iftex
19.29より前のバージョンでコンパイルしたファイルは、
修飾ビット(modifier bits)の立った文字定数が入っている場合、
動作しないことがあります。これは、
Emacs 19.29になって修飾ビットの番号がつけ直されたためです。

バイトコンパイル中に起きたエラーを調べる方法は
@xref{Compilation Errors}。

@menu
* Speed of Byte-Code::          バイトコンパイルによる速度向上の例
* Compilation Functions::       バイトコンパイルを行なう関数
* Docs and Compilation::        説明文字列の動的なロード
* Dynamic Loading::             個々の関数の動的なロード
* Eval During Compile::  	コンパイル時に評価されるコード
* Byte-Code Objects::		バイトコンパイルされた関数に使われるデータ・タイプ
* Disassembly::                 バイトコードの逆アセンブル; バイトコードの読み
方
@end menu

@node Speed of Byte-Code
@section バイトコンパイルされたコードの性能

バイトコンパイルされた関数は、C言語で書かれているプリミティブ関数ほどは
効率的ではありませんが、Lispで書いたままのもの
よりはかなり速く実行されます。
例を挙げましょう。

@example
@group
(defun silly-loop (n)
  "N回繰り返して、ループの前後の時間を返す。"
  (let ((t1 (current-time-string)))
    (while (> (setq n (1- n)) 
              0))
    (list t1 (current-time-string))))
@result{} silly-loop
@end group

@group
(silly-loop 100000)
@result{} ("Fri Mar 18 17:25:57 1994"
    "Fri Mar 18 17:26:28 1994")  ; @r{31秒}
@end group

@group
(byte-compile 'silly-loop)
@result{} @r{[コンパイルされたコードは省略]}
@end group

@group
(silly-loop 100000)
@result{} ("Fri Mar 18 17:26:52 1994"
    "Fri Mar 18 17:26:58 1994")  ; @r{6秒}
@end group
@end example

  この例では、インタプリタで実行したコードは実行に31秒かかっていますが、
バイトコンパイルしたコードでは6秒しかかかっていません。
この結果は代表例にすぎませんので、
実際に実行してみるとかなり違ったものになるでしょう。

@node Compilation Functions
@comment  node-name,  next,  previous,  up
@section バイトコンパイルを行なう関数
@cindex バイトコンパイルを行なう関数[はいとこんはいるをおこなうかんすう]

  個々の関数やマクロ定義のコンパイルは、@code{byte-compile}という
関数で行なうことができます。
ファイル全体のコンパイルは@code{byte-compile-file}で行ない、複数のファイルの
コンパイルは@code{byte-recompile-directory}や@code{batch-byte-compile}で
行ないます。

  バイトコンパイルを実行すると、@samp{*Compile-Log*}というバッファに
警告メッセージが表示されることがあります。
これは、プログラム中で
問題となりそうなものを指摘するもので、必ずしもエラーではありません。

@cindex マクロのコンパイル[まくろのこんはいる]
  マクロを使っているコードをバイトコンパイルするときは注意してください。
マクロの呼出しはコンパイルされるときに展開されてしまいます。
そこで、正しくコンパイルするためには、マクロはコンパイル時にすでに
定義されていなければいけません。
詳細は@ref{Compiling Macros}を参照してください。

  通常、ファイルをコンパイルしても、そのファイルの内容が評価されたり、
ファイルがロードされたりすることはありません。
しかし、ファイルのトップ・レベルにある@code{require}はいずれも実行されます。
必要なマクロ定義をコンパイル時に使えるようにする確実な方法の一つは
マクロを定義しているファイルをrequireしておくことです
(@pxref{Named Features})。
コンパイルしたプログラムを@emph{動作させる}際に
マクロ定義ファイルをロードしないようにするには、
@code{require}の呼出しを@code{eval-when-compile}で囲んで
プログラムを書いてください(@pxref{Eval During Compile})。

@defun byte-compile symbol
この関数は@var{symbol}の関数定義をバイトコンパイルし、その元の定義を
コンパイルしたものに置き換えます。
@var{symbol}の関数定義はその関数の実際のコードでなくてはいけません。
つまり、コンパイラはほかのシンボルへ間接的に関数定義をたどることは
しません。
@code{byte-compile}は新たにコンパイルされた@var{symbol}の関数定義を
返します。

  @var{symbol}の関数定義がバイトコード関数オブジェクトの場合、
@code{byte-compile}は何もせず@code{nil}を返します。
Lispでは、すべてのシンボルはそれぞれただ一つの関数定義しか保持しません。
そして、それがすでにコンパイルされている場合、コンパイルする前のコードは
どこからも得られません。
そのため``同じ関数定義を2度コンパイルする''方法はありません。

@example
@group
(defun factorial (integer)
  "INTEGERの階乗を計算する。"
  (if (= 1 integer) 1
    (* integer (factorial (1- integer)))))
@result{} factorial
@end group

@group
(byte-compile 'factorial)
@result{}
#[(integer)
  "^H\301U\203^H^@@\301\207\302^H\303^HS!\"\207"
  [integer 1 * factorial]
  4 "INTEGERの階乗を計算する。"]
@end group
@end example

@noindent
この結果はバイトコード関数オブジェクトです。
そのオブジェクトに含まれる文字列が実際のバイトコードで、
その中の各文字が命令や命令の作用対象(operand)です。
また、ベクタは、その関数で使われているすべての定数、変数名、関数名を含みます。
ただし、特別な命令として作られている特定のプリミティブは現れません。
@end defun

@deffn Command compile-defun
このコマンドはポイントを含むdefunを読み込んで、コンパイルし、
結果を評価します。
@footnote{訳注: ここでいうdefunはトップレベルにあるS式一般を指します。
これに関してはEmacsマニュアルのDefunsを
参照してください。}
@c このdefunがdef*であることがわかりにくいと思い、注を入れた。
このコマンドを、実際に関数を定義するdefunで実行した場合は、結果として
その関数のコンパイルされたバージョンが使われるようになります。
@end deffn

@deffn Command byte-compile-file filename
この関数は@var{filename}という名前のLispコード・ファイルを
バイトコード・ファイルにコンパイルします。
出力ファイル名は@var{filename}の最後に@samp{c}をつけたものになります。

コンパイル時には、入力ファイルを読み込みながら一度に一つの式が処理されます。
その式が関数やマクロの定義であれば、コンパイルした関数やマクロの
定義がファイルに書き出されます。
それ以外の式はまとめられ、それぞれは
コンパイルされ、そのファイルが読まれたとき、
コンパイル・コードが実行されるように書き込まれます。
入力ファイルが読み込まれるとき、すべてのコメントは無視されます。

このコマンドは@code{t}を返します。
対話的に呼ばれた場合はファイル名の入力を促します。

@example
@group
% ls -l push*
-rw-r--r--  1 lewis     791 Oct  5 20:31 push.el
@end group

@group
(byte-compile-file "~/emacs/push.el")
     @result{} t
@end group

@group
% ls -l push*
-rw-r--r--  1 lewis     791 Oct  5 20:31 push.el
-rw-rw-rw-  1 lewis     638 Oct  8 20:25 push.elc
@end group
@end example
@end deffn

@deffn Command byte-recompile-directory directory flag
@cindex library compilation
@cindex ライブラリのコンパイル[らいふらりのこんはいる]
この関数は、@var{directory}にあって再コンパイルが必要な@samp{.el}ファイル
すべてを再コンパイルします。
@samp{.elc}ファイルが存在しても@samp{.el}ファイルより古い場合、
そのファイルは再コンパイルが必要とされます。

@samp{.el}ファイルが対応する@samp{.elc}ファイルを持たない場合
には、@var{flag}によりどうするかが決まります。
この値が@code{nil}の場合はそのファイルは無視されます。
もし非@code{nil}の場合、ユーザに対しそれぞれのファイルを
コンパイルするかどうかを問い合わせます。

このコマンドの返り値は不定です。
@end deffn

@defun batch-byte-compile
この関数はコマンド行に指定されたファイルに対して、
@code{byte-compile-file}を実行します。
この関数はEmacsのバッチ走行でのみ使ってください。
関数の終了時にEmacsを終了させてしまうからです。
一つのファイルでエラーが起こっても、それに続くファイル
は処理され続けます。
(エラーの起きたファイルからは、もちろんコンパイル・コードは作成されません。)

@example
% emacs -batch -f batch-byte-compile *.el
@end example
@end defun

@defun byte-code code-string data-vector max-stack
@cindex バイトコード・インタプリタ[はいとこーといんたふりた]
この関数が実際にバイトコードを解釈(interpret)します。
バイトコンパイルされた関数は実際には@code{byte-code}を呼び出している
関数本体で定義されます。
この関数を自分で呼び出すのはやめてください。
この関数を正しく呼び出す方法はバイトコンパイラにしかわからないからです。

新しいEmacs (バージョン19とそれ以降)では、バイトコードは通常、
バイトコード関数オブジェクトの一部として実行されます。
@code{byte-code}を明示的に呼び出すことで実行されることは本当にまれです。
@end defun

@node Docs and Compilation
@section 説明文字列とコンパイル
@cindex ドキュメントの動的なロード[ときゅめんとのとうてきなろーと]

  バイトコンパイルされたファイルからロードされた関数と変数は、
必要になったときに、その説明文字列をファイルから動的に取ってきます。
これによりEmacs内部の領域を節約できます。
また、ファイルをロードするときに説明文字列自体を処理する必要が
なくなるので、ロードも早くなります。
この結果、説明文字列を実際に参照するのが遅くなりますが、
多くの場合、ユーザをいらいらさせるほどのものではありません。

  説明文字列を動的に参照することには以下のような問題点があります。

@itemize @bullet
@item
コンパイルしたファイルをロードした後で削除したり名前を変えたりすると、
そのファイルの関数や変数はもうEmacsから参照できなくなります。

@item
コンパイルしたファイルを変更してしまうと(たとえば新しいバージョンの
ファイルをコンパイルすると)、
それ以後、このファイルの説明文字列を参照して
得られた結果は意味のないものとなります。
@end itemize

  ただし、Emacsのインストールに通常の手段を用いていれば
これらの問題はまず起りません。
新しいバージョンをインストールするときには今までと違う名前の
新しいディレクトリを使います。
古いバージョンがインストールされているかぎり、
古いファイルは期待されているところに変更されずに残っているはずです。

  しかし、もしEmacsを自分自身で作成し、自分が作成したディレクトリで
使用している場合には、Lispファイルを編集し再コンパイルした場合、
時々、この問題に出くわすでしょう。
この問題が起きたなら再コンパイル後、ファイルを再ロードすれば
解決できます。

  Emacs 19.29で作成したバイトコンパイル・ファイルは、
それよりも古いバージョンの
Emacsではロードできません。古いバージョンではこの機能をサポートして
いないからです。
そのため、
@code{byte-compile-dynamic-docstrings}を@code{nil}に設定することで
この機能を使わないようにできます。
一度これを行うと、コンパイルしたファイルは、古いバージョンのEmacsでも
ロードできるようになります。
これはグローバルに設定することもできますし、
変数をファイルにローカル束縛するように指定することで、
一つのソース・ファイルに対してこの設定ができます。
その一つのやり方が以下のものです。

@example
-*-byte-compile-dynamic-docstrings: nil;-*-
@end example

@defvar byte-compile-dynamic-docstrings
この値が非@code{nil}の場合には、バイトコンパイラが生成する
コンパイル済みファイルが、
説明文字列の動的なロードを行うように設定されます。
@end defvar
@cindex @samp{#@@@var{count}}
@cindex @samp{#$}
  動的な説明文字列機能により、コンパイルされたファイルは、
特別なLispリーダ構文要素(construct)、@samp{#@@@var{count} }を使用して
書かれます。
この構文要素は次の@var{count}個の文字を読み飛ばします。
また、@samp{#$}という構文要素も使われるようになります。これは``文字列にした
このファイルの名前''を表します。
Lispソース・ファイルにはこれらの構文要素を使わないのが一番よいでしょう。

@node Dynamic Loading
@section 個々の関数の動的なロード

@cindex 関数の動的なロード[かんすうのとうてきなろーと]
@cindex 動的な関数ロード[とうてきなかんすうのろーと]
@cindex 遅延ロード[ちえんろーと]
コンパイルするときに@dfn{動的な関数ロード(dynamic function loading)}という
機能(@dfn{遅延ロード(lazy loading)}としても知られています)を
有効にするように選択できます。
動的な関数ロードにより、ファイルをロードしてもそのファイルの
関数定義の全部が読み込まれることはなくなります。
そのかわりに、それぞれの関数定義にはファイルを参照する「名札」が入ります。
それぞれの関数が最初に呼び出されるとき、ファイルから完全な関数定義が
読み込まれ、「名札」と置き換わります。

  動的な関数ロードの利点はファイルのロードがとても速くなることです。
たくさんのコマンドが独立して納められているファイルで、
そのうちの一つを使っても、すぐには
(もしくは、その後ずっと)それ以外のコマンドを
使うかどうかわからないような場合には便利です。
たくさんのキーボード・コマンドをもつ専門化したモードの場合、
コマンドは特定のパターンで使用されることが多いものです。
そのような場合、
ユーザはモードを呼び出したとしても、そのモードのもつごく少数の
コマンドしか使いません。

  動的なローディング機能は以下のような欠点を持っています。

@itemize @bullet
@item
コンパイルしたファイルをロードした後で削除したり名前を変えたりすると、
Emacsはまだロードされていない残りの関数定義を
もうロードできなくなります。

@item
コンパイルしたファイルを変更してしまうと(たとえば新しいバージョンの
ファイルをコンパイルすると)、
まだロードされていない関数をロードしようとしても
得られた結果は意味のないものとなります。
@end itemize

  もし、ファイルの新しいバージョンをコンパイルしたのなら、
すぐに新しくコンパイルしたファイルをロードするのが最もよいでしょう。
これにより将来起りうる問題を防ぐことができるからです。

  バイトコンパイラが動的な関数ロード機能を使うのは、コンパイル時に変数
@code{byte-compile-dynamic}が非@code{nil}である場合です。
ただし、この変数をグローバルに設定しないでください。
特定のファイルにしか動的なロード機能は望ましくありません。
そのかわり、ファイルローカルな変数束縛を行うことで
特定のソース・ファイルでのみ機能を有効にしてください。

@example
-*-byte-compile-dynamic: t;-*-
@end example

@defvar byte-compile-dynamic
この値が非@code{nil}の場合には、バイトコンパイラが生成する
コンパイル済みファイルが
動的な関数ロードを実行するように設定されます。
@end defvar

@defun fetch-bytecode function
この関数は、
@var{function}の関数定義がまだ完全にはロードされていない場合、
関数を含むバイトコンパイルされたファイルからこの関数を即座にロードします。
引数@var{function}はバイトコード関数オブジェクトでも関数名でも
構いません。
@end defun

@node Eval During Compile
@section コンパイル時に行なわれる評価

  ここで挙げる機能により、プログラムをコンパイルするときに実行される
コードを書くことができます。

@defspec eval-and-compile body
この形式で@var{body}を囲むことにより、その中のコードがコンパイル時にも
実行時にも(コンパイルされている、されていないに関わらず)、評価される
ようにします。

同様の結果を得るには@var{body}を別のファイルに納め、
そのファイルを@code{require}で参照します。
このように実行したいコードがかなりな量ある場合は、@code{require}を
使う方がよいでしょう。
@end defspec

@defspec eval-when-compile body
この形式で@var{body}を囲むことにより、そのコードは
コンパイル時に評価され、
コンパイルされたプログラムがロードされるときには評価されないように
なります。
コンパイラによる評価結果は定数になってコンパイルされたプログラムに
現れます。
プログラムがコンパイルされていなければ、インタプリタで
解釈されるとき@var{body}は通常どおり評価されます。

トップ・レベルでみると、これはCommon Lispでの語法である
@code{(eval-when (compile eval) @dots{})}と似ています。
それ以外の場所では、Common Lispの@samp{#.}リーダ・マクロ(こちらは
インタプリタによる解釈時に実行されるわけではありません)が
@code{eval-when-compile}の行うものに近くなります。
@end defspec

@node Byte-Code Objects
@section バイトコード関数オブジェクト
@cindex compiled function
@cindex byte-code function
@cindex コンパイルされた関数[こんはいるされたかんすう]
@cindex バイトコード関数[はいとこーとかんすう]

バイトコード関数は特別なデータ・タイプになります。
@dfn{バイトコード関数オブジェクト(byte-code function objects)}
というタイプです。

  内部的には、バイトコード関数オブジェクトはベクタにとてもよく似ています。
しかし、関数として呼ばれるべきところに現れたとき、評価子は
このデータ・タイプを特別に扱います。
バイトコード関数オブジェクトの印字表現は、最初の@samp{[}の前に
@samp{#}が追加される以外はベクタの表現と似ています。

  Emacsバージョン18にはバイトコード関数オブジェクトというデータ・タイプは
なく、コンパイルされた関数はバイトコードを実行するために
@code{byte-code}という関数を使っていました。

バイトコード関数オブジェクトは少なくとも4個の要素を持たなくてはなりません。
要素数にかぎりはありませんが最初の6個の要素のみが実際には使用されます。
その6要素は以下のものです。

@table @var
@item 引数リスト
引数シンボルのリスト。

@item バイトコード
バイトコード命令を含む文字列。

@item 定数
バイトコードが参照するLispオブジェクトのベクタ。
これは関数名や変数名として使われるシンボルを含みます。

@item スタックの大きさ
この関数が必要とするスタックの大きさの最大値。

@item 説明文字列
説明文字列。もしなければ@code{nil}になります。
これは数やリストの場合もあります。この場合、
説明文字列はファイルにしまわれています。
本当の説明文字列を得るには関数@code{documentation}を使います
(@pxref{Accessing Documentation}).

@item 対話引数
もしあれば、対話引数の指定。
これは文字列かLisp式のどちらかです。
対話的に使われる関数でなければこれは@code{nil}になります。
@end table

ここで印字表現によるバイトコード関数オブジェクトの例を示します。
コマンド@code{backward-sexp}の定義です。

@example
#[(&optional arg)
  "^H\204^F^@@\301^P\302^H[!\207"
  [arg 1 forward-sexp]
  2
  254435
  "p"]
@end example

バイトコード・オブジェクトを生成する基本的な方法は@code{make-byte-code}
を用いることです。

@defun make-byte-code &rest elements
この関数は@var{elements}をその要素とするバイトコード関数オブジェクトを
構築し、返します。
@end defun

関数 byte-code に与える要素を自分でなんとかしようなどとしてはいけません。
要素に不整合があると、その関数を呼んだときEmacsがクラッシュして
しまいかねないからです。
このオブジェクトを生成するのは常にバイトコンパイラに任せてください。
バイトコンパイラは要素に矛盾を生じさせません(というのが我々の
希望なのですが)。

  バイトコード・オブジェクトの要素は@code{aref}を使えば
アクセスできます。また、@code{vconcat}で同様の要素をもつベクタを
生成できます。

@node Disassembly
@section 逆アセンブルされたバイトコード
@cindex 逆アセンブルされたバイトコード[きゃくあせんふるされたはいとこーと]

  人間はバイトコードを書きません。その仕事はバイトコンパイラに任されて
います。
しかし、猫のような好奇心を満足させるために
逆アセンブラが提供されています。
逆アセンブラはバイトコンパイルされたコードを人間が読める形式に変換します。

バイトコード・インタプリタは単純なスタック・マシンとして
インプリメントされています。
これは、インタプリタがそれ自身でもつスタックに値をプッシュしていき、
それをポップして計算に用い、その結果自身をスタックにプッシュし戻します。

バイトコード関数は返るときスタックから値をポップし、それを関数の
値として返します。

このスタック操作に加え、変数とスタックの間で値を移すことで、
通常のLisp変数の値を使い、束縛し、設定することが可能になります。

@deffn Command disassemble object &optional stream
この関数は@var{object}の逆アセンブル・コードを印字します。
もし@var{stream}引数があれば、そこに出力されます。
さもなくば、逆アセンブルされたコードはストリーム@code{standard-output}
に印字されます。
引数@var{object}は関数名でもlambda式でも構いません。

特に例外として、この関数は対話的に使われると
@samp{*Disassemble*}というバッファに出力します。
@end deffn

  ここで@code{disassemble}関数を用いた例を二つあげます。
バイトコードとLispソースを関連づけやすいように説明のためのコメントを
付加しました。このコメントは@code{disassemble}の出力には現れません。
この例では最適化されていないバイトコードを示します。
最近ではバイトコードはたいてい最適化されていますが、
説明のためにはこれで十分ですので
この例を書き直すつもりはありません。

@example
@group
(defun factorial (integer)
  "整数integerの階乗を計算する。"
  (if (= 1 integer) 1
    (* integer (factorial (1- integer)))))
     @result{} factorial
@end group

@group
(factorial 4)
     @result{} 24
@end group

@group
(disassemble 'factorial)
     @print{} byte-code for factorial:
 doc: 整数integerの階乗を計算する。
 args: (integer)
@end group

@group
0   constant 1              ; @r{1をスタックにプッシュする。}

1   varref   integer        ; @r{環境から@code{integer}の}
                            ;   @r{値を取得し、}
                            ;   @r{その値をスタックにプッシュする。}
@end group

@group
2   eqlsign                 ; @r{スタックから一番上の}
                            ;   @r{二つの値をポップし、}
                            ;   @r{それを比較し、}
                            ;   @r{結果をスタックにプッシュする。}
@end group

@group
3   goto-if-nil 10          ; @r{スタックの一番上からポップし、}
                            ;   @r{値を調べる}
                            ;   @r{@code{nil}ならば10へ飛び、}
                            ;   @r{それ以外は次に続く。}
@end group

@group
6   constant 1              ; @r{スタックの一番上に1をプッシュする。}

7   goto     17             ; @r{17へ飛ぶ。(この場合、1が}
                            ;   @r{この関数から返される。)}
@end group

@group
10  constant *              ; @r{シンボル@code{*}をスタックにプッシュする。}

11  varref   integer        ; @r{@code{integer}の値を}
                            ;   @r{スタックにプッシュする。}
@end group

@group
12  constant factorial      ; @r{@code{factorial}をスタックにプッシュする。}

13  varref   integer        ; @r{@code{integer}の値を}
                            ;   @r{スタックにプッシュする。}

14  sub1                    ; @r{@code{integer}をポップし、値から1を引き、}
                            ;   @r{新しく得た値をスタックに}
                            ;   @r{プッシュする。}
@end group

@group
                            ; @r{スタックの現在の値は:}
                            ;   @minus{} @r{@code{integer}から1引いた値}
                            ;   @minus{} @r{@code{factorial}}
                            ;   @minus{} @r{@code{integer}の値}
                            ;   @minus{} @r{@code{*}}
@end group

@group
15  call     1              ; @r{関数@code{factorial}を呼び出す。}
                            ;   @r{このとき、スタックの}
                            ;   @r{最初(すなわち一番上)の}
                            ;   @r{要素を引数として用いる。}
                            ;   @r{返ってきた値をスタックに}
                            ;   @r{プッシュする。}
@end group

@group
                            ; @r{スタックの現在の値は:}
                            ;   @minus{} @r{@code{factorial}の}
                            ;        @r{再帰呼出しの結果}
                            ;   @minus{} @r{@code{integer}の値}
                            ;   @minus{} @r{@code{*}}
@end group

@group
16  call     2              ; @r{スタックの最初の二つ}
                            ;   @r{(すなわち、一番上の二つ)}
                            ;   @r{の要素を引数として、}
                            ;   @r{関数@code{*}を呼び出し、}
                            ;   @r{その結果をスタックにプッシュする。}
@end group

@group
17  return                  ; @r{スタックの一番上の要素を返す。}
     @result{} nil
@end group
@end example

@code{silly-loop}関数はもう少し複雑です。

@example
@group
(defun silly-loop (n)
  "N回繰り返して、ループの前後の時間を返す。"
  (let ((t1 (current-time-string)))
    (while (> (setq n (1- n)) 
              0))
    (list t1 (current-time-string))))
     @result{} silly-loop
@end group

@group
(disassemble 'silly-loop)
     @print{} byte-code for silly-loop:
 doc: N回繰り返して、ループの前後の時間を返す。
 args: (n)

0   constant current-time-string  ; @r{@code{current-time-string}を}
                                  ;   @r{スタックの一番上に}
                                  ;   @r{プッシュする。}
@end group

@group
1   call     0              ; @r{@code{current-time-string}を}
                            ;   @r{引数なしで呼び出し、}
                            ;   @r{結果をスタックにプッシュする。}
@end group

@group
2   varbind  t1             ; @r{スタックからポップし、ポップした}
                            ;   @r{値を@code{t1}に束縛する。}
@end group

@group
3   varref   n              ; @r{環境から@code{n}の値を取得し、}
                            ;   @r{その値をスタックにプッシュする。}
@end group

@group
4   sub1                    ; @r{スタックの一番上から1を引く。}
@end group

@group
5   dup                     ; @r{スタックの一番上を複製する。}
                            ;   @r{すなわち、スタックの一番上を}
                            ;   @r{コピーし、そのコピーを}
                            ;   @r{スタックにプッシュする。}
@end group

@group
6   varset   n              ; @r{スタックの一番からポップし、}
                            ;   @r{@code{n}にその値を束縛する。}

                            ; @r{要するに、@code{dup varset}を}
                            ;   @r{続けて行うことで、スタックの}
                            ;   @r{一番上の値を、ポップせずに}
                            ;   @r{ @code{n}にコピーする。}
@end group

@group
7   constant 0              ; @r{スタックに0をプッシュする。}
@end group

@group
8   gtr                     ; @r{スタックから一番上にある}
                            ;   @r{二つの値をポップし、}
                            ;   @r{@var{n}の値が0よりも大きいか調べ}
                            ;   @r{その結果をスタックにプッシュする。}
@end group

@group
9   goto-if-nil-else-pop 17 ; @r{@code{n} <= 0ならば、17へ飛び}
                            ;   @r{(これによりループから抜ける)、}
                            ;   @r{それ以外ならば、スタックの}
                            ;   @r{一番上をポップして次に続く。}
@end group

@group
12  constant nil            ; @r{@code{nil}をスタックにプッシュする}
                            ;   @r{(これがループの本体である)。}
@end group

@group
13  discard                 ; @r{ループ本体の結果を捨てる}
                            ;   @r{(whileループは常に、}
                            ;   @r{副作用を使うために評価される)。}
@end group

@group
14  goto     3              ; @r{whileループの最初に戻る。}
@end group

@group
17  discard                 ; @r{スタックの一番上をポップすることで、}
                            ;   @r{whileループの結果を捨てる。}
                            ;   @r{この結果とは、9のgoto文では}
                            ;   @r{ポップされなかった値@code{nil}である。}
@end group

@group
18  varref   t1             ; @r{@code{t1}の値をスタックにプッシュする。}
@end group

@group
19  constant current-time-string  ; @r{@code{current-time-string}を}
                                  ;   @r{スタックの一番上に}
                                  ;   @r{プッシュする。}
@end group

@group
20  call     0              ; @r{再度@code{current-time-string}を}
                            ;   @r{呼び出す。}
@end group

@group
21  list2                   ; @r{スタックの一番上の二つの要素を}
                            ;   @r{ポップし、そのリストを作成し、}
                            ;   @r{リストをスタックにプッシュする。}
@end group

@group
22  unbind   1              ; @r{局所環境での@code{t1}の束縛を外す。}

23  return                  ; @r{スタックの一番上の値を返す。}

     @result{} nil
@end group
@end example
