Julia のネイティブコード生成の概要

ポインタの表現

コードをオブジェクトファイルに書き出すとき、ポインタはリロケーション情報として書き出されます。そのためコードのデシリアライズで再生成されたオブジェクトに定数の参照が含まれる場合でも、その参照は実行時の正しいオブジェクトを指すことが保証されます。

ポインタ以外のオブジェクトはリテラルの定数として書き出されます。

ポインタのオブジェクトを書き出すのは literal_pointer_val です。この関数は Julia の値と LLVM のグローバル変数を適切に追跡し、オブジェクトは現在のランタイムとデシリアライズ後のランタイムの両方で正当となります。

オブジェクトに書き出されるとき、こういったグローバル変数は一つの大きな gvals テーブルに参照として格納されます。これによりデシリアライザは添え字で変数を参照でき、さらにオブジェクトを書き戻すときに Global Offset Table (GOT) に似た独自のマニュアルメカニズムを実装できるようになります。

関数ポインタも同様に処理されます。関数ポインタは値として一つの大きな fvals テーブルに格納され、グローバル変数と同様にデシリアライザからは添え字で参照できます。

extern の関数は個別に処理されることに注意してください。名前が割り当てられ、リンカが持つ通常のシンボル解決機構が使われます。

ccall 関数も個別に処理されることにも注意してください。マニュアルの GOT と Procedure Linkage Table (PLT) が使われます。

LLVM IR に含まれる値の表現

値は jl_cgval_t 構造体でやり取りされます。この構造体は右辺値を表現し、他の場所に代入したり渡したりする方法を決定するのに十分な情報を持ちます。

jl_cgval_t は通常ヘルパーコンストラクタ mark_julia_type または mark_julia_slot を通して作られます。mark_julia_type は即値用で、mark_julia_slot は値を指すポインタ用です。

convert_julia_type 関数を使うと任意の二つの型の間で変換が可能です。cgval.typtyp となった右辺値が返ります。この関数はオブジェクトを要求された型にキャストするときにヒープボックスを作り、スタックのコピーをアロケートし、さらに表現の変更で必要な場合はタグ付き共用体を計算します。

これに対して update_julia_type はキャストがゼロコストで (コードを生成せずに) 行えるときに限って cgval.typtyp に変更します。

タグを使った共用体の表現

型が型共用体と推論された値であっても、場合によってはタグの付いた型表現を使ってスタックにアロケートできることがあります。

タグ付き共用体を処理できる必要があるプリミティブルーチンは次の通りです:

これらのプリミティブを使って共用体の分割を実装することで、他のルーチンは推論中に処理できるはずです。

タグ付き共用体は <void* union, byte selector> という組で表現されます。selector は固定長であり、selector & 0x7f が共用体に含まれる最初の 126 個の isbits 型のいずれかを選択するタグとなります。この値は型共用体に対する 1 始まりで深い方から数えた isbits オブジェクトのカウントを表します。添え字 0union* が実際にはタグの付いたヒープアロケートの jl_value_t* であり、タグ付き共用体ではなくボックス化オブジェクトとして通常と同じ処理を行う必要があることを示します。

selector の最上位ビット (selector & 0x80) を調べれば、union がヒープアロケートの (jl_value_t* の) ボックスかどうかを判定できます。この情報により、下位ビットで共用体の分割を効率良く処理しながらもボックスの再アロケートにかかるコストを避けることができます。

値がタグで表現できる限り、byte & 0x7f が型に対する正確なテストであることは保証されています ──selector = 0x80 にはなりません。そのため isa をテストするときに型タグをテストする必要はありません。

union が指すメモリ領域のサイズは決まっておらず、唯一の制限は現在の selector が指定するデータを保持できることだけです。関連する共用体のフィールドによっては全ての型を一度に保持できるほどの大きさを持たない可能性もあるので、コピーするときには注意が必要です。

特殊化された呼び出し規約シグネチャの表現

jl_returninfo_t オブジェクトは任意の呼び出し可能オブジェクトの呼び出し規約に関する詳細を表します。

メソッドの引数あるいは返り値のいずれかがボックス化を解除した状態で表現できて、かつメソッドが可変長引数を取らないなら、その引数あるいは返り値には specTypesrettype フィールドに基づいて最適化された呼び出し規約シグネチャが与えられます。

一般的な原則は次の通りです:

この処理は get_specsig_functiondeserves_sret が行います。

また返り値が共用体のときは値の組 (ポインタとタグ) が返る場合があります。その共用体の値がスタックアロケート可能なら、その値を保持できるだけの空間が隠れた第一引数として関数に渡されます。関数から返るポインタが引数に渡されるこの空間を指すのか、それともボックス化オブジェクトまたはその他の定数メモリを指すのかは呼び出し側が決められます。

広告