nccc: エンディアン独立に向けたAPI/ABI調整
現在のNCCCでは、全てのC関数が uint64_t*
を受けて uint64_t*
に結果を書き込むというルールにしている。これだと float
のような型をやりとりするケースでCやC++のStrict aliasing ruleに反しているし、さらにビッグエンディアン環境では結果が曖昧なのでC/C++標準に適合したやりかたに変えたい。
union
とメンバのアクティベーション
簡単には、
union nccv64_u {
uint8_t u8;
int8_t s8;
:
uintptr_t uptr;
intptr_t sptr;
float f32;
double f64;
};
typedef union nccv64_u nccv64;
のような union
を定義して、それ経由でアクセスすることで目的を達成できる。ただしC++にはunionのメンバに "アクティブ" の概念があり、
-
union
のインスタンスは1度に1つのアクティブメンバしか持てない - メンバに対して書き込みを行うとそのメンバがアクティブになる
- アクティブでないメンバからの読み取りは未定義挙動である
というルールがある。更にアクティブなメンバの切り替えを constexpr
内で行えないなど微妙な制約があった(C++20で無くなった)。
(この違いにより、Cでは union
をtype punningのために使えるが、C++ではC++20の bit_cast
まで良い方法が無かった。
従来は void*
を確保して memcpy
するか、普通にunsafeにキャストして no-strict-aliasing
にコンパイルするという方法が使われていた。NCCCのユースケースでは、書き込み型と読み取り型は常に一致するのでこの問題はない。)
モジュールroot
NCCC経由で呼出せるAPIの集合を "NCCCコレクション" と呼ぶ。コレクションは名前で区別される複数のNCCCライブラリを含められる。コレクションの配置方法には複数あり:
- module: DLLやShared libraryの形で提供される。
<NCCC_MODULE_PREFIX>/nccc_<collection_name>.dll
のように配置される。これらのDLLは<collection_name>_nccc_root_00
のみ エクスポートする - embedded: 処理系に内蔵される。処理系依存の方法で
<collection_name>_nccc_root_00
に相当するポインタを取得できるようになっている
さらに特殊なケースとしてbootstrapがある。bootstrapはNCCCのサブセットであり、ABIの取得に使用される。また、RibbonやDuktapeのような標準でFFI機能を備えていない処理系ではFFI機能自体がNCCCで提供されるため、そこでもbootstrapが使用される。
CMake統合
NCCCコレクションやそれに含まれるライブラリはCMakeでビルドされることが前提となっている。モジュールrootやstubに相当するコードはビルド時にCMakeスクリプトによって生成される。
NCCC_HOST_DEV
(未実装: コレクションを /usr/lib
等にインストールする場合に偽にする)
NCCC_LOCATION_<COLLECTION>
コレクションの配置方法を選択する。 EMBEDDED
の場合は組込用のライブラリターゲット nccc_embedded_libs
に OBJECT
ライブラリとして生成される。未設定の場合は MODULE
ライブラリとなる。
convert_nccc_stub(stubfile filename)
スタブ関数を含んだCソースコードを生成する。 stubfile
に記述された内容に従ってNCCCラッパーを作成する。
add_nccc_library(tgt src_or_lib ...)
NCCCライブラリを表すCMakeターゲットを生成する。ターゲットは OBJECT
ライブラリとなる。
add_nccc_collection(tgt lib ...)
NCCCコレクションを表すCMakeターゲットを生成する。
型とABI bootstrap
NCCCはWebAssemblyを意識しているものの、型システムは大きく異なる。NCCCには符号があり、短いサイズの整数型も規定している(C構造体を表現する必要性から)。
型は番号で区別される:
#define CITYPE_VALUE_u32 0
#define CITYPE_VALUE_u64 1
#define CITYPE_VALUE_f32 2
#define CITYPE_VALUE_f64 3
#define CITYPE_VALUE_s32 4
#define CITYPE_VALUE_s64 5
#define CITYPE_VALUE_ptr 6
#define CITYPE_VALUE_uptr 7
#define CITYPE_VALUE_sptr 8
#define CITYPE_VALUE_u8 9
#define CITYPE_VALUE_u16 10
#define CITYPE_VALUE_s8 11
#define CITYPE_VALUE_s16 12
ポインタ型は都合3種類ある。 ptr
はアドレスを格納するためのポインタ型、 uptr
および sptr
はアドレスと同じ巾の整数を格納するための整数型となる。
ABI bootstrap
ブートストラップライブラリは特殊なNCCCライブラリで、NCCC呼出しを行うために必要なプリミティブを提供する。bootstrapは1つのNCCC関数のみ提供する。
get_abi_info [u64] => [u64]
get_abi_info
はABIの情報を表わす 8bit整数の配列 を返却する。サブコードとして64bit整数を与える。ポインタは(32bitsアーキテクチャであっても)64bit整数で返却する。
サブコード 0 = verinfo
は、文字列 NCCC64v0\0
となる。
サブコード 1 = typeinfo
は、型の数だけ以下のデータを繰り返す。
<bytelen> <typeid> <offset> <coretype>
bytelen
は 1: 8bit 、 2: 16bit 、 4: 32bit 、 8: 64bit となる。ゼロは終端を表す。
typeid
はその型に与えられた型IDとなる。
offset
は64bit長の値フィールドにデータを格納する際のオフセットとなる。 エンディアンを表現するものではない 点に注意。
coretype
は 1: 符号無整数、2: 符号付整数、 3: 浮動小数点数 、 4: ポインタ となる。