バイトコード(ポータブルコードまたはPコードとも呼ばれる)は、ソフトウェアインタープリタによる効率的な実行のために設計された命令セットの一種です。人間が読める[1]ソースコードとは異なり、バイトコードはコンパクトな数値コード、定数、参照(通常は数値アドレス)で構成され、コンパイラによる解析結果と、プログラムオブジェクトの型、スコープ、ネストの深さなどの 意味解析結果をエンコードします。
バイトコードという名称は、 1バイトの オペコードとそれに続くオプションパラメータを持つ命令セットに由来しています。バイトコードのような中間表現は、プログラミング言語の実装によって解釈を容易にするために出力される場合もあれば、同じコードをクロスプラットフォーム(異なるデバイス)で実行できるようにすることでハードウェアやオペレーティングシステムへの依存を軽減するために使用される場合もあります。バイトコードは、仮想マシン(Pコードマシン、つまりインタプリタ)上で直接実行される場合もあれば、パフォーマンス向上のためにマシンコードにコンパイルされる場合もあります。
バイトコード命令はソフトウェアによって処理されるため、任意の複雑さになる可能性がありますが、それでも従来のハードウェア命令に類似していることがよくあります。仮想スタックマシンが最も一般的ですが、仮想レジスタマシンも構築されています。[2] [3]異なる部分は、オブジェクトモジュールと同様に別々のファイルに格納されることがよくありますが、実行中に動的にロードされます。
バイトコードプログラムは、命令を1つずつ解析して直接実行することで実行できます。この種のバイトコードインタープリタは非常に移植性に優れています。動的トランスレータやジャストインタイム(JIT)コンパイラと呼ばれる一部のシステムは、実行時に必要に応じてバイトコードをマシンコードに変換します。これにより、仮想マシンはハードウェア固有になりますが、バイトコードの移植性は失われません。たとえば、JavaやSmalltalkのコードは通常バイトコード形式で保存され、実行前にJITコンパイルされてバイトコードをマシンコードに変換されます。これにより、バイトコードがネイティブマシンコードにコンパイルされる際にプログラム実行前に遅延が発生しますが、ソースコードを直接解釈する場合と比較して実行速度が大幅に向上します。通常は約1桁(10倍)向上します。[4]
パフォーマンス上の利点のため、今日では多くの言語実装がプログラムを2段階で実行します。まずソースコードをバイトコードにコンパイルし、次にそのバイトコードを仮想マシンに渡します。Java 、Raku、Python、PHP、[a] Tcl、mawk、Forthなどには、この種のバイトコードベースの仮想マシンが存在します(ただし、Forthはこのようにバイトコード経由でコンパイルされることは稀で、その仮想マシンはより汎用的です)。PerlとRuby 1.8の実装では、ソースコードから導出された 抽象構文木表現を辿ることで動作します。
最近では、 V8 [1]とDart [7]の作者たちが、高速かつ効率的な VM 実装には中間バイトコードが必要であるという考えに異議を唱えています。現在、これらの言語実装はどちらも、バイトコードを介さずにソースコードからマシンコードへの直接 JIT コンパイルを行っています。[8]
disassemble関数[10]が用意されています。結果は実装依存であり、バイトコードに解決される場合とされない場合があります。この関数の検査は、デバッグや最適化に利用できます。 [11] 例えば、Steel Bank Common Lispでは、以下のコードが生成されます。( disassemble ' ( lambda ( x ) ( print x ))) ; (LAMBDA (X)) の逆アセンブリ; 2436F6DF: 850500000F22 TEST EAX, [#x220F0000] ; 引数解析なしのエントリ ポイント; E5: 8BD6 MOV EDX, ESI ; E7: 8B05A8F63624 MOV EAX, [#x2436F6A8] ; #<PRINT の FDEFINITION オブジェクト> ; ED: B904000000 MOV ECX, 4 ; F2: FF7504 PUSH DWORD PTR [EBP+4] ; F5: FF6005 JMP DWORD PTR [EAX+5] ; F8: CC0A BREAK 10 ;エラー トラップ; FA: 02 バイト #X02 ; FB: 18 バイト #X18; INVALID-ARG-COUNT-ERROR ; FC: 4F バイト #X4F; ECX
>>> import dis # "dis" - Pythonバイトコードをニーモニックに逆アセンブラーします。>>> dis . dis ( 'print("Hello, World!")' ) 1 0 LOAD_NAME 0 (print) 2 LOAD_CONST 0 ('Hello, World!') 4 CALL_FUNCTION 1 6 RETURN_VALUE
[…] 実際、このフォーマットは、ロシア語、リトアニア語、中国語、日本語版を含む
MS-DOS
3.3 - 8.0、
PC DOS
3.3 - 2000、そしてWindows NT、2000、XPでも基本的に同じです[…]。細かな違いや非互換性はありますが、基本的なフォーマットは長年変わっていません[…]。[…] 一部のデータエントリには通常のテーブルが含まれています[…] ただし、ほとんどのエントリには、条件分岐などを含む、
何らかの
pコードインタープリタによって*
実行時
に*解釈される
実行コード
が含まれています。このため、
KEYB
ドライバーは、インタープリターを除けば同じレベルの機能を実現するのに 3 ~ 4 KB で済むテーブル駆動型キーボード ドライバーと比べて、非常に大きなメモリ フットプリントを持ちます。[…]
[…] Matthias [R.] Paul […] は、
IBM PC DOS
版のキーボード ドライバーは、
Microsoft
ドライバーでは認識されない内部プロシージャを使用しているため、可能であれば、
KEYB.COM
と
KEYBOARD.SYSの両方について、Microsoft 版と IBM 版を混在させるのではなく、
IBM
版の両方を使用する必要があると警告しています
[…]
(注: ここでの「手順」とは、Microsoft バージョンの KEYB ドライバーではサポートされていない、IBM KEYBOARD.SYS ファイル内の追加のバイトコードを意味します。)
Multiplanは
マシンコード
ではなく、
インタープリタ
によって実行される一種のバイトコードにコンパイルされました
。これは、当時の多種多様なハードウェア間でMultiplanを移植可能にするためでした。このバイトコードは、計算に使用するマシン固有の
浮動小数点形式
と、外部(標準)形式である
2進化10進数
(BCD)を区別していました。PACK命令とUNPACK命令は、この2つの形式間の変換を行いました。