5.0 KiB
5.0 KiB
C ABI統一設計 - JIT/AOT共通基盤
ChatGPT5さんからのアドバイスに基づく設計文書
🎯 核心的洞察
プラグインBoxのC ABI = そのままJIT/AOTの呼び出し土台
JITで今呼んでいるC ABIをAOTでは静的リンクに差し替えるだけでexe化まで一直線!
📊 全体アーキテクチャ
Nyash → MIR → VM/JIT/Cranelift ──┐
├─ 呼ぶ先は全部 C ABI: nyrt_* / nyplug_*
NyRT (libnyrt.a/.so) ←──────────┘
PluginBox 実装 (libnyplug_*.a/.so)
- JIT:
extern "C"シンボル(nyrt_*/nyplug_*)をその場で呼ぶ - AOT: 同じシンボルを
.oに未解決のまま出力→libnyrt.aとプラグイン.aをリンク - 動的配布:
.so/.dllに差し替え(同じC ABIでOK)
🔧 C ABIルール(小さく強い)
1. 命名/可視性
- コア:
nyrt_*(Box/weak/bus/gc/sync/alloc...) - プラグイン:
nyplug_{name}_*(ArrayBox, StringBox など) extern "C"+ 明示の可視性(ELF:__attribute__((visibility("default"))))
2. ABIの型
- 引数/戻り:
int32_t/int64_t/uint64_t/double/void*のみに限定 boolはuint8_t統一、構造体は不透明ポインタ(ハンドル)varargsと例外のABI横断は禁止(戻り値でエラーコード/out-paramで返す)
3. レイアウト/アライン
// Boxハンドル例
struct NyBox {
void* data;
uint64_t typeid;
uint32_t flags;
uint32_t gen;
};
※JIT側は中身に触らない。操作はAPI経由のみ。
4. 呼び出し規約
- x86_64 SysV / aarch64 AAPCS64 / Win64 をターゲットごとに固定
- Craneliftの
call_convを上記に合わせる(JIT/AOT共通)
5. バージョン管理(fail-fast)
nyrt_abi_version()/nyplug_{name}_abi_version()(v0=1)。不一致は起動時に即fail(ローダ側で検査)。
📝 最小ヘッダ雛形
nyrt.h(コアランタイム)
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct NyBox {
void* data;
uint64_t typeid;
uint32_t flags;
uint32_t gen;
} NyBox;
int32_t nyrt_abi_version(void);
// Box/weak
NyBox nyrt_box_new(uint64_t typeid, uint64_t size);
void nyrt_box_free(NyBox b);
int32_t nyrt_adopt(NyBox parent, NyBox child);
int32_t nyrt_release(NyBox parent, NyBox child, NyBox* out_weak);
NyBox nyrt_weak_load(NyBox weak); // gen一致ならBox, 失効なら{0}
// GC/epoch
void nyrt_epoch_collect(void);
// Sync(最低限)
void* nyrt_mutex_lock(NyBox sync);
void nyrt_mutex_unlock(void* guard);
// Bus
int32_t nyrt_bus_send(NyBox port, NyBox msg);
#ifdef __cplusplus
}
#endif
nyplug_array.h(プラグイン例:ArrayBox)
#pragma once
#include "nyrt.h"
#ifdef __cplusplus
extern "C" {
#endif
int32_t nyplug_array_abi_version(void);
NyBox nyplug_array_new(void);
int32_t nyplug_array_get(NyBox arr, uint64_t i, NyBox* out);
int32_t nyplug_array_set(NyBox arr, uint64_t i, NyBox v);
uint64_t nyplug_array_len(NyBox arr);
int32_t nyplug_array_push(NyBox arr, NyBox v);
#ifdef __cplusplus
}
#endif
🚀 ビルド・配布フロー(AOT静的リンク)
- JITと同じLoweringでCLIF生成
- ObjectWriterで
mod.o出力(未解決:nyrt_*/nyplug_*) - リンク
- Linux/macOS:
cc mod.o -static -L. -lnyrt -lnyplug_array -o app - Windows:
link mod.obj nyrt.lib nyplug_array.lib /OUT:app.exe
- Linux/macOS:
- 実行:
./appでJIT無しに動作
補足: 現行実装ではプラグインは nyash_plugin_invoke(BID-FFI v1, TLV)を用いる。v0ではこれを固定し、将来的に nyplug_* 直関数を併置する場合も *_abi_version() で互換を担保する。
⚡ 実装順序(重要!)
- 必要なビルトインBoxをプラグインBoxに変換
- VM動作確認
- JIT動作確認
- AOT実装
⚠️ 地雷と回避策
- 名前修飾/装飾: C++で実装するなら
extern "C"を絶対忘れない - サイズ違い:
bool/size_tのプラットフォーム差 → 明示幅型で統一 - 例外越境: C ABI越しに
throw/panic禁止。エラーコード+out-paramで返す - 並行: JITから
nyrt_mutex_*を呼ぶ箇所はSafepointとも整合するように(長保持しない)
📋 即実行ToDo(30分で前進)
nyrt.h最小セット確定(上の雛形でOK)- Craneliftの
call_convをターゲットに合わせて固定 - JIT経路で
nyrt_abi_version()==NYRT_ABIを起動時チェック - AOT試作:
add.oを吐いてlibnyrt.aとリンク→add()を呼ぶ最小exe
💡 まとめ
プラグインBoxのC ABI=JIT/AOTの土台だから、 いまのJITが動いている=静的リンクexeの最短ルートはもう目の前。 まずは
nyrt.hを固定して、JITとAOTの両方で同じシンボルを呼ぶ状態にしよう。 それで**"今日のJIT"が"明日のexe"**に化けるにゃ。
最終更新: 2025-08-28