Files
hakorune/docs/archive/phases/phase-10.1/c_abi_unified_design.md

5.0 KiB
Raw Blame History

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* のみに限定
  • booluint8_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静的リンク

  1. JITと同じLoweringでCLIF生成
  2. ObjectWritermod.o出力(未解決:nyrt_*/nyplug_*
  3. リンク
    • Linux/macOS: cc mod.o -static -L. -lnyrt -lnyplug_array -o app
    • Windows: link mod.obj nyrt.lib nyplug_array.lib /OUT:app.exe
  4. 実行:./appでJIT無しに動作

補足: 現行実装ではプラグインは nyash_plugin_invokeBID-FFI v1, TLVを用いる。v0ではこれを固定し、将来的に nyplug_* 直関数を併置する場合も *_abi_version() で互換を担保する。

実装順序(重要!)

  1. 必要なビルトインBoxをプラグインBoxに変換
  2. VM動作確認
  3. JIT動作確認
  4. AOT実装

⚠️ 地雷と回避策

  • 名前修飾/装飾: C++で実装するならextern "C"を絶対忘れない
  • サイズ違い: bool/size_tのプラットフォーム差 → 明示幅型で統一
  • 例外越境: C ABI越しにthrow/panic禁止。エラーコードout-paramで返す
  • 並行: JITからnyrt_mutex_*を呼ぶ箇所はSafepointとも整合するように長保持しない

📋 即実行ToDo30分で前進

  • nyrt.h最小セット確定上の雛形でOK
  • Craneliftのcall_convをターゲットに合わせて固定
  • JIT経路でnyrt_abi_version()==NYRT_ABIを起動時チェック
  • AOT試作add.oを吐いてlibnyrt.aとリンク→add()を呼ぶ最小exe

💡 まとめ

プラグインBoxのC ABIJIT/AOTの土台だから、 いまのJITが動いている静的リンクexeの最短ルートはもう目の前。 まずはnyrt.hを固定して、JITとAOTの両方で同じシンボルを呼ぶ状態にしよう。 それで**"今日のJIT"が"明日のexe"**に化けるにゃ。


最終更新: 2025-08-28