High-level Overview of the Native-Code Generation Process
Representation of Pointers
オブジェクトファイルにコードを出力する際、ポインタはリロケーションとして出力されます。デシリアライズコードは、これらの定数のいずれかを指していたオブジェクトが再作成され、正しいランタイムポインタを含むことを保証します。
そうでなければ、それらはリテラル定数として出力されます。
これらのオブジェクトのいずれかを発行するには、literal_pointer_valを呼び出します。これにより、Juliaの値とLLVMのグローバルを追跡し、現在のランタイムとデシリアライズ後の両方で有効であることが保証されます。
オブジェクトファイルに出力されると、これらのグローバルは大きな gvals テーブルに参照として格納されます。これにより、デシリアライザーはインデックスでそれらを参照し、グローバルオフセットテーブル (GOT) に似たカスタム手動メカニズムを実装してそれらを復元することができます。
関数ポインタは同様に扱われます。これらは大きな fvals テーブルに値として格納されます。グローバル変数と同様に、これによりデシリアライザはインデックスで参照することができます。
extern 関数は、通常のシンボル解決メカニズムを介してリンカーで名前が別々に処理されることに注意してください。
ccall 関数も手動の GOT および手続きリンクテーブル (PLT) を介して別途処理されることに注意してください。
Representation of Intermediate Values
値は jl_cgval_t 構造体で渡されます。これは R 値を表し、どのようにそれをどこかに割り当てたり渡したりするかを決定するのに十分な情報を含んでいます。
それらは、通常、mark_julia_type(即時値用)およびmark_julia_slot(値へのポインタ用)のいずれかのヘルパーコンストラクタを介して作成されます。
関数 convert_julia_type は、任意の2つの型間で変換を行うことができます。これは、cgval.typ が typ に設定されたR値を返します。オブジェクトを要求された表現にキャストし、必要に応じてヒープボックスを作成し、スタックコピーを割り当て、タグ付きユニオンを計算して表現を変更します。
対照的に、update_julia_typeは、ゼロコスト(つまり、コードを生成せずに)で可能な場合にのみ、cgval.typをtypに変更します。
Union representation
推論された共用体型は、タグ付き型表現を介してスタックに割り当てられる場合があります。
タグ付き共用体を処理できる必要がある基本的なルーチンは次のとおりです:
- マークタイプ
- load-local
- store-local
- isa
- is
- emit_typeof
- emit_sizeof
- ボックス化された
- アンボックス
- specialized cc-ret
他のすべては、これらのプリミティブを使用してユニオン分割を実装することで、推論で処理できるはずです。
タグ付き共用体の表現は、< void* union, byte selector > のペアとして表されます。セレクタは byte & 0x7f として固定サイズであり、最初の126のisbitsを共用体タグ付けします。これは、内部のisbitsオブジェクトの型共用体への深さ優先の1ベースのカウントを記録します。インデックスがゼロの場合、union* は実際にはタグ付きのヒープ割り当てされた jl_value_t* であり、タグ付き共用体としてではなく、ボックス化されたオブジェクトとして通常通り扱う必要があります。
セレクタの高ビット(byte & 0x80)をテストすることで、void* が実際にヒープに割り当てられた(jl_value_t*)ボックスであるかどうかを判断できるため、ボックスの再割り当てのコストを回避しつつ、低ビットに基づいて効率的にユニオン分割を処理する能力を維持できます。
byte & 0x7f は、値がタグで表現できる場合に型を正確にテストすることが保証されています - byte = 0x80 とマークされることは決してありません。isa をテストする際に型タグをテストする必要はありません。
union* メモリ領域は 任意の サイズで割り当てることができます。唯一の制約は、selector によって現在指定されているデータを含むのに十分な大きさであることです。関連する Union 型フィールドに従ってそこに格納できるすべての型のユニオンを含むには十分でない場合があります。コピーする際は適切な注意を払ってください。
Specialized Calling Convention Signature Representation
jl_returninfo_t オブジェクトは、呼び出し可能なものの特化された呼び出し規約の詳細を説明します。これは、CodeInstanceやそれが宣言されている他の場所など、任意の (specTypes, rettype) ペアから生成できます。これは specptr に対する期待される呼び出し規約ですが、他のデータがそこに保存される場合もあります。そこに保存されている関数ポインタが期待される特化された呼び出し規約を持っている場合にのみ、対応するフラグが specsigflags に設定され、使用可能であることを示します。
メソッドの引数または戻り値の型のいずれかがアンボックスされた形で表現でき、かつアンボックスされた形で表現できないもの(例えば、制限のない可変引数)がない場合、specTypesおよびrettypeの値に基づいて最適化された呼び出し規約のシグネチャが与えられます。
一般的な原則は次のとおりです:
- プリミティブ型はint/floatレジスタに渡されます。
- VecElement型のタプルはベクタレジスタに渡されます。
- 構造体はスタック上で渡されます。
- 戻り値は引数と同様に扱われ、サイズのカットオフがあり、そのサイズを超える場合は隠れたsret引数を介して返されます。
この全体のロジックは get_specsig_function と deserves_sret によって実装されています。
さらに、戻り値の型が共用体の場合、値のペア(ポインタとタグ)として返されることがあります。共用体の値がスタックに割り当て可能な場合、それらを格納するのに十分なスペースも隠れた最初の引数として渡されます。戻す構造体がガベージコレクションのルートを必要とする場合、それらのためのスペースは隠れた2番目の引数として渡されます。戻されたポインタがこのスペース、ボックス化されたオブジェクト、または他の定数メモリを指すかどうかは、呼び出し側の判断に委ねられます。