バイトコードについて

Taichi Ishitani edited this page Aug 31, 2016 · 2 revisions

バイトコードへのコンパイル

mrbcコマンドを使うことで、rubyのソースコードをVMが使うバイトコードにコンパイルできます。

$ mrbc sample.rb

上記のコマンドを実行すると、バイトコードがsample.mrbとして出力されます。


バイトコードのフォーマット

VM用のバイトコードは、バイトコードの長さやバージョンなどを記すバイナリヘッダーと、実際の命令コードや、デバッグ情報などを保持するセクションからなり、IREP、LINENO、DEBUG、LVAR、EOFの計5種類のセクションがあります。バイナリヘッダーの後に複数個のセクションが続き、EOFセクションが末尾のセクションとなります。

+-------------+
|Binary Header|
+-------------+
|Section 0    |
+-------------+
|Section 1    |
+-------------+
|....         |
+-------------+
|EOF Section  |
+-------------+

バイナリヘッダー

バイナリヘッダーは以下の構成になっています。

Field Name Size(byte) Type
identifier 4 char*
version 4 char*
crc 2 uint16
size 4 size_t
compiler_name 4 char*
compiler_version 4 char*

identifier

文字列で"RITE"または"ETIR"が埋め込まれています。このフィールドの値を調べることで、バイトコードのエンディアンを調べることが出来ます。

version

バイトデータのフォーマットのバージョンが文字列で埋め込まれています。1.2の時点では、"0003"をとります。

crc

バイトコード(バイナリヘッダーのidentifier、version、crcを除く)から計算されるCRCです。CRC-16-CCITTに則って計算されます。

size

バイナリヘッダーを含むバイトコード全体のバイト長です。

compiler_name

バイトコードのコンパイルに使われたコンパイラーの識別子が文字列で埋め込まれています。1.2の時点では、"MATZ"をとります。

compiler_version

バイトコードのコンパイルに使われたコンパイラーのバージョンが文字列で埋め込まれています。1.2の時点では、"0000"をとります。


セクションヘッダー

全てのセクションはセクションヘッダーから始まります。セクションヘッダーは以下の構造になっています。

Field Name Size(byte) Type
identifier 4 char*
size 4 size_t

identifier

セクションの種類を示す識別子です。以下の文字列が埋め込まれています。

Section Value
IREP "IREP"
LINENO "LINE"
DEBUG "DBG\0"
LVAR "LVAR"
EOF "END\0"

size

セクションヘッダーも含むセクション全体のバイト長です。


IREQセクション

実行コードを保持するセクションです。IREPセクションヘッダーと複数個のIREPレコードからなります。

IREPセクションヘッダー

通常のセクションヘッダーのフィールドのほかに、命令セットのバージョンを示すフィールドが含まれます。通常のセクションヘッダーのフィールドを含め、以下の構造になっています。

Field Name Size(byte) Type
identifier 4 char*
size 4 size_t
version 4 char*
version

命令セットのバージョンが文字列で埋め込まれています。1.2の時点では、"0000"をとります。

IREPレコード

サブルーチン(メソッド、ブロック?)についての情報を保持します。命令コードのほかに、必要なレジスタ数、従属するIREPレコードの個数などを保持します。IREPレコードは以下の構造を持ちます。

Field Name Size(byte) Type
size 4 size_t
nlocals 2 uint16
nregs 2 uint16
rlen 2 uint16
ilen 4 size_t
padding 0-3 N/A
iseq 4 * ilen uint32*
plen 4 size_t
pool
slen 4 size_t
syms

size

IREPレコードのバイト長です。

nlocals

IREPが使うローカル変数の個数です。

nregs

IREPが使うレジスタの個数です。

rlen

現在のIREPが従属するIREPの個数です。

padding

iseqを4バイトアラインさせるためのパディングビットです。

ilen/iseq

IREP内で実行する命令コードの配列で、要素数はilenです。また、命令コードについてはこちらを参照してください。

plen/pool

IREP内で使用するリテラルを保持する配列で、配列の要素数はplenです。リテラルの種類によらず、文字列として埋め込まれています。1つの要素は以下の様な構造になっています。

Field Size(byte) Type
tt 1 uint8
len 2 uint16
s len char*
tt

リテラルの種類を示します。以下の値をとります。

  • 0: 文字列
  • 1: 整数
  • 2: 実数
len

リテラルを保持する文字列の長さです。

s

リテラルを保持する文字列です。ttの値に応じて、この文字列を変換することで、リテラルの値を得ることが出来ます。

slen/syms

IREP内で使用するシンボル(メソッド名やシンボルリテラル等)を保持する配列で、配列の要素数はslenです。シンボル名が文字列として埋め込まれており、シンボル名からシンボル値を計算します。計算方法は実装依存です。1つの要素は以下の様な構造になっています。

Field Size(byte) Type
snl 2 uint16
sym snl + 1 char*
snl

シンボル名を保持する文字列の長さです。

sym

シンボル名を保持する文字列で、末尾は終端文字("\0")になります。


LINENOセクション

TBW


DEBUGセクション

TBW


LVARセクション

IREPレコード内で使用する全てのローカル変数名と、対応するレジスタ番号を紐付けるためのテーブルを保持しているセクションです。このテーブルを各IREPレコードが持つnlocals毎に分割することで、対応するIREPレコードと紐付けされます。また、このテーブルを使うことで、eval系メソッドでRubyのコード片文字列を実行することが出来ます(mruby-evalを参照)。LVARセクションは以下の様な構造になっています。

Field Size(byte) Type
syms_len 4 uint32
syms
lv 2 * syms_len uint16*

syms_len

LVARセクションが保持するローカル変数の個数です。

syms

LVARセクションが保持するローカル変数名の配列で、要素数はsyms_lenです。1つの要素は以下の様な構造になっています。

Field Size(byte) Type
len 2 uint16
name len char*
len

ローカル変数名の長さです。

name

ローカル変数名が埋め込まれている文字列です。

lv

ローカル変数に対応するレジスタ番号を保持する配列です。


EOFセクション

バイトコードの末尾を示すセクションです。セクションヘッダー以外の内容物はありません。

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.