バイトコード (bytecode) は、バイト指向の、中間表現のコードすなわち中間コードの総称である。バイトコードという名前は命令の構成がバイト指向であること、すなわち命令長がバイト可変長であったり、命令中のフィールドの区切りがビット単位でなくバイト単位になっているといったことから来ている。特にJavaの場合オペコードが1バイトである(Javaバイトコードの記事を参照)。しかし、仮想マシンの機械語をバイトコードと呼ぶことがJavaで広く一般的になったことから、前述のようなバイト指向でなくともバイトコードと呼んでいることも多い。バイト単位でなくビット単位の場合は、ビットコードとも呼ばれる。ワード指向のためにワードコードという語を使っている例も見られる。

存在意義 編集

バイトコードを用いる方式の利点は、ソースコードを直接解釈するインタプリタと同等の移植性を確保した上での、インタプリタのパフォーマンスの高さである(これはバイトコードに限らず、なんらかの中間表現を利用する場合全ての利点でもある。以下全て同様)。人の手で読み書きされる用途のソースコードよりもバイトコードのほうが、抽象度の低さ、データサイズなどの点で優れておりコンピュータにとって扱いやすいため、パフォーマンスは上回る。このパフォーマンス上の利点から、現在の多くのインタプリタ言語は実際にはバイトコードにコンパイルされた後、バイトコードインタプリタによって実行される。またパフォーマンス以外にも、ソースコードの中身を隠蔽する目的でバイトコードが用いられる場合がある。

バイトコードを使用した初期のコンピュータにはSystem/38があり、ソフトウェアのインストール時に機械語に変換された。またJavaのプログラムは、コンパイル済みバイトコードも利用者の環境を選ばないことから、ソースコードだけではなくバイトコードにコンパイルしたものも、標準的に配布されていることが多い。

スタックマシン vs レジスタマシン - その論点 編集

バイト指向でない中間コードもバイトコードも、その多くは、実ハードウェアの機械語と似た命令フォーマットや命令セットの構成をとっている[1]。ただし、ハードウェアで実装されたプロセッサでは比較的、スタックマシンよりレジスタマシンの方が多いのに対し、ソフトウェアによる仮想機械抽象機械ではJava仮想マシンなどスタックマシンも多いという特徴がある。なお、Luaのバージョン5やDalvik仮想マシンなど、レジスタマシンも多い。

理論的観点 編集

抽象機械」と呼ばれるような計算モデルとしての性格が強い機械の場合、理論的な扱いのためにスタックが使われているものもある。

命令の粒度 編集

レジスタマシン型命令セットではオペランドの位置を命令内で明示する。これに対しスタックマシン型命令セットでは、暗黙のうちにスタックトップをオペランドとして使い、オペランドの位置を明示する領域が不要である。目的のオペランドがスタックトップにない場合にはオペランド移動のための命令を追加する。大まかに言えば、スタックマシン命令一個当たりの長さと機能は、レジスタマシン一命令内の個々のオペランドフィールド一個、またはコードフィールドと同等である。

このためバイトコード全体の長さには大差はなさそうだが、スタックマシンでは命令順を並び換えることでオペランド移動命令を略せる場合がある。レジスタマシンではオペランド指定は省略できないので、オペランド移動を省略した分だけスタックマシンの方が同等のプログラムを短く記述できる場合が多い。又データキャッシュヒット率が高まるので、プログラムサイズ削減は実効速度改善にもなる。

しかしオペランド移動の省略を施しても、スタックマシンの方がレジスタマシンより命令数が多くなる。個々の命令が極めて単純な処理しか行わないバイトコードインタプリタでは、命令の種類を判定して分岐する処理が実行時間の大きな割合を占めるので、この点ではレジスタマシンの方が有利となる。

命令間でのオペランドの受け渡し 編集

レジスタマシン型インタプリタでは番号で仮想レジスタを指定するが、多くの実機では実行時に物理レジスタを番号で参照することができないため、メモリ配列によって仮想レジスタが実装されている場合が多い。これに対しスタックマシン型インタプリタでは、ほとんどの命令のオペランドがスタックトップに決め打ちされ、参照すべき物理レジスタをコンパイル時に決定できるので、スタックトップ数個を物理レジスタで実装している。多くの実機ではレジスタ経由でのデータ受け渡しは1クロックでできるのに対し、メモリ経由でのデータ受け渡しは数クロックを要するので、直前の命令の結果を直後の命令が使う処理が連なった場合は、レジスタマシンは不利となる。

ソースコードからバイトコードへの変換 編集

ALGOL以来、多くのプログラミング言語は文脈自由文法で記述でき、スタックマシンと類似したプッシュダウン・オートマトンで構文解析できる。このため、スタックマシン向けのコード生成器ならば、構文解析器と一体化させて省メモリ・高速なものにできる。

レジスタマシン向けの場合、有限のレジスタを使い回すレジスタ割り付けを行う必要がある。ただし、インタプリタの仮想レジスタはメモリ配列で実装されている場合が多い。このため、メモリ配列の大きさが許す限り、実機では非現実的な膨大な数の仮想レジスタを実装でき、その場合、レジスタの使い回しを省いてレジスタ割り付けを単純化することができる。レジスタ・ウィンドウも僅かなコストでレジスタ1本単位でスライドさせる柔軟なものが実装でき、スタックマシン向けと同様に構文解析器とコード生成器の一体化に役立つ。[1]

使用例 編集

編集

  1. ^ 実機ではデコーダやエンコーダといった論理回路に向いたビットパターンのほうが好まれるといった細かい傾向の違いなどはある。

関連項目 編集