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

158 lines
5.0 KiB
Markdown
Raw Normal View 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*` のみに限定
- `bool``uint8_t`統一、構造体は不透明ポインタ(ハンドル)
- `varargs`と例外のABI横断は**禁止**(戻り値でエラーコード/out-paramで返す
### 3. レイアウト/アライン
```c
// 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コアランタイム
```c
#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
```c
#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. **ObjectWriter**で`mod.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_invoke`BID-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*